diff options
Diffstat (limited to 'xbmc/windows/GUIWindowFileManager.cpp')
-rw-r--r-- | xbmc/windows/GUIWindowFileManager.cpp | 1331 |
1 files changed, 1331 insertions, 0 deletions
diff --git a/xbmc/windows/GUIWindowFileManager.cpp b/xbmc/windows/GUIWindowFileManager.cpp new file mode 100644 index 0000000..4d3f399 --- /dev/null +++ b/xbmc/windows/GUIWindowFileManager.cpp @@ -0,0 +1,1331 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "GUIWindowFileManager.h" + +#include "Autorun.h" +#include "GUIPassword.h" +#include "GUIUserMessages.h" +#include "PlayListPlayer.h" +#include "ServiceBroker.h" +#include "URL.h" +#include "Util.h" +#include "application/Application.h" +#include "application/ApplicationComponents.h" +#include "application/ApplicationPlayer.h" +#include "cores/playercorefactory/PlayerCoreFactory.h" +#include "dialogs/GUIDialogBusy.h" +#include "dialogs/GUIDialogContextMenu.h" +#include "dialogs/GUIDialogMediaSource.h" +#include "dialogs/GUIDialogProgress.h" +#include "dialogs/GUIDialogTextViewer.h" +#include "dialogs/GUIDialogYesNo.h" +#include "favourites/FavouritesService.h" +#include "filesystem/Directory.h" +#include "filesystem/FileDirectoryFactory.h" +#include "filesystem/ZipManager.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIKeyboardFactory.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "input/InputManager.h" +#include "interfaces/generic/ScriptInvocationManager.h" +#include "messaging/ApplicationMessenger.h" +#include "messaging/helpers/DialogOKHelper.h" +#include "network/Network.h" +#include "pictures/GUIWindowSlideShow.h" +#include "platform/Filesystem.h" +#include "playlists/PlayList.h" +#include "playlists/PlayListFactory.h" +#include "settings/MediaSourceSettings.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "storage/MediaManager.h" +#include "threads/IRunnable.h" +#include "utils/FileOperationJob.h" +#include "utils/FileUtils.h" +#include "utils/JobManager.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/Variant.h" +#include "utils/log.h" + +using namespace XFILE; +using namespace KODI::MESSAGING; + +#define CONTROL_BTNSELECTALL 1 +#define CONTROL_BTNFAVOURITES 2 +#define CONTROL_BTNPLAYWITH 3 +#define CONTROL_BTNRENAME 4 +#define CONTROL_BTNDELETE 5 +#define CONTROL_BTNCOPY 6 +#define CONTROL_BTNMOVE 7 +#define CONTROL_BTNNEWFOLDER 8 +#define CONTROL_BTNCALCSIZE 9 +#define CONTROL_BTNSWITCHMEDIA 11 +#define CONTROL_BTNCANCELJOB 12 +#define CONTROL_BTNVIEW 13 + + +#define CONTROL_NUMFILES_LEFT 12 +#define CONTROL_NUMFILES_RIGHT 13 + +#define CONTROL_LEFT_LIST 20 +#define CONTROL_RIGHT_LIST 21 + +#define CONTROL_CURRENTDIRLABEL_LEFT 101 +#define CONTROL_CURRENTDIRLABEL_RIGHT 102 + +namespace +{ +class CGetDirectoryItems : public IRunnable +{ +public: + CGetDirectoryItems(XFILE::CVirtualDirectory &dir, CURL &url, CFileItemList &items) + : m_result(false), m_dir(dir), m_url(url), m_items(items) + { + } + void Run() override + { + m_result = m_dir.GetDirectory(m_url, m_items, false, false); + } + void Cancel() override + { + m_dir.CancelDirectory(); + } + bool m_result; +protected: + XFILE::CVirtualDirectory &m_dir; + CURL m_url; + CFileItemList &m_items; +}; +} + +CGUIWindowFileManager::CGUIWindowFileManager(void) + : CGUIWindow(WINDOW_FILES, "FileManager.xml"), + CJobQueue(false,2) +{ + m_Directory[0] = new CFileItem; + m_Directory[1] = new CFileItem; + m_vecItems[0] = new CFileItemList; + m_vecItems[1] = new CFileItemList; + m_Directory[0]->SetPath("?"); + m_Directory[1]->SetPath("?"); + m_Directory[0]->m_bIsFolder = true; + m_Directory[1]->m_bIsFolder = true; + bCheckShareConnectivity = true; + m_loadType = KEEP_IN_MEMORY; +} + +CGUIWindowFileManager::~CGUIWindowFileManager(void) +{ + delete m_Directory[0]; + delete m_Directory[1]; + delete m_vecItems[0]; + delete m_vecItems[1]; +} + +bool CGUIWindowFileManager::OnAction(const CAction &action) +{ + int list = GetFocusedList(); + if (list >= 0 && list <= 1) + { + int item; + + // the non-contextual menu can be called at any time + if (action.GetID() == ACTION_CONTEXT_MENU && m_vecItems[list]->Size() == 0) + { + OnPopupMenu(list,-1, false); + return true; + } + if (action.GetID() == ACTION_DELETE_ITEM) + { + if (CanDelete(list)) + { + bool bDeselect = SelectItem(list, item); + OnDelete(list); + if (bDeselect) m_vecItems[list]->Get(item)->Select(false); + } + return true; + } + if (action.GetID() == ACTION_COPY_ITEM) + { + if (CanCopy(list)) + { + bool bDeselect = SelectItem(list, item); + OnCopy(list); + if (bDeselect) m_vecItems[list]->Get(item)->Select(false); + } + return true; + } + if (action.GetID() == ACTION_MOVE_ITEM) + { + if (CanMove(list)) + { + bool bDeselect = SelectItem(list, item); + OnMove(list); + if (bDeselect) m_vecItems[list]->Get(item)->Select(false); + } + return true; + } + if (action.GetID() == ACTION_RENAME_ITEM) + { + if (CanRename(list)) + { + bool bDeselect = SelectItem(list, item); + OnRename(list); + if (bDeselect) m_vecItems[list]->Get(item)->Select(false); + } + return true; + } + if (action.GetID() == ACTION_PARENT_DIR) + { + GoParentFolder(list); + return true; + } + if (action.GetID() == ACTION_PLAYER_PLAY) + { +#ifdef HAS_DVD_DRIVE + if (m_vecItems[list]->Get(GetSelectedItem(list))->IsDVD()) + return MEDIA_DETECT::CAutorun::PlayDiscAskResume(m_vecItems[list]->Get(GetSelectedItem(list))->GetPath()); +#endif + } + } + return CGUIWindow::OnAction(action); +} + +bool CGUIWindowFileManager::OnBack(int actionID) +{ + int list = GetFocusedList(); + if (list >= 0 && list <= 1 && actionID == ACTION_NAV_BACK && !m_vecItems[list]->IsVirtualDirectoryRoot()) + { + GoParentFolder(list); + return true; + } + return CGUIWindow::OnBack(actionID); +} + +bool CGUIWindowFileManager::OnMessage(CGUIMessage& message) +{ + switch ( message.GetMessage() ) + { + case GUI_MSG_NOTIFY_ALL: + { // Message is received even if window is inactive + if (message.GetParam1() == GUI_MSG_WINDOW_RESET) + { + m_Directory[0]->SetPath("?"); + m_Directory[1]->SetPath("?"); + m_Directory[0]->m_bIsFolder = true; + m_Directory[1]->m_bIsFolder = true; + return true; + } + + // handle removable media + if (message.GetParam1() == GUI_MSG_REMOVED_MEDIA) + { + for (int i = 0; i < 2; i++) + { + if (m_Directory[i]->IsVirtualDirectoryRoot() && IsActive()) + { + int iItem = GetSelectedItem(i); + Update(i, m_Directory[i]->GetPath()); + CONTROL_SELECT_ITEM(CONTROL_LEFT_LIST + i, iItem); + } + else if (m_Directory[i]->IsRemovable() && !m_rootDir.IsInSource(m_Directory[i]->GetPath())) + { // + if (IsActive()) + Update(i, ""); + else + m_Directory[i]->SetPath(""); + } + } + return true; + } + else if (message.GetParam1()==GUI_MSG_UPDATE_SOURCES) + { // State of the sources changed, so update our view + for (int i = 0; i < 2; i++) + { + if (m_Directory[i]->IsVirtualDirectoryRoot() && IsActive()) + { + int iItem = GetSelectedItem(i); + Update(i, m_Directory[i]->GetPath()); + CONTROL_SELECT_ITEM(CONTROL_LEFT_LIST + i, iItem); + } + } + return true; + } + else if (message.GetParam1()==GUI_MSG_UPDATE && IsActive()) + { + Refresh(); + return true; + } + } + break; + case GUI_MSG_PLAYBACK_STARTED: + case GUI_MSG_PLAYBACK_ENDED: + case GUI_MSG_PLAYBACK_STOPPED: + case GUI_MSG_PLAYLIST_CHANGED: + case GUI_MSG_PLAYLISTPLAYER_STOPPED: + case GUI_MSG_PLAYLISTPLAYER_STARTED: + case GUI_MSG_PLAYLISTPLAYER_CHANGED: + { // send a notify all to all controls on this window + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_REFRESH_LIST); + OnMessage(msg); + break; + } + case GUI_MSG_WINDOW_DEINIT: + { + CGUIWindow::OnMessage(message); + ClearFileItems(0); + ClearFileItems(1); + return true; + } + break; + + case GUI_MSG_WINDOW_INIT: + { + SetInitialPath(message.GetStringParam()); + message.SetStringParam(""); + + return CGUIWindow::OnMessage(message); + } + break; + case GUI_MSG_CLICKED: + { + int iControl = message.GetSenderId(); + + if (iControl == CONTROL_LEFT_LIST || iControl == CONTROL_RIGHT_LIST) // list/thumb control + { + // get selected item + int list = iControl - CONTROL_LEFT_LIST; + int iItem = GetSelectedItem(list); + int iAction = message.GetParam1(); + + // iItem is checked for validity inside these routines + if (iAction == ACTION_HIGHLIGHT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK) + { + OnMark(list, iItem); + if (!CServiceBroker::GetInputManager().IsMouseActive()) + { + //move to next item + CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), iControl, iItem + 1); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); + } + } + else if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_DOUBLE_CLICK) + { + OnClick(list, iItem); + } + else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK) + { + OnPopupMenu(list, iItem); + } + } + } + break; + // prevent touch/gesture unfocussing .. + case GUI_MSG_GESTURE_NOTIFY: + case GUI_MSG_UNFOCUS_ALL: + return true; + } + return CGUIWindow::OnMessage(message); +} + +void CGUIWindowFileManager::OnSort(int iList) +{ + using namespace KODI::PLATFORM::FILESYSTEM; + // always sort the list by label in ascending order + for (int i = 0; i < m_vecItems[iList]->Size(); i++) + { + CFileItemPtr pItem = m_vecItems[iList]->Get(i); + if (pItem->m_bIsFolder && (!pItem->m_dwSize || pItem->IsPath("add"))) + pItem->SetLabel2(""); + else + pItem->SetFileSizeLabel(); + + // Set free space on disc + if (pItem->m_bIsShareOrDrive) + { + if (pItem->IsHD()) + { + std::error_code ec; + auto freeSpace = space(pItem->GetPath(), ec); + if (ec.value() == 0) + { + pItem->m_dwSize = freeSpace.free; + pItem->SetFileSizeLabel(); + } + } + else if (pItem->IsDVD() && CServiceBroker::GetMediaManager().IsDiscInDrive()) + { + std::error_code ec; + auto freeSpace = space(pItem->GetPath(), ec); + if (ec.value() == 0) + { + pItem->m_dwSize = freeSpace.capacity; + pItem->SetFileSizeLabel(); + } + } + } // if (pItem->m_bIsShareOrDrive) + + } + + m_vecItems[iList]->Sort(SortByLabel, SortOrderAscending); +} + +void CGUIWindowFileManager::ClearFileItems(int iList) +{ + CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), iList + CONTROL_LEFT_LIST); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); + + m_vecItems[iList]->Clear(); // will clean up everything +} + +void CGUIWindowFileManager::UpdateButtons() +{ + // update our current directory labels + std::string strDir = CURL(m_Directory[0]->GetPath()).GetWithoutUserDetails(); + if (strDir.empty()) + { + SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_LEFT,g_localizeStrings.Get(20108)); + } + else + { + SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_LEFT, strDir); + } + strDir = CURL(m_Directory[1]->GetPath()).GetWithoutUserDetails(); + if (strDir.empty()) + { + SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_RIGHT,g_localizeStrings.Get(20108)); + } + else + { + SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_RIGHT, strDir); + } + + // update the number of items in each list + UpdateItemCounts(); +} + +void CGUIWindowFileManager::UpdateItemCounts() +{ + for (int i = 0; i < 2; i++) + { + unsigned int selectedCount = 0; + unsigned int totalCount = 0; + int64_t selectedSize = 0; + for (int j = 0; j < m_vecItems[i]->Size(); j++) + { + CFileItemPtr item = m_vecItems[i]->Get(j); + if (item->IsParentFolder()) continue; + if (item->IsSelected()) + { + selectedCount++; + selectedSize += item->m_dwSize; + } + totalCount++; + } + std::string items; + if (selectedCount > 0) + items = + StringUtils::Format("{}/{} {} ({})", selectedCount, totalCount, + g_localizeStrings.Get(127), StringUtils::SizeToString(selectedSize)); + else + items = StringUtils::Format("{} {}", totalCount, g_localizeStrings.Get(127)); + SET_CONTROL_LABEL(CONTROL_NUMFILES_LEFT + i, items); + } +} + +bool CGUIWindowFileManager::Update(int iList, const std::string &strDirectory) +{ + if (m_updating) + { + CLog::Log(LOGWARNING, "CGUIWindowFileManager::Update - updating in progress"); + return true; + } + CUpdateGuard ug(m_updating); + + // get selected item + int iItem = GetSelectedItem(iList); + std::string strSelectedItem = ""; + + if (iItem >= 0 && iItem < m_vecItems[iList]->Size()) + { + CFileItemPtr pItem = m_vecItems[iList]->Get(iItem); + if (!pItem->IsParentFolder()) + { + GetDirectoryHistoryString(pItem.get(), strSelectedItem); + m_history[iList].SetSelectedItem(strSelectedItem, m_Directory[iList]->GetPath()); + } + } + + std::string strOldDirectory=m_Directory[iList]->GetPath(); + m_Directory[iList]->SetPath(strDirectory); + + CFileItemList items; + if (!GetDirectory(iList, m_Directory[iList]->GetPath(), items)) + { + if (strDirectory != strOldDirectory && GetDirectory(iList, strOldDirectory, items)) + m_Directory[iList]->SetPath(strOldDirectory); // Fallback to old (previous) path) + else + Update(iList, ""); // Fallback to root + + return false; + } + + m_history[iList].SetSelectedItem(strSelectedItem, strOldDirectory); + + ClearFileItems(iList); + + m_vecItems[iList]->Append(items); + m_vecItems[iList]->SetPath(items.GetPath()); + + std::string strParentPath; + URIUtils::GetParentPath(strDirectory, strParentPath); + if (strDirectory.empty() && (m_vecItems[iList]->Size() == 0 || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWADDSOURCEBUTTONS))) + { // add 'add source button' + const std::string& strLabel = g_localizeStrings.Get(1026); + CFileItemPtr pItem(new CFileItem(strLabel)); + pItem->SetPath("add"); + pItem->SetArt("icon", "DefaultAddSource.png"); + pItem->SetLabel(strLabel); + pItem->SetLabelPreformatted(true); + pItem->m_bIsFolder = true; + pItem->SetSpecialSort(SortSpecialOnBottom); + m_vecItems[iList]->Add(pItem); + } + else if (items.IsEmpty() || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWPARENTDIRITEMS)) + { + CFileItemPtr pItem(new CFileItem("..")); + pItem->SetPath(m_rootDir.IsSource(strDirectory) ? "" : strParentPath); + pItem->m_bIsFolder = true; + pItem->m_bIsShareOrDrive = false; + m_vecItems[iList]->AddFront(pItem, 0); + } + + m_strParentPath[iList] = (m_rootDir.IsSource(strDirectory) ? "" : strParentPath); + + if (strDirectory.empty()) + { + CFileItemPtr pItem(new CFileItem("special://profile/", true)); + pItem->SetLabel(g_localizeStrings.Get(20070)); + pItem->SetArt("thumb", "DefaultFolder.png"); + pItem->SetLabelPreformatted(true); + m_vecItems[iList]->Add(pItem); + + #ifdef TARGET_DARWIN_EMBEDDED + CFileItemPtr iItem(new CFileItem("special://envhome/Documents/Inbox", true)); + iItem->SetLabel("Inbox"); + iItem->SetArt("thumb", "DefaultFolder.png"); + iItem->SetLabelPreformatted(true); + m_vecItems[iList]->Add(iItem); + #endif + } + + // if we have a .tbn file, use itself as the thumb + for (int i = 0; i < m_vecItems[iList]->Size(); i++) + { + CFileItemPtr pItem = m_vecItems[iList]->Get(i); + if (pItem->IsHD() && + URIUtils::HasExtension(pItem->GetPath(), ".tbn")) + { + pItem->SetArt("thumb", pItem->GetPath()); + } + } + m_vecItems[iList]->FillInDefaultIcons(); + + OnSort(iList); + UpdateButtons(); + + int item = 0; + strSelectedItem = m_history[iList].GetSelectedItem(m_Directory[iList]->GetPath()); + for (int i = 0; i < m_vecItems[iList]->Size(); ++i) + { + CFileItemPtr pItem = m_vecItems[iList]->Get(i); + std::string strHistory; + GetDirectoryHistoryString(pItem.get(), strHistory); + if (strHistory == strSelectedItem) + { + item = i; + break; + } + } + UpdateControl(iList, item); + return true; +} + + +void CGUIWindowFileManager::OnClick(int iList, int iItem) +{ + if ( iList < 0 || iList >= 2) return ; + if ( iItem < 0 || iItem >= m_vecItems[iList]->Size() ) return ; + + CFileItemPtr pItem = m_vecItems[iList]->Get(iItem); + if (pItem->GetPath() == "add" && pItem->GetLabel() == g_localizeStrings.Get(1026)) // 'add source button' in empty root + { + if (CGUIDialogMediaSource::ShowAndAddMediaSource("files")) + { + m_rootDir.SetSources(*CMediaSourceSettings::GetInstance().GetSources("files")); + Update(0,m_Directory[0]->GetPath()); + Update(1,m_Directory[1]->GetPath()); + } + return; + } + + if (!pItem->m_bIsFolder && pItem->IsFileFolder(EFILEFOLDER_MASK_ALL)) + { + XFILE::IFileDirectory *pFileDirectory = NULL; + pFileDirectory = XFILE::CFileDirectoryFactory::Create(pItem->GetURL(), pItem.get(), ""); + if(pFileDirectory) + pItem->m_bIsFolder = true; + else if(pItem->m_bIsFolder) + pItem->m_bIsFolder = false; + delete pFileDirectory; + } + + if (pItem->m_bIsFolder) + { + // save path + drive type because of the possible refresh + std::string strPath = pItem->GetPath(); + int iDriveType = pItem->m_iDriveType; + if ( pItem->m_bIsShareOrDrive ) + { + if ( !g_passwordManager.IsItemUnlocked( pItem.get(), "files" ) ) + { + Refresh(); + return ; + } + + if ( !HaveDiscOrConnection( strPath, iDriveType ) ) + return ; + } + if (!Update(iList, strPath)) + ShowShareErrorMessage(pItem.get()); + } + else if (pItem->IsZIP() || pItem->IsCBZ()) // mount zip archive + { + CURL pathToUrl = URIUtils::CreateArchivePath("zip", pItem->GetURL(), ""); + Update(iList, pathToUrl.Get()); + } + else if (pItem->IsRAR() || pItem->IsCBR()) + { + CURL pathToUrl = URIUtils::CreateArchivePath("rar", pItem->GetURL(), ""); + Update(iList, pathToUrl.Get()); + } + else + { + OnStart(pItem.get(), ""); + return ; + } + // UpdateButtons(); +} + +//! @todo 2.0: Can this be removed, or should we run without the "special" file directories while +// in filemanager view. +void CGUIWindowFileManager::OnStart(CFileItem *pItem, const std::string &player) +{ + // start playlists from file manager + if (pItem->IsPlayList()) + { + const std::string& strPlayList = pItem->GetPath(); + std::unique_ptr<PLAYLIST::CPlayList> pPlayList(PLAYLIST::CPlayListFactory::Create(strPlayList)); + if (nullptr != pPlayList) + { + if (!pPlayList->Load(strPlayList)) + { + HELPERS::ShowOKDialogText(CVariant{6}, CVariant{477}); + return; + } + } + g_application.ProcessAndStartPlaylist(strPlayList, *pPlayList, PLAYLIST::TYPE_MUSIC); + return; + } + if (pItem->IsAudio() || pItem->IsVideo()) + { + CServiceBroker::GetPlaylistPlayer().Play(std::make_shared<CFileItem>(*pItem), player); + return; + } + if (pItem->IsGame()) + { + g_application.PlayFile(*pItem, player); + return ; + } +#ifdef HAS_PYTHON + if (pItem->IsPythonScript()) + { + CScriptInvocationManager::GetInstance().ExecuteAsync(pItem->GetPath()); + return ; + } +#endif + if (pItem->IsPicture()) + { + CGUIWindowSlideShow *pSlideShow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW); + if (!pSlideShow) + return ; + + const auto& components = CServiceBroker::GetAppComponents(); + const auto appPlayer = components.GetComponent<CApplicationPlayer>(); + if (appPlayer->IsPlayingVideo()) + g_application.StopPlaying(); + + pSlideShow->Reset(); + pSlideShow->Add(pItem); + pSlideShow->Select(pItem->GetPath()); + + CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW); + return; + } + if (pItem->IsType(".txt") || pItem->IsType(".xml")) + CGUIDialogTextViewer::ShowForFile(pItem->GetPath(), true); +} + +bool CGUIWindowFileManager::HaveDiscOrConnection( std::string& strPath, int iDriveType ) +{ + if ( iDriveType == CMediaSource::SOURCE_TYPE_DVD ) + { + if (!CServiceBroker::GetMediaManager().IsDiscInDrive(strPath)) + { + HELPERS::ShowOKDialogText(CVariant{218}, CVariant{219}); + int iList = GetFocusedList(); + int iItem = GetSelectedItem(iList); + Update(iList, ""); + CONTROL_SELECT_ITEM(iList + CONTROL_LEFT_LIST, iItem); + return false; + } + } + else if ( iDriveType == CMediaSource::SOURCE_TYPE_REMOTE ) + { + //! @todo Handle not connected to a remote share + if (!CServiceBroker::GetNetwork().IsConnected()) + { + HELPERS::ShowOKDialogText(CVariant{220}, CVariant{221}); + return false; + } + } + else + return true; + return true; +} + +void CGUIWindowFileManager::UpdateControl(int iList, int item) +{ + CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), iList + CONTROL_LEFT_LIST, item, 0, m_vecItems[iList]); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); +} + +void CGUIWindowFileManager::OnMark(int iList, int iItem) +{ + CFileItemPtr pItem = m_vecItems[iList]->Get(iItem); + + if (!pItem->m_bIsShareOrDrive) + { + if (!pItem->IsParentFolder()) + { + // MARK file + pItem->Select(!pItem->IsSelected()); + } + } + + UpdateItemCounts(); + // UpdateButtons(); +} + +void CGUIWindowFileManager::OnCopy(int iList) +{ + if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{120}, CVariant{123})) + return; + + AddJob(new CFileOperationJob(CFileOperationJob::ActionCopy, + *m_vecItems[iList], + m_Directory[1 - iList]->GetPath(), + true, 16201, 16202)); +} + +void CGUIWindowFileManager::OnMove(int iList) +{ + if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{121}, CVariant{124})) + return; + + AddJob(new CFileOperationJob(CFileOperationJob::ActionMove, + *m_vecItems[iList], + m_Directory[1 - iList]->GetPath(), + true, 16203, 16204)); +} + +void CGUIWindowFileManager::OnDelete(int iList) +{ + if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{122}, CVariant{125})) + return; + + AddJob(new CFileOperationJob(CFileOperationJob::ActionDelete, + *m_vecItems[iList], + m_Directory[iList]->GetPath(), + true, 16205, 16206)); +} + +void CGUIWindowFileManager::OnRename(int iList) +{ + std::string strFile; + for (int i = 0; i < m_vecItems[iList]->Size();++i) + { + CFileItemPtr pItem = m_vecItems[iList]->Get(i); + if (pItem->IsSelected()) + { + strFile = pItem->GetPath(); + break; + } + } + + CFileUtils::RenameFile(strFile); + + Refresh(iList); +} + +void CGUIWindowFileManager::OnSelectAll(int iList) +{ + for (int i = 0; i < m_vecItems[iList]->Size();++i) + { + CFileItemPtr pItem = m_vecItems[iList]->Get(i); + if (!pItem->IsParentFolder()) + { + pItem->Select(true); + } + } +} + +void CGUIWindowFileManager::OnNewFolder(int iList) +{ + std::string strNewFolder = ""; + if (CGUIKeyboardFactory::ShowAndGetInput(strNewFolder, CVariant{g_localizeStrings.Get(16014)}, false)) + { + std::string strNewPath = m_Directory[iList]->GetPath(); + URIUtils::AddSlashAtEnd(strNewPath); + strNewPath += strNewFolder; + CDirectory::Create(strNewPath); + Refresh(iList); + + // select the new folder + for (int i=0; i<m_vecItems[iList]->Size(); ++i) + { + CFileItemPtr pItem=m_vecItems[iList]->Get(i); + std::string strPath=pItem->GetPath(); + URIUtils::RemoveSlashAtEnd(strPath); + if (strPath==strNewPath) + { + CONTROL_SELECT_ITEM(iList + CONTROL_LEFT_LIST, i); + break; + } + } + } +} + +void CGUIWindowFileManager::Refresh(int iList) +{ + int nSel = GetSelectedItem(iList); + // update the list views + Update(iList, m_Directory[iList]->GetPath()); + + while (nSel > m_vecItems[iList]->Size()) + nSel--; + + CONTROL_SELECT_ITEM(iList + CONTROL_LEFT_LIST, nSel); +} + + +void CGUIWindowFileManager::Refresh() +{ + int iList = GetFocusedList(); + int nSel = GetSelectedItem(iList); + // update the list views + Update(0, m_Directory[0]->GetPath()); + Update(1, m_Directory[1]->GetPath()); + + while (nSel > m_vecItems[iList]->Size()) + nSel--; + + CONTROL_SELECT_ITEM(iList + CONTROL_LEFT_LIST, nSel); +} + +int CGUIWindowFileManager::GetSelectedItem(int iControl) +{ + if (iControl < 0 || iControl > 1 || m_vecItems[iControl]->IsEmpty()) + return -1; + CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl + CONTROL_LEFT_LIST); + if (OnMessage(msg)) + return msg.GetParam1(); + return -1; +} + +void CGUIWindowFileManager::GoParentFolder(int iList) +{ + CURL url(m_Directory[iList]->GetPath()); + if (url.IsProtocol("rar") || url.IsProtocol("zip")) + { + // check for step-below, if, unmount rar + if (url.GetFileName().empty()) + if (url.IsProtocol("zip")) + g_ZipManager.release(m_Directory[iList]->GetPath()); // release resources + } + + std::string strPath(m_strParentPath[iList]), strOldPath(m_Directory[iList]->GetPath()); + Update(iList, strPath); +} + +/// \brief Build a directory history string +/// \param pItem Item to build the history string from +/// \param strHistoryString History string build as return value +void CGUIWindowFileManager::GetDirectoryHistoryString(const CFileItem* pItem, std::string& strHistoryString) +{ + if (pItem->m_bIsShareOrDrive) + { + // We are in the virtual directory + + // History string of the DVD drive + // must be handled separately + if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_DVD) + { + // Remove disc label from item label + // and use as history string, m_strPath + // can change for new discs + std::string strLabel = pItem->GetLabel(); + size_t nPosOpen = strLabel.find('('); + size_t nPosClose = strLabel.rfind(')'); + if (nPosOpen != std::string::npos && + nPosClose != std::string::npos && + nPosClose > nPosOpen) + { + strLabel.erase(nPosOpen + 1, (nPosClose) - (nPosOpen + 1)); + strHistoryString = strLabel; + } + else + strHistoryString = strLabel; + } + else + { + // Other items in virtual directory + strHistoryString = pItem->GetLabel() + pItem->GetPath(); + URIUtils::RemoveSlashAtEnd(strHistoryString); + } + } + else + { + // Normal directory items + strHistoryString = pItem->GetPath(); + URIUtils::RemoveSlashAtEnd(strHistoryString); + } +} + +bool CGUIWindowFileManager::GetDirectory(int iList, const std::string &strDirectory, CFileItemList &items) +{ + CURL pathToUrl(strDirectory); + + CGetDirectoryItems getItems(m_rootDir, pathToUrl, items); + if (!CGUIDialogBusy::Wait(&getItems, 100, true)) + { + return false; + } + return getItems.m_result; +} + +bool CGUIWindowFileManager::CanRename(int iList) +{ + //! @todo Renaming of shares (requires writing to sources.xml) + //! this might be able to be done via the webserver code stuff... + if (m_Directory[iList]->IsVirtualDirectoryRoot()) return false; + if (m_Directory[iList]->IsReadOnly()) return false; + + return true; +} + +bool CGUIWindowFileManager::CanCopy(int iList) +{ + // can't copy if the destination is not writeable, or if the source is a share! + //! @todo Perhaps if the source is removeable media (DVD/CD etc.) we could + //! put ripping/backup in here. + if (!CUtil::SupportsReadFileOperations(m_Directory[iList]->GetPath())) return false; + if (m_Directory[iList]->IsVirtualDirectoryRoot()) return false; + if (m_Directory[1 - iList]->IsVirtualDirectoryRoot()) return false; + if (m_Directory[iList]->IsVirtualDirectoryRoot()) return false; + if (m_Directory[1 -iList]->IsReadOnly()) return false; + return true; +} + +bool CGUIWindowFileManager::CanMove(int iList) +{ + // can't move if the destination is not writeable, or if the source is a share or not writeable! + if (m_Directory[0]->IsVirtualDirectoryRoot() || m_Directory[0]->IsReadOnly()) return false; + if (m_Directory[1]->IsVirtualDirectoryRoot() || m_Directory[1]->IsReadOnly()) return false; + return true; +} + +bool CGUIWindowFileManager::CanDelete(int iList) +{ + if (m_Directory[iList]->IsVirtualDirectoryRoot()) return false; + if (m_Directory[iList]->IsReadOnly()) return false; + return true; +} + +bool CGUIWindowFileManager::CanNewFolder(int iList) +{ + if (m_Directory[iList]->IsVirtualDirectoryRoot()) return false; + if (m_Directory[iList]->IsReadOnly()) return false; + return true; +} + +int CGUIWindowFileManager::NumSelected(int iList) +{ + int iSelectedItems = 0; + for (int iItem = 0; iItem < m_vecItems[iList]->Size(); ++iItem) + { + if (m_vecItems[iList]->Get(iItem)->IsSelected()) iSelectedItems++; + } + return iSelectedItems; +} + +int CGUIWindowFileManager::GetFocusedList() const +{ + return GetFocusedControlID() - CONTROL_LEFT_LIST; +} + +void CGUIWindowFileManager::OnPopupMenu(int list, int item, bool bContextDriven /* = true */) +{ + if (list < 0 || list >= 2) return ; + bool bDeselect = SelectItem(list, item); + // calculate the position for our menu + float posX = 200; + float posY = 100; + const CGUIControl *pList = GetControl(CONTROL_LEFT_LIST + list); + if (pList) + { + posX = pList->GetXPosition() + pList->GetWidth() / 2; + posY = pList->GetYPosition() + pList->GetHeight() / 2; + } + + CFileItemPtr pItem = m_vecItems[list]->Get(item); + if (!pItem.get()) + return; + + if (m_Directory[list]->IsVirtualDirectoryRoot()) + { + if (item < 0) + { //! @todo We should add the option here for shares to be added if there aren't any + return ; + } + + // and do the popup menu + if (CGUIDialogContextMenu::SourcesMenu("files", pItem, posX, posY)) + { + m_rootDir.SetSources(*CMediaSourceSettings::GetInstance().GetSources("files")); + if (m_Directory[1 - list]->IsVirtualDirectoryRoot()) + Refresh(); + else + Refresh(list); + return ; + } + pItem->Select(false); + return ; + } + + const CPlayerCoreFactory &playerCoreFactory = CServiceBroker::GetPlayerCoreFactory(); + + // popup the context menu + + bool showEntry = false; + if (item >= m_vecItems[list]->Size()) item = -1; + if (item >= 0) + showEntry=(!pItem->IsParentFolder() || (pItem->IsParentFolder() && m_vecItems[list]->GetSelectedCount()>0)); + + // determine available players + std::vector<std::string>players; + playerCoreFactory.GetPlayers(*pItem, players); + + // add the needed buttons + CContextButtons choices; + if (item >= 0) + { + //The ".." item is not selectable. Take that into account when figuring out if all items are selected + int notSelectable = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWPARENTDIRITEMS) ? 1 : 0; + if (NumSelected(list) < m_vecItems[list]->Size() - notSelectable) + choices.Add(CONTROL_BTNSELECTALL, 188); // SelectAll + if (!pItem->IsParentFolder()) + choices.Add(CONTROL_BTNFAVOURITES, CServiceBroker::GetFavouritesService().IsFavourited(*pItem.get(), GetID()) ? 14077 : 14076); // Add/Remove Favourite + if (players.size() > 1) + choices.Add(CONTROL_BTNPLAYWITH, 15213); + if (CanRename(list) && !pItem->IsParentFolder()) + choices.Add(CONTROL_BTNRENAME, 118); + if (CanDelete(list) && showEntry) + choices.Add(CONTROL_BTNDELETE, 117); + if (CanCopy(list) && showEntry) + choices.Add(CONTROL_BTNCOPY, 115); + if (CanMove(list) && showEntry) + choices.Add(CONTROL_BTNMOVE, 116); + } + if (CanNewFolder(list)) + choices.Add(CONTROL_BTNNEWFOLDER, 20309); + if (item >= 0 && pItem->m_bIsFolder && !pItem->IsParentFolder()) + choices.Add(CONTROL_BTNCALCSIZE, 13393); + choices.Add(CONTROL_BTNSWITCHMEDIA, 523); + if (CServiceBroker::GetJobManager()->IsProcessing("filemanager")) + choices.Add(CONTROL_BTNCANCELJOB, 167); + + if (!pItem->m_bIsFolder) + choices.Add(CONTROL_BTNVIEW, 39104); + + int btnid = CGUIDialogContextMenu::ShowAndGetChoice(choices); + if (btnid == CONTROL_BTNSELECTALL) + { + OnSelectAll(list); + bDeselect=false; + } + if (btnid == CONTROL_BTNFAVOURITES) + { + CServiceBroker::GetFavouritesService().AddOrRemove(*pItem.get(), GetID()); + return; + } + if (btnid == CONTROL_BTNPLAYWITH) + { + std::vector<std::string>players; + playerCoreFactory.GetPlayers(*pItem, players); + std::string player = playerCoreFactory.SelectPlayerDialog(players); + if (!player.empty()) + OnStart(pItem.get(), player); + } + if (btnid == CONTROL_BTNRENAME) + OnRename(list); + if (btnid == CONTROL_BTNDELETE) + OnDelete(list); + if (btnid == CONTROL_BTNCOPY) + OnCopy(list); + if (btnid == CONTROL_BTNMOVE) + OnMove(list); + if (btnid == CONTROL_BTNNEWFOLDER) + OnNewFolder(list); + if (btnid == CONTROL_BTNCALCSIZE) + { + // setup the progress dialog, and show it + CGUIDialogProgress *progress = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS); + if (progress) + { + progress->SetHeading(CVariant{13394}); + for (int i=0; i < 3; i++) + progress->SetLine(i, CVariant{""}); + progress->Open(); + } + + // Calculate folder size for each selected item + for (int i=0; i<m_vecItems[list]->Size(); ++i) + { + CFileItemPtr pItem2=m_vecItems[list]->Get(i); + if (pItem2->m_bIsFolder && pItem2->IsSelected()) + { + int64_t folderSize = CalculateFolderSize(pItem2->GetPath(), progress); + if (folderSize >= 0) + { + pItem2->m_dwSize = folderSize; + if (folderSize == 0) + pItem2->SetLabel2(StringUtils::SizeToString(folderSize)); + else + pItem2->SetFileSizeLabel(); + } + } + } + if (progress) + progress->Close(); + } + if (btnid == CONTROL_BTNSWITCHMEDIA) + { + CGUIDialogContextMenu::SwitchMedia("files", m_vecItems[list]->GetPath()); + return; + } + if (btnid == CONTROL_BTNCANCELJOB) + CancelJobs(); + if (btnid == CONTROL_BTNVIEW) + CGUIDialogTextViewer::ShowForFile(pItem->GetPath(), true); + + if (bDeselect && item >= 0 && item < m_vecItems[list]->Size()) + { // deselect item as we didn't do anything + pItem->Select(false); + } +} + +// Highlights the item in the list under the cursor +// returns true if we should deselect the item, false otherwise +bool CGUIWindowFileManager::SelectItem(int list, int &item) +{ + // get the currently selected item in the list + item = GetSelectedItem(list); + + // select the item if we need to + if (item > -1 && !NumSelected(list) && !m_vecItems[list]->Get(item)->IsParentFolder()) + { + m_vecItems[list]->Get(item)->Select(true); + return true; + } + return false; +} + +// recursively calculates the selected folder size +int64_t CGUIWindowFileManager::CalculateFolderSize(const std::string &strDirectory, CGUIDialogProgress *pProgress) +{ + const CURL pathToUrl(strDirectory); + if (pProgress) + { // update our progress control + pProgress->Progress(); + pProgress->SetLine(1, strDirectory); + if (pProgress->IsCanceled()) + return -1; + } + // start by calculating the size of the files in this folder... + int64_t totalSize = 0; + CFileItemList items; + CVirtualDirectory rootDir; + rootDir.SetSources(*CMediaSourceSettings::GetInstance().GetSources("files")); + rootDir.GetDirectory(pathToUrl, items, false, false); + for (int i=0; i < items.Size(); i++) + { + if (items[i]->m_bIsFolder && !items[i]->IsParentFolder()) // folder + { + int64_t folderSize = CalculateFolderSize(items[i]->GetPath(), pProgress); + if (folderSize < 0) return -1; + totalSize += folderSize; + } + else // file + totalSize += items[i]->m_dwSize; + } + return totalSize; +} + +void CGUIWindowFileManager::OnJobComplete(unsigned int jobID, bool success, CJob *job) +{ + if(!success) + { + CFileOperationJob* fileJob = static_cast<CFileOperationJob*>(job); + HELPERS::ShowOKDialogLines(CVariant{fileJob->GetHeading()}, + CVariant{fileJob->GetLine()}, CVariant{16200}, CVariant{0}); + } + + if (IsActive()) + { + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_UPDATE); + CServiceBroker::GetAppMessenger()->SendGUIMessage(msg, GetID(), false); + } + + CJobQueue::OnJobComplete(jobID, success, job); +} + +void CGUIWindowFileManager::ShowShareErrorMessage(CFileItem* pItem) +{ + int idMessageText = 0; + CURL url(pItem->GetPath()); + + if (url.IsProtocol("smb") && url.GetHostName().empty()) // smb workgroup + idMessageText = 15303; // Workgroup not found + else if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_REMOTE || URIUtils::IsRemote(pItem->GetPath())) + idMessageText = 15301; // Could not connect to network server + else + idMessageText = 15300; // Path not found or invalid + + HELPERS::ShowOKDialogText(CVariant{220}, CVariant{idMessageText}); +} + +void CGUIWindowFileManager::OnInitWindow() +{ + bool bResult0 = Update(0, m_Directory[0]->GetPath()); + bool bResult1 = Update(1, m_Directory[1]->GetPath()); + + CGUIWindow::OnInitWindow(); + + if (!bCheckShareConnectivity) + { + bCheckShareConnectivity = true; //reset + CFileItem pItem(strCheckSharePath, true); + ShowShareErrorMessage(&pItem); //show the error message after window is loaded! + Update(0,""); // reset view to root + } + else if (!bResult0) + { + ShowShareErrorMessage(m_Directory[0]); //show the error message after window is loaded! + Update(0, ""); // reset view to root + } + + if (!bResult1) + { + ShowShareErrorMessage(m_Directory[1]); //show the error message after window is loaded! + Update(1, ""); // reset view to root + } +} + +void CGUIWindowFileManager::SetInitialPath(const std::string &path) +{ + // check for a passed destination path + std::string strDestination = path; + m_rootDir.SetSources(*CMediaSourceSettings::GetInstance().GetSources("files")); + if (!strDestination.empty()) + { + CLog::Log(LOGINFO, "Attempting to quickpath to: {}", strDestination); + } + // otherwise, is this the first time accessing this window? + else if (m_Directory[0]->GetPath() == "?") + { + m_Directory[0]->SetPath(strDestination = CMediaSourceSettings::GetInstance().GetDefaultSource("files")); + CLog::Log(LOGINFO, "Attempting to default to: {}", strDestination); + } + // try to open the destination path + if (!strDestination.empty()) + { + // open root + if (StringUtils::EqualsNoCase(strDestination, "$ROOT")) + { + m_Directory[0]->SetPath(""); + CLog::Log(LOGINFO, " Success! Opening root listing."); + } + else + { + // default parameters if the jump fails + m_Directory[0]->SetPath(""); + + bool bIsSourceName = false; + VECSOURCES shares; + m_rootDir.GetSources(shares); + int iIndex = CUtil::GetMatchingSource(strDestination, shares, bIsSourceName); + if (iIndex > -1 +#if defined(TARGET_DARWIN_EMBEDDED) + || URIUtils::PathHasParent(strDestination, "special://envhome/Documents/Inbox/") +#endif + || URIUtils::PathHasParent(strDestination, "special://profile/")) + { + // set current directory to matching share + std::string path; + if (bIsSourceName && iIndex < (int)shares.size()) + path = shares[iIndex].strPath; + else + path = strDestination; + URIUtils::RemoveSlashAtEnd(path); + m_Directory[0]->SetPath(path); + CLog::Log(LOGINFO, " Success! Opened destination path: {}", strDestination); + + // outside call: check the share for connectivity + bCheckShareConnectivity = Update(0, m_Directory[0]->GetPath()); + if(!bCheckShareConnectivity) + strCheckSharePath = m_Directory[0]->GetPath(); + } + else + { + CLog::Log(LOGERROR, " Failed! Destination parameter ({}) does not match a valid share!", + strDestination); + } + } + } + + if (m_Directory[1]->GetPath() == "?") m_Directory[1]->SetPath(""); +} + +const CFileItem& CGUIWindowFileManager::CurrentDirectory(int indx) const +{ + return *m_Directory[indx]; +} |