Merge pull request #1281 from jmarshallnz/plugins_drop_progress
[vuplus_xbmc] / xbmc / filesystem / Directory.cpp
1 /*
2  *      Copyright (C) 2005-2012 Team XBMC
3  *      http://www.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 "Directory.h"
22 #include "DirectoryFactory.h"
23 #include "FileDirectoryFactory.h"
24 #include "commons/Exception.h"
25 #include "FileItem.h"
26 #include "DirectoryCache.h"
27 #include "settings/GUISettings.h"
28 #include "utils/log.h"
29 #include "utils/Job.h"
30 #include "utils/JobManager.h"
31 #include "Application.h"
32 #include "guilib/GUIWindowManager.h"
33 #include "dialogs/GUIDialogBusy.h"
34 #include "threads/SingleLock.h"
35 #include "utils/URIUtils.h"
36
37 using namespace std;
38 using namespace XFILE;
39
40 #define TIME_TO_BUSY_DIALOG 500
41
42 class CGetDirectory
43 {
44 private:
45
46   struct CResult
47   {
48     CResult(const CStdString& dir) : m_event(true), m_dir(dir), m_result(false) {}
49     CEvent        m_event;
50     CFileItemList m_list;
51     CStdString    m_dir;
52     bool          m_result;
53   };
54
55   struct CGetJob
56     : CJob
57   {
58     CGetJob(boost::shared_ptr<IDirectory>& imp
59           , boost::shared_ptr<CResult>& result)
60       : m_result(result)
61       , m_imp(imp)
62     {}
63   public:
64     virtual bool DoWork()
65     {
66       m_result->m_list.SetPath(m_result->m_dir);
67       m_result->m_result         = m_imp->GetDirectory(m_result->m_dir, m_result->m_list);
68       m_result->m_event.Set();
69       return m_result->m_result;
70     }
71
72     boost::shared_ptr<CResult>    m_result;
73     boost::shared_ptr<IDirectory> m_imp;
74   };
75
76 public:
77
78   CGetDirectory(boost::shared_ptr<IDirectory>& imp, const CStdString& dir) 
79     : m_result(new CResult(dir))
80   {
81     m_id = CJobManager::GetInstance().AddJob(new CGetJob(imp, m_result)
82                                            , NULL
83                                            , CJob::PRIORITY_HIGH);
84   }
85  ~CGetDirectory()
86   {
87     CJobManager::GetInstance().CancelJob(m_id);
88   }
89
90   bool Wait(unsigned int timeout)
91   {
92     return m_result->m_event.WaitMSec(timeout);
93   }
94
95   bool GetDirectory(CFileItemList& list)
96   {
97     /* if it was not finished or failed, return failure */
98     if(!m_result->m_event.WaitMSec(0) || !m_result->m_result)
99     {
100       list.Clear();
101       return false;
102     }
103
104     list.Copy(m_result->m_list);
105     return true;
106   }
107   boost::shared_ptr<CResult> m_result;
108   unsigned int               m_id;
109 };
110
111
112 CDirectory::CDirectory()
113 {}
114
115 CDirectory::~CDirectory()
116 {}
117
118 bool CDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items, const CStdString &strMask /*=""*/, int flags /*=DIR_FLAG_DEFAULTS*/, bool allowThreads /* = false */)
119 {
120   CHints hints;
121   hints.flags = flags;
122   hints.mask = strMask;
123   return GetDirectory(strPath, items, hints, allowThreads);
124 }
125
126 bool CDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items, const CHints &hints, bool allowThreads)
127 {
128   try
129   {
130     CStdString realPath = URIUtils::SubstitutePath(strPath);
131     boost::shared_ptr<IDirectory> pDirectory(CDirectoryFactory::Create(realPath));
132     if (!pDirectory.get())
133       return false;
134
135     // check our cache for this path
136     if (g_directoryCache.GetDirectory(strPath, items, (hints.flags & DIR_FLAG_READ_CACHE) == DIR_FLAG_READ_CACHE))
137       items.SetPath(strPath);
138     else
139     {
140       // need to clear the cache (in case the directory fetch fails)
141       // and (re)fetch the folder
142       if (!(hints.flags & DIR_FLAG_BYPASS_CACHE))
143         g_directoryCache.ClearDirectory(strPath);
144
145       pDirectory->SetFlags(hints.flags);
146
147       bool result = false, cancel = false;
148       while (!result && !cancel)
149       {
150         if (g_application.IsCurrentThread() && allowThreads && !URIUtils::IsSpecial(strPath))
151         {
152           CSingleExit ex(g_graphicsContext);
153
154           CGetDirectory get(pDirectory, realPath);
155           if(!get.Wait(TIME_TO_BUSY_DIALOG))
156           {
157             CGUIDialogBusy* dialog = (CGUIDialogBusy*)g_windowManager.GetWindow(WINDOW_DIALOG_BUSY);
158             dialog->Show();
159
160             while(!get.Wait(10))
161             {
162               CSingleLock lock(g_graphicsContext);
163
164               // update progress
165               float progress = pDirectory->GetProgress();
166               if (progress > 0)
167                 dialog->SetProgress(progress);
168
169               if(dialog->IsCanceled())
170               {
171                 cancel = true;
172                 pDirectory->CancelDirectory();
173                 break;
174               }
175
176               lock.Leave(); // prevent an occasional deadlock on exit
177               g_windowManager.ProcessRenderLoop(false);
178             }
179             if(dialog)
180               dialog->Close();
181           }
182           result = get.GetDirectory(items);
183         }
184         else
185         {
186           items.SetPath(strPath);
187           result = pDirectory->GetDirectory(realPath, items);
188         }
189
190         if (!result)
191         {
192           if (!cancel && g_application.IsCurrentThread() && pDirectory->ProcessRequirements())
193             continue;
194           CLog::Log(LOGERROR, "%s - Error getting %s", __FUNCTION__, strPath.c_str());
195           return false;
196         }
197       }
198
199       // cache the directory, if necessary
200       if (!(hints.flags & DIR_FLAG_BYPASS_CACHE))
201         g_directoryCache.SetDirectory(strPath, items, pDirectory->GetCacheType(strPath));
202     }
203
204     // now filter for allowed files
205     pDirectory->SetMask(hints.mask);
206     for (int i = 0; i < items.Size(); ++i)
207     {
208       CFileItemPtr item = items[i];
209       // TODO: we shouldn't be checking the gui setting here;
210       // callers should use getHidden instead
211       if ((!item->m_bIsFolder && !pDirectory->IsAllowed(item->GetPath())) ||
212           (item->GetProperty("file:hidden").asBoolean() && !(hints.flags & DIR_FLAG_GET_HIDDEN) && !g_guiSettings.GetBool("filelists.showhidden")))
213       {
214         items.Remove(i);
215         i--; // don't confuse loop
216       }
217     }
218
219     //  Should any of the files we read be treated as a directory?
220     //  Disable for database folders, as they already contain the extracted items
221     if (!(hints.flags & DIR_FLAG_NO_FILE_DIRS) && !items.IsMusicDb() && !items.IsVideoDb() && !items.IsSmartPlayList())
222       FilterFileDirectories(items, hints.mask);
223
224     return true;
225   }
226   XBMCCOMMONS_HANDLE_UNCHECKED
227   catch (...)
228   {
229     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
230   }
231   CLog::Log(LOGERROR, "%s - Error getting %s", __FUNCTION__, strPath.c_str());
232   return false;
233 }
234
235 bool CDirectory::Create(const CStdString& strPath)
236 {
237   try
238   {
239     CStdString realPath = URIUtils::SubstitutePath(strPath);
240     auto_ptr<IDirectory> pDirectory(CDirectoryFactory::Create(realPath));
241     if (pDirectory.get())
242       if(pDirectory->Create(realPath.c_str()))
243         return true;
244   }
245   XBMCCOMMONS_HANDLE_UNCHECKED
246   catch (...)
247   {
248     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
249   }
250   CLog::Log(LOGERROR, "%s - Error creating %s", __FUNCTION__, strPath.c_str());
251   return false;
252 }
253
254 bool CDirectory::Exists(const CStdString& strPath)
255 {
256   try
257   {
258     CStdString realPath = URIUtils::SubstitutePath(strPath);
259     auto_ptr<IDirectory> pDirectory(CDirectoryFactory::Create(realPath));
260     if (pDirectory.get())
261       return pDirectory->Exists(realPath.c_str());
262   }
263   XBMCCOMMONS_HANDLE_UNCHECKED
264   catch (...)
265   {
266     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
267   }
268   CLog::Log(LOGERROR, "%s - Error checking for %s", __FUNCTION__, strPath.c_str());
269   return false;
270 }
271
272 bool CDirectory::Remove(const CStdString& strPath)
273 {
274   try
275   {
276     CStdString realPath = URIUtils::SubstitutePath(strPath);
277     auto_ptr<IDirectory> pDirectory(CDirectoryFactory::Create(realPath));
278     if (pDirectory.get())
279       if(pDirectory->Remove(realPath.c_str()))
280         return true;
281   }
282   XBMCCOMMONS_HANDLE_UNCHECKED
283   catch (...)
284   {
285     CLog::Log(LOGERROR, "%s - Unhandled exception", __FUNCTION__);
286   }
287   CLog::Log(LOGERROR, "%s - Error removing %s", __FUNCTION__, strPath.c_str());
288   return false;
289 }
290
291 void CDirectory::FilterFileDirectories(CFileItemList &items, const CStdString &mask)
292 {
293   for (int i=0; i< items.Size(); ++i)
294   {
295     CFileItemPtr pItem=items[i];
296     if ((!pItem->m_bIsFolder) && (!pItem->IsInternetStream()))
297     {
298       auto_ptr<IFileDirectory> pDirectory(CFileDirectoryFactory::Create(pItem->GetPath(),pItem.get(),mask));
299       if (pDirectory.get())
300         pItem->m_bIsFolder = true;
301       else
302         if (pItem->m_bIsFolder)
303         {
304           items.Remove(i);
305           i--; // don't confuse loop
306         }
307     }
308   }
309 }