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 // URL Decode for cases where source uses URL encoding and target does not
162 if ( URIUtils::ProtocolHasEncodedFilename(CURL(pItem->GetPath()).GetProtocol() )
163 && !URIUtils::ProtocolHasEncodedFilename(CURL(strDestFile).GetProtocol() ) )
164 CURL::Decode(strFileName);
166 // special case for upnp
167 if (URIUtils::IsUPnP(items.GetPath()) || URIUtils::IsUPnP(pItem->GetPath()))
169 // get filename from label instead of path
170 strFileName = pItem->GetLabel();
172 if(!pItem->m_bIsFolder && !URIUtils::HasExtension(strFileName))
174 // FIXME: for now we only work well if the url has the extension
175 // we should map the content type to the extension otherwise
176 strFileName += URIUtils::GetExtension(pItem->GetPath());
179 strFileName = CUtil::MakeLegalFileName(strFileName);
182 CStdString strnewDestFile;
183 if(!strDestFile.IsEmpty()) // only do this if we have a destination
184 strnewDestFile = URIUtils::AddFileToFolder(strDestFile, strFileName);
186 if (pItem->m_bIsFolder)
188 // in ActionReplace mode all subdirectories will be removed by the below
189 // DoProcessFolder(ActionDelete) call as well, so ActionCopy is enough when
191 FileAction subdirAction = (action == ActionReplace) ? ActionCopy : action;
192 // create folder on dest. drive
193 if (action != ActionDelete && action != ActionDeleteFolder)
194 DoProcessFile(ActionCreateFolder, strnewDestFile, "", fileOperations, totalTime);
195 if (action == ActionReplace && CDirectory::Exists(strnewDestFile))
196 DoProcessFolder(ActionDelete, strnewDestFile, "", fileOperations, totalTime);
197 if (!DoProcessFolder(subdirAction, pItem->GetPath(), strnewDestFile, fileOperations, totalTime))
199 if (action == ActionDelete || action == ActionDeleteFolder)
200 DoProcessFile(ActionDeleteFolder, pItem->GetPath(), "", fileOperations, totalTime);
203 DoProcessFile(action, pItem->GetPath(), strnewDestFile, fileOperations, totalTime);
209 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)
215 CFileOperationJob *base;
220 CStdString CFileOperationJob::GetActionString(FileAction action)
227 result = g_localizeStrings.Get(115);
230 result = g_localizeStrings.Get(116);
233 case ActionDeleteFolder:
234 result = g_localizeStrings.Get(117);
236 case ActionCreateFolder:
237 result = g_localizeStrings.Get(119);
246 bool CFileOperationJob::CFileOperation::ExecuteOperation(CFileOperationJob *base, double ¤t, double opWeight)
250 base->m_currentFile = CURL(m_strFileA).GetFileNameWithoutPath();
251 base->m_currentOperation = GetActionString(m_action);
253 if (base->ShouldCancel((unsigned)current, 100))
258 base->m_handle->SetText(base->GetCurrentFile());
259 base->m_handle->SetPercentage((float)current);
262 DataHolder data = {base, current, opWeight};
269 CLog::Log(LOGDEBUG,"FileManager: copy %s -> %s\n", m_strFileA.c_str(), m_strFileB.c_str());
271 bResult = CFile::Cache(m_strFileA, m_strFileB, this, &data);
276 CLog::Log(LOGDEBUG,"FileManager: move %s -> %s\n", m_strFileA.c_str(), m_strFileB.c_str());
278 if (CanBeRenamed(m_strFileA, m_strFileB))
279 bResult = CFile::Rename(m_strFileA, m_strFileB);
280 else if (CFile::Cache(m_strFileA, m_strFileB, this, &data))
281 bResult = CFile::Delete(m_strFileA);
288 CLog::Log(LOGDEBUG,"FileManager: delete %s\n", m_strFileA.c_str());
290 bResult = CFile::Delete(m_strFileA);
293 case ActionDeleteFolder:
295 CLog::Log(LOGDEBUG,"FileManager: delete folder %s\n", m_strFileA.c_str());
297 bResult = CDirectory::Remove(m_strFileA);
300 case ActionCreateFolder:
302 CLog::Log(LOGDEBUG,"FileManager: create folder %s\n", m_strFileA.c_str());
304 bResult = CDirectory::Create(m_strFileA);
309 current += (double)m_time * opWeight;
314 inline bool CFileOperationJob::CanBeRenamed(const CStdString &strFileA, const CStdString &strFileB)
317 if (strFileA[1] == ':' && strFileA[0] == strFileB[0])
320 if (URIUtils::IsHD(strFileA) && URIUtils::IsHD(strFileB))
326 void CFileOperationJob::CFileOperation::Debug()
328 printf("%i | %s > %s\n", m_action, m_strFileA.c_str(), m_strFileB.c_str());
331 bool CFileOperationJob::CFileOperation::OnFileCallback(void* pContext, int ipercent, float avgSpeed)
333 DataHolder *data = (DataHolder *)pContext;
334 double current = data->current + ((double)ipercent * data->opWeight * (double)m_time)/ 100.0;
336 if (avgSpeed > 1000000.0f)
337 data->base->m_avgSpeed = StringUtils::Format("%.1f MB/s", avgSpeed / 1000000.0f);
339 data->base->m_avgSpeed = StringUtils::Format("%.1f KB/s", avgSpeed / 1000.0f);
341 if (data->base->m_handle)
344 line = StringUtils::Format("%s (%s)",
345 data->base->GetCurrentFile().c_str(),
346 data->base->GetAverageSpeed().c_str());
347 data->base->m_handle->SetText(line);
348 data->base->m_handle->SetPercentage((float)current);
351 return !data->base->ShouldCancel((unsigned)current, 100);
354 bool CFileOperationJob::operator==(const CJob* job) const
356 if (strcmp(job->GetType(),GetType()) == 0)
358 const CFileOperationJob* rjob = dynamic_cast<const CFileOperationJob*>(job);
361 if (GetAction() == rjob->GetAction() &&
362 m_strDestFile == rjob->m_strDestFile &&
363 m_items.Size() == rjob->m_items.Size())
365 for (int i=0;i<m_items.Size();++i)
367 if (m_items[i]->GetPath() != rjob->m_items[i]->GetPath() ||
368 m_items[i]->IsSelected() != rjob->m_items[i]->IsSelected())