aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--xbmc/GUIWindowFileManager.cpp70
-rw-r--r--xbmc/GUIWindowFileManager.h9
-rw-r--r--xbmc/utils/FileOperationJob.cpp284
-rw-r--r--xbmc/utils/FileOperationJob.h49
-rw-r--r--xbmc/utils/Makefile3
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 &current, 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 &current, 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