Merge pull request #5039 from CEikermann/patch-1
[vuplus_xbmc] / xbmc / listproviders / DirectoryProvider.cpp
1 /*
2  *      Copyright (C) 2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "DirectoryProvider.h"
22 #include "filesystem/Directory.h"
23 #include "filesystem/FavouritesDirectory.h"
24 #include "guilib/GUIWindowManager.h"
25 #include "utils/JobManager.h"
26 #include "utils/StringUtils.h"
27 #include "utils/TimeUtils.h"
28 #include "utils/XMLUtils.h"
29 #include "utils/StringUtils.h"
30 #include "utils/URIUtils.h"
31 #include "utils/log.h"
32 #include "threads/SingleLock.h"
33 #include "ApplicationMessenger.h"
34 #include "FileItem.h"
35 #include "video/VideoThumbLoader.h"
36 #include "music/MusicThumbLoader.h"
37 #include "pictures/PictureThumbLoader.h"
38 #include "boost/make_shared.hpp"
39 #include "interfaces/AnnouncementManager.h"
40
41 using namespace std;
42 using namespace XFILE;
43 using namespace ANNOUNCEMENT;
44
45 class CDirectoryJob : public CJob
46 {
47 public:
48   CDirectoryJob(const std::string &url, int parentID) : m_url(url), m_parentID(parentID) { };
49   virtual ~CDirectoryJob() {};
50
51   virtual const char* GetType() const { return "directory"; };
52   virtual bool operator==(const CJob *job) const
53   {
54     if (strcmp(job->GetType(),GetType()) == 0)
55     {
56       const CDirectoryJob* dirJob = dynamic_cast<const CDirectoryJob*>(job);
57       if (dirJob && dirJob->m_url == m_url)
58         return true;
59     }
60     return false;
61   }
62
63   virtual bool DoWork()
64   {
65     CFileItemList items;
66     if (CDirectory::GetDirectory(m_url, items, ""))
67     {
68       // convert to CGUIStaticItem's and set visibility and targets
69       m_items.reserve(items.Size());
70       for (int i = 0; i < items.Size(); i++)
71       {
72         CGUIStaticItemPtr item(new CGUIStaticItem(*items[i]));
73         if (item->HasProperty("node.visible"))
74           item->SetVisibleCondition(item->GetProperty("node.visible").asString(), m_parentID);
75
76         getThumbLoader(item)->LoadItem(item.get());
77
78         m_items.push_back(item);
79       }
80       m_target = items.GetProperty("node.target").asString();
81     }
82     return true;    
83   }
84
85   boost::shared_ptr<CThumbLoader> getThumbLoader(CGUIStaticItemPtr &item)
86   {
87     if (item->IsVideo())
88     {
89       initThumbLoader<CVideoThumbLoader>(VIDEO);
90       return m_thumbloaders[VIDEO];
91     }
92     if (item->IsAudio())
93     {
94       initThumbLoader<CMusicThumbLoader>(AUDIO);
95       return m_thumbloaders[AUDIO];
96     }
97     if (item->IsPicture())
98     {
99       initThumbLoader<CPictureThumbLoader>(PICTURE);
100       return m_thumbloaders[PICTURE];
101     }
102     initThumbLoader<CProgramThumbLoader>(PROGRAM);
103     return m_thumbloaders[PROGRAM];
104   }
105
106   template<class CThumbLoaderClass>
107   void initThumbLoader(InfoTagType type)
108   {
109     if (!m_thumbloaders.count(type))
110     {
111       boost::shared_ptr<CThumbLoader> thumbLoader = boost::make_shared<CThumbLoaderClass>();
112       thumbLoader->OnLoaderStart();
113       m_thumbloaders.insert(make_pair(type, thumbLoader));
114     }
115   }
116
117   const std::vector<CGUIStaticItemPtr> &GetItems() const { return m_items; }
118   const std::string &GetTarget() const { return m_target; }
119   std::vector<InfoTagType> GetItemTypes(std::vector<InfoTagType> &itemTypes) const
120   {
121     itemTypes.clear();
122     for (std::map<InfoTagType, boost::shared_ptr<CThumbLoader> >::const_iterator
123          i = m_thumbloaders.begin(); i != m_thumbloaders.end(); ++i)
124       itemTypes.push_back(i->first);
125     return itemTypes;
126   }
127 private:
128   std::string m_url;
129   std::string m_target;
130   int m_parentID;
131   std::vector<CGUIStaticItemPtr> m_items;
132   std::map<InfoTagType, boost::shared_ptr<CThumbLoader> > m_thumbloaders;
133 };
134
135 CDirectoryProvider::CDirectoryProvider(const TiXmlElement *element, int parentID)
136  : IListProvider(parentID),
137    m_updateTime(0),
138    m_updateState(OK),
139    m_isDbUpdating(false),
140    m_isAnnounced(false),
141    m_jobID(0)
142 {
143   assert(element);
144   if (!element->NoChildren())
145   {
146     const char *target = element->Attribute("target");
147     if (target)
148       m_target.SetLabel(target, "", parentID);
149     m_url.SetLabel(element->FirstChild()->ValueStr(), "", parentID);
150   }
151 }
152
153 CDirectoryProvider::~CDirectoryProvider()
154 {
155   Reset(true);
156 }
157
158 bool CDirectoryProvider::Update(bool forceRefresh)
159 {
160   // we never need to force refresh here
161   bool changed = false;
162   bool fireJob = false;
163   {
164     CSingleLock lock(m_section);
165     if (m_updateState == DONE)
166       changed = true;
167     else if (m_updateState == PENDING)
168       fireJob = true;
169     m_updateState = OK;
170   }
171
172   // update the URL and fire off a new job if needed
173   if (fireJob || UpdateURL())
174     FireJob();
175
176   for (vector<CGUIStaticItemPtr>::iterator i = m_items.begin(); i != m_items.end(); ++i)
177     changed |= (*i)->UpdateVisibility(m_parentID);
178   return changed; // TODO: Also returned changed if properties are changed (if so, need to update scroll to letter).
179 }
180
181 void CDirectoryProvider::Announce(AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
182 {
183   // we are only interested in library changes
184   if ((flag & (VideoLibrary | AudioLibrary)) == 0)
185     return;
186
187   {
188     CSingleLock lock(m_section);
189     // we don't need to refresh anything if there are no fitting
190     // items in this list provider for the announcement flag
191     if (((flag & VideoLibrary) &&
192          (std::find(m_itemTypes.begin(), m_itemTypes.end(), VIDEO) == m_itemTypes.end())) ||
193         ((flag & AudioLibrary) &&
194          (std::find(m_itemTypes.begin(), m_itemTypes.end(), AUDIO) == m_itemTypes.end())))
195       return;
196
197     // don't update while scanning / cleaning
198     if (strcmp(message, "OnScanStarted") == 0 ||
199         strcmp(message, "OnCleanStarted") == 0)
200     {
201       m_isDbUpdating = true;
202       return;
203     }
204
205     // if there was a database update, we set the update state
206     // to PENDING to fire off a new job in the next update
207     if (strcmp(message, "OnScanFinished") == 0 ||
208         strcmp(message, "OnCleanFinished") == 0 ||
209         ((strcmp(message, "OnUpdate") == 0 ||
210           strcmp(message, "OnRemove") == 0) && !m_isDbUpdating))
211     {
212       m_isDbUpdating = false;
213       m_updateState = PENDING;
214     }
215   }
216 }
217
218 void CDirectoryProvider::Fetch(vector<CGUIListItemPtr> &items) const
219 {
220   CSingleLock lock(m_section);
221   items.clear();
222   for (vector<CGUIStaticItemPtr>::const_iterator i = m_items.begin(); i != m_items.end(); ++i)
223   {
224     if ((*i)->IsVisible())
225       items.push_back(*i);
226   }
227 }
228
229 void CDirectoryProvider::Reset(bool immediately /* = false */)
230 {
231   // cancel any pending jobs
232   CSingleLock lock(m_section);
233   if (m_jobID)
234     CJobManager::GetInstance().CancelJob(m_jobID);
235   m_jobID = 0;
236   // reset only if this is going to be destructed
237   if (immediately)
238   {
239     m_items.clear();
240     m_currentTarget.clear();
241     m_currentUrl.clear();
242     m_itemTypes.clear();
243     m_updateState = OK;
244     RegisterListProvider(false);
245   }
246 }
247
248 void CDirectoryProvider::OnJobComplete(unsigned int jobID, bool success, CJob *job)
249 {
250   CSingleLock lock(m_section);
251   if (success)
252   {
253     m_items = ((CDirectoryJob*)job)->GetItems();
254     m_currentTarget = ((CDirectoryJob*)job)->GetTarget();
255     ((CDirectoryJob*)job)->GetItemTypes(m_itemTypes);
256     m_updateState = DONE;
257   }
258   m_jobID = 0;
259 }
260
261 bool CDirectoryProvider::OnClick(const CGUIListItemPtr &item)
262 {
263   CFileItem fileItem(*boost::static_pointer_cast<CFileItem>(item));
264   string target = fileItem.GetProperty("node.target").asString();
265   if (target.empty())
266     target = m_currentTarget;
267   if (target.empty())
268     target = m_target.GetLabel(m_parentID, false);
269   if (fileItem.HasProperty("node.target_url"))
270     fileItem.SetPath(fileItem.GetProperty("node.target_url").asString());
271   // grab the execute string
272   string execute = CFavouritesDirectory::GetExecutePath(fileItem, target);
273   if (!execute.empty())
274   {
275     CGUIMessage message(GUI_MSG_EXECUTE, 0, 0);
276     message.SetStringParam(execute);
277     g_windowManager.SendMessage(message);
278     return true;
279   }
280   return false;
281 }
282
283 bool CDirectoryProvider::IsUpdating() const
284 {
285   CSingleLock lock(m_section);
286   return m_jobID || (m_updateState == DONE);
287 }
288
289 void CDirectoryProvider::FireJob()
290 {
291   CSingleLock lock(m_section);
292   if (m_jobID)
293     CJobManager::GetInstance().CancelJob(m_jobID);
294   m_jobID = CJobManager::GetInstance().AddJob(new CDirectoryJob(m_currentUrl, m_parentID), this);
295 }
296
297 void CDirectoryProvider::RegisterListProvider(bool hasLibraryContent)
298 {
299   if (hasLibraryContent && !m_isAnnounced)
300   {
301     m_isAnnounced = true;
302     CAnnouncementManager::AddAnnouncer(this);
303   }
304   else if (!hasLibraryContent && m_isAnnounced)
305   {
306     m_isAnnounced = false;
307     m_isDbUpdating = false;
308     CAnnouncementManager::RemoveAnnouncer(this);
309   }
310 }
311
312 bool CDirectoryProvider::UpdateURL()
313 {
314   CStdString value(m_url.GetLabel(m_parentID, false));
315   if (value == m_currentUrl)
316     return false;
317
318   m_currentUrl = value;
319
320   // Register this provider only if we have library content
321   RegisterListProvider(URIUtils::IsLibraryContent(m_currentUrl));
322
323   return true;
324 }