2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
22 #include "WindowXML.h"
24 #include "WindowInterceptor.h"
25 #include "guilib/GUIWindowManager.h"
26 #include "settings/GUISettings.h"
27 #include "addons/Skin.h"
28 #include "filesystem/File.h"
29 #include "utils/URIUtils.h"
30 #include "addons/Addon.h"
32 // These #defs are for WindowXML
33 #define CONTROL_BTNVIEWASICONS 2
34 #define CONTROL_BTNSORTBY 3
35 #define CONTROL_BTNSORTASC 4
36 #define CONTROL_LABELFILES 12
38 #define A(x) interceptor->x
44 template class Interceptor<CGUIMediaWindow>;
47 * This class extends the Interceptor<CGUIMediaWindow> in order to
48 * add behavior for a few more virtual functions that were unneccessary
49 * in the Window or WindowDialog.
51 #define checkedb(methcall) ( window.isNotNull() ? xwin-> methcall : false )
52 #define checkedv(methcall) { if (window.isNotNull()) xwin-> methcall ; }
55 // TODO: This should be done with template specialization
56 class WindowXMLInterceptor : public InterceptorDialog<CGUIMediaWindow>
60 WindowXMLInterceptor(WindowXML* _window, int windowid,const char* xmlfile) :
61 InterceptorDialog<CGUIMediaWindow>("CGUIMediaWindow",_window,windowid,xmlfile), xwin(_window)
64 virtual void AllocResources(bool forceLoad = false)
65 { TRACE; if(up()) CGUIMediaWindow::AllocResources(forceLoad); else checkedv(AllocResources(forceLoad)); }
66 virtual void FreeResources(bool forceUnLoad = false)
67 { TRACE; if(up()) CGUIMediaWindow::FreeResources(forceUnLoad); else checkedv(FreeResources(forceUnLoad)); }
68 virtual bool OnClick(int iItem) { TRACE; return up() ? CGUIMediaWindow::OnClick(iItem) : checkedb(OnClick(iItem)); }
70 virtual void Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
71 { TRACE; if(up()) CGUIMediaWindow::Process(currentTime,dirtyregions); else checkedv(Process(currentTime,dirtyregions)); }
73 // this is a hack to SKIP the CGUIMediaWindow
74 virtual bool OnAction(const CAction &action)
75 { TRACE; return up() ? CGUIWindow::OnAction(action) : checkedb(OnAction(action)); }
79 virtual bool LoadXML(const CStdString &strPath, const CStdString &strPathLower)
80 { TRACE; return up() ? CGUIMediaWindow::LoadXML(strPath,strPathLower) : xwin->LoadXML(strPath,strPathLower); }
83 virtual void GetContextButtons(int itemNumber, CContextButtons &buttons)
84 { TRACE; if (up()) CGUIMediaWindow::GetContextButtons(itemNumber,buttons); else xwin->GetContextButtons(itemNumber,buttons); }
85 virtual bool Update(const CStdString &strPath)
86 { TRACE; return up() ? CGUIMediaWindow::Update(strPath) : xwin->Update(strPath); }
87 virtual void SetupShares() { TRACE; if(up()) CGUIMediaWindow::SetupShares(); else checkedv(SetupShares()); }
89 friend class WindowXML;
90 friend class WindowXMLDialog;
94 WindowXML::WindowXML(const String& xmlFilename,
95 const String& scriptPath,
96 const String& defaultSkin,
97 const String& defaultRes) throw(WindowException) :
100 initialize(xmlFilename,scriptPath,defaultSkin,defaultRes);
103 WindowXML::WindowXML(const char* classname,
104 const String& xmlFilename,
105 const String& scriptPath,
106 const String& defaultSkin,
107 const String& defaultRes) throw(WindowException) :
111 initialize(xmlFilename,scriptPath,defaultSkin,defaultRes);
114 WindowXML::~WindowXML() { TRACE; deallocating(); }
116 void WindowXML::initialize(const String& xmlFilename,
117 const String& scriptPath,
118 const String& defaultSkin,
119 const String& defaultRes)
123 CStdString strSkinPath = g_SkinInfo->GetSkinPath(xmlFilename, &res);
125 if (!XFILE::CFile::Exists(strSkinPath))
127 CStdString str("none");
128 ADDON::AddonProps props(str, ADDON::ADDON_SKIN, "", "");
129 ADDON::CSkinInfo::TranslateResolution(defaultRes, res);
131 // Check for the matching folder for the skin in the fallback skins folder
132 CStdString fallbackPath = URIUtils::AddFileToFolder(scriptPath, "resources");
133 fallbackPath = URIUtils::AddFileToFolder(fallbackPath, "skins");
134 CStdString basePath = URIUtils::AddFileToFolder(fallbackPath, g_SkinInfo->ID());
136 strSkinPath = g_SkinInfo->GetSkinPath(xmlFilename, &res, basePath);
138 // Check for the matching folder for the skin in the fallback skins folder (if it exists)
139 if (XFILE::CFile::Exists(basePath))
141 props.path = basePath;
142 ADDON::CSkinInfo skinInfo(props, res);
144 strSkinPath = skinInfo.GetSkinPath(xmlFilename, &res);
147 if (!XFILE::CFile::Exists(strSkinPath))
149 // Finally fallback to the DefaultSkin as it didn't exist in either the XBMC Skin folder or the fallback skin folder
150 props.path = URIUtils::AddFileToFolder(fallbackPath, defaultSkin);
151 ADDON::CSkinInfo skinInfo(props, res);
154 strSkinPath = skinInfo.GetSkinPath(xmlFilename, &res);
155 if (!XFILE::CFile::Exists(strSkinPath))
156 throw WindowException("XML File for Window is missing");
160 m_scriptPath = scriptPath;
161 // sXMLFileName = strSkinPath;
163 interceptor = new WindowXMLInterceptor(this, lockingGetNextAvailalbeWindowId(),strSkinPath.c_str());
164 setWindow(interceptor);
165 interceptor->SetCoordsRes(res);
168 int WindowXML::lockingGetNextAvailalbeWindowId() throw (WindowException)
171 CSingleLock lock(g_graphicsContext);
172 return getNextAvailalbeWindowId();
175 void WindowXML::addItem(const String& item, int pos)
178 AddonClass::Ref<ListItem> ritem(ListItem::fromString(item));
179 addListItem(ritem.get(),pos);
182 void WindowXML::addListItem(ListItem* item, int pos)
185 // item could be deleted if the reference count is 0.
186 // so I MAY need to check prior to using a Ref just in
187 // case this object is managed by Python. I'm not sure
189 AddonClass::Ref<ListItem> ritem(item);
191 // Tells the window to add the item to FileItem vector
195 //----------------------------------------------------
196 // Former AddItem call
197 //AddItem(ritem->item, pos);
199 CFileItemPtr& fileItem = ritem->item;
200 if (pos == INT_MAX || pos > A(m_vecItems)->Size())
202 A(m_vecItems)->Add(fileItem);
204 else if (pos < -1 && !(pos*-1 < A(m_vecItems)->Size()))
206 A(m_vecItems)->AddFront(fileItem,0);
210 A(m_vecItems)->AddFront(fileItem,pos);
212 A(m_viewControl).SetItems(*(A(m_vecItems)));
215 //----------------------------------------------------
219 void WindowXML::removeItem(int position)
222 // Tells the window to remove the item at the specified position from the FileItem vector
224 A(m_vecItems)->Remove(position);
225 A(m_viewControl).SetItems(*(A(m_vecItems)));
229 int WindowXML::getCurrentListPosition()
233 int listPos = A(m_viewControl).GetSelectedItem();
237 void WindowXML::setCurrentListPosition(int position)
241 A(m_viewControl).SetSelectedItem(position);
244 ListItem* WindowXML::getListItem(int position) throw (WindowException)
247 //CFileItemPtr fi = pwx->GetListItem(listPos);
250 if (position < 0 || position >= A(m_vecItems)->Size())
251 return new ListItem();
252 fi = A(m_vecItems)->Get(position);
257 XBMCAddonUtils::guiUnlock();
258 throw WindowException("Index out of range (%i)",position);
261 ListItem* sListItem = new ListItem();
262 sListItem->item = fi;
264 // let's hope someone reference counts this.
268 int WindowXML::getListSize()
271 return A(m_vecItems)->Size();
274 void WindowXML::clearList()
279 A(m_viewControl).SetItems(*(A(m_vecItems)));
283 void WindowXML::setProperty(const String& key, const String& value)
286 A(m_vecItems)->SetProperty(key, value);
289 bool WindowXML::OnAction(const CAction &action)
292 // do the base class window first, and the call to python after this
293 bool ret = ref(window)->OnAction(action); // we don't currently want the mediawindow actions here
294 // look at the WindowXMLInterceptor onAction, it skips
295 // the CGUIMediaWindow::OnAction and calls directly to
296 // CGUIWindow::OnAction
297 AddonClass::Ref<Action> inf(new Action(action));
298 invokeCallback(new CallbackFunction<WindowXML,AddonClass::Ref<Action> >(this,&WindowXML::onAction,inf.get()));
303 bool WindowXML::OnMessage(CGUIMessage& message)
305 #ifdef ENABLE_TRACE_API
307 CLog::Log(LOGDEBUG,"%sMessage id:%d",_tg.getSpaces(),(int)message.GetMessage());
310 // TODO: We shouldn't be dropping down to CGUIWindow in any of this ideally.
311 // We have to make up our minds about what python should be doing and
312 // what this side of things should be doing
313 switch (message.GetMessage())
315 case GUI_MSG_WINDOW_DEINIT:
317 return ref(window)->OnMessage(message);
321 case GUI_MSG_WINDOW_INIT:
323 ref(window)->OnMessage(message);
324 invokeCallback(new CallbackFunction<WindowXML>(this,&WindowXML::onInit));
330 case GUI_MSG_FOCUSED:
332 if (A(m_viewControl).HasControl(message.GetControlId()) &&
333 A(m_viewControl).GetCurrentControl() != (int)message.GetControlId())
335 A(m_viewControl).SetFocused();
338 // check if our focused control is one of our category buttons
339 int iControl=message.GetControlId();
341 invokeCallback(new CallbackFunction<WindowXML,int>(this,&WindowXML::onFocus,iControl));
346 case GUI_MSG_CLICKED:
348 int iControl=message.GetSenderId();
349 // Handle Sort/View internally. Scripters shouldn't use ID 2, 3 or 4.
350 if (iControl == CONTROL_BTNSORTASC) // sort asc
352 CLog::Log(LOGINFO, "WindowXML: Internal asc/dsc button not implemented");
353 /*if (m_guiState.get())
354 m_guiState->SetNextSortOrder();
358 else if (iControl == CONTROL_BTNSORTBY) // sort by
360 CLog::Log(LOGINFO, "WindowXML: Internal sort button not implemented");
361 /*if (m_guiState.get())
362 m_guiState->SetNextSortMethod();
367 if(iControl && iControl != (int)interceptor->GetID()) // pCallbackWindow && != this->GetID())
369 CGUIControl* controlClicked = (CGUIControl*)interceptor->GetControl(iControl);
371 // The old python way used to check list AND SELECITEM method
372 // or if its a button, checkmark.
373 // Its done this way for now to allow other controls without a
374 // python version like togglebutton to still raise a onAction event
375 if (controlClicked) // Will get problems if we the id is not on the window
376 // and we try to do GetControlType on it. So check to make sure it exists
378 if ((controlClicked->IsContainer() && (message.GetParam1() == ACTION_SELECT_ITEM || message.GetParam1() == ACTION_MOUSE_LEFT_CLICK)) || !controlClicked->IsContainer())
380 invokeCallback(new CallbackFunction<WindowXML,int>(this,&WindowXML::onClick,iControl));
384 else if (controlClicked->IsContainer() && message.GetParam1() == ACTION_MOUSE_RIGHT_CLICK)
386 AddonClass::Ref<Action> inf(new Action(CAction(ACTION_CONTEXT_MENU)));
387 invokeCallback(new CallbackFunction<WindowXML,AddonClass::Ref<Action> >(this,&WindowXML::onAction,inf.get()));
397 return A(CGUIMediaWindow::OnMessage(message));
400 void WindowXML::AllocResources(bool forceLoad /*= FALSE */)
404 URIUtils::GetDirectory(ref(window)->GetProperty("xmlfile").asString(), tmpDir);
405 CStdString fallbackMediaPath;
406 URIUtils::GetParentPath(tmpDir, fallbackMediaPath);
407 URIUtils::RemoveSlashAtEnd(fallbackMediaPath);
408 m_mediaDir = fallbackMediaPath;
410 //CLog::Log(LOGDEBUG, "CGUIPythonWindowXML::AllocResources called: %s", fallbackMediaPath.c_str());
411 g_TextureManager.AddTexturePath(m_mediaDir);
412 ref(window)->AllocResources(forceLoad);
413 g_TextureManager.RemoveTexturePath(m_mediaDir);
416 void WindowXML::FreeResources(bool forceUnLoad /*= FALSE */)
419 // Unload temporary language strings
420 ClearScriptStrings();
422 ref(window)->FreeResources(forceUnLoad);
425 void WindowXML::Process(unsigned int currentTime, CDirtyRegionList ®ions)
428 g_TextureManager.AddTexturePath(m_mediaDir);
429 ref(window)->Process(currentTime, regions);
430 g_TextureManager.RemoveTexturePath(m_mediaDir);
433 bool WindowXML::OnClick(int iItem)
436 // Hook Over calling CGUIMediaWindow::OnClick(iItem) results in it trying to PLAY the file item
437 // which if its not media is BAD and 99 out of 100 times undesireable.
441 void WindowXML::GetContextButtons(int itemNumber, CContextButtons &buttons)
444 // maybe on day we can make an easy way to do this context menu
445 // with out this method overriding the MediaWindow version, it will display 'Add to Favorites'
448 bool WindowXML::LoadXML(const String &strPath, const String &strLowerPath)
453 if (!file.Open(strPath) && !file.Open(CStdString(strPath).ToLower()) && !file.Open(strLowerPath))
455 // fail - can't load the file
456 CLog::Log(LOGERROR, "%s: Unable to load skin file %s", __FUNCTION__, strPath.c_str());
459 // load the strings in
460 unsigned int offset = LoadScriptStrings();
463 char *buffer = new char[(unsigned int)file.GetLength()+1];
466 int size = file.Read(buffer, file.GetLength());
473 // replace the occurences of SCRIPT### with offset+###
474 // not particularly efficient, but it works
475 int pos = xml.Find("SCRIPT");
476 while (pos != (int)CStdString::npos)
478 CStdString num = xml.Mid(pos + 6, 4);
479 int number = atol(num.c_str());
480 CStdString oldNumber, newNumber;
481 oldNumber.Format("SCRIPT%d", number);
482 newNumber.Format("%lu", offset + number);
483 xml.Replace(oldNumber, newNumber);
484 pos = xml.Find("SCRIPT", pos + 6);
491 xmlDoc.Parse(xml.c_str());
496 return interceptor->Load(xmlDoc.RootElement());
499 unsigned int WindowXML::LoadScriptStrings()
502 // Path where the language strings reside
503 CStdString pathToLanguageFile = m_scriptPath;
504 URIUtils::AddFileToFolder(pathToLanguageFile, "resources", pathToLanguageFile);
505 URIUtils::AddFileToFolder(pathToLanguageFile, "language", pathToLanguageFile);
506 URIUtils::AddSlashAtEnd(pathToLanguageFile);
508 // allocate a bunch of strings
509 return g_localizeStrings.LoadBlock(m_scriptPath, pathToLanguageFile, g_guiSettings.GetString("locale.language"));
512 void WindowXML::ClearScriptStrings()
515 // Unload temporary language strings
516 g_localizeStrings.ClearBlock(m_scriptPath);
519 void WindowXML::SetupShares()
525 bool WindowXML::Update(const String &strPath)
531 WindowXMLDialog::WindowXMLDialog(const String& xmlFilename, const String& scriptPath,
532 const String& defaultSkin,
533 const String& defaultRes) throw(WindowException) :
534 WindowXML("WindowXMLDialog",xmlFilename, scriptPath, defaultSkin, defaultRes),
535 WindowDialogMixin(this)
538 WindowXMLDialog::~WindowXMLDialog() { TRACE; deallocating(); }
540 bool WindowXMLDialog::OnMessage(CGUIMessage &message)
543 if (message.GetMessage() == GUI_MSG_WINDOW_DEINIT)
545 CGUIWindow *pWindow = g_windowManager.GetWindow(g_windowManager.GetActiveWindow());
547 g_windowManager.ShowOverlay(pWindow->GetOverlayState());
548 return A(CGUIWindow::OnMessage(message));
550 return WindowXML::OnMessage(message);
553 bool WindowXMLDialog::OnAction(const CAction &action)
556 return WindowDialogMixin::OnAction(action) ? true : WindowXML::OnAction(action);
559 void WindowXMLDialog::OnDeinitWindow(int nextWindowID)
562 g_windowManager.RemoveDialog(interceptor->GetID());
563 WindowXML::OnDeinitWindow(nextWindowID);