Fix keymap.
[vuplus_xbmc] / xbmc / guilib / GUIWindowManager.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "GUIWindowManager.h"
22 #include "GUIAudioManager.h"
23 #include "GUIDialog.h"
24 #include "Application.h"
25 #include "ApplicationMessenger.h"
26 #include "GUIPassword.h"
27 #include "GUIInfoManager.h"
28 #include "threads/SingleLock.h"
29 #include "utils/URIUtils.h"
30 #include "settings/AdvancedSettings.h"
31 #include "settings/Settings.h"
32 #include "addons/Skin.h"
33 #include "GUITexture.h"
34 #include "windowing/WindowingFactory.h"
35 #include "utils/Variant.h"
36 #include "Key.h"
37
38 using namespace std;
39
40 CGUIWindowManager::CGUIWindowManager(void)
41 {
42   m_pCallback = NULL;
43   m_bShowOverlay = true;
44   m_iNested = 0;
45   m_initialized = false;
46 }
47
48 CGUIWindowManager::~CGUIWindowManager(void)
49 {
50 }
51
52 void CGUIWindowManager::Initialize()
53 {
54   m_tracker.SelectAlgorithm();
55   m_initialized = true;
56
57   LoadNotOnDemandWindows();
58 }
59
60 bool CGUIWindowManager::SendMessage(int message, int senderID, int destID, int param1, int param2)
61 {
62   CGUIMessage msg(message, senderID, destID, param1, param2);
63   return SendMessage(msg);
64 }
65
66 bool CGUIWindowManager::SendMessage(CGUIMessage& message)
67 {
68   bool handled = false;
69 //  CLog::Log(LOGDEBUG,"SendMessage: mess=%d send=%d control=%d param1=%d", message.GetMessage(), message.GetSenderId(), message.GetControlId(), message.GetParam1());
70   // Send the message to all none window targets
71   for (int i = 0; i < (int) m_vecMsgTargets.size(); i++)
72   {
73     IMsgTargetCallback* pMsgTarget = m_vecMsgTargets[i];
74
75     if (pMsgTarget)
76     {
77       if (pMsgTarget->OnMessage( message )) handled = true;
78     }
79   }
80
81   //  A GUI_MSG_NOTIFY_ALL is send to any active modal dialog
82   //  and all windows whether they are active or not
83   if (message.GetMessage()==GUI_MSG_NOTIFY_ALL)
84   {
85     CSingleLock lock(g_graphicsContext);
86     for (rDialog it = m_activeDialogs.rbegin(); it != m_activeDialogs.rend(); ++it)
87     {
88       CGUIWindow *dialog = *it;
89       dialog->OnMessage(message);
90     }
91
92     for (WindowMap::iterator it = m_mapWindows.begin(); it != m_mapWindows.end(); ++it)
93     {
94       CGUIWindow *pWindow = (*it).second;
95       pWindow->OnMessage(message);
96     }
97     return true;
98   }
99
100   // Normal messages are sent to:
101   // 1. All active modeless dialogs
102   // 2. The topmost dialog that accepts the message
103   // 3. The underlying window (only if it is the sender or receiver if a modal dialog is active)
104
105   bool hasModalDialog(false);
106   bool modalAcceptedMessage(false);
107   // don't use an iterator for this loop, as some messages mean that m_activeDialogs is altered,
108   // which will invalidate any iterator
109   CSingleLock lock(g_graphicsContext);
110   unsigned int topWindow = m_activeDialogs.size();
111   while (topWindow)
112   {
113     CGUIWindow* dialog = m_activeDialogs[--topWindow];
114     lock.Leave();
115     if (!modalAcceptedMessage && dialog->IsModalDialog())
116     { // modal window
117       hasModalDialog = true;
118       if (!modalAcceptedMessage && dialog->OnMessage( message ))
119       {
120         modalAcceptedMessage = handled = true;
121       }
122     }
123     else if (!dialog->IsModalDialog())
124     { // modeless
125       if (dialog->OnMessage( message ))
126         handled = true;
127     }
128     lock.Enter();
129     if (topWindow > m_activeDialogs.size())
130       topWindow = m_activeDialogs.size();
131   }
132   lock.Leave();
133
134   // now send to the underlying window
135   CGUIWindow* window = GetWindow(GetActiveWindow());
136   if (window)
137   {
138     if (hasModalDialog)
139     {
140       // only send the message to the underlying window if it's the recipient
141       // or sender (or we have no sender)
142       if (message.GetSenderId() == window->GetID() ||
143           message.GetControlId() == window->GetID() ||
144           message.GetSenderId() == 0 )
145       {
146         if (window->OnMessage(message)) handled = true;
147       }
148     }
149     else
150     {
151       if (window->OnMessage(message)) handled = true;
152     }
153   }
154   return handled;
155 }
156
157 bool CGUIWindowManager::SendMessage(CGUIMessage& message, int window)
158 {
159   if (window == 0)
160     // send to no specified windows.
161     return SendMessage(message);
162   CGUIWindow* pWindow = GetWindow(window);
163   if(pWindow)
164     return pWindow->OnMessage(message);
165   else
166     return false;
167 }
168
169 void CGUIWindowManager::AddUniqueInstance(CGUIWindow *window)
170 {
171   CSingleLock lock(g_graphicsContext);
172   // increment our instance (upper word of windowID)
173   // until we get a window we don't have
174   int instance = 0;
175   while (GetWindow(window->GetID()))
176     window->SetID(window->GetID() + (++instance << 16));
177   Add(window);
178 }
179
180 void CGUIWindowManager::Add(CGUIWindow* pWindow)
181 {
182   if (!pWindow)
183   {
184     CLog::Log(LOGERROR, "Attempted to add a NULL window pointer to the window manager.");
185     return;
186   }
187   // push back all the windows if there are more than one covered by this class
188   CSingleLock lock(g_graphicsContext);
189   m_idCache.Invalidate();
190   const vector<int>& idRange = pWindow->GetIDRange();
191   for (vector<int>::const_iterator idIt = idRange.begin(); idIt != idRange.end() ; ++idIt)
192   {
193     WindowMap::iterator it = m_mapWindows.find(*idIt);
194     if (it != m_mapWindows.end())
195     {
196       CLog::Log(LOGERROR, "Error, trying to add a second window with id %u "
197                           "to the window manager", *idIt);
198       return;
199     }
200     m_mapWindows.insert(pair<int, CGUIWindow *>(*idIt, pWindow));
201   }
202 }
203
204 void CGUIWindowManager::AddCustomWindow(CGUIWindow* pWindow)
205 {
206   CSingleLock lock(g_graphicsContext);
207   Add(pWindow);
208   m_vecCustomWindows.push_back(pWindow);
209 }
210
211 void CGUIWindowManager::AddModeless(CGUIWindow* dialog)
212 {
213   CSingleLock lock(g_graphicsContext);
214   // only add the window if it's not already added
215   for (iDialog it = m_activeDialogs.begin(); it != m_activeDialogs.end(); ++it)
216     if (*it == dialog) return;
217   m_activeDialogs.push_back(dialog);
218 }
219
220 void CGUIWindowManager::Remove(int id)
221 {
222   CSingleLock lock(g_graphicsContext);
223   m_idCache.Invalidate();
224   WindowMap::iterator it = m_mapWindows.find(id);
225   if (it != m_mapWindows.end())
226   {
227     for(vector<CGUIWindow*>::iterator it2 = m_activeDialogs.begin(); it2 != m_activeDialogs.end();)
228     {
229       if(*it2 == it->second)
230         it2 = m_activeDialogs.erase(it2);
231       else
232         ++it2;
233     }
234
235     m_mapWindows.erase(it);
236   }
237   else
238   {
239     CLog::Log(LOGWARNING, "Attempted to remove window %u "
240                           "from the window manager when it didn't exist",
241               id);
242   }
243 }
244
245 // removes and deletes the window.  Should only be called
246 // from the class that created the window using new.
247 void CGUIWindowManager::Delete(int id)
248 {
249   CSingleLock lock(g_graphicsContext);
250   CGUIWindow *pWindow = GetWindow(id);
251   if (pWindow)
252   {
253     Remove(id);
254     m_deleteWindows.push_back(pWindow);
255   }
256 }
257
258 void CGUIWindowManager::PreviousWindow()
259 {
260   // deactivate any window
261   CSingleLock lock(g_graphicsContext);
262   CLog::Log(LOGDEBUG,"CGUIWindowManager::PreviousWindow: Deactivate");
263   int currentWindow = GetActiveWindow();
264   CGUIWindow *pCurrentWindow = GetWindow(currentWindow);
265   if (!pCurrentWindow)
266     return;     // no windows or window history yet
267
268   // check to see whether our current window has a <previouswindow> tag
269   if (pCurrentWindow->GetPreviousWindow() != WINDOW_INVALID)
270   {
271     // TODO: we may need to test here for the
272     //       whether our history should be changed
273
274     // don't reactivate the previouswindow if it is ourselves.
275     if (currentWindow != pCurrentWindow->GetPreviousWindow())
276       ActivateWindow(pCurrentWindow->GetPreviousWindow());
277     return;
278   }
279   // get the previous window in our stack
280   if (m_windowHistory.size() < 2)
281   { // no previous window history yet - check if we should just activate home
282     if (GetActiveWindow() != WINDOW_INVALID && GetActiveWindow() != WINDOW_HOME)
283     {
284       ClearWindowHistory();
285       ActivateWindow(WINDOW_HOME);
286     }
287     return;
288   }
289   m_windowHistory.pop();
290   int previousWindow = GetActiveWindow();
291   m_windowHistory.push(currentWindow);
292
293   CGUIWindow *pNewWindow = GetWindow(previousWindow);
294   if (!pNewWindow)
295   {
296     CLog::Log(LOGERROR, "Unable to activate the previous window");
297     ClearWindowHistory();
298     ActivateWindow(WINDOW_HOME);
299     return;
300   }
301
302   // ok to go to the previous window now
303
304   // tell our info manager which window we are going to
305   g_infoManager.SetNextWindow(previousWindow);
306
307   // set our overlay state (enables out animations on window change)
308   HideOverlay(pNewWindow->GetOverlayState());
309
310   // deinitialize our window
311   CloseWindowSync(pCurrentWindow);
312
313   g_infoManager.SetNextWindow(WINDOW_INVALID);
314   g_infoManager.SetPreviousWindow(currentWindow);
315
316   // remove the current window off our window stack
317   m_windowHistory.pop();
318
319   // ok, initialize the new window
320   CLog::Log(LOGDEBUG,"CGUIWindowManager::PreviousWindow: Activate new");
321   CGUIMessage msg2(GUI_MSG_WINDOW_INIT, 0, 0, WINDOW_INVALID, GetActiveWindow());
322   pNewWindow->OnMessage(msg2);
323
324   g_infoManager.SetPreviousWindow(WINDOW_INVALID);
325   return;
326 }
327
328 void CGUIWindowManager::ChangeActiveWindow(int newWindow, const CStdString& strPath)
329 {
330   vector<CStdString> params;
331   if (!strPath.empty())
332     params.push_back(strPath);
333   ActivateWindow(newWindow, params, true);
334 }
335
336 void CGUIWindowManager::ActivateWindow(int iWindowID, const CStdString& strPath)
337 {
338   vector<CStdString> params;
339   if (!strPath.empty())
340     params.push_back(strPath);
341   ActivateWindow(iWindowID, params, false);
342 }
343
344 void CGUIWindowManager::ActivateWindow(int iWindowID, const vector<CStdString>& params, bool swappingWindows)
345 {
346   if (!g_application.IsCurrentThread())
347   {
348     // make sure graphics lock is not held
349     CSingleExit leaveIt(g_graphicsContext);
350     CApplicationMessenger::Get().ActivateWindow(iWindowID, params, swappingWindows);
351   }
352   else
353   {
354     CSingleLock lock(g_graphicsContext);
355     ActivateWindow_Internal(iWindowID, params, swappingWindows);
356   }
357 }
358
359 void CGUIWindowManager::ActivateWindow_Internal(int iWindowID, const vector<CStdString>& params, bool swappingWindows)
360 {
361   // translate virtual windows
362   // virtual music window which returns the last open music window (aka the music start window)
363   if (iWindowID == WINDOW_MUSIC)
364   {
365     iWindowID = CSettings::Get().GetInt("mymusic.startwindow");
366     // ensure the music virtual window only returns music files and music library windows
367     if (iWindowID != WINDOW_MUSIC_NAV)
368       iWindowID = WINDOW_MUSIC_FILES;
369   }
370   // virtual video window which returns the last open video window (aka the video start window)
371   if (iWindowID == WINDOW_VIDEOS || iWindowID == WINDOW_VIDEO_FILES)
372   { // backward compatibility for pre-Eden
373     iWindowID = WINDOW_VIDEO_NAV;
374   }
375   if (iWindowID == WINDOW_SCRIPTS)
376   { // backward compatibility for pre-Dharma
377     iWindowID = WINDOW_PROGRAMS;
378   }
379   if (iWindowID == WINDOW_START)
380   { // virtual start window
381     iWindowID = g_SkinInfo->GetStartWindow();
382   }
383
384   // debug
385   CLog::Log(LOGDEBUG, "Activating window ID: %i", iWindowID);
386
387   if (!g_passwordManager.CheckMenuLock(iWindowID))
388   {
389     CLog::Log(LOGERROR, "MasterCode is Wrong: Window with id %d will not be loaded! Enter a correct MasterCode!", iWindowID);
390     if (GetActiveWindow() == WINDOW_INVALID && iWindowID != WINDOW_HOME)
391       ActivateWindow(WINDOW_HOME);
392     return;
393   }
394
395   // first check existence of the window we wish to activate.
396   CGUIWindow *pNewWindow = GetWindow(iWindowID);
397   if (!pNewWindow)
398   { // nothing to see here - move along
399     CLog::Log(LOGERROR, "Unable to locate window with id %d.  Check skin files", iWindowID - WINDOW_HOME);
400     return ;
401   }
402   else if (pNewWindow->IsDialog())
403   { // if we have a dialog, we do a DoModal() rather than activate the window
404     if (!pNewWindow->IsDialogRunning())
405     {
406       CSingleExit exitit(g_graphicsContext);
407       ((CGUIDialog *)pNewWindow)->DoModal(iWindowID, params.size() ? params[0] : "");
408     }
409     return;
410   }
411
412   g_infoManager.SetNextWindow(iWindowID);
413
414   // set our overlay state
415   HideOverlay(pNewWindow->GetOverlayState());
416
417   // deactivate any window
418   int currentWindow = GetActiveWindow();
419   CGUIWindow *pWindow = GetWindow(currentWindow);
420   if (pWindow)
421     CloseWindowSync(pWindow, iWindowID);
422   g_infoManager.SetNextWindow(WINDOW_INVALID);
423
424   // Add window to the history list (we must do this before we activate it,
425   // as all messages done in WINDOW_INIT will want to be sent to the new
426   // topmost window).  If we are swapping windows, we pop the old window
427   // off the history stack
428   if (swappingWindows && !m_windowHistory.empty())
429     m_windowHistory.pop();
430   AddToWindowHistory(iWindowID);
431
432   g_infoManager.SetPreviousWindow(currentWindow);
433   // Send the init message
434   CGUIMessage msg(GUI_MSG_WINDOW_INIT, 0, 0, currentWindow, iWindowID);
435   msg.SetStringParams(params);
436   pNewWindow->OnMessage(msg);
437 //  g_infoManager.SetPreviousWindow(WINDOW_INVALID);
438 }
439
440 void CGUIWindowManager::CloseDialogs(bool forceClose) const
441 {
442   CSingleLock lock(g_graphicsContext);
443   while (m_activeDialogs.size() > 0)
444   {
445     CGUIWindow* win = m_activeDialogs[0];
446     win->Close(forceClose);
447   }
448 }
449
450 bool CGUIWindowManager::OnAction(const CAction &action) const
451 {
452   CSingleLock lock(g_graphicsContext);
453   unsigned int topMost = m_activeDialogs.size();
454   while (topMost)
455   {
456     CGUIWindow *dialog = m_activeDialogs[--topMost];
457     lock.Leave();
458     if (dialog->IsModalDialog())
459     { // we have the topmost modal dialog
460       if (!dialog->IsAnimating(ANIM_TYPE_WINDOW_CLOSE))
461       {
462         bool fallThrough = (dialog->GetID() == WINDOW_DIALOG_FULLSCREEN_INFO);
463         if (dialog->OnAction(action))
464           return true;
465         // dialog didn't want the action - we'd normally return false
466         // but for some dialogs we want to drop the actions through
467         if (fallThrough)
468           break;
469         return false;
470       }
471       return true; // do nothing with the action until the anim is finished
472     }
473     // music or video overlay are handled as a special case, as they're modeless, but we allow
474     // clicking on them with the mouse.
475     if (action.IsMouse() && (dialog->GetID() == WINDOW_DIALOG_VIDEO_OVERLAY ||
476                              dialog->GetID() == WINDOW_DIALOG_MUSIC_OVERLAY))
477     {
478       if (dialog->OnAction(action))
479         return true;
480     }
481     lock.Enter();
482     if (topMost > m_activeDialogs.size())
483       topMost = m_activeDialogs.size();
484   }
485   lock.Leave();
486   CGUIWindow* window = GetWindow(GetActiveWindow());
487   if (window)
488     return window->OnAction(action);
489   return false;
490 }
491
492 bool RenderOrderSortFunction(CGUIWindow *first, CGUIWindow *second)
493 {
494   return first->GetRenderOrder() < second->GetRenderOrder();
495 }
496
497 void CGUIWindowManager::Process(unsigned int currentTime)
498 {
499   assert(g_application.IsCurrentThread());
500   CSingleLock lock(g_graphicsContext);
501
502   CDirtyRegionList dirtyregions;
503
504   CGUIWindow* pWindow = GetWindow(GetActiveWindow());
505   if (pWindow)
506     pWindow->DoProcess(currentTime, dirtyregions);
507
508   // process all dialogs - visibility may change etc.
509   for (WindowMap::iterator it = m_mapWindows.begin(); it != m_mapWindows.end(); ++it)
510   {
511     CGUIWindow *pWindow = (*it).second;
512     if (pWindow && pWindow->IsDialog())
513       pWindow->DoProcess(currentTime, dirtyregions);
514   }
515
516   for (CDirtyRegionList::iterator itr = dirtyregions.begin(); itr != dirtyregions.end(); ++itr)
517     m_tracker.MarkDirtyRegion(*itr);
518 }
519
520 void CGUIWindowManager::MarkDirty()
521 {
522   m_tracker.MarkDirtyRegion(CRect(0, 0, (float)g_graphicsContext.GetWidth(), (float)g_graphicsContext.GetHeight()));
523 }
524
525 void CGUIWindowManager::MarkDirty(const CRect& rect)
526 {
527   m_tracker.MarkDirtyRegion(rect);
528 }
529
530 void CGUIWindowManager::RenderPass() const
531 {
532   CGUIWindow* pWindow = GetWindow(GetActiveWindow());
533   if (pWindow)
534   {
535     pWindow->ClearBackground();
536     pWindow->DoRender();
537   }
538
539   // we render the dialogs based on their render order.
540   vector<CGUIWindow *> renderList = m_activeDialogs;
541   stable_sort(renderList.begin(), renderList.end(), RenderOrderSortFunction);
542   
543   for (iDialog it = renderList.begin(); it != renderList.end(); ++it)
544   {
545     if ((*it)->IsDialogRunning())
546       (*it)->DoRender();
547   }
548 }
549
550 bool CGUIWindowManager::Render()
551 {
552   assert(g_application.IsCurrentThread());
553   CSingleLock lock(g_graphicsContext);
554
555   CDirtyRegionList dirtyRegions = m_tracker.GetDirtyRegions();
556
557   bool hasRendered = false;
558   // If we visualize the regions we will always render the entire viewport
559   if (g_advancedSettings.m_guiVisualizeDirtyRegions || g_advancedSettings.m_guiAlgorithmDirtyRegions == DIRTYREGION_SOLVER_FILL_VIEWPORT_ALWAYS)
560   {
561     RenderPass();
562     hasRendered = true;
563   }
564   else if (g_advancedSettings.m_guiAlgorithmDirtyRegions == DIRTYREGION_SOLVER_FILL_VIEWPORT_ON_CHANGE)
565   {
566     if (dirtyRegions.size() > 0)
567     {
568       RenderPass();
569       hasRendered = true;
570     }
571   }
572   else
573   {
574     for (CDirtyRegionList::const_iterator i = dirtyRegions.begin(); i != dirtyRegions.end(); ++i)
575     {
576       if (i->IsEmpty())
577         continue;
578
579       g_graphicsContext.SetScissors(*i);
580       RenderPass();
581       hasRendered = true;
582     }
583     g_graphicsContext.ResetScissors();
584   }
585
586   if (g_advancedSettings.m_guiVisualizeDirtyRegions)
587   {
588     g_graphicsContext.SetRenderingResolution(g_graphicsContext.GetResInfo(), false);
589     const CDirtyRegionList &markedRegions  = m_tracker.GetMarkedRegions(); 
590     for (CDirtyRegionList::const_iterator i = markedRegions.begin(); i != markedRegions.end(); ++i)
591       CGUITexture::DrawQuad(*i, 0x0fff0000);
592     for (CDirtyRegionList::const_iterator i = dirtyRegions.begin(); i != dirtyRegions.end(); ++i)
593       CGUITexture::DrawQuad(*i, 0x4c00ff00);
594   }
595
596   return hasRendered;
597 }
598
599 void CGUIWindowManager::AfterRender()
600 {
601   m_tracker.CleanMarkedRegions();
602
603   CGUIWindow* pWindow = GetWindow(GetActiveWindow());
604   if (pWindow)
605     pWindow->AfterRender();
606
607   // make copy of vector as we may remove items from it as we go
608   vector<CGUIWindow *> activeDialogs = m_activeDialogs;
609   for (iDialog it = activeDialogs.begin(); it != activeDialogs.end(); ++it)
610   {
611     if ((*it)->IsDialogRunning())
612       (*it)->AfterRender();
613   }
614 }
615
616 void CGUIWindowManager::FrameMove()
617 {
618   assert(g_application.IsCurrentThread());
619   CSingleLock lock(g_graphicsContext);
620
621   if(m_iNested == 0)
622   {
623     // delete any windows queued for deletion
624     for(iDialog it = m_deleteWindows.begin(); it != m_deleteWindows.end(); ++it)
625     {
626       // Free any window resources
627       (*it)->FreeResources(true);
628       delete *it;
629     }
630     m_deleteWindows.clear();
631   }
632
633   CGUIWindow* pWindow = GetWindow(GetActiveWindow());
634   if (pWindow)
635     pWindow->FrameMove();
636   // update any dialogs - we take a copy of the vector as some dialogs may close themselves
637   // during this call
638   vector<CGUIWindow *> dialogs = m_activeDialogs;
639   for (iDialog it = dialogs.begin(); it != dialogs.end(); ++it)
640     (*it)->FrameMove();
641
642   g_infoManager.m_AVInfoValid = false;
643 }
644
645 CGUIWindow* CGUIWindowManager::GetWindow(int id) const
646 {
647   CGUIWindow *window;
648   if (id == 0 || id == WINDOW_INVALID)
649     return NULL;
650   window = m_idCache.Get(id);
651   if (window)
652     return window;
653
654   CSingleLock lock(g_graphicsContext);
655   WindowMap::const_iterator it = m_mapWindows.find(id);
656   if (it != m_mapWindows.end())
657     window = (*it).second;
658   else
659     window = NULL;
660   m_idCache.Set(id, window);
661   return window;
662 }
663
664 void CGUIWindowManager::ProcessRenderLoop(bool renderOnly /*= false*/)
665 {
666   if (g_application.IsCurrentThread() && m_pCallback)
667   {
668     m_iNested++;
669     if (!renderOnly)
670       m_pCallback->Process();
671     m_pCallback->FrameMove(!renderOnly);
672     m_pCallback->Render();
673     m_iNested--;
674   }
675 }
676
677 void CGUIWindowManager::SetCallback(IWindowManagerCallback& callback)
678 {
679   m_pCallback = &callback;
680 }
681
682 void CGUIWindowManager::DeInitialize()
683 {
684   CSingleLock lock(g_graphicsContext);
685   for (WindowMap::iterator it = m_mapWindows.begin(); it != m_mapWindows.end(); ++it)
686   {
687     CGUIWindow* pWindow = (*it).second;
688     if (IsWindowActive(it->first))
689     {
690       pWindow->DisableAnimations();
691       pWindow->Close(true);
692     }
693     pWindow->ResetControlStates();
694     pWindow->FreeResources(true);
695   }
696   UnloadNotOnDemandWindows();
697
698   m_vecMsgTargets.erase( m_vecMsgTargets.begin(), m_vecMsgTargets.end() );
699
700   // destroy our custom windows...
701   for (int i = 0; i < (int)m_vecCustomWindows.size(); i++)
702   {
703     CGUIWindow *pWindow = m_vecCustomWindows[i];
704     Remove(pWindow->GetID());
705     delete pWindow;
706   }
707
708   // clear our vectors of windows
709   m_vecCustomWindows.clear();
710   m_activeDialogs.clear();
711
712   m_initialized = false;
713 }
714
715 /// \brief Route to a window
716 /// \param pWindow Window to route to
717 void CGUIWindowManager::RouteToWindow(CGUIWindow* dialog)
718 {
719   CSingleLock lock(g_graphicsContext);
720   // Just to be sure: Unroute this window,
721   // #we may have routed to it before
722   RemoveDialog(dialog->GetID());
723
724   m_activeDialogs.push_back(dialog);
725 }
726
727 /// \brief Unroute window
728 /// \param id ID of the window routed
729 void CGUIWindowManager::RemoveDialog(int id)
730 {
731   CSingleLock lock(g_graphicsContext);
732   for (iDialog it = m_activeDialogs.begin(); it != m_activeDialogs.end(); ++it)
733   {
734     if ((*it)->GetID() == id)
735     {
736       m_activeDialogs.erase(it);
737       return;
738     }
739   }
740 }
741
742 bool CGUIWindowManager::HasModalDialog() const
743 {
744   CSingleLock lock(g_graphicsContext);
745   for (ciDialog it = m_activeDialogs.begin(); it != m_activeDialogs.end(); ++it)
746   {
747     CGUIWindow *window = *it;
748     if (window->IsModalDialog())
749     { // have a modal window
750       if (!window->IsAnimating(ANIM_TYPE_WINDOW_CLOSE))
751         return true;
752     }
753   }
754   return false;
755 }
756
757 bool CGUIWindowManager::HasDialogOnScreen() const
758 {
759   return (m_activeDialogs.size() > 0);
760 }
761
762 /// \brief Get the ID of the top most routed window
763 /// \return id ID of the window or WINDOW_INVALID if no routed window available
764 int CGUIWindowManager::GetTopMostModalDialogID(bool ignoreClosing /*= false*/) const
765 {
766   CSingleLock lock(g_graphicsContext);
767   for (crDialog it = m_activeDialogs.rbegin(); it != m_activeDialogs.rend(); ++it)
768   {
769     CGUIWindow *dialog = *it;
770     if (dialog->IsModalDialog() && (!ignoreClosing || !dialog->IsAnimating(ANIM_TYPE_WINDOW_CLOSE)))
771     { // have a modal window
772       return dialog->GetID();
773     }
774   }
775   return WINDOW_INVALID;
776 }
777
778 void CGUIWindowManager::SendThreadMessage(CGUIMessage& message, int window /*= 0*/)
779 {
780   CSingleLock lock(m_critSection);
781
782   CGUIMessage* msg = new CGUIMessage(message);
783   m_vecThreadMessages.push_back( pair<CGUIMessage*,int>(msg,window) );
784 }
785
786 void CGUIWindowManager::DispatchThreadMessages()
787 {
788   // This method only be called in the xbmc main thread.
789
790   // XXX: for more info of this method
791   //      check the pr here: https://github.com/xbmc/xbmc/pull/2253
792
793   // As a thread message queue service, it should follow these rules:
794   // 1. [Must] Thread safe, message can be pushed into queue in arbitrary thread context.
795   // 2. Messages [must] be processed in dispatch message thread context with the same
796   //    order as they be pushed into the queue.
797   // 3. Dispatch function [must] support call itself during message process procedure,
798   //    and do not break other rules listed here. to make it clear: in the
799   //    SendMessage(), it could start another xbmc main thread loop, calling
800   //    DispatchThreadMessages() in it's internal loop, this must be supported.
801   // 4. During DispatchThreadMessages() processing, any new pushed message [should] not
802   //    be processed by the current loop in DispatchThreadMessages(), prevent dead loop.
803   // 5. If possible, queued messages can be removed by certain filter condition
804   //    and not break above.
805
806   CSingleLock lock(m_critSection);
807
808   for(int msgCount = m_vecThreadMessages.size(); !m_vecThreadMessages.empty() && msgCount > 0; --msgCount)
809   {
810     // pop up one message per time to make messages be processed by order.
811     // this will ensure rule No.2 & No.3
812     CGUIMessage *pMsg = m_vecThreadMessages.front().first;
813     int window = m_vecThreadMessages.front().second;
814     m_vecThreadMessages.pop_front();
815
816     lock.Leave();
817
818     // XXX: during SendMessage(), there could be a deeper 'xbmc main loop' inited by e.g. doModal
819     //      which may loop there and callback to DispatchThreadMessages() multiple times.
820     if (window)
821       SendMessage( *pMsg, window );
822     else
823       SendMessage( *pMsg );
824     delete pMsg;
825
826     lock.Enter();
827   }
828 }
829
830 int CGUIWindowManager::RemoveThreadMessageByMessageIds(int *pMessageIDList)
831 {
832   CSingleLock lock(m_critSection);
833   int removedMsgCount = 0;
834   for (std::list < std::pair<CGUIMessage*,int> >::iterator it = m_vecThreadMessages.begin();
835        it != m_vecThreadMessages.end();)
836   {
837     CGUIMessage *pMsg = it->first;
838     int *pMsgID;
839     for(pMsgID = pMessageIDList; *pMsgID != 0; ++pMsgID)
840       if (pMsg->GetMessage() == *pMsgID)
841         break;
842     if (*pMsgID)
843     {
844       it = m_vecThreadMessages.erase(it);
845       delete pMsg;
846       ++removedMsgCount;
847     }
848     else
849     {
850       ++it;
851     }
852   }
853   return removedMsgCount;
854 }
855
856 void CGUIWindowManager::AddMsgTarget( IMsgTargetCallback* pMsgTarget )
857 {
858   m_vecMsgTargets.push_back( pMsgTarget );
859 }
860
861 int CGUIWindowManager::GetActiveWindow() const
862 {
863   if (!m_windowHistory.empty())
864     return m_windowHistory.top();
865   return WINDOW_INVALID;
866 }
867
868 // same as GetActiveWindow() except it first grabs dialogs
869 int CGUIWindowManager::GetFocusedWindow() const
870 {
871   int dialog = GetTopMostModalDialogID(true);
872   if (dialog != WINDOW_INVALID)
873     return dialog;
874
875   return GetActiveWindow();
876 }
877
878 bool CGUIWindowManager::IsWindowActive(int id, bool ignoreClosing /* = true */) const
879 {
880   // mask out multiple instances of the same window
881   id &= WINDOW_ID_MASK;
882   if ((GetActiveWindow() & WINDOW_ID_MASK) == id) return true;
883   // run through the dialogs
884   CSingleLock lock(g_graphicsContext);
885   for (ciDialog it = m_activeDialogs.begin(); it != m_activeDialogs.end(); ++it)
886   {
887     CGUIWindow *window = *it;
888     if ((window->GetID() & WINDOW_ID_MASK) == id && (!ignoreClosing || !window->IsAnimating(ANIM_TYPE_WINDOW_CLOSE)))
889       return true;
890   }
891   return false; // window isn't active
892 }
893
894 bool CGUIWindowManager::IsWindowActive(const CStdString &xmlFile, bool ignoreClosing /* = true */) const
895 {
896   CSingleLock lock(g_graphicsContext);
897   CGUIWindow *window = GetWindow(GetActiveWindow());
898   if (window && URIUtils::GetFileName(window->GetProperty("xmlfile").asString()).Equals(xmlFile)) return true;
899   // run through the dialogs
900   for (ciDialog it = m_activeDialogs.begin(); it != m_activeDialogs.end(); ++it)
901   {
902     CGUIWindow *window = *it;
903     if (URIUtils::GetFileName(window->GetProperty("xmlfile").asString()).Equals(xmlFile) && (!ignoreClosing || !window->IsAnimating(ANIM_TYPE_WINDOW_CLOSE)))
904       return true;
905   }
906   return false; // window isn't active
907 }
908
909 bool CGUIWindowManager::IsWindowVisible(int id) const
910 {
911   return IsWindowActive(id, false);
912 }
913
914 bool CGUIWindowManager::IsWindowVisible(const CStdString &xmlFile) const
915 {
916   return IsWindowActive(xmlFile, false);
917 }
918
919 void CGUIWindowManager::LoadNotOnDemandWindows()
920 {
921   CSingleLock lock(g_graphicsContext);
922   for (WindowMap::iterator it = m_mapWindows.begin(); it != m_mapWindows.end(); ++it)
923   {
924     CGUIWindow *pWindow = (*it).second;
925     if (pWindow->GetLoadType() == CGUIWindow::LOAD_ON_GUI_INIT)
926     {
927       pWindow->FreeResources(true);
928       pWindow->Initialize();
929     }
930   }
931 }
932
933 void CGUIWindowManager::UnloadNotOnDemandWindows()
934 {
935   CSingleLock lock(g_graphicsContext);
936   for (WindowMap::iterator it = m_mapWindows.begin(); it != m_mapWindows.end(); ++it)
937   {
938     CGUIWindow *pWindow = (*it).second;
939     if (pWindow->GetLoadType() == CGUIWindow::LOAD_ON_GUI_INIT ||
940         pWindow->GetLoadType() == CGUIWindow::KEEP_IN_MEMORY)
941     {
942       pWindow->FreeResources(true);
943     }
944   }
945 }
946
947 bool CGUIWindowManager::IsOverlayAllowed() const
948 {
949   if (GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO ||
950       GetActiveWindow() == WINDOW_SCREENSAVER)
951     return false;
952   return m_bShowOverlay;
953 }
954
955 void CGUIWindowManager::ShowOverlay(CGUIWindow::OVERLAY_STATE state)
956 {
957   if (state != CGUIWindow::OVERLAY_STATE_PARENT_WINDOW)
958     m_bShowOverlay = state == CGUIWindow::OVERLAY_STATE_SHOWN;
959 }
960
961 void CGUIWindowManager::HideOverlay(CGUIWindow::OVERLAY_STATE state)
962 {
963   if (state == CGUIWindow::OVERLAY_STATE_HIDDEN)
964     m_bShowOverlay = false;
965 }
966
967 void CGUIWindowManager::AddToWindowHistory(int newWindowID)
968 {
969   // Check the window stack to see if this window is in our history,
970   // and if so, pop all the other windows off the stack so that we
971   // always have a predictable "Back" behaviour for each window
972   stack<int> historySave = m_windowHistory;
973   while (!historySave.empty())
974   {
975     if (historySave.top() == newWindowID)
976       break;
977     historySave.pop();
978   }
979   if (!historySave.empty())
980   { // found window in history
981     m_windowHistory = historySave;
982   }
983   else
984   { // didn't find window in history - add it to the stack
985     m_windowHistory.push(newWindowID);
986   }
987 }
988
989 void CGUIWindowManager::GetActiveModelessWindows(vector<int> &ids)
990 {
991   // run through our modeless windows, and construct a vector of them
992   // useful for saving and restoring the modeless windows on skin change etc.
993   CSingleLock lock(g_graphicsContext);
994   for (iDialog it = m_activeDialogs.begin(); it != m_activeDialogs.end(); ++it)
995   {
996     if (!(*it)->IsModalDialog())
997       ids.push_back((*it)->GetID());
998   }
999 }
1000
1001 CGUIWindow *CGUIWindowManager::GetTopMostDialog() const
1002 {
1003   CSingleLock lock(g_graphicsContext);
1004   // find the window with the lowest render order
1005   vector<CGUIWindow *> renderList = m_activeDialogs;
1006   stable_sort(renderList.begin(), renderList.end(), RenderOrderSortFunction);
1007
1008   if (!renderList.size())
1009     return NULL;
1010
1011   // return the last window in the list
1012   return *renderList.rbegin();
1013 }
1014
1015 bool CGUIWindowManager::IsWindowTopMost(int id) const
1016 {
1017   CGUIWindow *topMost = GetTopMostDialog();
1018   if (topMost && (topMost->GetID() & WINDOW_ID_MASK) == id)
1019     return true;
1020   return false;
1021 }
1022
1023 bool CGUIWindowManager::IsWindowTopMost(const CStdString &xmlFile) const
1024 {
1025   CGUIWindow *topMost = GetTopMostDialog();
1026   if (topMost && URIUtils::GetFileName(topMost->GetProperty("xmlfile").asString()).Equals(xmlFile))
1027     return true;
1028   return false;
1029 }
1030
1031 void CGUIWindowManager::ClearWindowHistory()
1032 {
1033   while (!m_windowHistory.empty())
1034     m_windowHistory.pop();
1035 }
1036
1037 void CGUIWindowManager::CloseWindowSync(CGUIWindow *window, int nextWindowID /*= 0*/)
1038 {
1039   window->Close(false, nextWindowID);
1040   while (window->IsAnimating(ANIM_TYPE_WINDOW_CLOSE))
1041     ProcessRenderLoop(true);
1042 }
1043
1044 #ifdef _DEBUG
1045 void CGUIWindowManager::DumpTextureUse()
1046 {
1047   CGUIWindow* pWindow = GetWindow(GetActiveWindow());
1048   if (pWindow)
1049     pWindow->DumpTextureUse();
1050
1051   CSingleLock lock(g_graphicsContext);
1052   for (iDialog it = m_activeDialogs.begin(); it != m_activeDialogs.end(); ++it)
1053   {
1054     if ((*it)->IsDialogRunning())
1055       (*it)->DumpTextureUse();
1056   }
1057 }
1058 #endif