[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / interfaces / legacy / Window.cpp
1  /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.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, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "Window.h"
23 #include "WindowInterceptor.h"
24 #include "guilib/GUIButtonControl.h"
25 #include "guilib/GUIEditControl.h"
26 #include "guilib/GUICheckMarkControl.h"
27 #include "guilib/GUIRadioButtonControl.h"
28 #include "guilib/GUIWindowManager.h"
29 #include "settings/Settings.h"
30 #include "Application.h"
31 #include "ApplicationMessenger.h"
32 #include "utils/Variant.h"
33
34 #define ACTIVE_WINDOW g_windowManager.GetActiveWindow()
35
36 namespace XBMCAddon
37 {
38   namespace xbmcgui
39   {
40     XbmcThreads::ThreadLocal<ref> InterceptorBase::upcallTls;
41
42     /**
43      * Used in add/remove control. It only locks if it's given a 
44      * non-NULL CCriticalSection. It's given a NULL CCriticalSection
45      * when a function higher in the call stack already has a 
46      */
47     class MaybeLock
48     {
49       CCriticalSection* lock;
50     public:
51       inline MaybeLock(CCriticalSection* p_lock) : lock(p_lock) { if (lock) lock->lock(); }
52       inline ~MaybeLock() { if (lock) lock->unlock(); }
53     };
54
55     class SingleLockWithDelayGuard
56     {
57       DelayedCallGuard dcg;
58       CCriticalSection& lock;
59     public:
60       inline SingleLockWithDelayGuard(CCriticalSection& ccrit, LanguageHook* lh) : dcg(lh), lock(ccrit) { lock.lock(); }
61       inline ~SingleLockWithDelayGuard() { lock.unlock(); }
62     };
63
64     /**
65      * Explicit template instantiation
66      */
67     template class Interceptor<CGUIWindow>;
68
69     /**
70      * This interceptor is a simple, non-callbackable (is that a word?)
71      *  Interceptor to satisfy the Window requirements for upcalling
72      *  for the purposes of instantiating a Window instance from
73      *  an already existing window.
74      */
75     class ProxyExistingWindowInterceptor : public InterceptorBase
76     {
77       CGUIWindow* cguiwindow;
78
79     public:
80       inline ProxyExistingWindowInterceptor(CGUIWindow* window) :
81         cguiwindow(window) { TRACE; }
82
83       virtual CGUIWindow* get();
84     };
85
86     CGUIWindow* ProxyExistingWindowInterceptor::get() { TRACE; return cguiwindow; }
87
88     Window::Window(const char* classname) throw (WindowException): 
89       AddonCallback(classname), isDisposed(false), window(NULL), iWindowId(-1),
90       iOldWindowId(0), iCurrentControlId(3000), bModal(false), m_actionEvent(true),
91       canPulse(true), existingWindow(false), destroyAfterDeInit(false)
92     {
93       TRACE;
94     }
95
96     /**
97      * This just creates a default window.
98      */
99     Window::Window(int existingWindowId) throw (WindowException) : 
100       AddonCallback("Window"), isDisposed(false), window(NULL), iWindowId(-1),
101       iOldWindowId(0), iCurrentControlId(3000), bModal(false), m_actionEvent(true),
102       canPulse(false), existingWindow(true), destroyAfterDeInit(false)
103     {
104       TRACE;
105       SingleLockWithDelayGuard gslock(g_graphicsContext,languageHook);
106
107       if (existingWindowId == -1)
108       {
109         // in this case just do the other constructor.
110         canPulse = true;
111         existingWindow = false;
112
113         setWindow(new Interceptor<CGUIWindow>("CGUIWindow",this,getNextAvailalbeWindowId()));
114       }
115       else
116       {
117         // user specified window id, use this one if it exists
118         // It is not possible to capture key presses or button presses
119         CGUIWindow* pWindow = g_windowManager.GetWindow(existingWindowId);
120         if (!pWindow)
121           throw WindowException("Window id does not exist");
122
123         setWindow(new ProxyExistingWindowInterceptor(pWindow));
124       }
125     }
126
127     Window::~Window()
128     {
129       TRACE;
130
131       deallocating();
132     }
133
134     void Window::deallocating()
135     {
136       AddonCallback::deallocating();
137
138       dispose();
139     }
140
141     void Window::dispose()
142     {
143       TRACE;
144
145       // this is called from non-scripting-language callstacks. Don't use the delayed call guard.
146       CSingleLock lock(g_graphicsContext);
147       if (!isDisposed)
148       {
149         isDisposed = true;
150
151         // no callbacks are possible any longer
152         //   - this will be handled by the parent constructor
153
154         // first change to an existing window
155         if (!existingWindow)
156         {
157           if (ACTIVE_WINDOW == iWindowId && !g_application.m_bStop)
158           {
159             if(g_windowManager.GetWindow(iOldWindowId))
160             {
161               g_windowManager.ActivateWindow(iOldWindowId);
162             }
163             // old window does not exist anymore, switch to home
164             else g_windowManager.ActivateWindow(WINDOW_HOME);
165           }
166
167         }
168         else
169         {
170           // BUG:
171           // This is an existing window, so no resources are free'd.  Note that
172           // THIS WILL FAIL for any controls newly created by python - they will
173           // remain after the script ends.  Ideally this would be remedied by
174           // a flag in Control that specifies that it was python created - any python
175           // created controls could then be removed + free'd from the window.
176           // how this works with controlgroups though could be a bit tricky.
177         }
178
179         // and free our list of controls
180         std::vector<AddonClass::Ref<Control> >::iterator it = vecControls.begin();
181         while (it != vecControls.end())
182         {
183           AddonClass::Ref<Control> pControl = *it;
184           // initialize control to zero
185           pControl->pGUIControl = NULL;
186           pControl->iControlId = 0;
187           pControl->iParentId = 0;
188           ++it;
189         }
190
191         if (!existingWindow)
192         {
193           if (window)
194           {
195             if (g_windowManager.IsWindowVisible(ref(window)->GetID()))
196             {
197               destroyAfterDeInit = true;
198               close();
199             }
200             else
201               g_windowManager.Delete(ref(window)->GetID());
202           }
203         }
204
205         vecControls.clear();
206       }
207     }
208
209     void Window::setWindow(InterceptorBase* _window) 
210     { 
211       TRACE;
212       window = _window; 
213       iWindowId = _window->get()->GetID(); 
214
215       if (!existingWindow)
216         g_windowManager.Add(window->get());
217     }
218
219     int Window::getNextAvailalbeWindowId() throw (WindowException)
220     {
221       TRACE;
222       // window id's 13000 - 13100 are reserved for python
223       // get first window id that is not in use
224       int id = WINDOW_PYTHON_START;
225       // if window 13099 is in use it means python can't create more windows
226       if (g_windowManager.GetWindow(WINDOW_PYTHON_END))
227         throw WindowException("maximum number of windows reached");
228
229       while(id < WINDOW_PYTHON_END && g_windowManager.GetWindow(id) != NULL) id++;
230       return id;
231     }
232
233     void Window::popActiveWindowId()
234     {
235       TRACE;
236       if (iOldWindowId != iWindowId &&
237           iWindowId != ACTIVE_WINDOW)
238         iOldWindowId = ACTIVE_WINDOW;
239     }
240
241     // Internal helper method
242     /* Searches for a control in Window->vecControls
243      * If we can't find any but the window has the controlId (in case of a not python window)
244      * we create a new control with basic functionality
245      */
246     Control* Window::GetControlById(int iControlId, CCriticalSection* gc) throw (WindowException)
247     {
248       TRACE;
249
250       // find in window vector first!!!
251       // this saves us from creating a complete new control
252       std::vector<AddonClass::Ref<Control> >::iterator it = vecControls.begin();
253       while (it != vecControls.end())
254       {
255         AddonClass::Ref<Control> control = (*it);
256         if (control->iControlId == iControlId)
257         {
258           return control.get();
259         } else ++it;
260       }
261
262       // lock xbmc GUI before accessing data from it
263       MaybeLock lock(gc);
264
265       // check if control exists
266       CGUIControl* pGUIControl = (CGUIControl*)ref(window)->GetControl(iControlId); 
267       if (!pGUIControl)
268       {
269         // control does not exist.
270         throw WindowException("Non-Existent Control %d",iControlId);
271       }
272
273       // allocate a new control with a new reference
274       CLabelInfo li;
275
276       Control* pControl = NULL;
277
278       // TODO: Yuck! Should probably be done with a Factory pattern
279       switch(pGUIControl->GetControlType())
280       {
281       case CGUIControl::GUICONTROL_BUTTON:
282         pControl = new ControlButton();
283
284         li = ((CGUIButtonControl *)pGUIControl)->GetLabelInfo();
285
286         // note: conversion from infocolors -> plain colors here
287         ((ControlButton*)pControl)->disabledColor = li.disabledColor;
288         ((ControlButton*)pControl)->focusedColor  = li.focusedColor;
289         ((ControlButton*)pControl)->textColor  = li.textColor;
290         ((ControlButton*)pControl)->shadowColor   = li.shadowColor;
291         if (li.font) ((ControlButton*)pControl)->strFont = li.font->GetFontName();
292         ((ControlButton*)pControl)->align = li.align;
293         break;
294       case CGUIControl::GUICONTROL_CHECKMARK:
295         pControl = new ControlCheckMark();
296
297         li = ((CGUICheckMarkControl *)pGUIControl)->GetLabelInfo();
298
299         // note: conversion to plain colors from infocolors.
300         ((ControlCheckMark*)pControl)->disabledColor = li.disabledColor;
301         //((ControlCheckMark*)pControl)->shadowColor = li.shadowColor;
302         ((ControlCheckMark*)pControl)->textColor  = li.textColor;
303         if (li.font) ((ControlCheckMark*)pControl)->strFont = li.font->GetFontName();
304         ((ControlCheckMark*)pControl)->align = li.align;
305         break;
306       case CGUIControl::GUICONTROL_LABEL:
307         pControl = new ControlLabel();
308         break;
309       case CGUIControl::GUICONTROL_SPIN:
310         pControl = new ControlSpin();
311         break;
312       case CGUIControl::GUICONTROL_FADELABEL:
313         pControl = new ControlFadeLabel();
314         break;
315       case CGUIControl::GUICONTROL_TEXTBOX:
316         pControl = new ControlTextBox();
317         break;
318       case CGUIControl::GUICONTROL_IMAGE:
319         pControl = new ControlImage();
320         break;
321       case CGUIControl::GUICONTROL_PROGRESS:
322         pControl = new ControlProgress();
323         break;
324       case CGUIControl::GUICONTROL_SLIDER:
325         pControl = new ControlSlider();
326         break;                  
327       case CGUIControl::GUICONTAINER_LIST:
328       case CGUIControl::GUICONTAINER_WRAPLIST:
329       case CGUIControl::GUICONTAINER_FIXEDLIST:
330       case CGUIControl::GUICONTAINER_PANEL:
331         pControl = new ControlList();
332         // create a python spin control
333         ((ControlList*)pControl)->pControlSpin = new ControlSpin();
334         break;
335       case CGUIControl::GUICONTROL_GROUP:
336         pControl = new ControlGroup();
337         break;
338       case CGUIControl::GUICONTROL_RADIO:
339         pControl = new ControlRadioButton();
340
341         li = ((CGUIRadioButtonControl *)pGUIControl)->GetLabelInfo();
342
343         // note: conversion from infocolors -> plain colors here
344         ((ControlRadioButton*)pControl)->disabledColor = li.disabledColor;
345         ((ControlRadioButton*)pControl)->focusedColor  = li.focusedColor;
346         ((ControlRadioButton*)pControl)->textColor  = li.textColor;
347         ((ControlRadioButton*)pControl)->shadowColor   = li.shadowColor;
348         if (li.font) ((ControlRadioButton*)pControl)->strFont = li.font->GetFontName();
349         ((ControlRadioButton*)pControl)->align = li.align;
350         break;
351       case CGUIControl::GUICONTROL_EDIT:
352         pControl = new ControlEdit();
353
354         li = ((CGUIEditControl *)pGUIControl)->GetLabelInfo();
355
356         // note: conversion from infocolors -> plain colors here
357         ((ControlEdit*)pControl)->disabledColor = li.disabledColor;
358         ((ControlEdit*)pControl)->textColor  = li.textColor;
359         if (li.font) ((ControlEdit*)pControl)->strFont = li.font->GetFontName();
360         ((ControlButton*)pControl)->align = li.align;
361         break;
362       default:
363         break;
364       }
365
366       if (!pControl)
367         // throw an exeption
368         throw WindowException("Unknown control type for python");
369
370       // we have a valid control here, fill in all the 'Control' data
371       pControl->pGUIControl = pGUIControl;
372       pControl->iControlId = pGUIControl->GetID();
373       pControl->iParentId = iWindowId;
374       pControl->dwHeight = (int)pGUIControl->GetHeight();
375       pControl->dwWidth = (int)pGUIControl->GetWidth();
376       pControl->dwPosX = (int)pGUIControl->GetXPosition();
377       pControl->dwPosY = (int)pGUIControl->GetYPosition();
378       pControl->iControlUp = pGUIControl->GetControlIdUp();
379       pControl->iControlDown = pGUIControl->GetControlIdDown();
380       pControl->iControlLeft = pGUIControl->GetControlIdLeft();
381       pControl->iControlRight = pGUIControl->GetControlIdRight();
382
383       // It got this far so means the control isn't actually in the vector of controls
384       // so lets add it to save doing all that next time
385       vecControls.push_back(AddonClass::Ref<Control>(pControl));
386
387       // return the control with increased reference (+1)
388       return pControl;
389     }
390
391     void Window::PulseActionEvent()
392     {
393       TRACE;
394       if (canPulse)
395         m_actionEvent.Set();
396     }
397
398     bool Window::WaitForActionEvent(unsigned int milliseconds)
399     {
400       TRACE;
401       // DO NOT MAKE THIS A DELAYED CALL!!!!
402       bool ret = languageHook == NULL ? m_actionEvent.WaitMSec(milliseconds) : languageHook->WaitForEvent(m_actionEvent,milliseconds);
403       if (ret)
404         m_actionEvent.Reset();
405       return ret;
406     }
407
408     bool Window::OnAction(const CAction &action)
409     {
410       TRACE;
411       // do the base class window first, and the call to python after this
412       bool ret = ref(window)->OnAction(action);
413
414       // workaround - for scripts which try to access the active control (focused) when there is none.
415       // for example - the case when the mouse enters the screen.
416       CGUIControl *pControl = ref(window)->GetFocusedControl();
417       if (action.IsMouse() && !pControl)
418         return ret;
419
420       AddonClass::Ref<Action> inf(new Action(action));
421       invokeCallback(new CallbackFunction<Window,AddonClass::Ref<Action> >(this,&Window::onAction,inf.get()));
422       PulseActionEvent();
423
424       return ret;
425     }
426
427     bool Window::OnBack(int actionID)
428     {
429       // we are always a Python window ... keep that in mind when reviewing the old code
430       return true;
431     }
432
433     void Window::OnDeinitWindow(int nextWindowID /*= 0*/)
434     {
435       // NOTE!: This handle child classes correctly. XML windows will call
436       // the OnDeinitWindow from CGUIMediaWindow while non-XML classes will
437       // call the OnDeinitWindow on CGUIWindow
438       ref(window)->OnDeinitWindow(nextWindowID);
439       if (destroyAfterDeInit)
440         g_windowManager.Delete(window->get()->GetID());
441     }
442
443     void Window::onAction(Action* action)
444     {
445       TRACE;
446       // default onAction behavior
447       if(action->id == ACTION_PREVIOUS_MENU || action->id == ACTION_NAV_BACK)
448         close();
449     }
450
451     bool Window::OnMessage(CGUIMessage& message)
452     {
453       TRACE;
454       switch (message.GetMessage())
455       {
456       case GUI_MSG_WINDOW_DEINIT:
457         {
458           g_windowManager.ShowOverlay(ref(window)->OVERLAY_STATE_SHOWN);
459         }
460         break;
461
462       case GUI_MSG_WINDOW_INIT:
463         {
464           ref(window)->OnMessage(message);
465           g_windowManager.ShowOverlay(ref(window)->OVERLAY_STATE_HIDDEN);
466           return true;
467         }
468         break;
469
470       case GUI_MSG_CLICKED:
471         {
472           int iControl=message.GetSenderId();
473           AddonClass::Ref<Control> inf;
474           // find python control object with same iControl
475           std::vector<AddonClass::Ref<Control> >::iterator it = vecControls.begin();
476           while (it != vecControls.end())
477           {
478             AddonClass::Ref<Control> pControl = (*it);
479             if (pControl->iControlId == iControl)
480             {
481               inf = pControl.get();
482               break;
483             }
484             ++it;
485           }
486
487           // did we find our control?
488           if (inf.isNotNull())
489           {
490             // currently we only accept messages from a button or controllist with a select action
491             if (inf->canAcceptMessages(message.GetParam1()))
492             {
493               invokeCallback(new CallbackFunction<Window,AddonClass::Ref<Control> >(this,&Window::onControl,inf.get()));
494               PulseActionEvent();
495
496               // return true here as we are handling the event
497               return true;
498             }
499           }
500           // if we get here, we didn't add the action
501         }
502         break;
503       }
504
505       return ref(window)->OnMessage(message);
506     }
507
508     void Window::onControl(Control* action) { TRACE; /* do nothing by default */ }
509     void Window::onClick(int controlId) { TRACE; /* do nothing by default */ }
510     void Window::onFocus(int controlId) { TRACE; /* do nothing by default */ }
511     void Window::onInit() { TRACE; /* do nothing by default */ }
512
513     void Window::show()
514     {
515       TRACE;
516       DelayedCallGuard dcguard(languageHook);
517       popActiveWindowId();
518
519       std::vector<CStdString> params;
520       CApplicationMessenger::Get().ActivateWindow(iWindowId, params, false);
521     }
522
523     void Window::setFocus(Control* pControl) throw (WindowException)
524     {
525       TRACE;
526       if(pControl == NULL)
527         throw WindowException("Object should be of type Control");
528
529       CGUIMessage msg = CGUIMessage(GUI_MSG_SETFOCUS,pControl->iParentId, pControl->iControlId);
530       g_windowManager.SendThreadMessage(msg, pControl->iParentId);
531     }
532
533     void Window::setFocusId(int iControlId)
534     {
535       TRACE;
536       CGUIMessage msg = CGUIMessage(GUI_MSG_SETFOCUS,iWindowId,iControlId);
537       g_windowManager.SendThreadMessage(msg, iWindowId);
538     }
539
540     Control* Window::getFocus() throw (WindowException)
541     {
542       TRACE;
543       SingleLockWithDelayGuard gslock(g_graphicsContext,languageHook);
544
545       int iControlId = ref(window)->GetFocusedControlID();
546       if(iControlId == -1)
547         throw WindowException("No control in this window has focus");
548       // Sine I'm already holding the lock theres no reason to give it to GetFocusedControlID
549       return GetControlById(iControlId,NULL);
550     }
551
552     long Window::getFocusId() throw (WindowException)
553     {
554       TRACE;
555       SingleLockWithDelayGuard gslock(g_graphicsContext,languageHook);
556       int iControlId = ref(window)->GetFocusedControlID();
557       if(iControlId == -1)
558         throw WindowException("No control in this window has focus");
559       return (long)iControlId;
560     }
561
562     void Window::removeControl(Control* pControl) throw (WindowException)
563     {
564       TRACE;
565       DelayedCallGuard dg(languageHook);
566       doRemoveControl(pControl,&g_graphicsContext,true);
567     }
568
569     void Window::doRemoveControl(Control* pControl, CCriticalSection* gcontext, bool wait) throw (WindowException)
570     {
571       TRACE;
572       // type checking, object should be of type Control
573       if(pControl == NULL)
574         throw WindowException("Object should be of type Control");
575
576       {
577         MaybeLock mlock(gcontext);
578         if(!ref(window)->GetControl(pControl->iControlId))
579           throw WindowException("Control does not exist in window");
580       }
581
582       // delete control from vecControls in window object
583       std::vector<AddonClass::Ref<Control> >::iterator it = vecControls.begin();
584       while (it != vecControls.end())
585       {
586         AddonClass::Ref<Control> control = (*it);
587         if (control->iControlId == pControl->iControlId)
588         {
589           it = vecControls.erase(it);
590         } else ++it;
591       }
592
593       CGUIMessage msg(GUI_MSG_REMOVE_CONTROL, 0, 0);
594       msg.SetPointer(pControl->pGUIControl);
595       CApplicationMessenger::Get().SendGUIMessage(msg, iWindowId, wait);
596
597       // initialize control to zero
598       pControl->pGUIControl = NULL;
599       pControl->iControlId = 0;
600       pControl->iParentId = 0;
601     }
602
603     void Window::removeControls(std::vector<Control*> pControls) throw (WindowException)
604     {
605       TRACE;
606       DelayedCallGuard dg(languageHook);
607       int count = 1; int size = pControls.size();
608       for (std::vector<Control*>::iterator iter = pControls.begin(); iter != pControls.end(); count++, iter++)
609         doRemoveControl(*iter,NULL, count == size);
610     }
611
612     long Window::getHeight()
613     {
614       TRACE;
615       return g_graphicsContext.GetHeight();
616     }
617
618     long Window::getWidth()
619     {
620       TRACE;
621       return g_graphicsContext.GetWidth();
622     }
623
624     long Window::getResolution()
625     {
626       TRACE;
627       return (long)g_graphicsContext.GetVideoResolution();
628     }
629
630     void Window::setCoordinateResolution(long res) throw (WindowException)
631     {
632       TRACE;
633       if (res < RES_HDTV_1080i || res > RES_AUTORES)
634         throw WindowException("Invalid resolution.");
635
636       SingleLockWithDelayGuard gslock(g_graphicsContext,languageHook);
637       ref(window)->SetCoordsRes(g_settings.m_ResInfo[res]);
638     }
639
640     void Window::setProperty(const char* key, const String& value)
641     {
642       TRACE;
643       SingleLockWithDelayGuard gslock(g_graphicsContext,languageHook);
644       CStdString lowerKey = key;
645
646       ref(window)->SetProperty(lowerKey.ToLower(), value);
647     }
648
649     String Window::getProperty(const char* key)
650     {
651       TRACE;
652       SingleLockWithDelayGuard gslock(g_graphicsContext,languageHook);
653       CStdString lowerKey = key;
654       std::string value = ref(window)->GetProperty(lowerKey.ToLower()).asString();
655       return value.c_str();
656     }
657
658     void Window::clearProperty(const char* key)
659     {
660       TRACE;
661       if (!key) return;
662       SingleLockWithDelayGuard gslock(g_graphicsContext,languageHook);
663
664       CStdString lowerKey = key;
665       ref(window)->SetProperty(lowerKey.ToLower(), "");
666     }
667
668     void Window::clearProperties()
669     {
670       TRACE;
671       SingleLockWithDelayGuard gslock(g_graphicsContext,languageHook);
672       ref(window)->ClearProperties();
673     }
674
675     void Window::close()
676     {
677       TRACE;
678       bModal = false;
679
680       if (!existingWindow)
681         PulseActionEvent();
682
683       std::vector<CStdString> params;
684       CApplicationMessenger::Get().ActivateWindow(iOldWindowId, params, false);
685
686       iOldWindowId = 0;
687     }
688
689     void Window::doModal()
690     {
691       TRACE;
692       if (!existingWindow)
693       {
694         bModal = true;
695
696         if(iWindowId != ACTIVE_WINDOW) 
697           show();
698
699         while (bModal && !g_application.m_bStop)
700         {
701 // TODO: garbear added this code to the pythin window.cpp class and
702 //  commented in XBPyThread.cpp. I'm not sure how to handle this 
703 //  in this native implementation.
704 //          // Check if XBPyThread::stop() raised a SystemExit exception
705 //          if (PyThreadState_Get()->async_exc == PyExc_SystemExit)
706 //          {
707 //            CLog::Log(LOGDEBUG, "PYTHON: doModal() encountered a SystemExit exception, closing window and returning");
708 //            Window_Close(self, NULL);
709 //            break;
710 //          }
711           languageHook->MakePendingCalls(); // MakePendingCalls
712
713           bool stillWaiting;
714           do
715           {
716             {
717               DelayedCallGuard dcguard(languageHook);            
718               stillWaiting = WaitForActionEvent(100) ? false : true;
719             }
720             languageHook->MakePendingCalls();
721           } while (stillWaiting);
722         }
723       }
724     }
725
726     void Window::addControl(Control* pControl) throw (WindowException)
727     {
728       TRACE;
729       DelayedCallGuard dg(languageHook);
730       doAddControl(pControl,&g_graphicsContext,true);
731     }
732
733     void Window::doAddControl(Control* pControl, CCriticalSection* gcontext, bool wait) throw (WindowException)
734     {
735       TRACE;
736       if(pControl == NULL)
737         throw WindowException("NULL Control passed to WindowBase::addControl");
738
739       if(pControl->iControlId != 0)
740         throw WindowException("Control is already used");
741
742       // lock xbmc GUI before accessing data from it
743       pControl->iParentId = iWindowId;
744
745       {
746         MaybeLock mlock(gcontext);
747         // assign control id, if id is already in use, try next id
748         do pControl->iControlId = ++iCurrentControlId;
749         while (ref(window)->GetControl(pControl->iControlId));
750       }
751
752       pControl->Create();
753
754       // set default navigation for control
755       pControl->iControlUp = pControl->iControlId;
756       pControl->iControlDown = pControl->iControlId;
757       pControl->iControlLeft = pControl->iControlId;
758       pControl->iControlRight = pControl->iControlId;
759
760       pControl->pGUIControl->SetNavigation(pControl->iControlUp,
761           pControl->iControlDown, pControl->iControlLeft, pControl->iControlRight);
762
763       // add control to list and allocate recources for the control
764       vecControls.push_back(AddonClass::Ref<Control>(pControl));
765       pControl->pGUIControl->AllocResources();
766
767       // This calls the CGUIWindow parent class to do the final add
768       CGUIMessage msg(GUI_MSG_ADD_CONTROL, 0, 0);
769       msg.SetPointer(pControl->pGUIControl);
770       CApplicationMessenger::Get().SendGUIMessage(msg, iWindowId, wait);
771     }
772
773     void Window::addControls(std::vector<Control*> pControls) throw (WindowException)
774     {
775       TRACE;
776       SingleLockWithDelayGuard gslock(g_graphicsContext,languageHook);
777       int count = 1; int size = pControls.size();
778       for (std::vector<Control*>::iterator iter = pControls.begin(); iter != pControls.end(); count++, iter++)
779         doAddControl(*iter,NULL, count == size);
780     }
781
782     Control* Window::getControl(int iControlId) throw (WindowException)
783     {
784       TRACE;
785       DelayedCallGuard dg(languageHook);
786       return GetControlById(iControlId,&g_graphicsContext);
787     }
788
789     void Action::setFromCAction(const CAction& action)
790     {
791       TRACE;
792       id = action.GetID();
793       buttonCode = action.GetButtonCode();
794       fAmount1 = action.GetAmount(0);
795       fAmount2 = action.GetAmount(1);
796       fRepeat = action.GetRepeat();
797       strAction = action.GetName();
798     }
799
800   }
801 }