[PVR][Estuary] Timer settings dialog: Show client name in timer type selection dialog...
[xbmc.git] / xbmc / interfaces / legacy / WindowXML.cpp
blob51a4fbf94323c30b8731a8f92da8f81abe5f592e
1 /*
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.
7 */
9 #include "WindowXML.h"
11 #include "ServiceBroker.h"
12 #include "WindowException.h"
13 #include "WindowInterceptor.h"
14 #include "addons/Addon.h"
15 #include "addons/Skin.h"
16 #include "addons/addoninfo/AddonInfo.h"
17 #include "addons/addoninfo/AddonType.h"
18 #include "guilib/GUIComponent.h"
19 #include "guilib/GUIWindowManager.h"
20 #include "guilib/TextureManager.h"
21 #include "utils/FileUtils.h"
22 #include "utils/StringUtils.h"
23 #include "utils/URIUtils.h"
25 #include <mutex>
27 // These #defs are for WindowXML
28 #define CONTROL_BTNVIEWASICONS 2
29 #define CONTROL_BTNSORTBY 3
30 #define CONTROL_BTNSORTASC 4
31 #define CONTROL_LABELFILES 12
33 #define A(x) interceptor->x
35 namespace XBMCAddon
37 namespace xbmcgui
39 template class Interceptor<CGUIMediaWindow>;
41 /**
42 * This class extends the Interceptor<CGUIMediaWindow> in order to
43 * add behavior for a few more virtual functions that were unnecessary
44 * in the Window or WindowDialog.
46 #define checkedb(methcall) ( window.isNotNull() ? xwin-> methcall : false )
47 #define checkedv(methcall) { if (window.isNotNull()) xwin-> methcall ; }
50 //! @todo This should be done with template specialization
51 class WindowXMLInterceptor : public InterceptorDialog<CGUIMediaWindow>
53 WindowXML* xwin;
54 public:
55 WindowXMLInterceptor(WindowXML* _window, int windowid,const char* xmlfile) :
56 InterceptorDialog<CGUIMediaWindow>("CGUIMediaWindow",_window,windowid,xmlfile), xwin(_window)
57 { }
59 void AllocResources(bool forceLoad = false) override
60 { XBMC_TRACE; if(up()) CGUIMediaWindow::AllocResources(forceLoad); else checkedv(AllocResources(forceLoad)); }
61 void FreeResources(bool forceUnLoad = false) override
62 { XBMC_TRACE; if(up()) CGUIMediaWindow::FreeResources(forceUnLoad); else checkedv(FreeResources(forceUnLoad)); }
63 bool OnClick(int iItem, const std::string &player = "") override { XBMC_TRACE; return up() ? CGUIMediaWindow::OnClick(iItem, player) : checkedb(OnClick(iItem)); }
65 void Process(unsigned int currentTime, CDirtyRegionList &dirtyregions) override
66 { XBMC_TRACE; if(up()) CGUIMediaWindow::Process(currentTime,dirtyregions); else checkedv(Process(currentTime,dirtyregions)); }
68 // this is a hack to SKIP the CGUIMediaWindow
69 bool OnAction(const CAction &action) override
70 { XBMC_TRACE; return up() ? CGUIWindow::OnAction(action) : checkedb(OnAction(action)); }
72 protected:
73 // CGUIWindow
74 bool LoadXML(const std::string &strPath, const std::string &strPathLower) override
75 { XBMC_TRACE; return up() ? CGUIMediaWindow::LoadXML(strPath,strPathLower) : xwin->LoadXML(strPath,strPathLower); }
77 // CGUIMediaWindow
78 void GetContextButtons(int itemNumber, CContextButtons &buttons) override
79 { XBMC_TRACE; if (up()) CGUIMediaWindow::GetContextButtons(itemNumber,buttons); else xwin->GetContextButtons(itemNumber,buttons); }
80 bool Update(const std::string &strPath, bool) override
81 { XBMC_TRACE; return up() ? CGUIMediaWindow::Update(strPath) : xwin->Update(strPath); }
82 void SetupShares() override { XBMC_TRACE; if(up()) CGUIMediaWindow::SetupShares(); else checkedv(SetupShares()); }
84 friend class WindowXML;
85 friend class WindowXMLDialog;
89 WindowXML::~WindowXML() { XBMC_TRACE; deallocating(); }
91 WindowXML::WindowXML(const String& xmlFilename,
92 const String& scriptPath,
93 const String& defaultSkin,
94 const String& defaultRes,
95 bool isMedia) :
96 Window(true)
98 XBMC_TRACE;
99 RESOLUTION_INFO res;
100 std::string strSkinPath = g_SkinInfo->GetSkinPath(xmlFilename, &res);
101 m_isMedia = isMedia;
103 if (!CFileUtils::Exists(strSkinPath))
105 std::string str("none");
106 ADDON::AddonInfoPtr addonInfo =
107 std::make_shared<ADDON::CAddonInfo>(str, ADDON::AddonType::SKIN);
108 ADDON::CSkinInfo::TranslateResolution(defaultRes, res);
110 // Check for the matching folder for the skin in the fallback skins folder
111 std::string fallbackPath = URIUtils::AddFileToFolder(scriptPath, "resources", "skins");
112 std::string basePath = URIUtils::AddFileToFolder(fallbackPath, g_SkinInfo->ID());
114 strSkinPath = g_SkinInfo->GetSkinPath(xmlFilename, &res, basePath);
116 // Check for the matching folder for the skin in the fallback skins folder (if it exists)
117 if (CFileUtils::Exists(basePath))
119 addonInfo->SetPath(basePath);
120 std::shared_ptr<ADDON::CSkinInfo> skinInfo = std::make_shared<ADDON::CSkinInfo>(addonInfo, res);
121 skinInfo->Start();
122 strSkinPath = skinInfo->GetSkinPath(xmlFilename, &res);
125 if (!CFileUtils::Exists(strSkinPath))
127 // Finally fallback to the DefaultSkin as it didn't exist in either the XBMC Skin folder or the fallback skin folder
128 addonInfo->SetPath(URIUtils::AddFileToFolder(fallbackPath, defaultSkin));
129 std::shared_ptr<ADDON::CSkinInfo> skinInfo = std::make_shared<ADDON::CSkinInfo>(addonInfo, res);
131 skinInfo->Start();
132 strSkinPath = skinInfo->GetSkinPath(xmlFilename, &res);
133 if (!CFileUtils::Exists(strSkinPath))
134 throw WindowException("XML File for Window is missing");
138 m_scriptPath = scriptPath;
139 // sXMLFileName = strSkinPath;
141 interceptor = new WindowXMLInterceptor(this, lockingGetNextAvailableWindowId(),strSkinPath.c_str());
142 setWindow(interceptor);
143 interceptor->SetCoordsRes(res);
146 int WindowXML::lockingGetNextAvailableWindowId()
148 XBMC_TRACE;
149 std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext());
150 return getNextAvailableWindowId();
153 void WindowXML::addItem(const Alternative<String, const ListItem*>& item, int position)
155 XBMC_TRACE;
156 // item could be deleted if the reference count is 0.
157 // so I MAY need to check prior to using a Ref just in
158 // case this object is managed by Python. I'm not sure
159 // though.
160 AddonClass::Ref<ListItem> ritem = item.which() == XBMCAddon::first ? ListItem::fromString(item.former()) : AddonClass::Ref<ListItem>(item.later());
162 // Tells the window to add the item to FileItem vector
164 XBMCAddonUtils::GuiLock lock(languageHook, false);
166 //----------------------------------------------------
167 // Former AddItem call
168 //AddItem(ritem->item, pos);
170 CFileItemPtr& fileItem = ritem->item;
171 if (position == INT_MAX || position > A(m_vecItems)->Size())
173 A(m_vecItems)->Add(fileItem);
175 else if (position < -1 && !(position*-1 < A(m_vecItems)->Size()))
177 A(m_vecItems)->AddFront(fileItem,0);
179 else
181 A(m_vecItems)->AddFront(fileItem,position);
183 A(m_viewControl).SetItems(*(A(m_vecItems)));
185 //----------------------------------------------------
189 void WindowXML::addItems(const std::vector<Alternative<String, const XBMCAddon::xbmcgui::ListItem* > > & items)
191 XBMC_TRACE;
192 XBMCAddonUtils::GuiLock lock(languageHook, false);
193 for (auto item : items)
195 AddonClass::Ref<ListItem> ritem = item.which() == XBMCAddon::first ? ListItem::fromString(item.former()) : AddonClass::Ref<ListItem>(item.later());
196 CFileItemPtr& fileItem = ritem->item;
197 A(m_vecItems)->Add(fileItem);
199 A(m_viewControl).SetItems(*(A(m_vecItems)));
203 void WindowXML::removeItem(int position)
205 XBMC_TRACE;
206 // Tells the window to remove the item at the specified position from the FileItem vector
207 XBMCAddonUtils::GuiLock lock(languageHook, false);
208 A(m_vecItems)->Remove(position);
209 A(m_viewControl).SetItems(*(A(m_vecItems)));
212 int WindowXML::getCurrentListPosition()
214 XBMC_TRACE;
215 XBMCAddonUtils::GuiLock lock(languageHook, false);
216 int listPos = A(m_viewControl).GetSelectedItem();
217 return listPos;
220 void WindowXML::setCurrentListPosition(int position)
222 XBMC_TRACE;
223 XBMCAddonUtils::GuiLock lock(languageHook, false);
224 A(m_viewControl).SetSelectedItem(position);
227 ListItem* WindowXML::getListItem(int position)
229 XBMCAddonUtils::GuiLock lock(languageHook, false);
230 //CFileItemPtr fi = pwx->GetListItem(listPos);
231 CFileItemPtr fi;
233 if (position < 0 || position >= A(m_vecItems)->Size())
234 return new ListItem();
235 fi = A(m_vecItems)->Get(position);
238 if (fi == NULL)
240 throw WindowException("Index out of range (%i)",position);
243 ListItem* sListItem = new ListItem();
244 sListItem->item = fi;
246 // let's hope someone reference counts this.
247 return sListItem;
250 int WindowXML::getListSize()
252 XBMC_TRACE;
253 return A(m_vecItems)->Size();
256 void WindowXML::clearList()
258 XBMC_TRACE;
259 XBMCAddonUtils::GuiLock lock(languageHook, false);
260 A(ClearFileItems());
262 A(m_viewControl).SetItems(*(A(m_vecItems)));
265 void WindowXML::setContainerProperty(const String& key, const String& value)
267 XBMC_TRACE;
268 A(m_vecItems)->SetProperty(key, value);
271 void WindowXML::setContent(const String& value)
273 XBMC_TRACE;
274 XBMCAddonUtils::GuiLock lock(languageHook, false);
275 A(m_vecItems)->SetContent(value);
278 int WindowXML::getCurrentContainerId()
280 XBMC_TRACE;
281 XBMCAddonUtils::GuiLock lock(languageHook, false);
282 return A(m_viewControl.GetCurrentControl());
285 bool WindowXML::OnAction(const CAction &action)
287 XBMC_TRACE;
288 // do the base class window first, and the call to python after this
289 bool ret = ref(window)->OnAction(action); // we don't currently want the mediawindow actions here
290 // look at the WindowXMLInterceptor onAction, it skips
291 // the CGUIMediaWindow::OnAction and calls directly to
292 // CGUIWindow::OnAction
293 AddonClass::Ref<Action> inf(new Action(action));
294 invokeCallback(new CallbackFunction<WindowXML,AddonClass::Ref<Action> >(this,&WindowXML::onAction,inf.get()));
295 PulseActionEvent();
296 return ret;
299 bool WindowXML::OnMessage(CGUIMessage& message)
301 #ifdef ENABLE_XBMC_TRACE_API
302 XBMC_TRACE;
303 CLog::Log(LOGDEBUG, "{}Message id:{}", _tg.getSpaces(), (int)message.GetMessage());
304 #endif
306 //! @todo We shouldn't be dropping down to CGUIWindow in any of this ideally.
307 //! We have to make up our minds about what python should be doing and
308 //! what this side of things should be doing
309 switch (message.GetMessage())
311 case GUI_MSG_WINDOW_DEINIT:
313 return ref(window)->OnMessage(message);
315 break;
317 case GUI_MSG_WINDOW_INIT:
319 ref(window)->OnMessage(message);
320 invokeCallback(new CallbackFunction<WindowXML>(this,&WindowXML::onInit));
321 PulseActionEvent();
322 return true;
324 break;
326 case GUI_MSG_FOCUSED:
328 if (A(m_viewControl).HasControl(message.GetControlId()) &&
329 A(m_viewControl).GetCurrentControl() != message.GetControlId())
331 A(m_viewControl).SetFocused();
332 return true;
334 // check if our focused control is one of our category buttons
335 int iControl=message.GetControlId();
337 invokeCallback(new CallbackFunction<WindowXML,int>(this,&WindowXML::onFocus,iControl));
338 PulseActionEvent();
340 break;
342 case GUI_MSG_NOTIFY_ALL:
343 // most messages from GUI_MSG_NOTIFY_ALL break container content, whitelist working ones.
344 if (message.GetParam1() == GUI_MSG_PAGE_CHANGE || message.GetParam1() == GUI_MSG_WINDOW_RESIZE)
345 return A(CGUIMediaWindow::OnMessage(message));
346 return true;
348 case GUI_MSG_CLICKED:
350 int iControl=message.GetSenderId();
351 // Handle Sort/View internally. Scripters shouldn't use ID 2, 3 or 4.
352 if (iControl == CONTROL_BTNSORTASC) // sort asc
354 CLog::Log(LOGINFO, "WindowXML: Internal asc/dsc button not implemented");
355 /*if (m_guiState.get())
356 m_guiState->SetNextSortOrder();
357 UpdateFileList();*/
358 return true;
360 else if (iControl == CONTROL_BTNSORTBY) // sort by
362 CLog::Log(LOGINFO, "WindowXML: Internal sort button not implemented");
363 /*if (m_guiState.get())
364 m_guiState->SetNextSortMethod();
365 UpdateFileList();*/
366 return true;
369 if(iControl && iControl != interceptor->GetID()) // pCallbackWindow && != this->GetID())
371 CGUIControl* controlClicked = interceptor->GetControl(iControl);
373 // The old python way used to check list AND SELECITEM method
374 // or if its a button, radiobutton.
375 // Its done this way for now to allow other controls without a
376 // python version like togglebutton to still raise a onAction event
377 if (controlClicked) // Will get problems if we the id is not on the window
378 // and we try to do GetControlType on it. So check to make sure it exists
380 if ((controlClicked->IsContainer() && (message.GetParam1() == ACTION_SELECT_ITEM || message.GetParam1() == ACTION_MOUSE_LEFT_CLICK)) || !controlClicked->IsContainer())
382 invokeCallback(new CallbackFunction<WindowXML,int>(this,&WindowXML::onClick,iControl));
383 PulseActionEvent();
384 return true;
386 else if (controlClicked->IsContainer() && message.GetParam1() == ACTION_MOUSE_DOUBLE_CLICK)
388 invokeCallback(new CallbackFunction<WindowXML,int>(this,&WindowXML::onDoubleClick,iControl));
389 PulseActionEvent();
390 return true;
392 else if (controlClicked->IsContainer() && message.GetParam1() == ACTION_MOUSE_RIGHT_CLICK)
394 AddonClass::Ref<Action> inf(new Action(CAction(ACTION_CONTEXT_MENU)));
395 invokeCallback(new CallbackFunction<WindowXML,AddonClass::Ref<Action> >(this,&WindowXML::onAction,inf.get()));
396 PulseActionEvent();
397 return true;
399 // the core context menu can lead to all sort of issues right now when used with WindowXMLs, so lets intercept the corresponding message
400 else if (controlClicked->IsContainer() && message.GetParam1() == ACTION_CONTEXT_MENU)
401 return true;
405 break;
408 return A(CGUIMediaWindow::OnMessage(message));
411 void WindowXML::AllocResources(bool forceLoad /*= false */)
413 XBMC_TRACE;
414 std::string tmpDir = URIUtils::GetDirectory(ref(window)->GetProperty("xmlfile").asString());
415 std::string fallbackMediaPath;
416 URIUtils::GetParentPath(tmpDir, fallbackMediaPath);
417 URIUtils::RemoveSlashAtEnd(fallbackMediaPath);
418 m_mediaDir = fallbackMediaPath;
420 //CLog::Log(LOGDEBUG, "CGUIPythonWindowXML::AllocResources called: {}", fallbackMediaPath);
421 CServiceBroker::GetGUI()->GetTextureManager().AddTexturePath(m_mediaDir);
422 ref(window)->AllocResources(forceLoad);
423 CServiceBroker::GetGUI()->GetTextureManager().RemoveTexturePath(m_mediaDir);
426 void WindowXML::FreeResources(bool forceUnLoad /*= false */)
428 XBMC_TRACE;
430 ref(window)->FreeResources(forceUnLoad);
433 void WindowXML::Process(unsigned int currentTime, CDirtyRegionList &regions)
435 XBMC_TRACE;
436 CServiceBroker::GetGUI()->GetTextureManager().AddTexturePath(m_mediaDir);
437 ref(window)->Process(currentTime, regions);
438 CServiceBroker::GetGUI()->GetTextureManager().RemoveTexturePath(m_mediaDir);
441 bool WindowXML::OnClick(int iItem)
443 XBMC_TRACE;
444 // Hook Over calling CGUIMediaWindow::OnClick(iItem) results in it trying to PLAY the file item
445 // which if its not media is BAD and 99 out of 100 times undesirable.
446 return false;
449 bool WindowXML::OnDoubleClick(int iItem)
451 XBMC_TRACE;
452 return false;
455 void WindowXML::GetContextButtons(int itemNumber, CContextButtons &buttons)
457 XBMC_TRACE;
458 // maybe on day we can make an easy way to do this context menu
459 // with out this method overriding the MediaWindow version, it will display 'Add to Favorites'
462 bool WindowXML::LoadXML(const String &strPath, const String &strLowerPath)
464 XBMC_TRACE;
465 return A(CGUIWindow::LoadXML(strPath, strLowerPath));
468 void WindowXML::SetupShares()
470 XBMC_TRACE;
473 bool WindowXML::Update(const String &strPath)
475 XBMC_TRACE;
476 return true;
479 WindowXMLDialog::WindowXMLDialog(const String& xmlFilename, const String& scriptPath,
480 const String& defaultSkin,
481 const String& defaultRes) :
482 WindowXML(xmlFilename, scriptPath, defaultSkin, defaultRes),
483 WindowDialogMixin(this)
484 { XBMC_TRACE; }
486 WindowXMLDialog::~WindowXMLDialog() { XBMC_TRACE; deallocating(); }
488 bool WindowXMLDialog::OnMessage(CGUIMessage &message)
490 XBMC_TRACE;
491 if (message.GetMessage() == GUI_MSG_WINDOW_DEINIT)
492 return A(CGUIWindow::OnMessage(message));
494 return WindowXML::OnMessage(message);
497 bool WindowXMLDialog::OnAction(const CAction &action)
499 XBMC_TRACE;
500 return WindowDialogMixin::OnAction(action) ? true : WindowXML::OnAction(action);
503 void WindowXMLDialog::OnDeinitWindow(int nextWindowID)
505 XBMC_TRACE;
506 CServiceBroker::GetGUI()->GetWindowManager().RemoveDialog(interceptor->GetID());
507 WindowXML::OnDeinitWindow(nextWindowID);
510 bool WindowXMLDialog::LoadXML(const String &strPath, const String &strLowerPath)
512 XBMC_TRACE;
513 if (WindowXML::LoadXML(strPath, strLowerPath))
515 // Set the render order to the dialog's default in case it's not specified in the skin xml
516 // because this dialog is mapped to CGUIMediaWindow instead of CGUIDialog.
517 // This must be done here, because the render order will be reset before loading the skin xml.
518 if (ref(window)->GetRenderOrder() == RENDER_ORDER_WINDOW)
519 window->SetRenderOrder(RENDER_ORDER_DIALOG);
520 return true;
522 return false;