1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "addons/kodi-dev-kit/include/kodi/gui/Window.h"
10 
11 #include "Application.h"
12 #include "FileItem.h"
13 #include "GUITranslator.h"
14 #include "General.h"
15 #include "ServiceBroker.h"
16 #include "Window.h"
17 #include "addons/Skin.h"
18 #include "addons/binary-addons/AddonDll.h"
19 #include "controls/Rendering.h"
20 #include "filesystem/File.h"
21 #include "guilib/GUIComponent.h"
22 #include "guilib/GUIRenderingControl.h"
23 #include "guilib/GUIWindowManager.h"
24 #include "guilib/TextureManager.h"
25 #include "input/Key.h"
26 #include "messaging/ApplicationMessenger.h"
27 #include "utils/StringUtils.h"
28 #include "utils/URIUtils.h"
29 #include "utils/Variant.h"
30 #include "utils/log.h"
31 
32 using namespace ADDON;
33 using namespace KODI::MESSAGING;
34 
35 namespace ADDON
36 {
37 
Init(AddonGlobalInterface * addonInterface)38 void Interface_GUIWindow::Init(AddonGlobalInterface* addonInterface)
39 {
40   addonInterface->toKodi->kodi_gui->window = new AddonToKodiFuncTable_kodi_gui_window();
41 
42   /* Window creation functions */
43   addonInterface->toKodi->kodi_gui->window->create = create;
44   addonInterface->toKodi->kodi_gui->window->destroy = destroy;
45   addonInterface->toKodi->kodi_gui->window->set_callbacks = set_callbacks;
46   addonInterface->toKodi->kodi_gui->window->show = show;
47   addonInterface->toKodi->kodi_gui->window->close = close;
48   addonInterface->toKodi->kodi_gui->window->do_modal = do_modal;
49 
50   /* Window control functions */
51   addonInterface->toKodi->kodi_gui->window->set_focus_id = set_focus_id;
52   addonInterface->toKodi->kodi_gui->window->get_focus_id = get_focus_id;
53   addonInterface->toKodi->kodi_gui->window->set_control_label = set_control_label;
54   addonInterface->toKodi->kodi_gui->window->set_control_visible = set_control_visible;
55   addonInterface->toKodi->kodi_gui->window->set_control_selected = set_control_selected;
56 
57   /* Window property functions */
58   addonInterface->toKodi->kodi_gui->window->set_property = set_property;
59   addonInterface->toKodi->kodi_gui->window->set_property_int = set_property_int;
60   addonInterface->toKodi->kodi_gui->window->set_property_bool = set_property_bool;
61   addonInterface->toKodi->kodi_gui->window->set_property_double = set_property_double;
62   addonInterface->toKodi->kodi_gui->window->get_property = get_property;
63   addonInterface->toKodi->kodi_gui->window->get_property_int = get_property_int;
64   addonInterface->toKodi->kodi_gui->window->get_property_bool = get_property_bool;
65   addonInterface->toKodi->kodi_gui->window->get_property_double = get_property_double;
66   addonInterface->toKodi->kodi_gui->window->clear_properties = clear_properties;
67   addonInterface->toKodi->kodi_gui->window->clear_property = clear_property;
68 
69   /* List item functions */
70   addonInterface->toKodi->kodi_gui->window->clear_item_list = clear_item_list;
71   addonInterface->toKodi->kodi_gui->window->add_list_item = add_list_item;
72   addonInterface->toKodi->kodi_gui->window->remove_list_item_from_position =
73       remove_list_item_from_position;
74   addonInterface->toKodi->kodi_gui->window->remove_list_item = remove_list_item;
75   addonInterface->toKodi->kodi_gui->window->get_list_item = get_list_item;
76   addonInterface->toKodi->kodi_gui->window->set_current_list_position = set_current_list_position;
77   addonInterface->toKodi->kodi_gui->window->get_current_list_position = get_current_list_position;
78   addonInterface->toKodi->kodi_gui->window->get_list_size = get_list_size;
79   addonInterface->toKodi->kodi_gui->window->set_container_property = set_container_property;
80   addonInterface->toKodi->kodi_gui->window->set_container_content = set_container_content;
81   addonInterface->toKodi->kodi_gui->window->get_current_container_id = get_current_container_id;
82 
83   /* Various functions */
84   addonInterface->toKodi->kodi_gui->window->mark_dirty_region = mark_dirty_region;
85 
86   /* GUI control access functions */
87   addonInterface->toKodi->kodi_gui->window->get_control_button = get_control_button;
88   addonInterface->toKodi->kodi_gui->window->get_control_edit = get_control_edit;
89   addonInterface->toKodi->kodi_gui->window->get_control_fade_label = get_control_fade_label;
90   addonInterface->toKodi->kodi_gui->window->get_control_image = get_control_image;
91   addonInterface->toKodi->kodi_gui->window->get_control_label = get_control_label;
92   addonInterface->toKodi->kodi_gui->window->get_control_progress = get_control_progress;
93   addonInterface->toKodi->kodi_gui->window->get_control_radio_button = get_control_radio_button;
94   addonInterface->toKodi->kodi_gui->window->get_control_render_addon = get_control_render_addon;
95   addonInterface->toKodi->kodi_gui->window->get_control_settings_slider =
96       get_control_settings_slider;
97   addonInterface->toKodi->kodi_gui->window->get_control_slider = get_control_slider;
98   addonInterface->toKodi->kodi_gui->window->get_control_spin = get_control_spin;
99   addonInterface->toKodi->kodi_gui->window->get_control_text_box = get_control_text_box;
100 }
101 
DeInit(AddonGlobalInterface * addonInterface)102 void Interface_GUIWindow::DeInit(AddonGlobalInterface* addonInterface)
103 {
104   delete addonInterface->toKodi->kodi_gui->window;
105 }
106 
107 /*!
108  * Window creation functions
109  */
110 //@{
create(KODI_HANDLE kodiBase,const char * xml_filename,const char * default_skin,bool as_dialog,bool is_media)111 KODI_GUI_WINDOW_HANDLE Interface_GUIWindow::create(KODI_HANDLE kodiBase,
112                                                    const char* xml_filename,
113                                                    const char* default_skin,
114                                                    bool as_dialog,
115                                                    bool is_media)
116 {
117   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
118   if (!addon || !xml_filename || !default_skin)
119   {
120     CLog::Log(LOGERROR,
121               "Interface_GUIWindow::{} - invalid handler data (xml_filename='{}', "
122               "default_skin='{}') on addon '{}'",
123               __func__, static_cast<const void*>(xml_filename),
124               static_cast<const void*>(default_skin), addon ? addon->ID() : "unknown");
125     return nullptr;
126   }
127 
128   if (as_dialog && is_media)
129   {
130     CLog::Log(LOGWARNING,
131               "Interface_GUIWindow::{}: {}/{} - addon tries to create dialog as media window who "
132               "not allowed, contact Developer '{}' of this addon",
133               __func__, CAddonInfo::TranslateType(addon->Type()), addon->Name(), addon->Author());
134   }
135 
136   RESOLUTION_INFO res;
137   std::string strSkinPath = g_SkinInfo->GetSkinPath(xml_filename, &res);
138 
139   if (!XFILE::CFile::Exists(strSkinPath))
140   {
141     std::string str("none");
142     ADDON::AddonInfoPtr addonInfo = std::make_shared<ADDON::CAddonInfo>(str, ADDON::ADDON_SKIN);
143 
144     // Check for the matching folder for the skin in the fallback skins folder
145     std::string fallbackPath = URIUtils::AddFileToFolder(addon->Path(), "resources", "skins");
146     std::string basePath = URIUtils::AddFileToFolder(fallbackPath, g_SkinInfo->ID());
147 
148     strSkinPath = g_SkinInfo->GetSkinPath(xml_filename, &res, basePath);
149 
150     // Check for the matching folder for the skin in the fallback skins folder (if it exists)
151     if (XFILE::CFile::Exists(basePath))
152     {
153       addonInfo->SetPath(basePath);
154       const std::shared_ptr<ADDON::CSkinInfo> skinInfo =
155           std::make_shared<ADDON::CSkinInfo>(addonInfo, res);
156       skinInfo->Start();
157       strSkinPath = skinInfo->GetSkinPath(xml_filename, &res);
158     }
159 
160     if (!XFILE::CFile::Exists(strSkinPath))
161     {
162       // Finally fallback to the DefaultSkin as it didn't exist in either the Kodi Skin folder or the fallback skin folder
163       addonInfo->SetPath(URIUtils::AddFileToFolder(fallbackPath, default_skin));
164       const std::shared_ptr<ADDON::CSkinInfo> skinInfo =
165           std::make_shared<ADDON::CSkinInfo>(addonInfo, res);
166 
167       skinInfo->Start();
168       strSkinPath = skinInfo->GetSkinPath(xml_filename, &res);
169       if (!XFILE::CFile::Exists(strSkinPath))
170       {
171         CLog::Log(LOGERROR,
172                   "Interface_GUIWindow::{}: {}/{} - XML File '{}' for Window is missing, contact "
173                   "Developer '{}' of this addon",
174                   __func__, CAddonInfo::TranslateType(addon->Type()), addon->Name(), strSkinPath,
175                   addon->Author());
176         return nullptr;
177       }
178     }
179   }
180 
181   int id = GetNextAvailableWindowId();
182   if (id < 0)
183     return nullptr;
184 
185   CGUIWindow* window;
186   if (!as_dialog)
187     window = new CGUIAddonWindow(id, strSkinPath, addon, is_media);
188   else
189     window = new CGUIAddonWindowDialog(id, strSkinPath, addon);
190 
191   Interface_GUIGeneral::lock();
192   CServiceBroker::GetGUI()->GetWindowManager().Add(window);
193   Interface_GUIGeneral::unlock();
194 
195   if (!CServiceBroker::GetGUI()->GetWindowManager().GetWindow(id))
196   {
197     CLog::Log(LOGERROR,
198               "Interface_GUIWindow::{} - Requested window id '{}' does not exist for addon '{}'",
199               __func__, id, addon->ID());
200     delete window;
201     return nullptr;
202   }
203   window->SetCoordsRes(res);
204   return window;
205 }
206 
destroy(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle)207 void Interface_GUIWindow::destroy(KODI_HANDLE kodiBase, KODI_GUI_WINDOW_HANDLE handle)
208 {
209   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
210   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
211   if (!addon || !pAddonWindow)
212   {
213     CLog::Log(LOGERROR,
214               "Interface_GUIWindow::{} - invalid handler data (handle='{}') on addon '{}'",
215               __func__, handle, addon ? addon->ID() : "unknown");
216     return;
217   }
218 
219   Interface_GUIGeneral::lock();
220   CGUIWindow* pWindow =
221       CServiceBroker::GetGUI()->GetWindowManager().GetWindow(pAddonWindow->GetID());
222   if (pWindow)
223   {
224     // first change to an existing window
225     if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == pAddonWindow->GetID() &&
226         !g_application.m_bStop)
227     {
228       if (CServiceBroker::GetGUI()->GetWindowManager().GetWindow(pAddonWindow->m_oldWindowId))
229         CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(pAddonWindow->m_oldWindowId);
230       else // old window does not exist anymore, switch to home
231         CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_HOME);
232     }
233     // Free any window properties
234     pAddonWindow->ClearProperties();
235     // free the window's resources and unload it (free all guicontrols)
236     pAddonWindow->FreeResources(true);
237 
238     CServiceBroker::GetGUI()->GetWindowManager().Remove(pAddonWindow->GetID());
239   }
240   delete pAddonWindow;
241   Interface_GUIGeneral::unlock();
242 }
243 
set_callbacks(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,KODI_GUI_CLIENT_HANDLE clienthandle,bool (* CBOnInit)(KODI_GUI_CLIENT_HANDLE),bool (* CBOnFocus)(KODI_GUI_CLIENT_HANDLE,int),bool (* CBOnClick)(KODI_GUI_CLIENT_HANDLE,int),bool (* CBOnAction)(KODI_GUI_CLIENT_HANDLE,ADDON_ACTION),void (* CBGetContextButtons)(KODI_GUI_CLIENT_HANDLE,int,gui_context_menu_pair *,unsigned int *),bool (* CBOnContextButton)(KODI_GUI_CLIENT_HANDLE,int,unsigned int))244 void Interface_GUIWindow::set_callbacks(
245     KODI_HANDLE kodiBase,
246     KODI_GUI_WINDOW_HANDLE handle,
247     KODI_GUI_CLIENT_HANDLE clienthandle,
248     bool (*CBOnInit)(KODI_GUI_CLIENT_HANDLE),
249     bool (*CBOnFocus)(KODI_GUI_CLIENT_HANDLE, int),
250     bool (*CBOnClick)(KODI_GUI_CLIENT_HANDLE, int),
251     bool (*CBOnAction)(KODI_GUI_CLIENT_HANDLE, ADDON_ACTION),
252     void (*CBGetContextButtons)(KODI_GUI_CLIENT_HANDLE, int, gui_context_menu_pair*, unsigned int*),
253     bool (*CBOnContextButton)(KODI_GUI_CLIENT_HANDLE, int, unsigned int))
254 {
255   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
256   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
257   if (!addon || !pAddonWindow || !clienthandle)
258   {
259     CLog::Log(LOGERROR,
260               "Interface_GUIWindow::{} - invalid handler data (handle='{}', clienthandle='{}') "
261               "on addon '{}'",
262               __func__, handle, clienthandle, addon ? addon->ID() : "unknown");
263     return;
264   }
265 
266   Interface_GUIGeneral::lock();
267   pAddonWindow->m_clientHandle = clienthandle;
268   pAddonWindow->CBOnInit = CBOnInit;
269   pAddonWindow->CBOnClick = CBOnClick;
270   pAddonWindow->CBOnFocus = CBOnFocus;
271   pAddonWindow->CBOnAction = CBOnAction;
272   pAddonWindow->CBGetContextButtons = CBGetContextButtons;
273   pAddonWindow->CBOnContextButton = CBOnContextButton;
274   Interface_GUIGeneral::unlock();
275 }
276 
show(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle)277 bool Interface_GUIWindow::show(KODI_HANDLE kodiBase, KODI_GUI_WINDOW_HANDLE handle)
278 {
279   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
280   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
281   if (!addon || !pAddonWindow)
282   {
283     CLog::Log(LOGERROR,
284               "Interface_GUIWindow::{} - invalid handler data (handle='{}') on addon '{}'",
285               __func__, handle, addon ? addon->ID() : "unknown");
286     return false;
287   }
288 
289   if (pAddonWindow->m_oldWindowId != pAddonWindow->m_windowId &&
290       pAddonWindow->m_windowId != CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow())
291     pAddonWindow->m_oldWindowId = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
292 
293   Interface_GUIGeneral::lock();
294   if (pAddonWindow->IsDialog())
295     dynamic_cast<CGUIAddonWindowDialog*>(pAddonWindow)->Show(true, false);
296   else
297     CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(pAddonWindow->GetID());
298   Interface_GUIGeneral::unlock();
299 
300   return true;
301 }
302 
close(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle)303 bool Interface_GUIWindow::close(KODI_HANDLE kodiBase, KODI_GUI_WINDOW_HANDLE handle)
304 {
305   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
306   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
307   if (!addon || !pAddonWindow)
308   {
309     CLog::Log(LOGERROR,
310               "Interface_GUIWindow::{} - invalid handler data (handle='{}') on addon '{}'",
311               __func__, handle, addon ? addon->ID() : "unknown");
312     return false;
313   }
314 
315   pAddonWindow->PulseActionEvent();
316 
317   Interface_GUIGeneral::lock();
318 
319   // if it's a dialog, we have to close it a bit different
320   if (pAddonWindow->IsDialog())
321     dynamic_cast<CGUIAddonWindowDialog*>(pAddonWindow)->Show(false);
322   else
323     CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(pAddonWindow->m_oldWindowId);
324   pAddonWindow->m_oldWindowId = 0;
325 
326   Interface_GUIGeneral::unlock();
327 
328   return true;
329 }
330 
do_modal(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle)331 bool Interface_GUIWindow::do_modal(KODI_HANDLE kodiBase, KODI_GUI_WINDOW_HANDLE handle)
332 {
333   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
334   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
335   if (!addon || !pAddonWindow)
336   {
337     CLog::Log(LOGERROR,
338               "Interface_GUIWindow::{} - invalid handler data (handle='{}') on addon '{}'",
339               __func__, handle, addon ? addon->ID() : "unknown");
340     return false;
341   }
342 
343   if (pAddonWindow->GetID() == CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow())
344     return true;
345 
346   if (pAddonWindow->m_oldWindowId != pAddonWindow->m_windowId &&
347       pAddonWindow->m_windowId != CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow())
348     pAddonWindow->m_oldWindowId = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
349 
350   Interface_GUIGeneral::lock();
351   if (pAddonWindow->IsDialog())
352     dynamic_cast<CGUIAddonWindowDialog*>(pAddonWindow)->Show(true, true);
353   else
354     CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(pAddonWindow->GetID());
355   Interface_GUIGeneral::unlock();
356 
357   return true;
358 }
359 //@}
360 
361 /*!
362  * Window control functions
363  */
364 //@{
set_focus_id(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int control_id)365 bool Interface_GUIWindow::set_focus_id(KODI_HANDLE kodiBase,
366                                        KODI_GUI_WINDOW_HANDLE handle,
367                                        int control_id)
368 {
369   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
370   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
371   if (!addon || !pAddonWindow)
372   {
373     CLog::Log(LOGERROR,
374               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}') on "
375               "addon '{}'",
376               __func__, kodiBase, handle, addon ? addon->ID() : "unknown");
377     return false;
378   }
379 
380   if (!pAddonWindow->GetControl(control_id))
381   {
382     CLog::Log(LOGERROR, "Interface_GUIWindow - {}: {} - Control does not exist in window", __func__,
383               addon->Name());
384     return false;
385   }
386 
387   Interface_GUIGeneral::lock();
388   CGUIMessage msg(GUI_MSG_SETFOCUS, pAddonWindow->m_windowId, control_id);
389   pAddonWindow->OnMessage(msg);
390   Interface_GUIGeneral::unlock();
391 
392   return true;
393 }
394 
get_focus_id(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle)395 int Interface_GUIWindow::get_focus_id(KODI_HANDLE kodiBase, KODI_GUI_WINDOW_HANDLE handle)
396 {
397   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
398   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
399   if (!addon || !pAddonWindow)
400   {
401     CLog::Log(LOGERROR,
402               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}') on "
403               "addon '{}'",
404               __func__, kodiBase, handle, addon ? addon->ID() : "unknown");
405     return -1;
406   }
407 
408   Interface_GUIGeneral::lock();
409   int control_id = pAddonWindow->GetFocusedControlID();
410   Interface_GUIGeneral::unlock();
411 
412   if (control_id == -1)
413     CLog::Log(LOGERROR, "Interface_GUIWindow - {}: {} - No control in this window has focus",
414               __func__, addon->Name());
415 
416   return control_id;
417 }
418 
set_control_label(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int control_id,const char * label)419 void Interface_GUIWindow::set_control_label(KODI_HANDLE kodiBase,
420                                             KODI_GUI_WINDOW_HANDLE handle,
421                                             int control_id,
422                                             const char* label)
423 {
424   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
425   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
426   if (!addon || !pAddonWindow || !label)
427   {
428     CLog::Log(LOGERROR,
429               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}', "
430               "label='{}') on addon '{}'",
431               __func__, kodiBase, handle, static_cast<const void*>(label),
432               addon ? addon->ID() : "unknown");
433     return;
434   }
435 
436   Interface_GUIGeneral::lock();
437   CGUIMessage msg(GUI_MSG_LABEL_SET, pAddonWindow->m_windowId, control_id);
438   msg.SetLabel(label);
439   pAddonWindow->OnMessage(msg);
440   Interface_GUIGeneral::unlock();
441 }
442 
set_control_visible(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int control_id,bool visible)443 void Interface_GUIWindow::set_control_visible(KODI_HANDLE kodiBase,
444                                               KODI_GUI_WINDOW_HANDLE handle,
445                                               int control_id,
446                                               bool visible)
447 {
448   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
449   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
450   if (!addon || !pAddonWindow)
451   {
452     CLog::Log(LOGERROR,
453               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}') on "
454               "addon '{}'",
455               __func__, kodiBase, handle, addon ? addon->ID() : "unknown");
456     return;
457   }
458 
459   Interface_GUIGeneral::lock();
460   CGUIMessage msg(visible ? GUI_MSG_VISIBLE : GUI_MSG_HIDDEN, pAddonWindow->m_windowId, control_id);
461   pAddonWindow->OnMessage(msg);
462   Interface_GUIGeneral::unlock();
463 }
464 
set_control_selected(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int control_id,bool selected)465 void Interface_GUIWindow::set_control_selected(KODI_HANDLE kodiBase,
466                                                KODI_GUI_WINDOW_HANDLE handle,
467                                                int control_id,
468                                                bool selected)
469 {
470   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
471   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
472   if (!addon || !pAddonWindow)
473   {
474     CLog::Log(LOGERROR,
475               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}') on "
476               "addon '{}'",
477               __func__, kodiBase, handle, addon ? addon->ID() : "unknown");
478     return;
479   }
480 
481   Interface_GUIGeneral::lock();
482   CGUIMessage msg(selected ? GUI_MSG_SET_SELECTED : GUI_MSG_SET_DESELECTED,
483                   pAddonWindow->m_windowId, control_id);
484   pAddonWindow->OnMessage(msg);
485   Interface_GUIGeneral::unlock();
486 }
487 //@}
488 
489 /*!
490  * Window property functions
491  */
492 //@{
set_property(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,const char * key,const char * value)493 void Interface_GUIWindow::set_property(KODI_HANDLE kodiBase,
494                                        KODI_GUI_WINDOW_HANDLE handle,
495                                        const char* key,
496                                        const char* value)
497 {
498   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
499   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
500   if (!addon || !pAddonWindow || !key || !value)
501   {
502     CLog::Log(LOGERROR,
503               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}', "
504               "key='{}', value='{}') on addon '{}'",
505               __func__, kodiBase, handle, static_cast<const void*>(key),
506               static_cast<const void*>(value), addon ? addon->ID() : "unknown");
507     return;
508   }
509 
510   std::string lowerKey = key;
511   StringUtils::ToLower(lowerKey);
512 
513   Interface_GUIGeneral::lock();
514   pAddonWindow->SetProperty(lowerKey, value);
515   Interface_GUIGeneral::unlock();
516 }
517 
set_property_int(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,const char * key,int value)518 void Interface_GUIWindow::set_property_int(KODI_HANDLE kodiBase,
519                                            KODI_GUI_WINDOW_HANDLE handle,
520                                            const char* key,
521                                            int value)
522 {
523   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
524   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
525   if (!addon || !pAddonWindow || !key)
526   {
527     CLog::Log(LOGERROR,
528               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}', "
529               "key='{}') on addon '{}'",
530               __func__, kodiBase, handle, static_cast<const void*>(key),
531               addon ? addon->ID() : "unknown");
532     return;
533   }
534 
535   std::string lowerKey = key;
536   StringUtils::ToLower(lowerKey);
537 
538   Interface_GUIGeneral::lock();
539   pAddonWindow->SetProperty(lowerKey, value);
540   Interface_GUIGeneral::unlock();
541 }
542 
set_property_bool(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,const char * key,bool value)543 void Interface_GUIWindow::set_property_bool(KODI_HANDLE kodiBase,
544                                             KODI_GUI_WINDOW_HANDLE handle,
545                                             const char* key,
546                                             bool value)
547 {
548   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
549   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
550   if (!addon || !pAddonWindow || !key)
551   {
552     CLog::Log(LOGERROR,
553               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}', "
554               "key='{}') on addon '{}'",
555               __func__, kodiBase, handle, static_cast<const void*>(key),
556               addon ? addon->ID() : "unknown");
557     return;
558   }
559 
560   std::string lowerKey = key;
561   StringUtils::ToLower(lowerKey);
562 
563   Interface_GUIGeneral::lock();
564   pAddonWindow->SetProperty(lowerKey, value);
565   Interface_GUIGeneral::unlock();
566 }
567 
set_property_double(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,const char * key,double value)568 void Interface_GUIWindow::set_property_double(KODI_HANDLE kodiBase,
569                                               KODI_GUI_WINDOW_HANDLE handle,
570                                               const char* key,
571                                               double value)
572 {
573   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
574   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
575   if (!addon || !pAddonWindow || !key)
576   {
577     CLog::Log(LOGERROR,
578               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}', "
579               "key='{}') on addon '{}'",
580               __func__, kodiBase, handle, static_cast<const void*>(key),
581               addon ? addon->ID() : "unknown");
582     return;
583   }
584 
585   std::string lowerKey = key;
586   StringUtils::ToLower(lowerKey);
587 
588   Interface_GUIGeneral::lock();
589   pAddonWindow->SetProperty(lowerKey, value);
590   Interface_GUIGeneral::unlock();
591 }
592 
get_property(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,const char * key)593 char* Interface_GUIWindow::get_property(KODI_HANDLE kodiBase,
594                                         KODI_GUI_WINDOW_HANDLE handle,
595                                         const char* key)
596 {
597   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
598   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
599   if (!addon || !pAddonWindow || !key)
600   {
601     CLog::Log(LOGERROR,
602               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}', "
603               "key='{}') on addon '{}'",
604               __func__, kodiBase, handle, static_cast<const void*>(key),
605               addon ? addon->ID() : "unknown");
606     return nullptr;
607   }
608 
609   std::string lowerKey = key;
610   StringUtils::ToLower(lowerKey);
611 
612   Interface_GUIGeneral::lock();
613   std::string value = pAddonWindow->GetProperty(lowerKey).asString();
614   Interface_GUIGeneral::unlock();
615 
616   return strdup(value.c_str());
617 }
618 
get_property_int(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,const char * key)619 int Interface_GUIWindow::get_property_int(KODI_HANDLE kodiBase,
620                                           KODI_GUI_WINDOW_HANDLE handle,
621                                           const char* key)
622 {
623   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
624   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
625   if (!addon || !pAddonWindow || !key)
626   {
627     CLog::Log(LOGERROR,
628               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}', "
629               "key='{}') on addon '{}'",
630               __func__, kodiBase, handle, static_cast<const void*>(key),
631               addon ? addon->ID() : "unknown");
632     return -1;
633   }
634 
635   std::string lowerKey = key;
636   StringUtils::ToLower(lowerKey);
637 
638   Interface_GUIGeneral::lock();
639   int value = static_cast<int>(pAddonWindow->GetProperty(lowerKey).asInteger());
640   Interface_GUIGeneral::unlock();
641 
642   return value;
643 }
644 
get_property_bool(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,const char * key)645 bool Interface_GUIWindow::get_property_bool(KODI_HANDLE kodiBase,
646                                             KODI_GUI_WINDOW_HANDLE handle,
647                                             const char* key)
648 {
649   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
650   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
651   if (!addon || !pAddonWindow || !key)
652   {
653     CLog::Log(LOGERROR,
654               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}', "
655               "key='{}') on addon '{}'",
656               __func__, kodiBase, handle, static_cast<const void*>(key),
657               addon ? addon->ID() : "unknown");
658     return false;
659   }
660 
661   std::string lowerKey = key;
662   StringUtils::ToLower(lowerKey);
663 
664   Interface_GUIGeneral::lock();
665   bool value = pAddonWindow->GetProperty(lowerKey).asBoolean();
666   Interface_GUIGeneral::unlock();
667 
668   return value;
669 }
670 
get_property_double(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,const char * key)671 double Interface_GUIWindow::get_property_double(KODI_HANDLE kodiBase,
672                                                 KODI_GUI_WINDOW_HANDLE handle,
673                                                 const char* key)
674 {
675   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
676   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
677   if (!addon || !pAddonWindow || !key)
678   {
679     CLog::Log(LOGERROR,
680               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}', "
681               "key='{}') on addon '{}'",
682               __func__, kodiBase, handle, static_cast<const void*>(key),
683               addon ? addon->ID() : "unknown");
684     return 0.0;
685   }
686 
687   std::string lowerKey = key;
688   StringUtils::ToLower(lowerKey);
689 
690   Interface_GUIGeneral::lock();
691   double value = pAddonWindow->GetProperty(lowerKey).asDouble();
692   Interface_GUIGeneral::unlock();
693 
694   return value;
695 }
696 
clear_properties(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle)697 void Interface_GUIWindow::clear_properties(KODI_HANDLE kodiBase, KODI_GUI_WINDOW_HANDLE handle)
698 {
699   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
700   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
701   if (!addon || !pAddonWindow)
702   {
703     CLog::Log(LOGERROR,
704               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}') on "
705               "addon '{}'",
706               __func__, kodiBase, handle, addon ? addon->ID() : "unknown");
707     return;
708   }
709 
710   Interface_GUIGeneral::lock();
711   pAddonWindow->ClearProperties();
712   Interface_GUIGeneral::unlock();
713 }
714 
clear_property(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,const char * key)715 void Interface_GUIWindow::clear_property(KODI_HANDLE kodiBase,
716                                          KODI_GUI_WINDOW_HANDLE handle,
717                                          const char* key)
718 {
719   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
720   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
721   if (!addon || !pAddonWindow || !key)
722   {
723     CLog::Log(LOGERROR,
724               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}', "
725               "key='{}') on addon '{}'",
726               __func__, kodiBase, handle, static_cast<const void*>(key),
727               addon ? addon->ID() : "unknown");
728     return;
729   }
730 
731   std::string lowerKey = key;
732   StringUtils::ToLower(lowerKey);
733 
734   Interface_GUIGeneral::lock();
735   pAddonWindow->SetProperty(lowerKey, "");
736   Interface_GUIGeneral::unlock();
737 }
738 //@}
739 
740 /*!
741  * List item functions
742  */
743 //@{
clear_item_list(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle)744 void Interface_GUIWindow::clear_item_list(KODI_HANDLE kodiBase, KODI_GUI_WINDOW_HANDLE handle)
745 {
746   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
747   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
748   if (!addon || !pAddonWindow)
749   {
750     CLog::Log(LOGERROR,
751               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}') on "
752               "addon '{}'",
753               __func__, kodiBase, handle, addon ? addon->ID() : "unknown");
754     return;
755   }
756 
757   Interface_GUIGeneral::lock();
758   pAddonWindow->ClearList();
759   Interface_GUIGeneral::unlock();
760 }
761 
add_list_item(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,KODI_GUI_LISTITEM_HANDLE item,int list_position)762 void Interface_GUIWindow::add_list_item(KODI_HANDLE kodiBase,
763                                         KODI_GUI_WINDOW_HANDLE handle,
764                                         KODI_GUI_LISTITEM_HANDLE item,
765                                         int list_position)
766 {
767   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
768   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
769   if (!addon || !pAddonWindow || !item)
770   {
771     CLog::Log(LOGERROR,
772               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}', "
773               "item='{}') on addon '{}'",
774               __func__, kodiBase, handle, item, addon ? addon->ID() : "unknown");
775     return;
776   }
777 
778   CFileItemPtr* pItem(static_cast<CFileItemPtr*>(item));
779   if (pItem->get() == nullptr)
780   {
781     CLog::Log(LOGERROR, "Interface_GUIWindow::{} - empty list item called on addon '{}'", __func__,
782               addon->ID());
783     return;
784   }
785 
786   Interface_GUIGeneral::lock();
787   pAddonWindow->AddItem(pItem, list_position);
788   Interface_GUIGeneral::unlock();
789 }
790 
remove_list_item_from_position(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int list_position)791 void Interface_GUIWindow::remove_list_item_from_position(KODI_HANDLE kodiBase,
792                                                          KODI_GUI_WINDOW_HANDLE handle,
793                                                          int list_position)
794 {
795   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
796   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
797   if (!addon || !pAddonWindow)
798   {
799     CLog::Log(LOGERROR,
800               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}') on "
801               "addon '{}'",
802               __func__, kodiBase, handle, addon ? addon->ID() : "unknown");
803     return;
804   }
805 
806   Interface_GUIGeneral::lock();
807   pAddonWindow->RemoveItem(list_position);
808   Interface_GUIGeneral::unlock();
809 }
810 
remove_list_item(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,KODI_GUI_LISTITEM_HANDLE item)811 void Interface_GUIWindow::remove_list_item(KODI_HANDLE kodiBase,
812                                            KODI_GUI_WINDOW_HANDLE handle,
813                                            KODI_GUI_LISTITEM_HANDLE item)
814 {
815   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
816   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
817   if (!addon || !pAddonWindow || !item)
818   {
819     CLog::Log(LOGERROR,
820               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}', "
821               "item='{}') on addon '{}'",
822               __func__, kodiBase, handle, item, addon ? addon->ID() : "unknown");
823     return;
824   }
825 
826   CFileItemPtr* pItem(static_cast<CFileItemPtr*>(item));
827   if (pItem->get() == nullptr)
828   {
829     CLog::Log(LOGERROR, "Interface_GUIWindow::{} - empty list item called on addon '{}'", __func__,
830               addon->ID());
831     return;
832   }
833 
834   Interface_GUIGeneral::lock();
835   pAddonWindow->RemoveItem(pItem);
836   Interface_GUIGeneral::unlock();
837 }
838 
get_list_item(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int list_position)839 KODI_GUI_LISTITEM_HANDLE Interface_GUIWindow::get_list_item(KODI_HANDLE kodiBase,
840                                                             KODI_GUI_WINDOW_HANDLE handle,
841                                                             int list_position)
842 {
843   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
844   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
845   if (!addon || !pAddonWindow)
846   {
847     CLog::Log(LOGERROR,
848               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}') on "
849               "addon '{}'",
850               __func__, kodiBase, handle, addon ? addon->ID() : "unknown");
851     return nullptr;
852   }
853 
854   Interface_GUIGeneral::lock();
855   CFileItemPtr* pItem(pAddonWindow->GetListItem(list_position));
856   if (pItem == nullptr || pItem->get() == nullptr)
857   {
858     CLog::Log(LOGERROR, "ADDON::Interface_GUIWindow - {}: {} - Index out of range", __func__,
859               addon->Name());
860 
861     if (pItem)
862     {
863       delete pItem;
864       pItem = nullptr;
865     }
866   }
867   Interface_GUIGeneral::unlock();
868 
869   return pItem;
870 }
871 
set_current_list_position(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int list_position)872 void Interface_GUIWindow::set_current_list_position(KODI_HANDLE kodiBase,
873                                                     KODI_GUI_WINDOW_HANDLE handle,
874                                                     int list_position)
875 {
876   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
877   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
878   if (!addon || !pAddonWindow)
879   {
880     CLog::Log(LOGERROR,
881               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}') on "
882               "addon '{}'",
883               __func__, kodiBase, handle, addon ? addon->ID() : "unknown");
884     return;
885   }
886 
887   Interface_GUIGeneral::lock();
888   pAddonWindow->SetCurrentListPosition(list_position);
889   Interface_GUIGeneral::unlock();
890 }
891 
get_current_list_position(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle)892 int Interface_GUIWindow::get_current_list_position(KODI_HANDLE kodiBase,
893                                                    KODI_GUI_WINDOW_HANDLE handle)
894 {
895   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
896   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
897   if (!addon || !pAddonWindow)
898   {
899     CLog::Log(LOGERROR,
900               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}') on "
901               "addon '{}'",
902               __func__, kodiBase, handle, addon ? addon->ID() : "unknown");
903     return -1;
904   }
905 
906   Interface_GUIGeneral::lock();
907   int listPos = pAddonWindow->GetCurrentListPosition();
908   Interface_GUIGeneral::unlock();
909 
910   return listPos;
911 }
912 
get_list_size(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle)913 int Interface_GUIWindow::get_list_size(KODI_HANDLE kodiBase, KODI_GUI_WINDOW_HANDLE handle)
914 {
915   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
916   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
917   if (!addon || !pAddonWindow)
918   {
919     CLog::Log(LOGERROR,
920               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}') on "
921               "addon '{}'",
922               __func__, kodiBase, handle, addon ? addon->ID() : "unknown");
923     return -1;
924   }
925 
926   Interface_GUIGeneral::lock();
927   int listSize = pAddonWindow->GetListSize();
928   Interface_GUIGeneral::unlock();
929 
930   return listSize;
931 }
932 
set_container_property(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,const char * key,const char * value)933 void Interface_GUIWindow::set_container_property(KODI_HANDLE kodiBase,
934                                                  KODI_GUI_WINDOW_HANDLE handle,
935                                                  const char* key,
936                                                  const char* value)
937 {
938   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
939   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
940   if (!addon || !pAddonWindow || !key || !value)
941   {
942     CLog::Log(LOGERROR,
943               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}', "
944               "key='{}', value='{}') on addon '{}'",
945               __func__, kodiBase, handle, static_cast<const void*>(key),
946               static_cast<const void*>(value), addon ? addon->ID() : "unknown");
947     return;
948   }
949 
950   Interface_GUIGeneral::lock();
951   pAddonWindow->SetContainerProperty(key, value);
952   Interface_GUIGeneral::unlock();
953 }
954 
set_container_content(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,const char * value)955 void Interface_GUIWindow::set_container_content(KODI_HANDLE kodiBase,
956                                                 KODI_GUI_WINDOW_HANDLE handle,
957                                                 const char* value)
958 {
959   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
960   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
961   if (!addon || !pAddonWindow || !value)
962   {
963     CLog::Log(LOGERROR,
964               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}', "
965               "value='{}') on addon '{}'",
966               __func__, kodiBase, handle, static_cast<const void*>(value),
967               addon ? addon->ID() : "unknown");
968     return;
969   }
970 
971   Interface_GUIGeneral::lock();
972   pAddonWindow->SetContainerContent(value);
973   Interface_GUIGeneral::unlock();
974 }
975 
get_current_container_id(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle)976 int Interface_GUIWindow::get_current_container_id(KODI_HANDLE kodiBase,
977                                                   KODI_GUI_WINDOW_HANDLE handle)
978 {
979   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
980   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
981   if (!addon || !pAddonWindow)
982   {
983     CLog::Log(LOGERROR,
984               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}') on "
985               "addon '{}'",
986               __func__, kodiBase, handle, addon ? addon->ID() : "unknown");
987     return -1;
988   }
989 
990   Interface_GUIGeneral::lock();
991   int id = pAddonWindow->GetCurrentContainerControlId();
992   Interface_GUIGeneral::unlock();
993 
994   return id;
995 }
996 //@}
997 
998 /*!
999  * Various functions
1000  */
1001 //@{
mark_dirty_region(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle)1002 void Interface_GUIWindow::mark_dirty_region(KODI_HANDLE kodiBase, KODI_GUI_WINDOW_HANDLE handle)
1003 {
1004   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
1005   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
1006   if (!addon || !pAddonWindow)
1007   {
1008     CLog::Log(LOGERROR,
1009               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}') on "
1010               "addon '{}'",
1011               __func__, kodiBase, handle, addon ? addon->ID() : "unknown");
1012     return;
1013   }
1014 
1015   Interface_GUIGeneral::lock();
1016   pAddonWindow->MarkDirtyRegion();
1017   Interface_GUIGeneral::unlock();
1018 }
1019 //@}
1020 
1021 /*!
1022  * GUI control access functions
1023  */
1024 //@{
get_control_button(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int control_id)1025 KODI_GUI_CONTROL_HANDLE Interface_GUIWindow::get_control_button(KODI_HANDLE kodiBase,
1026                                                                 KODI_GUI_WINDOW_HANDLE handle,
1027                                                                 int control_id)
1028 {
1029   return GetControl(kodiBase, handle, control_id, __func__, CGUIControl::GUICONTROL_BUTTON,
1030                     "button");
1031 }
1032 
get_control_edit(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int control_id)1033 KODI_GUI_CONTROL_HANDLE Interface_GUIWindow::get_control_edit(KODI_HANDLE kodiBase,
1034                                                               KODI_GUI_WINDOW_HANDLE handle,
1035                                                               int control_id)
1036 {
1037   return GetControl(kodiBase, handle, control_id, __func__, CGUIControl::GUICONTROL_EDIT, "edit");
1038 }
1039 
get_control_fade_label(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int control_id)1040 KODI_GUI_CONTROL_HANDLE Interface_GUIWindow::get_control_fade_label(KODI_HANDLE kodiBase,
1041                                                                     KODI_GUI_WINDOW_HANDLE handle,
1042                                                                     int control_id)
1043 {
1044   return GetControl(kodiBase, handle, control_id, __func__, CGUIControl::GUICONTROL_FADELABEL,
1045                     "fade label");
1046 }
1047 
get_control_image(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int control_id)1048 KODI_GUI_CONTROL_HANDLE Interface_GUIWindow::get_control_image(KODI_HANDLE kodiBase,
1049                                                                KODI_GUI_WINDOW_HANDLE handle,
1050                                                                int control_id)
1051 {
1052   return GetControl(kodiBase, handle, control_id, __func__, CGUIControl::GUICONTROL_IMAGE, "image");
1053 }
1054 
get_control_label(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int control_id)1055 KODI_GUI_CONTROL_HANDLE Interface_GUIWindow::get_control_label(KODI_HANDLE kodiBase,
1056                                                                KODI_GUI_WINDOW_HANDLE handle,
1057                                                                int control_id)
1058 {
1059   return GetControl(kodiBase, handle, control_id, __func__, CGUIControl::GUICONTROL_LABEL, "label");
1060 }
get_control_progress(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int control_id)1061 KODI_GUI_CONTROL_HANDLE Interface_GUIWindow::get_control_progress(KODI_HANDLE kodiBase,
1062                                                                   KODI_GUI_WINDOW_HANDLE handle,
1063                                                                   int control_id)
1064 {
1065   return GetControl(kodiBase, handle, control_id, __func__, CGUIControl::GUICONTROL_PROGRESS,
1066                     "progress");
1067 }
1068 
get_control_radio_button(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int control_id)1069 KODI_GUI_CONTROL_HANDLE Interface_GUIWindow::get_control_radio_button(KODI_HANDLE kodiBase,
1070                                                                       KODI_GUI_WINDOW_HANDLE handle,
1071                                                                       int control_id)
1072 {
1073   return GetControl(kodiBase, handle, control_id, __func__, CGUIControl::GUICONTROL_RADIO,
1074                     "radio button");
1075 }
1076 
get_control_render_addon(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int control_id)1077 KODI_GUI_CONTROL_HANDLE Interface_GUIWindow::get_control_render_addon(KODI_HANDLE kodiBase,
1078                                                                       KODI_GUI_WINDOW_HANDLE handle,
1079                                                                       int control_id)
1080 {
1081   CGUIControl* pGUIControl = static_cast<CGUIControl*>(GetControl(
1082       kodiBase, handle, control_id, __func__, CGUIControl::GUICONTROL_RENDERADDON, "renderaddon"));
1083   if (!pGUIControl)
1084     return nullptr;
1085 
1086   CGUIAddonRenderingControl* pRenderControl =
1087       new CGUIAddonRenderingControl(dynamic_cast<CGUIRenderingControl*>(pGUIControl));
1088   return pRenderControl;
1089 }
1090 
get_control_settings_slider(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int control_id)1091 KODI_GUI_CONTROL_HANDLE Interface_GUIWindow::get_control_settings_slider(
1092     KODI_HANDLE kodiBase, KODI_GUI_WINDOW_HANDLE handle, int control_id)
1093 {
1094   return GetControl(kodiBase, handle, control_id, __func__, CGUIControl::GUICONTROL_SETTINGS_SLIDER,
1095                     "settings slider");
1096 }
1097 
get_control_slider(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int control_id)1098 KODI_GUI_CONTROL_HANDLE Interface_GUIWindow::get_control_slider(KODI_HANDLE kodiBase,
1099                                                                 KODI_GUI_WINDOW_HANDLE handle,
1100                                                                 int control_id)
1101 {
1102   return GetControl(kodiBase, handle, control_id, __func__, CGUIControl::GUICONTROL_SLIDER,
1103                     "slider");
1104 }
1105 
get_control_spin(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int control_id)1106 KODI_GUI_CONTROL_HANDLE Interface_GUIWindow::get_control_spin(KODI_HANDLE kodiBase,
1107                                                               KODI_GUI_WINDOW_HANDLE handle,
1108                                                               int control_id)
1109 {
1110   return GetControl(kodiBase, handle, control_id, __func__, CGUIControl::GUICONTROL_SPINEX, "spin");
1111 }
1112 
get_control_text_box(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int control_id)1113 KODI_GUI_CONTROL_HANDLE Interface_GUIWindow::get_control_text_box(KODI_HANDLE kodiBase,
1114                                                                   KODI_GUI_WINDOW_HANDLE handle,
1115                                                                   int control_id)
1116 {
1117   return GetControl(kodiBase, handle, control_id, __func__, CGUIControl::GUICONTROL_TEXTBOX,
1118                     "textbox");
1119 }
1120 //@}
1121 
GetControl(KODI_HANDLE kodiBase,KODI_GUI_WINDOW_HANDLE handle,int control_id,const char * function,CGUIControl::GUICONTROLTYPES type,const std::string & typeName)1122 KODI_GUI_CONTROL_HANDLE Interface_GUIWindow::GetControl(KODI_HANDLE kodiBase,
1123                                                         KODI_GUI_WINDOW_HANDLE handle,
1124                                                         int control_id,
1125                                                         const char* function,
1126                                                         CGUIControl::GUICONTROLTYPES type,
1127                                                         const std::string& typeName)
1128 {
1129   CAddonDll* addon = static_cast<CAddonDll*>(kodiBase);
1130   CGUIAddonWindow* pAddonWindow = static_cast<CGUIAddonWindow*>(handle);
1131   if (!addon || !pAddonWindow)
1132   {
1133     CLog::Log(LOGERROR,
1134               "Interface_GUIWindow::{} - invalid handler data (kodiBase='{}', handle='{}') on "
1135               "addon '{}'",
1136               __func__, kodiBase, handle, addon ? addon->ID() : "unknown");
1137     return nullptr;
1138   }
1139 
1140   return pAddonWindow->GetAddonControl(control_id, type, typeName);
1141 }
1142 
GetNextAvailableWindowId()1143 int Interface_GUIWindow::GetNextAvailableWindowId()
1144 {
1145   Interface_GUIGeneral::lock();
1146 
1147   // if window WINDOW_ADDON_END is in use it means addon can't create more windows
1148   if (CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_ADDON_END))
1149   {
1150     Interface_GUIGeneral::unlock();
1151     CLog::Log(LOGERROR,
1152               "Interface_GUIWindow::{} - Maximum number of windows for binary addons reached",
1153               __func__);
1154     return -1;
1155   }
1156 
1157   // window id's WINDOW_ADDON_START - WINDOW_ADDON_END are reserved for addons
1158   // get first window id that is not in use
1159   int id = WINDOW_ADDON_START;
1160   while (id < WINDOW_ADDON_END &&
1161          CServiceBroker::GetGUI()->GetWindowManager().GetWindow(id) != nullptr)
1162     id++;
1163 
1164   Interface_GUIGeneral::unlock();
1165   return id;
1166 }
1167 
CGUIAddonWindow(int id,const std::string & strXML,CAddonDll * addon,bool isMedia)1168 CGUIAddonWindow::CGUIAddonWindow(int id, const std::string& strXML, CAddonDll* addon, bool isMedia)
1169   : CGUIMediaWindow(id, strXML.c_str()),
1170     m_clientHandle{nullptr},
1171     CBOnInit{nullptr},
1172     CBOnFocus{nullptr},
1173     CBOnClick{nullptr},
1174     CBOnAction{nullptr},
1175     CBGetContextButtons{nullptr},
1176     CBOnContextButton{nullptr},
1177     m_windowId(id),
1178     m_oldWindowId(0),
1179     m_actionEvent(true),
1180     m_addon(addon),
1181     m_isMedia(isMedia)
1182 {
1183   m_loadType = LOAD_ON_GUI_INIT;
1184 }
1185 
GetAddonControl(int controlId,CGUIControl::GUICONTROLTYPES type,const std::string & typeName)1186 CGUIControl* CGUIAddonWindow::GetAddonControl(int controlId,
1187                                               CGUIControl::GUICONTROLTYPES type,
1188                                               const std::string& typeName)
1189 {
1190   // Load window resources, if not already done, to have related xml content
1191   // present and to let control find it
1192   if (!m_windowLoaded)
1193   {
1194     if (!Initialize())
1195     {
1196       CLog::Log(LOGERROR,
1197                 "CGUIAddonGUI_Window::{}: {} - Window initialize failed by control id '{}' request "
1198                 "for '{}'",
1199                 __func__, m_addon->Name(), controlId, typeName);
1200       return nullptr;
1201     }
1202   }
1203 
1204   CGUIControl* pGUIControl = GetControl(controlId);
1205   if (!pGUIControl)
1206   {
1207     CLog::Log(LOGERROR,
1208               "CGUIAddonGUI_Window::{}: {} - Requested GUI control Id '{}' for '{}' not present!",
1209               __func__, m_addon->Name(), controlId, typeName);
1210     return nullptr;
1211   }
1212   else if (pGUIControl->GetControlType() != type)
1213   {
1214     CLog::Log(LOGERROR,
1215               "CGUIAddonGUI_Window::{}: {} - Requested GUI control Id '{}' not the type '{}'!",
1216               __func__, m_addon->Name(), controlId, typeName);
1217     return nullptr;
1218   }
1219 
1220   return pGUIControl;
1221 }
1222 
OnAction(const CAction & action)1223 bool CGUIAddonWindow::OnAction(const CAction& action)
1224 {
1225   // Let addon decide whether it wants to handle action first
1226   if (CBOnAction &&
1227       CBOnAction(m_clientHandle, CAddonGUITranslator::TranslateActionIdToAddon(action.GetID())))
1228     return true;
1229 
1230   return CGUIWindow::OnAction(action);
1231 }
1232 
OnMessage(CGUIMessage & message)1233 bool CGUIAddonWindow::OnMessage(CGUIMessage& message)
1234 {
1235   switch (message.GetMessage())
1236   {
1237     case GUI_MSG_WINDOW_DEINIT:
1238     {
1239       return CGUIMediaWindow::OnMessage(message);
1240     }
1241     break;
1242 
1243     case GUI_MSG_WINDOW_INIT:
1244     {
1245       CGUIMediaWindow::OnMessage(message);
1246 
1247       if (CBOnInit)
1248         CBOnInit(m_clientHandle);
1249       return true;
1250     }
1251     break;
1252 
1253     case GUI_MSG_FOCUSED:
1254     {
1255       if (m_viewControl.HasControl(message.GetControlId()) &&
1256           m_viewControl.GetCurrentControl() != message.GetControlId())
1257       {
1258         m_viewControl.SetFocused();
1259         return true;
1260       }
1261       // check if our focused control is one of our category buttons
1262       int iControl = message.GetControlId();
1263       if (CBOnFocus)
1264         CBOnFocus(m_clientHandle, iControl);
1265     }
1266     break;
1267 
1268     case GUI_MSG_NOTIFY_ALL:
1269     {
1270       // most messages from GUI_MSG_NOTIFY_ALL break container content, whitelist working ones.
1271       if (message.GetParam1() == GUI_MSG_PAGE_CHANGE ||
1272           message.GetParam1() == GUI_MSG_WINDOW_RESIZE)
1273         return CGUIMediaWindow::OnMessage(message);
1274       return true;
1275     }
1276 
1277     case GUI_MSG_CLICKED:
1278     {
1279       int iControl = message.GetSenderId();
1280       if (iControl && iControl != this->GetID())
1281       {
1282         CGUIControl* controlClicked = this->GetControl(iControl);
1283 
1284         // The old python way used to check list AND SELECITEM method or if its a button, checkmark.
1285         // Its done this way for now to allow other controls without a python version like togglebutton to still raise a onAction event
1286         if (controlClicked) // Will get problems if we the id is not on the window and we try to do GetControlType on it. So check to make sure it exists
1287         {
1288           if ((controlClicked->IsContainer() && (message.GetParam1() == ACTION_SELECT_ITEM ||
1289                                                  message.GetParam1() == ACTION_MOUSE_LEFT_CLICK)) ||
1290               !controlClicked->IsContainer())
1291           {
1292             if (CBOnClick)
1293               return CBOnClick(m_clientHandle, iControl);
1294           }
1295           else if (controlClicked->IsContainer() &&
1296                    (message.GetParam1() == ACTION_MOUSE_RIGHT_CLICK ||
1297                     message.GetParam1() == ACTION_CONTEXT_MENU))
1298           {
1299             if (CBOnAction)
1300             {
1301               // Check addon want to handle right click for a context menu, if
1302               // not used from addon becomes "GetContextButtons(...)" called.
1303               if (CBOnAction(m_clientHandle, ADDON_ACTION_CONTEXT_MENU))
1304                 return true;
1305             }
1306           }
1307         }
1308       }
1309     }
1310     break;
1311   }
1312 
1313   return CGUIMediaWindow::OnMessage(message);
1314 }
1315 
AllocResources(bool forceLoad)1316 void CGUIAddonWindow::AllocResources(bool forceLoad /*= false */)
1317 {
1318   std::string tmpDir = URIUtils::GetDirectory(GetProperty("xmlfile").asString());
1319   std::string fallbackMediaPath;
1320   URIUtils::GetParentPath(tmpDir, fallbackMediaPath);
1321   URIUtils::RemoveSlashAtEnd(fallbackMediaPath);
1322   m_mediaDir = fallbackMediaPath;
1323 
1324   CServiceBroker::GetGUI()->GetTextureManager().AddTexturePath(m_mediaDir);
1325   CGUIMediaWindow::AllocResources(forceLoad);
1326   CServiceBroker::GetGUI()->GetTextureManager().RemoveTexturePath(m_mediaDir);
1327 }
1328 
Render()1329 void CGUIAddonWindow::Render()
1330 {
1331   CServiceBroker::GetGUI()->GetTextureManager().AddTexturePath(m_mediaDir);
1332   CGUIMediaWindow::Render();
1333   CServiceBroker::GetGUI()->GetTextureManager().RemoveTexturePath(m_mediaDir);
1334 }
1335 
AddItem(CFileItemPtr * fileItem,int itemPosition)1336 void CGUIAddonWindow::AddItem(CFileItemPtr* fileItem, int itemPosition)
1337 {
1338   if (itemPosition == -1 || itemPosition > m_vecItems->Size())
1339   {
1340     m_vecItems->Add(*fileItem);
1341   }
1342   else if (itemPosition < -1 && !(itemPosition - 1 < m_vecItems->Size()))
1343   {
1344     m_vecItems->AddFront(*fileItem, 0);
1345   }
1346   else
1347   {
1348     m_vecItems->AddFront(*fileItem, itemPosition);
1349   }
1350   m_viewControl.SetItems(*m_vecItems);
1351   UpdateButtons();
1352 }
1353 
RemoveItem(int itemPosition)1354 void CGUIAddonWindow::RemoveItem(int itemPosition)
1355 {
1356   m_vecItems->Remove(itemPosition);
1357   m_viewControl.SetItems(*m_vecItems);
1358   UpdateButtons();
1359 }
1360 
RemoveItem(CFileItemPtr * fileItem)1361 void CGUIAddonWindow::RemoveItem(CFileItemPtr* fileItem)
1362 {
1363   m_vecItems->Remove(fileItem->get());
1364   m_viewControl.SetItems(*m_vecItems);
1365   UpdateButtons();
1366 }
1367 
GetCurrentListPosition()1368 int CGUIAddonWindow::GetCurrentListPosition()
1369 {
1370   return m_viewControl.GetSelectedItem();
1371 }
1372 
SetCurrentListPosition(int item)1373 void CGUIAddonWindow::SetCurrentListPosition(int item)
1374 {
1375   m_viewControl.SetSelectedItem(item);
1376 }
1377 
GetListSize()1378 int CGUIAddonWindow::GetListSize()
1379 {
1380   return m_vecItems->Size();
1381 }
1382 
GetListItem(int position)1383 CFileItemPtr* CGUIAddonWindow::GetListItem(int position)
1384 {
1385   if (position < 0 || position >= m_vecItems->Size())
1386     return nullptr;
1387   return new CFileItemPtr(m_vecItems->Get(position));
1388 }
1389 
ClearList()1390 void CGUIAddonWindow::ClearList()
1391 {
1392   ClearFileItems();
1393 
1394   m_viewControl.SetItems(*m_vecItems);
1395   UpdateButtons();
1396 }
1397 
SetContainerProperty(const std::string & key,const std::string & value)1398 void CGUIAddonWindow::SetContainerProperty(const std::string& key, const std::string& value)
1399 {
1400   m_vecItems->SetProperty(key, value);
1401 }
1402 
SetContainerContent(const std::string & value)1403 void CGUIAddonWindow::SetContainerContent(const std::string& value)
1404 {
1405   m_vecItems->SetContent(value);
1406 }
1407 
GetCurrentContainerControlId()1408 int CGUIAddonWindow::GetCurrentContainerControlId()
1409 {
1410   return m_viewControl.GetCurrentControl();
1411 }
1412 
GetContextButtons(int itemNumber,CContextButtons & buttons)1413 void CGUIAddonWindow::GetContextButtons(int itemNumber, CContextButtons& buttons)
1414 {
1415   gui_context_menu_pair c_buttons[ADDON_MAX_CONTEXT_ENTRIES] = {{0}};
1416   unsigned int size = ADDON_MAX_CONTEXT_ENTRIES;
1417   if (CBGetContextButtons)
1418   {
1419     CBGetContextButtons(m_clientHandle, itemNumber, c_buttons, &size);
1420     for (unsigned int i = 0; i < size; ++i)
1421       buttons.push_back(std::pair<unsigned int, std::string>(c_buttons[i].id, c_buttons[i].name));
1422   }
1423 }
1424 
OnContextButton(int itemNumber,CONTEXT_BUTTON button)1425 bool CGUIAddonWindow::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
1426 {
1427   if (CBOnContextButton)
1428     return CBOnContextButton(m_clientHandle, itemNumber, static_cast<unsigned int>(button));
1429   return false;
1430 }
1431 
WaitForActionEvent(unsigned int timeout)1432 void CGUIAddonWindow::WaitForActionEvent(unsigned int timeout)
1433 {
1434   m_actionEvent.WaitMSec(timeout);
1435   m_actionEvent.Reset();
1436 }
1437 
PulseActionEvent()1438 void CGUIAddonWindow::PulseActionEvent()
1439 {
1440   m_actionEvent.Set();
1441 }
1442 
SetupShares()1443 void CGUIAddonWindow::SetupShares()
1444 {
1445   UpdateButtons();
1446 }
1447 
1448 
CGUIAddonWindowDialog(int id,const std::string & strXML,CAddonDll * addon)1449 CGUIAddonWindowDialog::CGUIAddonWindowDialog(int id, const std::string& strXML, CAddonDll* addon)
1450   : CGUIAddonWindow(id, strXML, addon, false), m_bRunning(false)
1451 {
1452 }
1453 
Show(bool show,bool modal)1454 void CGUIAddonWindowDialog::Show(bool show /* = true */, bool modal /* = true*/)
1455 {
1456   if (modal)
1457   {
1458     unsigned int count = CServiceBroker::GetWinSystem()->GetGfxContext().exit();
1459     CApplicationMessenger::GetInstance().SendMsg(TMSG_GUI_ADDON_DIALOG, 0, show ? 1 : 0,
1460                                                  static_cast<void*>(this));
1461     CServiceBroker::GetWinSystem()->GetGfxContext().restore(count);
1462   }
1463   else
1464     CApplicationMessenger::GetInstance().PostMsg(TMSG_GUI_ADDON_DIALOG, 0, show ? 1 : 0,
1465                                                  static_cast<void*>(this));
1466 }
1467 
Show_Internal(bool show)1468 void CGUIAddonWindowDialog::Show_Internal(bool show /* = true */)
1469 {
1470   if (show)
1471   {
1472     m_bRunning = true;
1473     CServiceBroker::GetGUI()->GetWindowManager().RegisterDialog(this);
1474 
1475     // activate this window...
1476     CGUIMessage msg(GUI_MSG_WINDOW_INIT, 0, 0, WINDOW_INVALID, GetID());
1477     OnMessage(msg);
1478 
1479     // this dialog is derived from GUIMediaWindow
1480     // make sure it is rendered last
1481     m_renderOrder = RENDER_ORDER_DIALOG;
1482     while (m_bRunning)
1483     {
1484       if (!ProcessRenderLoop(false))
1485         break;
1486     }
1487   }
1488   else // hide
1489   {
1490     m_bRunning = false;
1491 
1492     CGUIMessage msg(GUI_MSG_WINDOW_DEINIT, 0, 0);
1493     OnMessage(msg);
1494 
1495     CServiceBroker::GetGUI()->GetWindowManager().RemoveDialog(GetID());
1496   }
1497 }
1498 
1499 } /* namespace ADDON */
1500