diff options
-rw-r--r-- | xbmc/GUIWindowFileManager.cpp | 70 | ||||
-rw-r--r-- | xbmc/GUIWindowFileManager.h | 9 | ||||
-rw-r--r-- | xbmc/utils/FileOperationJob.cpp | 284 | ||||
-rw-r--r-- | xbmc/utils/FileOperationJob.h | 49 | ||||
-rw-r--r-- | xbmc/utils/Makefile | 3 |
5 files changed, 392 insertions, 23 deletions
diff --git a/xbmc/GUIWindowFileManager.cpp b/xbmc/GUIWindowFileManager.cpp index 09630ac3c0..9f263e6c35 100644 --- a/xbmc/GUIWindowFileManager.cpp +++ b/xbmc/GUIWindowFileManager.cpp @@ -63,6 +63,9 @@ #include "StringUtils.h" #include "utils/log.h" +#include "JobManager.h" +#include "FileOperationJob.h" + using namespace std; using namespace XFILE; using namespace DIRECTORY; @@ -887,14 +890,10 @@ void CGUIWindowFileManager::OnCopy(int iList) ResetProgressBar(); - bool success = DoProcess(ACTION_COPY, *m_vecItems[iList], m_Directory[1 - iList]->m_strPath); - - if (m_dlgProgress) m_dlgProgress->Close(); - - if(!success) - CGUIDialogOK::ShowAndGetInput(16201, 16202, 16200, 0); + m_errorHeading = 16201; + m_errorLine = 16202; - Refresh(1 - iList); + CJobManager::GetInstance().AddJob(new CFileOperationJob(CFileOperationJob::ActionCopy, *m_vecItems[iList], m_Directory[1 - iList]->m_strPath), this); } void CGUIWindowFileManager::OnMove(int iList) @@ -904,14 +903,10 @@ void CGUIWindowFileManager::OnMove(int iList) ResetProgressBar(); - bool success = DoProcess(ACTION_MOVE, *m_vecItems[iList], m_Directory[1 - iList]->m_strPath); - - if (m_dlgProgress) m_dlgProgress->Close(); - - if(!success) - CGUIDialogOK::ShowAndGetInput(16203, 16204, 16200, 0); + m_errorHeading = 16203; + m_errorLine = 16204; - Refresh(); + CJobManager::GetInstance().AddJob(new CFileOperationJob(CFileOperationJob::ActionMove, *m_vecItems[iList], m_Directory[1 - iList]->m_strPath), this); } void CGUIWindowFileManager::OnDelete(int iList) @@ -921,14 +916,10 @@ void CGUIWindowFileManager::OnDelete(int iList) ResetProgressBar(false); - bool success = DoProcess(ACTION_DELETE, *m_vecItems[iList], m_Directory[iList]->m_strPath); - - if (m_dlgProgress) m_dlgProgress->Close(); - - if(!success) - CGUIDialogOK::ShowAndGetInput(16205, 16206, 16200, 0); + m_errorHeading = 16205; + m_errorLine = 16206; - Refresh(iList); + CJobManager::GetInstance().AddJob(new CFileOperationJob(CFileOperationJob::ActionDelete, *m_vecItems[iList], m_Directory[iList]->m_strPath), this); } void CGUIWindowFileManager::OnRename(int iList) @@ -1444,6 +1435,43 @@ int64_t CGUIWindowFileManager::CalculateFolderSize(const CStdString &strDirector return totalSize; } +void CGUIWindowFileManager::OnJobComplete(unsigned int jobID, bool success, CJob *job) +{ + m_dlgProgress->SetLine(0, 1040); + m_dlgProgress->SetLine(1, ""); + m_dlgProgress->SetLine(2, ""); + m_dlgProgress->SetPercentage(100); + Refresh(); + m_dlgProgress->Close(); + + if(!success) + CGUIDialogOK::ShowAndGetInput(m_errorHeading, m_errorLine, 16200, 0); +} + +void CGUIWindowFileManager::OnJobProgress(unsigned int jobID, unsigned int progress, unsigned int total, const CJob *job) +{ + if (m_dlgProgress->IsCanceled()) + { + CJobManager::GetInstance().CancelJob(jobID); + m_dlgProgress->SetLine(0, 1040); + m_dlgProgress->SetLine(1, ""); + m_dlgProgress->SetLine(2, ""); + Refresh(); + m_dlgProgress->Close(); + } + else + { + CFileOperationJob *fileJob = (CFileOperationJob *)job; + + m_dlgProgress->SetLine(0, fileJob->GetCurrentOperation()); + m_dlgProgress->SetLine(1, fileJob->GetCurrentFile()); + m_dlgProgress->SetLine(2, fileJob->GetAverageSpeed()); + + if (total > 0) + m_dlgProgress->SetPercentage((int)((float)progress * 100.0f / (float)total)); + } +} + bool CGUIWindowFileManager::DeleteItem(const CFileItem *pItem) { if (!pItem) return false; diff --git a/xbmc/GUIWindowFileManager.h b/xbmc/GUIWindowFileManager.h index 86b56762b9..c7c019b611 100644 --- a/xbmc/GUIWindowFileManager.h +++ b/xbmc/GUIWindowFileManager.h @@ -26,6 +26,7 @@ #include "FileSystem/DirectoryHistory.h" #include "utils/CriticalSection.h" #include "FileSystem/File.h" +#include "Job.h" class CFileItem; class CFileItemList; @@ -33,7 +34,8 @@ class CGUIDialogProgress; class CGUIWindowFileManager : public CGUIWindow, - public XFILE::IFileCallback + public XFILE::IFileCallback, + public IJobCallback { public: @@ -53,6 +55,9 @@ public: void ResetProgressBar(bool showProgress = true); static int64_t CalculateFolderSize(const CStdString &strDirectory, CGUIDialogProgress *pProgress = NULL); + + virtual void OnJobComplete(unsigned int jobID, bool success, CJob *job); + virtual void OnJobProgress(unsigned int jobID, unsigned int progress, unsigned int total, const CJob *job); protected: virtual void OnInitWindow(); void SetInitialPath(const CStdString &path); @@ -105,4 +110,6 @@ protected: CStdString m_strParentPath[2]; CGUIDialogProgress* m_dlgProgress; CDirectoryHistory m_history[2]; + + int m_errorHeading, m_errorLine; }; diff --git a/xbmc/utils/FileOperationJob.cpp b/xbmc/utils/FileOperationJob.cpp new file mode 100644 index 0000000000..9946887868 --- /dev/null +++ b/xbmc/utils/FileOperationJob.cpp @@ -0,0 +1,284 @@ +#include "FileOperationJob.h" +#include "FileSystem/File.h" +#include "FileSystem/Directory.h" +#include "FileSystem/ZipManager.h" +#include "FileSystem/FactoryFileDirectory.h" +#include "FileSystem/MultiPathDirectory.h" +#include "FileSystem/SpecialProtocol.h" +#include "log.h" +#include "Util.h" +#include "AdvancedSettings.h" +#include "LocalizeStrings.h" +#ifdef HAVE_XBMC_NONFREE +#include "FileSystem/RarManager.h" +#endif + +using namespace std; +using namespace XFILE; +using namespace DIRECTORY; + +CFileOperationJob::CFileOperationJob(FileAction action, CFileItemList & items, const CStdString& strDestFile) +{ + m_action = action; + m_strDestFile = strDestFile; + + for (int i = 0; i < items.Size(); i++) + m_items.Add(CFileItemPtr(new CFileItem(*items[i]))); +} + +bool CFileOperationJob::DoWork() +{ + FileOperationList ops; + double totalTime = 0.0; + bool success = DoProcess(m_action, m_items, m_strDestFile, ops, totalTime); + + unsigned int size = ops.size(); + + double opWeight = 100.0 / totalTime; + double current = 0.0; + + for (unsigned int i = 0; i < size && success; i++) + success &= ops[i].ExecuteOperation(this, current, opWeight); + + return success; +} + +bool CFileOperationJob::DoProcessFile(FileAction action, const CStdString& strFileA, const CStdString& strFileB, FileOperationList &fileOperations, double &totalTime) +{ + int64_t time = 1; + + if (action == ActionCopy || (action == ActionMove && !CanBeRenamed(strFileA, strFileB))) + { + CFile file; + if (file.Open(strFileA)) + { + time += file.GetLength(); + file.Close(); + } + } + + fileOperations.push_back(CFileOperation(action, strFileA, strFileB, time)); + + totalTime += time; + + return true; +} + +bool CFileOperationJob::DoProcessFolder(FileAction action, const CStdString& strPath, const CStdString& strDestFile, FileOperationList &fileOperations, double &totalTime) +{ + // check whether this folder is a filedirectory - if so, we don't process it's contents + CFileItem item(strPath, false); + IFileDirectory *file = CFactoryFileDirectory::Create(strPath, &item); + if (file) + { + delete file; + return true; + } + CLog::Log(LOGDEBUG,"FileManager, processing folder: %s",strPath.c_str()); + CFileItemList items; + //m_rootDir.GetDirectory(strPath, items); + CDirectory::GetDirectory(strPath, items, "", false); + for (int i = 0; i < items.Size(); i++) + { + CFileItemPtr pItem = items[i]; + pItem->Select(true); + CLog::Log(LOGDEBUG," -- %s",pItem->m_strPath.c_str()); + } + + if (!DoProcess(action, items, strDestFile, fileOperations, totalTime)) return false; + + if (action == ActionMove) + { + fileOperations.push_back(CFileOperation(ActionDeleteFolder, strPath, "", 1)); + totalTime += 1.0; + } + + return true; +} + +bool CFileOperationJob::DoProcess(FileAction action, CFileItemList & items, const CStdString& strDestFile, FileOperationList &fileOperations, double &totalTime) +{ + for (int iItem = 0; iItem < items.Size(); ++iItem) + { + CFileItemPtr pItem = items[iItem]; + if (pItem->IsSelected()) + { + CStdString strNoSlash = pItem->m_strPath; + CUtil::RemoveSlashAtEnd(strNoSlash); + CStdString strFileName = CUtil::GetFileName(strNoSlash); + + // special case for upnp + if (CUtil::IsUPnP(items.m_strPath) || CUtil::IsUPnP(pItem->m_strPath)) + { + // get filename from label instead of path + strFileName = pItem->GetLabel(); + + if(!pItem->m_bIsFolder && CUtil::GetExtension(strFileName).length() == 0) + { + // FIXME: for now we only work well if the url has the extension + // we should map the content type to the extension otherwise + strFileName += CUtil::GetExtension(pItem->m_strPath); + } + + strFileName = CUtil::MakeLegalFileName(strFileName); + } + + CStdString strnewDestFile; + if(!strDestFile.IsEmpty()) // only do this if we have a destination + CUtil::AddFileToFolder(strDestFile, strFileName, strnewDestFile); + + if (pItem->m_bIsFolder) + { + // create folder on dest. drive + if (action != ActionDelete) + DoProcessFile(ActionCreateFolder, strnewDestFile, "", fileOperations, totalTime); + if (!DoProcessFolder(action, pItem->m_strPath, strnewDestFile, fileOperations, totalTime)) + return false; + if (action == ActionDelete) + DoProcessFile(ActionDeleteFolder, pItem->m_strPath, "", fileOperations, totalTime); + } + else + DoProcessFile(action, pItem->m_strPath, strnewDestFile, fileOperations, totalTime); + } + } + return true; +} + +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) +{ +} + +struct DataHolder +{ + CFileOperationJob *base; + double current; + double opWeight; +}; + +bool CFileOperationJob::CFileOperation::ExecuteOperation(CFileOperationJob *base, double ¤t, double opWeight) +{ + bool bResult = true; + + base->m_currentFile = CURL(m_strFileA).GetFileNameWithoutPath(); + + switch (m_action) + { + case ActionCopy: + base->m_currentOperation = g_localizeStrings.Get(115); + break; + case ActionMove: + base->m_currentOperation = g_localizeStrings.Get(116); + break; + case ActionDelete: + case ActionDeleteFolder: + base->m_currentOperation = g_localizeStrings.Get(117); + break; + case ActionCreateFolder: + base->m_currentOperation = g_localizeStrings.Get(119); + break; + default: + base->m_currentOperation = ""; + break; + } + + if (base->ShouldCancel(current, 100.0)) + return false; + + DataHolder data = {base, current, opWeight}; + + switch (m_action) + { + case ActionCopy: + { + CLog::Log(LOGDEBUG,"FileManager: copy %s->%s\n", m_strFileA.c_str(), m_strFileB.c_str()); + + CURL url(m_strFileA); + if (url.GetProtocol() == "rar") + { +#ifdef HAVE_XBMC_NONFREE + g_RarManager.SetWipeAtWill(false); + CStdString strOriginalCachePath = g_advancedSettings.m_cachePath; + CStdString strDestPath; + CUtil::GetDirectory(m_strFileB, strDestPath); + g_advancedSettings.m_cachePath = strDestPath; + CLog::Log(LOGDEBUG, "CacheRarredFile: dest=%s, file=%s",strDestPath.c_str(), url.GetFileName().c_str()); + bResult = g_RarManager.CacheRarredFile(strDestPath,url.GetHostName(),url.GetFileName(),0,strDestPath,-2); + g_advancedSettings.m_cachePath = strOriginalCachePath; + g_RarManager.SetWipeAtWill(true); +#else + bResult = false; +#endif + } + else + bResult = CFile::Cache(m_strFileA, m_strFileB, this, &data); + } + break; + case ActionMove: + { + CLog::Log(LOGDEBUG,"FileManager: move %s->%s\n", m_strFileA.c_str(), m_strFileB.c_str()); + + if (CanBeRenamed(m_strFileA, m_strFileB)) + bResult = CFile::Rename(m_strFileA, m_strFileB); + else if (CFile::Cache(m_strFileA, m_strFileB, this, &data)) + bResult = CFile::Delete(m_strFileA); + else + bResult = false; + } + break; + case ActionDelete: + { + CLog::Log(LOGDEBUG,"FileManager: delete %s\n", m_strFileA.c_str()); + + bResult = CFile::Delete(m_strFileA); + } + break; + case ActionDeleteFolder: + { + CLog::Log(LOGDEBUG,"FileManager: delete folder %s\n", m_strFileA.c_str()); + + bResult = CDirectory::Remove(m_strFileA); + } + break; + case ActionCreateFolder: + { + CLog::Log(LOGDEBUG,"FileManager: create folder %s\n", m_strFileA.c_str()); + + bResult = CDirectory::Create(m_strFileA); + } + break; + } + + current += (double)m_time * opWeight; + + return bResult; +} + +inline bool CFileOperationJob::CanBeRenamed(const CStdString &strFileA, const CStdString &strFileB) +{ +#ifndef _LINUX + if (strFileA[1] == ':' && strFileA[0] == strFileB[0]) + return true; +#else + if (CUtil::IsHD(strFileA) && CUtil::IsHD(strFileB)) + return true; +#endif + return false; +} + +void CFileOperationJob::CFileOperation::Debug() +{ + printf("%i | %s > %s\n", m_action, m_strFileA.c_str(), m_strFileB.c_str()); +} + +bool CFileOperationJob::CFileOperation::OnFileCallback(void* pContext, int ipercent, float avgSpeed) +{ + DataHolder *data = (DataHolder *)pContext; + double current = data->current + ((double)ipercent * data->opWeight * (double)m_time)/ 100.0; + + if (avgSpeed > 1000000.0f) + data->base->m_avgSpeed.Format("%.1f Mb/s", avgSpeed / 1000000.0f); + else + data->base->m_avgSpeed.Format("%.1f Kb/s", avgSpeed / 1000.0f); + + return !data->base->ShouldCancel(current, 100.0); +} diff --git a/xbmc/utils/FileOperationJob.h b/xbmc/utils/FileOperationJob.h new file mode 100644 index 0000000000..c578d7ba34 --- /dev/null +++ b/xbmc/utils/FileOperationJob.h @@ -0,0 +1,49 @@ +#include "system.h" +#include "FileItem.h" +#include "Job.h" +#include "../FileSystem/File.h" + +class CFileOperationJob : public CJob +{ +public: + enum FileAction + { + ActionCopy = 1, + ActionMove, + ActionDelete, + ActionCreateFolder, + ActionDeleteFolder, + }; + + CFileOperationJob(FileAction action, CFileItemList & items, const CStdString& strDestFile); + + virtual bool DoWork(); + const CStdString &GetAverageSpeed() { return m_avgSpeed; } + const CStdString &GetCurrentOperation() { return m_currentOperation; } + const CStdString &GetCurrentFile() { return m_currentFile; } +private: + class CFileOperation : public XFILE::IFileCallback + { + public: + CFileOperation(FileAction action, const CStdString &strFileA, const CStdString &strFileB, int64_t time); + bool ExecuteOperation(CFileOperationJob *base, double ¤t, double opWeight); + void Debug(); + virtual bool OnFileCallback(void* pContext, int ipercent, float avgSpeed); + private: + FileAction m_action; + CStdString m_strFileA, m_strFileB; + int64_t m_time; + }; + friend class CFileOperation; + typedef std::vector<CFileOperation> FileOperationList; + bool DoProcess(FileAction action, CFileItemList & items, const CStdString& strDestFile, FileOperationList &fileOperations, double &totalTime); + bool DoProcessFolder(FileAction action, const CStdString& strPath, const CStdString& strDestFile, FileOperationList &fileOperations, double &totalTime); + bool DoProcessFile(FileAction action, const CStdString& strFileA, const CStdString& strFileB, FileOperationList &fileOperations, double &totalTime); + + static inline bool CanBeRenamed(const CStdString &strFileA, const CStdString &strFileB); + + FileAction m_action; + CFileItemList m_items; + CStdString m_strDestFile; + CStdString m_avgSpeed, m_currentOperation, m_currentFile; +}; diff --git a/xbmc/utils/Makefile b/xbmc/utils/Makefile index dba236bd65..bc5adbe534 100644 --- a/xbmc/utils/Makefile +++ b/xbmc/utils/Makefile @@ -61,7 +61,8 @@ SRCS=AlarmClock.cpp \ PasswordManager.cpp \ AliasShortcutUtils.cpp \ RingBuffer.cpp \ - Semaphore.cpp + Semaphore.cpp \ + FileOperationJob.cpp LIB=utils.a |