2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
11 #include "AddonCallback.h"
12 #include "AddonString.h"
14 #include "swighelper.h"
24 // Forward declare the interceptor as the AddonWindowInterceptor.h
25 // file needs to include the Window class because of the template
26 class InterceptorBase
;
29 /// \defgroup python_xbmcgui_action Action
30 /// \ingroup python_xbmcgui
32 /// @brief **Action class.**
34 /// \python_class{ xbmcgui.Action(): }
36 /// This class serves in addition to identify carried out
37 /// \ref kodi_key_action_ids of Kodi and to be able to carry out thereby own
40 /// The data of this class are always transmitted by callback
41 /// Window::onAction at a window.
43 class Action
: public AddonClass
49 explicit Action(const CAction
& caction
) { setFromCAction(caction
); }
51 void setFromCAction(const CAction
& caction
);
54 float fAmount1
= 0.0f
;
55 float fAmount2
= 0.0f
;
57 unsigned long buttonCode
= 0;
58 std::string strAction
;
60 // Not sure if this is correct but it's here for now.
61 AddonClass::Ref
<Control
> control
; // previously pObject
64 #ifdef DOXYGEN_SHOULD_USE_THIS
66 /// \ingroup python_xbmcgui_action
67 /// @brief \python_func{ getId() }
68 /// To get \ref kodi_key_action_ids
70 /// This function returns the identification code used by the explained
71 /// order, it is necessary to determine the type of command from
72 /// \ref kodi_key_action_ids.
74 /// @return The action's current id as a long or 0 if
75 /// no action is mapped in the xml's.
78 ///-----------------------------------------------------------------------
81 /// ~~~~~~~~~~~~~{.py}
83 /// def onAction(self, action):
84 /// if action.getId() == ACTION_PREVIOUS_MENU:
85 /// print('action received: previous')
91 long getId() { XBMC_TRACE
; return id
; }
94 #ifdef DOXYGEN_SHOULD_USE_THIS
96 /// \ingroup python_xbmcgui_action
97 /// @brief \python_func{ getButtonCode() }
98 /// Returns the button code for this action.
100 /// @return [integer] button code
104 long getButtonCode() { XBMC_TRACE
; return buttonCode
; }
107 #ifdef DOXYGEN_SHOULD_USE_THIS
109 /// \ingroup python_xbmcgui_action
110 /// @brief \python_func{ getAmount1() }
111 /// Returns the first amount of force applied to the thumbstick.
113 /// @return [float] first amount
117 float getAmount1() { XBMC_TRACE
; return fAmount1
; }
120 #ifdef DOXYGEN_SHOULD_USE_THIS
122 /// \ingroup python_xbmcgui_action
123 /// @brief \python_func{ getAmount2() }
124 /// Returns the second amount of force applied to the thumbstick.
126 /// @return [float] second amount
130 float getAmount2() { XBMC_TRACE
; return fAmount2
; }
135 //==========================================================================
136 // This is the main class for the xbmcgui.Window functionality. It is tied
137 // into the main Kodi windowing system via the Interceptor
138 //==========================================================================
140 /// \defgroup python_xbmcgui_window Window
141 /// \ingroup python_xbmcgui
143 /// @brief __GUI window class for Add-Ons.__
145 /// This class allows over their functions to create and edit windows that
146 /// can be accessed from an Add-On.
148 /// Likewise, all functions from here as well in the other window classes
149 /// \ref python_xbmcgui_window_dialog "WindowDialog",
150 /// \ref python_xbmcgui_window_xml "WindowXML" and
151 /// \ref python_xbmcgui_window_dialog_xml "WindowXMLDialog"
152 /// with inserted and available.
155 ///--------------------------------------------------------------------------
156 /// Constructor for window
157 /// ----------------------------
159 /// \python_class{ xbmcgui.Window([existingWindowId]): }
161 /// Creates a new from Add-On usable window class. This is to create
162 /// window for related controls by system calls.
164 /// @param existingWindowId [opt] Specify an id to use an existing
166 /// @throws ValueError if supplied window Id does not exist.
167 /// @throws Exception if more then 200 windows are created.
169 /// Deleting this window will activate the old window that was active
170 /// and resets (not delete) all controls that are associated with this
174 ///--------------------------------------------------------------------------
177 /// ~~~~~~~~~~~~~{.py}
179 /// win = xbmcgui.Window()
180 /// width = win.getWidth()
186 class Window
: public AddonCallback
188 friend class WindowDialogMixin
;
189 bool isDisposed
= false;
191 void doAddControl(Control
* pControl
, CCriticalSection
* gcontext
, bool wait
);
192 void doRemoveControl(Control
* pControl
, CCriticalSection
* gcontext
, bool wait
);
196 InterceptorBase
* window
;
199 std::vector
<AddonClass::Ref
<Control
> > vecControls
;
200 int iOldWindowId
= 0;
201 int iCurrentControlId
= 3000;
203 CEvent m_actionEvent
;
205 bool canPulse
= false;
207 // I REALLY hate this ... but it's the simplest fix right now.
208 bool existingWindow
= true;
209 bool destroyAfterDeInit
= false;
212 * This only takes a boolean to allow subclasses to explicitly use it. A
213 * default constructor can be used as a concrete class and we need to tell
215 * subclasses should use this constructor and not the other.
217 explicit Window(bool discrim
);
219 void deallocating() override
;
222 * This helper retrieves the next available id. It is assumed that the
223 * global lock is already being held.
225 static int getNextAvailableWindowId();
228 * Child classes MUST call this in their constructors. It should be an
229 * instance of Interceptor<P extends CGUIWindow>. Control of memory
230 * management for this class is then given to the Window.
232 void setWindow(InterceptorBase
* _window
);
235 * This is a helper method since popping the previous window id is a common
238 void popActiveWindowId();
241 * This is a helper method since getting a control by it's id is a common
244 Control
* GetControlById(int iControlId
, CCriticalSection
* gc
);
246 SWIGHIDDENVIRTUAL
void PulseActionEvent();
247 SWIGHIDDENVIRTUAL
bool WaitForActionEvent(unsigned int milliseconds
);
251 explicit Window(int existingWindowId
= -1);
256 SWIGHIDDENVIRTUAL
bool OnMessage(CGUIMessage
& message
);
257 SWIGHIDDENVIRTUAL
bool OnAction(const CAction
&action
);
258 SWIGHIDDENVIRTUAL
bool OnBack(int actionId
);
259 SWIGHIDDENVIRTUAL
void OnDeinitWindow(int nextWindowID
);
261 SWIGHIDDENVIRTUAL
bool IsDialogRunning() const
266 SWIGHIDDENVIRTUAL
bool IsDialog() const
271 SWIGHIDDENVIRTUAL
bool IsModalDialog() const
276 SWIGHIDDENVIRTUAL
bool IsMediaWindow() const
281 SWIGHIDDENVIRTUAL
void dispose();
284 * This is called from the InterceptorBase destructor to prevent further
285 * use of the interceptor from the window.
287 inline void interceptorClear()
289 std::unique_lock
<CCriticalSection
> lock(*this);
295 /// @defgroup python_xbmcgui_window_cb Callback functions from Kodi to add-on
296 /// \ingroup python_xbmcgui_window
298 /// @brief __GUI window callback functions.__
300 /// Functions to handle control callbacks from Kodi.
302 /// Likewise, all functions from here as well in the all window classes
303 /// (\ref python_xbmcgui_window "Window",
304 /// \ref python_xbmcgui_window_dialog "WindowDialog",
305 /// \ref python_xbmcgui_window_xml "WindowXML" and
306 /// \ref python_xbmcgui_window_dialog_xml "WindowXMLDialog") with inserted
309 /// ------------------------------------------------------------------------
311 /// @link python_xbmcgui_window Go back to normal functions from window@endlink
314 // callback takes a parameter
315 #ifdef DOXYGEN_SHOULD_USE_THIS
317 /// \ingroup python_xbmcgui_window_cb
318 /// @brief \python_func{ onAction(self, Action action) }
319 /// **onAction method.**
321 /// This method will receive all actions that the main program will send
324 /// @param self Own base class pointer
325 /// @param action The action id to perform, see
326 /// \ref python_xbmcgui_action to get use
330 /// - By default, only the `PREVIOUS_MENU` and `NAV_BACK actions` are
332 /// - Overwrite this method to let your script handle all actions.
333 /// - Don't forget to capture `ACTION_PREVIOUS_MENU` or `ACTION_NAV_BACK`,
334 /// else the user can't close this window.
337 ///-----------------------------------------------------------------------
340 /// ~~~~~~~~~~~~~{.py}
342 /// # Define own function where becomes called from Kodi
343 /// def onAction(self, action):
344 /// if action.getId() == ACTION_PREVIOUS_MENU:
345 /// print('action received: previous')
347 /// if action.getId() == ACTION_SHOW_INFO:
348 /// print('action received: show info')
349 /// if action.getId() == ACTION_STOP:
350 /// print('action received: stop')
351 /// if action.getId() == ACTION_PAUSE:
352 /// print('action received: pause')
358 virtual void onAction(Action
* action
);
361 // on control is not actually on Window in the api but is called into Python anyway.
362 #ifdef DOXYGEN_SHOULD_USE_THIS
364 /// \ingroup python_xbmcgui_window_cb
365 /// @brief \python_func{ onControl(self, Control) }
366 /// **onControl method.**
368 /// This method will receive all click events on owned and selected
369 /// controls when the control itself doesn't handle the message.
371 /// @param self Own base class pointer
372 /// @param control The \ref python_xbmcgui_control "Control" class
375 ///-----------------------------------------------------------------------
378 /// ~~~~~~~~~~~~~{.py}
380 /// # Define own function where becomes called from Kodi
381 /// def onControl(self, control):
382 /// print("Window.onControl(control=[%s])"%control)
388 virtual void onControl(Control
* control
);
391 #ifdef DOXYGEN_SHOULD_USE_THIS
393 /// \ingroup python_xbmcgui_window_cb
394 /// @brief \python_func{ onClick(self, int controlId) }
395 /// **onClick method.**
397 /// This method will receive all click events that the main program will
398 /// send to this window.
400 /// @param self Own base class pointer
401 /// @param controlId The one time clicked GUI control
405 ///-----------------------------------------------------------------------
408 /// ~~~~~~~~~~~~~{.py}
410 /// # Define own function where becomes called from Kodi
411 /// def onClick(self,controlId):
412 /// if controlId == 10:
413 /// print("The control with Id 10 is clicked")
419 virtual void onClick(int controlId
);
422 #ifdef DOXYGEN_SHOULD_USE_THIS
424 /// \ingroup python_xbmcgui_window_cb
425 /// @brief \python_func{ onDoubleClick(self, int controlId) }
426 /// __onDoubleClick method.__
428 /// This method will receive all double click events that the main program
429 /// will send to this window.
431 /// @param self Own base class pointer
432 /// @param controlId The double clicked GUI control
436 ///-----------------------------------------------------------------------
439 /// ~~~~~~~~~~~~~{.py}
441 /// # Define own function where becomes called from Kodi
442 /// def onDoubleClick(self,controlId):
443 /// if controlId == 10:
444 /// print("The control with Id 10 is double clicked")
450 virtual void onDoubleClick(int controlId
);
453 #ifdef DOXYGEN_SHOULD_USE_THIS
455 /// \ingroup python_xbmcgui_window_cb
456 /// @brief \python_func{ onFocus(self, int controlId) }
457 /// __onFocus method.__
459 /// This method will receive all focus events that the main program will
460 /// send to this window.
462 /// @param self Own base class pointer
463 /// @param controlId The focused GUI control identifier
466 ///-----------------------------------------------------------------------
469 /// ~~~~~~~~~~~~~{.py}
471 /// # Define own function where becomes called from Kodi
472 /// def onDoubleClick(self,controlId):
473 /// if controlId == 10:
474 /// print("The control with Id 10 is focused")
480 virtual void onFocus(int controlId
);
483 #ifdef DOXYGEN_SHOULD_USE_THIS
485 /// \ingroup python_xbmcgui_window_cb
486 /// @brief \python_func{ onInit(self) }
487 /// __onInit method.__
489 /// This method will be called to initialize the window
491 /// @param self Own base class pointer
494 ///-----------------------------------------------------------------------
497 /// ~~~~~~~~~~~~~{.py}
499 /// # Define own function where becomes called from Kodi
500 /// def onInit(self):
501 /// print("Window.onInit method called from Kodi")
507 virtual void onInit();
511 #ifdef DOXYGEN_SHOULD_USE_THIS
513 /// \ingroup python_xbmcgui_window
514 /// @brief \python_func{ show() }
515 /// Show this window.
517 /// Shows this window by activating it, calling close() after it wil
518 /// activate the current window again.
520 /// @note If your script ends this window will be closed to. To show it
521 /// forever, make a loop at the end of your script or use doModal()
526 SWIGHIDDENVIRTUAL
void show();
529 #ifdef DOXYGEN_SHOULD_USE_THIS
531 /// \ingroup python_xbmcgui_window
532 /// @brief \python_func{ setFocus(Control) }
533 /// Give the supplied control focus.
535 /// @param Control \ref python_xbmcgui_control "Control" class to focus
536 /// @throws TypeError If supplied argument is not a \ref python_xbmcgui_control "Control"
538 /// @throws SystemError On Internal error
539 /// @throws RuntimeError If control is not added to a window
543 SWIGHIDDENVIRTUAL
void setFocus(Control
* pControl
);
546 #ifdef DOXYGEN_SHOULD_USE_THIS
548 /// \ingroup python_xbmcgui_window
549 /// @brief \python_func{ setFocusId(ControlId) }
550 /// Gives the control with the supplied focus.
552 /// @param ControlId [integer] On skin defined id of control
553 /// @throws SystemError On Internal error
554 /// @throws RuntimeError If control is not added to a window
558 SWIGHIDDENVIRTUAL
void setFocusId(int iControlId
);
561 #ifdef DOXYGEN_SHOULD_USE_THIS
563 /// \ingroup python_xbmcgui_window
564 /// @brief \python_func{ getFocus(Control) }
565 /// Returns the control which is focused.
567 /// @return Focused control class
568 /// @throws SystemError On Internal error
569 /// @throws RuntimeError If no control has focus
573 SWIGHIDDENVIRTUAL Control
* getFocus();
576 #ifdef DOXYGEN_SHOULD_USE_THIS
578 /// \ingroup python_xbmcgui_window
579 /// @brief \python_func{ getFocusId(int) }
580 /// Returns the id of the control which is focused.
582 /// @return Focused control id
583 /// @throws SystemError On Internal error
584 /// @throws RuntimeError If no control has focus
588 SWIGHIDDENVIRTUAL
long getFocusId();
591 #ifdef DOXYGEN_SHOULD_USE_THIS
593 /// \ingroup python_xbmcgui_window
594 /// @brief \python_func{ removeControl(Control) }
595 /// Removes the control from this window.
597 /// @param Control \ref python_xbmcgui_control "Control" class to remove
598 /// @throws TypeError If supplied argument is not a \ref python_xbmcgui_control
600 /// @throws RuntimeError If control is not added to this window
602 /// This will not delete the control. It is only removed from the window.
606 SWIGHIDDENVIRTUAL
void removeControl(Control
* pControl
);
609 #ifdef DOXYGEN_SHOULD_USE_THIS
611 /// \ingroup python_xbmcgui_window
612 /// @brief \python_func{ removeControls(List) }
613 /// Removes a list of controls from this window.
615 /// @param List List with controls to remove
616 /// @throws TypeError If supplied argument is not a \ref python_xbmcgui_control
618 /// @throws RuntimeError If control is not added to this window
620 /// This will not delete the controls. They are only removed from the
625 SWIGHIDDENVIRTUAL
void removeControls(std::vector
<Control
*> pControls
);
628 #ifdef DOXYGEN_SHOULD_USE_THIS
630 /// \ingroup python_xbmcgui_window
631 /// @brief \python_func{ getHeight() }
632 /// Returns the height of this Window instance.
634 /// @return Window height in pixels
636 ///-----------------------------------------------------------------------
637 /// @python_v18 Function changed
641 SWIGHIDDENVIRTUAL
long getHeight();
644 #ifdef DOXYGEN_SHOULD_USE_THIS
646 /// \ingroup python_xbmcgui_window
647 /// @brief \python_func{ getWidth() }
648 /// Returns the width of this Window instance.
650 /// @return Window width in pixels
652 ///-----------------------------------------------------------------------
653 /// @python_v18 Function changed
657 SWIGHIDDENVIRTUAL
long getWidth();
660 #ifdef DOXYGEN_SHOULD_USE_THIS
662 /// \ingroup python_xbmcgui_window
663 /// @brief \python_func{ setProperty(key, value) }
664 /// Sets a window property, similar to an infolabel.
666 /// @param key string - property name.
667 /// @param value string or unicode - value of property.
669 /// @note Key is NOT case sensitive. Setting value to an empty string is
670 /// equivalent to clearProperty(key).\n
671 /// You can use the above as keywords for arguments and skip
672 /// certain optional arguments.\n
673 /// Once you use a keyword, all following arguments require the
677 ///-----------------------------------------------------------------------
680 /// ~~~~~~~~~~~~~{.py}
682 /// win = xbmcgui.Window(xbmcgui.getCurrentWindowId())
683 /// win.setProperty('Category', 'Newest')
689 SWIGHIDDENVIRTUAL
void setProperty(const char* key
, const String
& value
);
692 #ifdef DOXYGEN_SHOULD_USE_THIS
694 /// \ingroup python_xbmcgui_window
695 /// @brief \python_func{ getProperty(key) }
696 /// Returns a window property as a string, similar to an infolabel.
698 /// @param key string - property name.
700 /// @note Key is NOT case sensitive.\n
701 /// You can use the above as keywords for arguments and skip
702 /// certain optional arguments.
703 /// Once you use a keyword, all following arguments require the
707 ///-----------------------------------------------------------------------
710 /// ~~~~~~~~~~~~~{.py}
712 /// win = xbmcgui.Window(xbmcgui.getCurrentWindowId())
713 /// category = win.getProperty('Category')
719 SWIGHIDDENVIRTUAL String
getProperty(const char* key
);
722 #ifdef DOXYGEN_SHOULD_USE_THIS
724 /// \ingroup python_xbmcgui_window
725 /// @brief \python_func{ clearProperty(key) }
726 /// Clears the specific window property.
728 /// @param key string - property name.
730 /// @note Key is NOT case sensitive. Equivalent to setProperty(key,'')
731 /// You can use the above as keywords for arguments and skip certain
732 /// optional arguments.
733 /// Once you use a keyword, all following arguments require the
737 ///-----------------------------------------------------------------------
740 /// ~~~~~~~~~~~~~{.py}
742 /// win = xbmcgui.Window(xbmcgui.getCurrentWindowId())
743 /// win.clearProperty('Category')
749 SWIGHIDDENVIRTUAL
void clearProperty(const char* key
);
752 #ifdef DOXYGEN_SHOULD_USE_THIS
754 /// \ingroup python_xbmcgui_window
755 /// @brief \python_func{ clearProperties() }
756 /// Clears all window properties.
759 ///-----------------------------------------------------------------------
762 /// ~~~~~~~~~~~~~{.py}
764 /// win = xbmcgui.Window(xbmcgui.getCurrentWindowId())
765 /// win.clearProperties()
771 SWIGHIDDENVIRTUAL
void clearProperties();
774 #ifdef DOXYGEN_SHOULD_USE_THIS
776 /// \ingroup python_xbmcgui_window
777 /// @brief \python_func{ close() }
778 /// Closes this window.
780 /// Closes this window by activating the old window.
782 /// @note The window is not deleted with this method.
786 SWIGHIDDENVIRTUAL
void close();
789 #ifdef DOXYGEN_SHOULD_USE_THIS
791 /// \ingroup python_xbmcgui_window
792 /// @brief \python_func{ doModal() }
793 /// Display this window until close() is called.
798 SWIGHIDDENVIRTUAL
void doModal();
801 #ifdef DOXYGEN_SHOULD_USE_THIS
803 /// \ingroup python_xbmcgui_window
804 /// @brief \python_func{ addControl(Control) }
805 /// Add a \ref python_xbmcgui_control "Control" to this window.
807 /// @param Control \ref python_xbmcgui_control "Control" to add
808 /// @throws TypeError If supplied argument is not a \ref python_xbmcgui_control
810 /// @throws ReferenceError If control is already used in another
812 /// @throws RuntimeError Should not happen :-)
814 /// The next controls can be added to a window atm
815 /// | Control-class | Description |
816 /// |---------------------|------------------------------------------------------------|
817 /// | \ref python_xbmcgui_control_label "ControlLabel" | Label control to show text
818 /// | \ref python_xbmcgui_control_fadelabel "ControlFadeLabel" | The fadelabel has multiple labels which it cycles through
819 /// | \ref python_xbmcgui_control_textbox "ControlTextBox" | To show bigger text field
820 /// | \ref python_xbmcgui_control_button "ControlButton" | Brings a button to do some actions
821 /// | \ref python_xbmcgui_control_edit "ControlEdit" | The edit control allows a user to input text in Kodi
822 /// | \ref python_xbmcgui_control_fadelabel "ControlFadeLabel" | The fade label control is used for displaying multiple pieces of text in the same space in Kodi
823 /// | \ref python_xbmcgui_control_list "ControlList" | Add a list for something like files
824 /// | \ref python_xbmcgui_control_group "ControlGroup" | Is for a group which brings the others together
825 /// | \ref python_xbmcgui_control_image "ControlImage" | Controls a image on skin
826 /// | \ref python_xbmcgui_control_radiobutton "ControlRadioButton" | For a radio button which handle boolean values
827 /// | \ref python_xbmcgui_control_progress "ControlProgress" | Progress bar for a performed work or something else
828 /// | \ref python_xbmcgui_control_slider "ControlSlider" | The slider control is used for things where a sliding bar best represents the operation at hand
829 /// | \ref python_xbmcgui_control_spin "ControlSpin" | The spin control is used for when a list of options can be chosen
830 /// | \ref python_xbmcgui_control_textbox "ControlTextBox" | The text box is used for showing a large multipage piece of text in Kodi
834 SWIGHIDDENVIRTUAL
void addControl(Control
* pControl
);
837 #ifdef DOXYGEN_SHOULD_USE_THIS
839 /// \ingroup python_xbmcgui_window
840 /// @brief \python_func{ addControls(List) }
841 /// Add a list of Controls to this window.
843 /// @param List List with controls to add
844 /// @throws TypeError If supplied argument is not of List
845 /// type, or a control is not of \ref python_xbmcgui_control
847 /// @throws ReferenceError If control is already used in another
849 /// @throws RuntimeError Should not happen :-)
853 SWIGHIDDENVIRTUAL
void addControls(std::vector
<Control
*> pControls
);
856 #ifdef DOXYGEN_SHOULD_USE_THIS
858 /// \ingroup python_xbmcgui_window
859 /// @brief \python_func{ getControl(controlId) }
860 /// Gets the control from this window.
862 /// @param controlId \ref python_xbmcgui_control id to get
863 /// @throws Exception If \ref python_xbmcgui_control doesn't exist
865 /// @remark controlId doesn't have to be a python control, it can be a
866 /// control id from a Kodi window too (you can find id's in the xml files.
868 /// @note Not python controls are not completely usable yet
869 /// You can only use the \ref python_xbmcgui_control functions
873 SWIGHIDDENVIRTUAL Control
* getControl(int iControlId
);