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, see
17 * <http://www.gnu.org/licenses/>.
21 #include "FileOperationJob.h"
22 #include "filesystem/File.h"
23 #include "filesystem/Directory.h"
24 #include "filesystem/ZipManager.h"
25 #include "filesystem/FileDirectoryFactory.h"
26 #include "filesystem/MultiPathDirectory.h"
27 #include "filesystem/SpecialProtocol.h"
31 #include "utils/StringUtils.h"
33 #include "guilib/LocalizeStrings.h"
34 #include "guilib/GUIWindowManager.h"
35 #include "dialogs/GUIDialogExtendedProgressBar.h"
37 #ifdef HAS_FILESYSTEM_RAR
38 #include "filesystem/RarManager.h"
42 using namespace XFILE;
44 CFileOperationJob::CFileOperationJob()
47 m_displayProgress = false;
50 CFileOperationJob::CFileOperationJob(FileAction action, CFileItemList & items,
51 const CStdString& strDestFile,
53 int heading, int line)
56 m_displayProgress = displayProgress;
59 SetFileOperation(action, items, strDestFile);
62 void CFileOperationJob::SetFileOperation(FileAction action, CFileItemList &items, const CStdString &strDestFile)
65 m_strDestFile = strDestFile;
68 for (int i = 0; i < items.Size(); i++)
69 m_items.Add(CFileItemPtr(new CFileItem(*items[i])));
72 bool CFileOperationJob::DoWork()
74 FileOperationList ops;
75 double totalTime = 0.0;
77 if (m_displayProgress)
79 CGUIDialogExtendedProgressBar* dialog =
80 (CGUIDialogExtendedProgressBar*)g_windowManager.GetWindow(WINDOW_DIALOG_EXT_PROGRESS);
81 m_handle = dialog->GetHandle(GetActionString(m_action));
84 bool success = DoProcess(m_action, m_items, m_strDestFile, ops, totalTime);
86 unsigned int size = ops.size();
88 double opWeight = 100.0 / totalTime;
91 for (unsigned int i = 0; i < size && success; i++)
92 success &= ops[i].ExecuteOperation(this, current, opWeight);
95 m_handle->MarkFinished();
100 bool CFileOperationJob::DoProcessFile(FileAction action, const CStdString& strFileA, const CStdString& strFileB, FileOperationList &fileOperations, double &totalTime)
104 if (action == ActionCopy || action == ActionReplace || (action == ActionMove && !CanBeRenamed(strFileA, strFileB)))
106 struct __stat64 data;
107 if(CFile::Stat(strFileA, &data) == 0)
108 time += data.st_size;
111 fileOperations.push_back(CFileOperation(action, strFileA, strFileB, time));
118 bool CFileOperationJob::DoProcessFolder(FileAction action, const CStdString& strPath, const CStdString& strDestFile, FileOperationList &fileOperations, double &totalTime)
120 // check whether this folder is a filedirectory - if so, we don't process it's contents
121 CFileItem item(strPath, false);
122 IFileDirectory *file = CFileDirectoryFactory::Create(strPath, &item);
128 CLog::Log(LOGDEBUG,"FileManager, processing folder: %s",strPath.c_str());
130 //m_rootDir.GetDirectory(strPath, items);
131 CDirectory::GetDirectory(strPath, items, "", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_GET_HIDDEN);
132 for (int i = 0; i < items.Size(); i++)
134 CFileItemPtr pItem = items[i];
136 CLog::Log(LOGDEBUG," -- %s",pItem->GetPath().c_str());
139 if (!DoProcess(action, items, strDestFile, fileOperations, totalTime)) return false;
141 if (action == ActionMove)
143 fileOperations.push_back(CFileOperation(ActionDeleteFolder, strPath, "", 1));
150 bool CFileOperationJob::DoProcess(FileAction action, CFileItemList & items, const CStdString& strDestFile, FileOperationList &fileOperations, double &totalTime)
152 for (int iItem = 0; iItem < items.Size(); ++iItem)
154 CFileItemPtr pItem = items[iItem];
155 if (pItem->IsSelected())
157 CStdString strNoSlash = pItem->GetPath();
158 URIUtils::RemoveSlashAtEnd(strNoSlash);
159 CStdString strFileName = URIUtils::GetFileName(strNoSlash);
161 // special case for upnp
162 if (URIUtils::IsUPnP(items.GetPath()) || URIUtils::IsUPnP(pItem->GetPath()))
164 // get filename from label instead of path
165 strFileName = pItem->GetLabel();
167 if(!pItem->m_bIsFolder && !URIUtils::HasExtension(strFileName))
169 // FIXME: for now we only work well if the url has the extension
170 // we should map the content type to the extension otherwise
171 strFileName += URIUtils::GetExtension(pItem->GetPath());
174 strFileName = CUtil::MakeLegalFileName(strFileName);
177 CStdString strnewDestFile;
178 if(!strDestFile.empty()) // only do this if we have a destination
179 strnewDestFile = URIUtils::ChangeBasePath(pItem->GetPath(), strFileName, strDestFile); // Convert (URL) encoding + slashes (if source / target differ)
181 if (pItem->m_bIsFolder)
183 // in ActionReplace mode all subdirectories will be removed by the below
184 // DoProcessFolder(ActionDelete) call as well, so ActionCopy is enough when
186 FileAction subdirAction = (action == ActionReplace) ? ActionCopy : action;
187 // create folder on dest. drive
188 if (action != ActionDelete && action != ActionDeleteFolder)
189 DoProcessFile(ActionCreateFolder, strnewDestFile, "", fileOperations, totalTime);
190 if (action == ActionReplace && CDirectory::Exists(strnewDestFile))
191 DoProcessFolder(ActionDelete, strnewDestFile, "", fileOperations, totalTime);
192 if (!DoProcessFolder(subdirAction, pItem->GetPath(), strnewDestFile, fileOperations, totalTime))
194 if (action == ActionDelete || action == ActionDeleteFolder)
195 DoProcessFile(ActionDeleteFolder, pItem->GetPath(), "", fileOperations, totalTime);
198 DoProcessFile(action, pItem->GetPath(), strnewDestFile, fileOperations, totalTime);
204 CFileOperationJob::CFileOperation::CFileOperation(FileAction action, const CStdString &strFileA, const CStdString &strFileB, int64_t time) : m_action(action), m_strFileA(strFileA), m_strFileB(strFileB), m_time(time)
210 CFileOperationJob *base;
215 CStdString CFileOperationJob::GetActionString(FileAction action)
222 result = g_localizeStrings.Get(115);
225 result = g_localizeStrings.Get(116);
228 case ActionDeleteFolder:
229 result = g_localizeStrings.Get(117);
231 case ActionCreateFolder:
232 result = g_localizeStrings.Get(119);
241 bool CFileOperationJob::CFileOperation::ExecuteOperation(CFileOperationJob *base, double ¤t, double opWeight)
245 base->m_currentFile = CURL(m_strFileA).GetFileNameWithoutPath();
246 base->m_currentOperation = GetActionString(m_action);
248 if (base->ShouldCancel((unsigned)current, 100))
253 base->m_handle->SetText(base->GetCurrentFile());
254 base->m_handle->SetPercentage((float)current);
257 DataHolder data = {base, current, opWeight};
264 CLog::Log(LOGDEBUG,"FileManager: copy %s -> %s\n", m_strFileA.c_str(), m_strFileB.c_str());
266 bResult = CFile::Cache(m_strFileA, m_strFileB, this, &data);
271 CLog::Log(LOGDEBUG,"FileManager: move %s -> %s\n", m_strFileA.c_str(), m_strFileB.c_str());
273 if (CanBeRenamed(m_strFileA, m_strFileB))
274 bResult = CFile::Rename(m_strFileA, m_strFileB);
275 else if (CFile::Cache(m_strFileA, m_strFileB, this, &data))
276 bResult = CFile::Delete(m_strFileA);
283 CLog::Log(LOGDEBUG,"FileManager: delete %s\n", m_strFileA.c_str());
285 bResult = CFile::Delete(m_strFileA);
288 case ActionDeleteFolder:
290 CLog::Log(LOGDEBUG,"FileManager: delete folder %s\n", m_strFileA.c_str());
292 bResult = CDirectory::Remove(m_strFileA);
295 case ActionCreateFolder:
297 CLog::Log(LOGDEBUG,"FileManager: create folder %s\n", m_strFileA.c_str());
299 bResult = CDirectory::Create(m_strFileA);
304 current += (double)m_time * opWeight;
309 inline bool CFileOperationJob::CanBeRenamed(const CStdString &strFileA, const CStdString &strFileB)
312 if (strFileA[1] == ':' && strFileA[0] == strFileB[0])
315 if (URIUtils::IsHD(strFileA) && URIUtils::IsHD(strFileB))
321 void CFileOperationJob::CFileOperation::Debug()
323 printf("%i | %s > %s\n", m_action, m_strFileA.c_str(), m_strFileB.c_str());
326 bool CFileOperationJob::CFileOperation::OnFileCallback(void* pContext, int ipercent, float avgSpeed)
328 DataHolder *data = (DataHolder *)pContext;
329 double current = data->current + ((double)ipercent * data->opWeight * (double)m_time)/ 100.0;
331 if (avgSpeed > 1000000.0f)
332 data->base->m_avgSpeed = StringUtils::Format("%.1f MB/s", avgSpeed / 1000000.0f);
334 data->base->m_avgSpeed = StringUtils::Format("%.1f KB/s", avgSpeed / 1000.0f);
336 if (data->base->m_handle)
339 line = StringUtils::Format("%s (%s)",
340 data->base->GetCurrentFile().c_str(),
341 data->base->GetAverageSpeed().c_str());
342 data->base->m_handle->SetText(line);
343 data->base->m_handle->SetPercentage((float)current);
346 return !data->base->ShouldCancel((unsigned)current, 100);
349 bool CFileOperationJob::operator==(const CJob* job) const
351 if (strcmp(job->GetType(),GetType()) == 0)
353 const CFileOperationJob* rjob = dynamic_cast<const CFileOperationJob*>(job);
356 if (GetAction() == rjob->GetAction() &&
357 m_strDestFile == rjob->m_strDestFile &&
358 m_items.Size() == rjob->m_items.Size())
360 for (int i=0;i<m_items.Size();++i)
362 if (m_items[i]->GetPath() != rjob->m_items[i]->GetPath() ||
363 m_items[i]->IsSelected() != rjob->m_items[i]->IsSelected())