2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "GUIWindowFileManager.h"
12 #include "GUIPassword.h"
13 #include "GUIUserMessages.h"
14 #include "PlayListPlayer.h"
15 #include "ServiceBroker.h"
18 #include "application/Application.h"
19 #include "application/ApplicationComponents.h"
20 #include "application/ApplicationPlayer.h"
21 #include "cores/playercorefactory/PlayerCoreFactory.h"
22 #include "dialogs/GUIDialogBusy.h"
23 #include "dialogs/GUIDialogContextMenu.h"
24 #include "dialogs/GUIDialogMediaSource.h"
25 #include "dialogs/GUIDialogProgress.h"
26 #include "dialogs/GUIDialogTextViewer.h"
27 #include "dialogs/GUIDialogYesNo.h"
28 #include "favourites/FavouritesService.h"
29 #include "filesystem/Directory.h"
30 #include "filesystem/FileDirectoryFactory.h"
31 #include "filesystem/ZipManager.h"
32 #include "guilib/GUIComponent.h"
33 #include "guilib/GUIKeyboardFactory.h"
34 #include "guilib/GUIWindowManager.h"
35 #include "guilib/LocalizeStrings.h"
36 #include "input/InputManager.h"
37 #include "input/actions/Action.h"
38 #include "input/actions/ActionIDs.h"
39 #include "interfaces/generic/ScriptInvocationManager.h"
40 #include "messaging/ApplicationMessenger.h"
41 #include "messaging/helpers/DialogOKHelper.h"
42 #include "network/Network.h"
43 #include "pictures/SlideShowDelegator.h"
44 #include "platform/Filesystem.h"
45 #include "playlists/PlayList.h"
46 #include "playlists/PlayListFactory.h"
47 #include "settings/MediaSourceSettings.h"
48 #include "settings/Settings.h"
49 #include "settings/SettingsComponent.h"
50 #include "storage/MediaManager.h"
51 #include "threads/IRunnable.h"
52 #include "utils/FileOperationJob.h"
53 #include "utils/FileUtils.h"
54 #include "utils/JobManager.h"
55 #include "utils/StringUtils.h"
56 #include "utils/URIUtils.h"
57 #include "utils/Variant.h"
58 #include "utils/log.h"
60 using namespace XFILE
;
61 using namespace KODI::MESSAGING
;
63 #define CONTROL_BTNSELECTALL 1
64 #define CONTROL_BTNFAVOURITES 2
65 #define CONTROL_BTNPLAYWITH 3
66 #define CONTROL_BTNRENAME 4
67 #define CONTROL_BTNDELETE 5
68 #define CONTROL_BTNCOPY 6
69 #define CONTROL_BTNMOVE 7
70 #define CONTROL_BTNNEWFOLDER 8
71 #define CONTROL_BTNCALCSIZE 9
72 #define CONTROL_BTNSWITCHMEDIA 11
73 #define CONTROL_BTNCANCELJOB 12
74 #define CONTROL_BTNVIEW 13
77 #define CONTROL_NUMFILES_LEFT 12
78 #define CONTROL_NUMFILES_RIGHT 13
80 #define CONTROL_LEFT_LIST 20
81 #define CONTROL_RIGHT_LIST 21
83 #define CONTROL_CURRENTDIRLABEL_LEFT 101
84 #define CONTROL_CURRENTDIRLABEL_RIGHT 102
88 class CGetDirectoryItems
: public IRunnable
91 CGetDirectoryItems(XFILE::CVirtualDirectory
& dir
, CURL
& url
, CFileItemList
& items
)
92 : m_dir(dir
), m_url(url
), m_items(items
)
97 m_result
= m_dir
.GetDirectory(m_url
, m_items
, false, false);
99 void Cancel() override
101 m_dir
.CancelDirectory();
103 bool m_result
= false;
106 XFILE::CVirtualDirectory
&m_dir
;
108 CFileItemList
&m_items
;
112 CGUIWindowFileManager::CGUIWindowFileManager(void)
113 : CGUIWindow(WINDOW_FILES
, "FileManager.xml"),
116 m_Directory
[0] = new CFileItem
;
117 m_Directory
[1] = new CFileItem
;
118 m_vecItems
[0] = new CFileItemList
;
119 m_vecItems
[1] = new CFileItemList
;
120 m_Directory
[0]->SetPath("?");
121 m_Directory
[1]->SetPath("?");
122 m_Directory
[0]->m_bIsFolder
= true;
123 m_Directory
[1]->m_bIsFolder
= true;
124 bCheckShareConnectivity
= true;
125 m_loadType
= KEEP_IN_MEMORY
;
128 CGUIWindowFileManager::~CGUIWindowFileManager(void)
130 delete m_Directory
[0];
131 delete m_Directory
[1];
132 delete m_vecItems
[0];
133 delete m_vecItems
[1];
136 bool CGUIWindowFileManager::OnAction(const CAction
&action
)
138 int list
= GetFocusedList();
139 if (list
>= 0 && list
<= 1)
143 // the non-contextual menu can be called at any time
144 if (action
.GetID() == ACTION_CONTEXT_MENU
&& m_vecItems
[list
]->Size() == 0)
146 OnPopupMenu(list
,-1, false);
149 if (action
.GetID() == ACTION_DELETE_ITEM
)
153 bool bDeselect
= SelectItem(list
, item
);
155 if (bDeselect
) m_vecItems
[list
]->Get(item
)->Select(false);
159 if (action
.GetID() == ACTION_COPY_ITEM
)
163 bool bDeselect
= SelectItem(list
, item
);
165 if (bDeselect
) m_vecItems
[list
]->Get(item
)->Select(false);
169 if (action
.GetID() == ACTION_MOVE_ITEM
)
173 bool bDeselect
= SelectItem(list
, item
);
175 if (bDeselect
) m_vecItems
[list
]->Get(item
)->Select(false);
179 if (action
.GetID() == ACTION_RENAME_ITEM
)
183 bool bDeselect
= SelectItem(list
, item
);
185 if (bDeselect
) m_vecItems
[list
]->Get(item
)->Select(false);
189 if (action
.GetID() == ACTION_PARENT_DIR
)
191 GoParentFolder(list
);
194 if (action
.GetID() == ACTION_PLAYER_PLAY
)
196 #ifdef HAS_OPTICAL_DRIVE
197 if (m_vecItems
[list
]->Get(GetSelectedItem(list
))->IsDVD())
198 return MEDIA_DETECT::CAutorun::PlayDiscAskResume(m_vecItems
[list
]->Get(GetSelectedItem(list
))->GetPath());
202 return CGUIWindow::OnAction(action
);
205 bool CGUIWindowFileManager::OnBack(int actionID
)
207 int list
= GetFocusedList();
208 if (list
>= 0 && list
<= 1 && actionID
== ACTION_NAV_BACK
&& !m_vecItems
[list
]->IsVirtualDirectoryRoot())
210 GoParentFolder(list
);
213 return CGUIWindow::OnBack(actionID
);
216 bool CGUIWindowFileManager::OnMessage(CGUIMessage
& message
)
218 switch ( message
.GetMessage() )
220 case GUI_MSG_NOTIFY_ALL
:
221 { // Message is received even if window is inactive
222 if (message
.GetParam1() == GUI_MSG_WINDOW_RESET
)
224 m_Directory
[0]->SetPath("?");
225 m_Directory
[1]->SetPath("?");
226 m_Directory
[0]->m_bIsFolder
= true;
227 m_Directory
[1]->m_bIsFolder
= true;
231 // handle removable media
232 if (message
.GetParam1() == GUI_MSG_REMOVED_MEDIA
)
234 for (int i
= 0; i
< 2; i
++)
236 if (m_Directory
[i
]->IsVirtualDirectoryRoot() && IsActive())
238 int iItem
= GetSelectedItem(i
);
239 Update(i
, m_Directory
[i
]->GetPath());
240 CONTROL_SELECT_ITEM(CONTROL_LEFT_LIST
+ i
, iItem
);
242 else if (m_Directory
[i
]->IsRemovable() && !m_rootDir
.IsInSource(m_Directory
[i
]->GetPath()))
247 m_Directory
[i
]->SetPath("");
252 else if (message
.GetParam1()==GUI_MSG_UPDATE_SOURCES
)
253 { // State of the sources changed, so update our view
254 for (int i
= 0; i
< 2; i
++)
256 if (m_Directory
[i
]->IsVirtualDirectoryRoot() && IsActive())
258 int iItem
= GetSelectedItem(i
);
259 Update(i
, m_Directory
[i
]->GetPath());
260 CONTROL_SELECT_ITEM(CONTROL_LEFT_LIST
+ i
, iItem
);
265 else if (message
.GetParam1()==GUI_MSG_UPDATE
&& IsActive())
272 case GUI_MSG_PLAYBACK_STARTED
:
273 case GUI_MSG_PLAYBACK_ENDED
:
274 case GUI_MSG_PLAYBACK_STOPPED
:
275 case GUI_MSG_PLAYLIST_CHANGED
:
276 case GUI_MSG_PLAYLISTPLAYER_STOPPED
:
277 case GUI_MSG_PLAYLISTPLAYER_STARTED
:
278 case GUI_MSG_PLAYLISTPLAYER_CHANGED
:
279 { // send a notify all to all controls on this window
280 CGUIMessage
msg(GUI_MSG_NOTIFY_ALL
, GetID(), 0, GUI_MSG_REFRESH_LIST
);
284 case GUI_MSG_WINDOW_DEINIT
:
286 CGUIWindow::OnMessage(message
);
293 case GUI_MSG_WINDOW_INIT
:
295 SetInitialPath(message
.GetStringParam());
296 message
.SetStringParam("");
298 return CGUIWindow::OnMessage(message
);
301 case GUI_MSG_CLICKED
:
303 int iControl
= message
.GetSenderId();
305 if (iControl
== CONTROL_LEFT_LIST
|| iControl
== CONTROL_RIGHT_LIST
) // list/thumb control
308 int list
= iControl
- CONTROL_LEFT_LIST
;
309 int iItem
= GetSelectedItem(list
);
310 int iAction
= message
.GetParam1();
312 // iItem is checked for validity inside these routines
313 if (iAction
== ACTION_HIGHLIGHT_ITEM
|| iAction
== ACTION_MOUSE_LEFT_CLICK
)
316 if (!CServiceBroker::GetInputManager().IsMouseActive())
319 CGUIMessage
msg(GUI_MSG_ITEM_SELECT
, GetID(), iControl
, iItem
+ 1);
320 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg
);
323 else if (iAction
== ACTION_SELECT_ITEM
|| iAction
== ACTION_MOUSE_DOUBLE_CLICK
)
325 OnClick(list
, iItem
);
327 else if (iAction
== ACTION_CONTEXT_MENU
|| iAction
== ACTION_MOUSE_RIGHT_CLICK
)
329 OnPopupMenu(list
, iItem
);
334 // prevent touch/gesture unfocussing ..
335 case GUI_MSG_GESTURE_NOTIFY
:
336 case GUI_MSG_UNFOCUS_ALL
:
339 return CGUIWindow::OnMessage(message
);
342 void CGUIWindowFileManager::OnSort(int iList
)
344 using namespace KODI::PLATFORM::FILESYSTEM
;
345 // always sort the list by label in ascending order
346 for (int i
= 0; i
< m_vecItems
[iList
]->Size(); i
++)
348 CFileItemPtr pItem
= m_vecItems
[iList
]->Get(i
);
349 if (pItem
->m_bIsFolder
&& (!pItem
->m_dwSize
|| pItem
->IsPath("add")))
350 pItem
->SetLabel2("");
352 pItem
->SetFileSizeLabel();
354 // Set free space on disc
355 if (pItem
->m_bIsShareOrDrive
)
360 auto freeSpace
= space(pItem
->GetPath(), ec
);
363 pItem
->m_dwSize
= freeSpace
.free
;
364 pItem
->SetFileSizeLabel();
367 else if (pItem
->IsDVD() && CServiceBroker::GetMediaManager().IsDiscInDrive())
370 auto freeSpace
= space(pItem
->GetPath(), ec
);
373 pItem
->m_dwSize
= freeSpace
.capacity
;
374 pItem
->SetFileSizeLabel();
377 } // if (pItem->m_bIsShareOrDrive)
381 m_vecItems
[iList
]->Sort(SortByLabel
, SortOrderAscending
);
384 void CGUIWindowFileManager::ClearFileItems(int iList
)
386 CGUIMessage
msg(GUI_MSG_LABEL_RESET
, GetID(), iList
+ CONTROL_LEFT_LIST
);
387 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg
);
389 m_vecItems
[iList
]->Clear(); // will clean up everything
392 void CGUIWindowFileManager::UpdateButtons()
394 // update our current directory labels
395 std::string strDir
= CURL(m_Directory
[0]->GetPath()).GetWithoutUserDetails();
398 SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_LEFT
,g_localizeStrings
.Get(20108));
402 SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_LEFT
, strDir
);
404 strDir
= CURL(m_Directory
[1]->GetPath()).GetWithoutUserDetails();
407 SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_RIGHT
,g_localizeStrings
.Get(20108));
411 SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_RIGHT
, strDir
);
414 // update the number of items in each list
418 void CGUIWindowFileManager::UpdateItemCounts()
420 for (int i
= 0; i
< 2; i
++)
422 unsigned int selectedCount
= 0;
423 unsigned int totalCount
= 0;
424 int64_t selectedSize
= 0;
425 for (int j
= 0; j
< m_vecItems
[i
]->Size(); j
++)
427 CFileItemPtr item
= m_vecItems
[i
]->Get(j
);
428 if (item
->IsParentFolder()) continue;
429 if (item
->IsSelected())
432 selectedSize
+= item
->m_dwSize
;
437 if (selectedCount
> 0)
439 StringUtils::Format("{}/{} {} ({})", selectedCount
, totalCount
,
440 g_localizeStrings
.Get(127), StringUtils::SizeToString(selectedSize
));
442 items
= StringUtils::Format("{} {}", totalCount
, g_localizeStrings
.Get(127));
443 SET_CONTROL_LABEL(CONTROL_NUMFILES_LEFT
+ i
, items
);
447 bool CGUIWindowFileManager::Update(int iList
, const std::string
&strDirectory
)
451 CLog::Log(LOGWARNING
, "CGUIWindowFileManager::Update - updating in progress");
454 CUpdateGuard
ug(m_updating
);
457 int iItem
= GetSelectedItem(iList
);
458 std::string strSelectedItem
= "";
460 if (iItem
>= 0 && iItem
< m_vecItems
[iList
]->Size())
462 CFileItemPtr pItem
= m_vecItems
[iList
]->Get(iItem
);
463 if (!pItem
->IsParentFolder())
465 GetDirectoryHistoryString(pItem
.get(), strSelectedItem
);
466 m_history
[iList
].SetSelectedItem(strSelectedItem
, m_Directory
[iList
]->GetPath(), iItem
);
470 std::string strOldDirectory
=m_Directory
[iList
]->GetPath();
471 m_Directory
[iList
]->SetPath(strDirectory
);
474 if (!GetDirectory(iList
, m_Directory
[iList
]->GetPath(), items
))
476 if (strDirectory
!= strOldDirectory
&& GetDirectory(iList
, strOldDirectory
, items
))
477 m_Directory
[iList
]->SetPath(strOldDirectory
); // Fallback to old (previous) path)
479 Update(iList
, ""); // Fallback to root
484 m_history
[iList
].SetSelectedItem(strSelectedItem
, strOldDirectory
);
486 ClearFileItems(iList
);
488 m_vecItems
[iList
]->Append(items
);
489 m_vecItems
[iList
]->SetPath(items
.GetPath());
491 std::string strParentPath
;
492 URIUtils::GetParentPath(strDirectory
, strParentPath
);
493 if (strDirectory
.empty() && (m_vecItems
[iList
]->Size() == 0 || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWADDSOURCEBUTTONS
)))
494 { // add 'add source button'
495 const std::string
& strLabel
= g_localizeStrings
.Get(1026);
496 CFileItemPtr
pItem(new CFileItem(strLabel
));
497 pItem
->SetPath("add");
498 pItem
->SetArt("icon", "DefaultAddSource.png");
499 pItem
->SetLabel(strLabel
);
500 pItem
->SetLabelPreformatted(true);
501 pItem
->m_bIsFolder
= true;
502 pItem
->SetSpecialSort(SortSpecialOnBottom
);
503 m_vecItems
[iList
]->Add(pItem
);
505 else if (items
.IsEmpty() || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWPARENTDIRITEMS
))
507 CFileItemPtr
pItem(new CFileItem(".."));
508 pItem
->SetPath(m_rootDir
.IsSource(strDirectory
) ? "" : strParentPath
);
509 pItem
->m_bIsFolder
= true;
510 pItem
->m_bIsShareOrDrive
= false;
511 m_vecItems
[iList
]->AddFront(pItem
, 0);
514 m_strParentPath
[iList
] = (m_rootDir
.IsSource(strDirectory
) ? "" : strParentPath
);
516 if (strDirectory
.empty())
518 CFileItemPtr
pItem(new CFileItem("special://profile/", true));
519 pItem
->SetLabel(g_localizeStrings
.Get(20070));
520 pItem
->SetArt("thumb", "DefaultFolder.png");
521 pItem
->SetLabelPreformatted(true);
522 m_vecItems
[iList
]->Add(pItem
);
524 #ifdef TARGET_DARWIN_EMBEDDED
525 CFileItemPtr
iItem(new CFileItem("special://envhome/Documents/Inbox", true));
526 iItem
->SetLabel("Inbox");
527 iItem
->SetArt("thumb", "DefaultFolder.png");
528 iItem
->SetLabelPreformatted(true);
529 m_vecItems
[iList
]->Add(iItem
);
531 #ifdef TARGET_ANDROID
532 CFileItemPtr
iItem(new CFileItem("special://logpath", true));
533 iItem
->SetLabel("Logs");
534 iItem
->SetArt("thumb", "DefaultFolder.png");
535 iItem
->SetLabelPreformatted(true);
536 m_vecItems
[iList
]->Add(iItem
);
540 // if we have a .tbn file, use itself as the thumb
541 for (int i
= 0; i
< m_vecItems
[iList
]->Size(); i
++)
543 CFileItemPtr pItem
= m_vecItems
[iList
]->Get(i
);
545 URIUtils::HasExtension(pItem
->GetPath(), ".tbn"))
547 pItem
->SetArt("thumb", pItem
->GetPath());
550 m_vecItems
[iList
]->FillInDefaultIcons();
556 strSelectedItem
= m_history
[iList
].GetSelectedItem(m_Directory
[iList
]->GetPath());
557 for (int i
= 0; i
< m_vecItems
[iList
]->Size(); ++i
)
559 CFileItemPtr pItem
= m_vecItems
[iList
]->Get(i
);
560 std::string strHistory
;
561 GetDirectoryHistoryString(pItem
.get(), strHistory
);
562 if (strHistory
== strSelectedItem
)
568 UpdateControl(iList
, item
);
573 void CGUIWindowFileManager::OnClick(int iList
, int iItem
)
575 if ( iList
< 0 || iList
>= 2) return ;
576 if ( iItem
< 0 || iItem
>= m_vecItems
[iList
]->Size() ) return ;
578 CFileItemPtr pItem
= m_vecItems
[iList
]->Get(iItem
);
579 if (pItem
->GetPath() == "add" && pItem
->GetLabel() == g_localizeStrings
.Get(1026)) // 'add source button' in empty root
581 if (CGUIDialogMediaSource::ShowAndAddMediaSource("files"))
583 m_rootDir
.SetSources(*CMediaSourceSettings::GetInstance().GetSources("files"));
584 Update(0,m_Directory
[0]->GetPath());
585 Update(1,m_Directory
[1]->GetPath());
590 if (!pItem
->m_bIsFolder
&& pItem
->IsFileFolder(EFILEFOLDER_MASK_ALL
))
592 XFILE::IFileDirectory
*pFileDirectory
= NULL
;
593 pFileDirectory
= XFILE::CFileDirectoryFactory::Create(pItem
->GetURL(), pItem
.get(), "");
595 pItem
->m_bIsFolder
= true;
596 else if(pItem
->m_bIsFolder
)
597 pItem
->m_bIsFolder
= false;
598 delete pFileDirectory
;
601 if (pItem
->m_bIsFolder
)
603 // save path + drive type because of the possible refresh
604 std::string strPath
= pItem
->GetPath();
605 int iDriveType
= pItem
->m_iDriveType
;
606 if ( pItem
->m_bIsShareOrDrive
)
608 if ( !g_passwordManager
.IsItemUnlocked( pItem
.get(), "files" ) )
614 if ( !HaveDiscOrConnection( strPath
, iDriveType
) )
617 if (!Update(iList
, strPath
))
618 ShowShareErrorMessage(pItem
.get());
620 else if (pItem
->IsZIP() || pItem
->IsCBZ()) // mount zip archive
622 CURL pathToUrl
= URIUtils::CreateArchivePath("zip", pItem
->GetURL(), "");
623 Update(iList
, pathToUrl
.Get());
625 else if (pItem
->IsRAR() || pItem
->IsCBR())
627 CURL pathToUrl
= URIUtils::CreateArchivePath("rar", pItem
->GetURL(), "");
628 Update(iList
, pathToUrl
.Get());
632 OnStart(pItem
.get(), "");
638 //! @todo 2.0: Can this be removed, or should we run without the "special" file directories while
639 // in filemanager view.
640 void CGUIWindowFileManager::OnStart(CFileItem
*pItem
, const std::string
&player
)
642 // start playlists from file manager
643 if (pItem
->IsPlayList())
645 const std::string
& strPlayList
= pItem
->GetPath();
646 std::unique_ptr
<PLAYLIST::CPlayList
> pPlayList(PLAYLIST::CPlayListFactory::Create(strPlayList
));
647 if (nullptr != pPlayList
)
649 if (!pPlayList
->Load(strPlayList
))
651 HELPERS::ShowOKDialogText(CVariant
{6}, CVariant
{477});
655 g_application
.ProcessAndStartPlaylist(strPlayList
, *pPlayList
, PLAYLIST::TYPE_MUSIC
);
658 if (pItem
->IsAudio() || pItem
->IsVideo())
660 CServiceBroker::GetPlaylistPlayer().Play(std::make_shared
<CFileItem
>(*pItem
), player
);
665 g_application
.PlayFile(*pItem
, player
);
669 if (pItem
->IsPythonScript())
671 CScriptInvocationManager::GetInstance().ExecuteAsync(pItem
->GetPath());
675 if (pItem
->IsPicture())
677 const auto& components
= CServiceBroker::GetAppComponents();
678 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
679 if (appPlayer
->IsPlayingVideo())
680 g_application
.StopPlaying();
682 CSlideShowDelegator
& slideShow
= CServiceBroker::GetSlideShowDelegator();
684 slideShow
.Add(pItem
);
685 slideShow
.Select(pItem
->GetPath());
687 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW
);
690 if (pItem
->IsType(".txt") || pItem
->IsType(".xml"))
691 CGUIDialogTextViewer::ShowForFile(pItem
->GetPath(), true);
694 bool CGUIWindowFileManager::HaveDiscOrConnection( std::string
& strPath
, int iDriveType
)
696 if ( iDriveType
== CMediaSource::SOURCE_TYPE_DVD
)
698 if (!CServiceBroker::GetMediaManager().IsDiscInDrive(strPath
))
700 HELPERS::ShowOKDialogText(CVariant
{218}, CVariant
{219});
701 int iList
= GetFocusedList();
702 int iItem
= GetSelectedItem(iList
);
704 CONTROL_SELECT_ITEM(iList
+ CONTROL_LEFT_LIST
, iItem
);
708 else if ( iDriveType
== CMediaSource::SOURCE_TYPE_REMOTE
)
710 //! @todo Handle not connected to a remote share
711 if (!CServiceBroker::GetNetwork().IsConnected())
713 HELPERS::ShowOKDialogText(CVariant
{220}, CVariant
{221});
722 void CGUIWindowFileManager::UpdateControl(int iList
, int item
)
724 CGUIMessage
msg(GUI_MSG_LABEL_BIND
, GetID(), iList
+ CONTROL_LEFT_LIST
, item
, 0, m_vecItems
[iList
]);
725 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg
);
728 void CGUIWindowFileManager::OnMark(int iList
, int iItem
)
730 CFileItemPtr pItem
= m_vecItems
[iList
]->Get(iItem
);
732 if (!pItem
->m_bIsShareOrDrive
)
734 if (!pItem
->IsParentFolder())
737 pItem
->Select(!pItem
->IsSelected());
745 void CGUIWindowFileManager::OnCopy(int iList
)
747 if (!CGUIDialogYesNo::ShowAndGetInput(CVariant
{120}, CVariant
{123}))
750 AddJob(new CFileOperationJob(CFileOperationJob::ActionCopy
,
752 m_Directory
[1 - iList
]->GetPath(),
753 true, 16201, 16202));
756 void CGUIWindowFileManager::OnMove(int iList
)
758 if (!CGUIDialogYesNo::ShowAndGetInput(CVariant
{121}, CVariant
{124}))
761 AddJob(new CFileOperationJob(CFileOperationJob::ActionMove
,
763 m_Directory
[1 - iList
]->GetPath(),
764 true, 16203, 16204));
767 void CGUIWindowFileManager::OnDelete(int iList
)
769 if (!CGUIDialogYesNo::ShowAndGetInput(CVariant
{122}, CVariant
{125}))
772 AddJob(new CFileOperationJob(CFileOperationJob::ActionDelete
,
774 m_Directory
[iList
]->GetPath(),
775 true, 16205, 16206));
778 void CGUIWindowFileManager::OnRename(int iList
)
781 for (int i
= 0; i
< m_vecItems
[iList
]->Size();++i
)
783 CFileItemPtr pItem
= m_vecItems
[iList
]->Get(i
);
784 if (pItem
->IsSelected())
786 strFile
= pItem
->GetPath();
791 CFileUtils::RenameFile(strFile
);
796 void CGUIWindowFileManager::OnSelectAll(int iList
)
798 for (int i
= 0; i
< m_vecItems
[iList
]->Size();++i
)
800 CFileItemPtr pItem
= m_vecItems
[iList
]->Get(i
);
801 if (!pItem
->IsParentFolder())
808 void CGUIWindowFileManager::OnNewFolder(int iList
)
810 std::string strNewFolder
= "";
811 if (CGUIKeyboardFactory::ShowAndGetInput(strNewFolder
, CVariant
{g_localizeStrings
.Get(16014)}, false))
813 std::string strNewPath
= m_Directory
[iList
]->GetPath();
814 URIUtils::AddSlashAtEnd(strNewPath
);
815 strNewPath
+= strNewFolder
;
816 CDirectory::Create(strNewPath
);
819 // select the new folder
820 for (int i
=0; i
<m_vecItems
[iList
]->Size(); ++i
)
822 CFileItemPtr pItem
=m_vecItems
[iList
]->Get(i
);
823 std::string strPath
=pItem
->GetPath();
824 URIUtils::RemoveSlashAtEnd(strPath
);
825 if (strPath
==strNewPath
)
827 CONTROL_SELECT_ITEM(iList
+ CONTROL_LEFT_LIST
, i
);
834 void CGUIWindowFileManager::Refresh(int iList
)
836 int nSel
= GetSelectedItem(iList
);
837 // update the list views
838 Update(iList
, m_Directory
[iList
]->GetPath());
840 while (nSel
> m_vecItems
[iList
]->Size())
843 CONTROL_SELECT_ITEM(iList
+ CONTROL_LEFT_LIST
, nSel
);
847 void CGUIWindowFileManager::Refresh()
849 int iList
= GetFocusedList();
850 int nSel
= GetSelectedItem(iList
);
851 // update the list views
852 Update(0, m_Directory
[0]->GetPath());
853 Update(1, m_Directory
[1]->GetPath());
855 while (nSel
> m_vecItems
[iList
]->Size())
858 CONTROL_SELECT_ITEM(iList
+ CONTROL_LEFT_LIST
, nSel
);
861 int CGUIWindowFileManager::GetSelectedItem(int iControl
)
863 if (iControl
< 0 || iControl
> 1 || m_vecItems
[iControl
]->IsEmpty())
865 CGUIMessage
msg(GUI_MSG_ITEM_SELECTED
, GetID(), iControl
+ CONTROL_LEFT_LIST
);
867 return msg
.GetParam1();
871 void CGUIWindowFileManager::GoParentFolder(int iList
)
873 CURL
url(m_Directory
[iList
]->GetPath());
874 if (url
.IsProtocol("rar") || url
.IsProtocol("zip"))
876 // check for step-below, if, unmount rar
877 if (url
.GetFileName().empty())
878 if (url
.IsProtocol("zip"))
879 g_ZipManager
.release(m_Directory
[iList
]->GetPath()); // release resources
882 std::string
strPath(m_strParentPath
[iList
]), strOldPath(m_Directory
[iList
]->GetPath());
883 Update(iList
, strPath
);
886 /// \brief Build a directory history string
887 /// \param pItem Item to build the history string from
888 /// \param strHistoryString History string build as return value
889 void CGUIWindowFileManager::GetDirectoryHistoryString(const CFileItem
* pItem
, std::string
& strHistoryString
)
891 if (pItem
->m_bIsShareOrDrive
)
893 // We are in the virtual directory
895 // History string of the DVD drive
896 // must be handled separately
897 if (pItem
->m_iDriveType
== CMediaSource::SOURCE_TYPE_DVD
)
899 // Remove disc label from item label
900 // and use as history string, m_strPath
901 // can change for new discs
902 std::string strLabel
= pItem
->GetLabel();
903 size_t nPosOpen
= strLabel
.find('(');
904 size_t nPosClose
= strLabel
.rfind(')');
905 if (nPosOpen
!= std::string::npos
&&
906 nPosClose
!= std::string::npos
&&
907 nPosClose
> nPosOpen
)
909 strLabel
.erase(nPosOpen
+ 1, (nPosClose
) - (nPosOpen
+ 1));
910 strHistoryString
= strLabel
;
913 strHistoryString
= strLabel
;
917 // Other items in virtual directory
918 strHistoryString
= pItem
->GetLabel() + pItem
->GetPath();
919 URIUtils::RemoveSlashAtEnd(strHistoryString
);
924 // Normal directory items
925 strHistoryString
= pItem
->GetPath();
926 URIUtils::RemoveSlashAtEnd(strHistoryString
);
930 bool CGUIWindowFileManager::GetDirectory(int iList
, const std::string
&strDirectory
, CFileItemList
&items
)
932 CURL
pathToUrl(strDirectory
);
934 CGetDirectoryItems
getItems(m_rootDir
, pathToUrl
, items
);
935 if (!CGUIDialogBusy::Wait(&getItems
, 100, true))
939 return getItems
.m_result
;
942 bool CGUIWindowFileManager::CanRename(int iList
)
944 //! @todo Renaming of shares (requires writing to sources.xml)
945 //! this might be able to be done via the webserver code stuff...
946 if (m_Directory
[iList
]->IsVirtualDirectoryRoot()) return false;
947 if (m_Directory
[iList
]->IsReadOnly()) return false;
952 bool CGUIWindowFileManager::CanCopy(int iList
)
954 // can't copy if the destination is not writeable, or if the source is a share!
955 //! @todo Perhaps if the source is removeable media (DVD/CD etc.) we could
956 //! put ripping/backup in here.
957 if (!CUtil::SupportsReadFileOperations(m_Directory
[iList
]->GetPath())) return false;
958 if (m_Directory
[iList
]->IsVirtualDirectoryRoot()) return false;
959 if (m_Directory
[1 - iList
]->IsVirtualDirectoryRoot()) return false;
960 if (m_Directory
[iList
]->IsVirtualDirectoryRoot()) return false;
961 if (m_Directory
[1 -iList
]->IsReadOnly()) return false;
965 bool CGUIWindowFileManager::CanMove(int iList
)
967 // can't move if the destination is not writeable, or if the source is a share or not writeable!
968 if (m_Directory
[0]->IsVirtualDirectoryRoot() || m_Directory
[0]->IsReadOnly()) return false;
969 if (m_Directory
[1]->IsVirtualDirectoryRoot() || m_Directory
[1]->IsReadOnly()) return false;
973 bool CGUIWindowFileManager::CanDelete(int iList
)
975 if (m_Directory
[iList
]->IsVirtualDirectoryRoot()) return false;
976 if (m_Directory
[iList
]->IsReadOnly()) return false;
980 bool CGUIWindowFileManager::CanNewFolder(int iList
)
982 if (m_Directory
[iList
]->IsVirtualDirectoryRoot()) return false;
983 if (m_Directory
[iList
]->IsReadOnly()) return false;
987 int CGUIWindowFileManager::NumSelected(int iList
)
989 int iSelectedItems
= 0;
990 for (int iItem
= 0; iItem
< m_vecItems
[iList
]->Size(); ++iItem
)
992 if (m_vecItems
[iList
]->Get(iItem
)->IsSelected()) iSelectedItems
++;
994 return iSelectedItems
;
997 int CGUIWindowFileManager::GetFocusedList() const
999 return GetFocusedControlID() - CONTROL_LEFT_LIST
;
1002 void CGUIWindowFileManager::OnPopupMenu(int list
, int item
, bool bContextDriven
/* = true */)
1004 if (list
< 0 || list
>= 2) return ;
1005 bool bDeselect
= SelectItem(list
, item
);
1006 // calculate the position for our menu
1009 const CGUIControl
*pList
= GetControl(CONTROL_LEFT_LIST
+ list
);
1012 posX
= pList
->GetXPosition() + pList
->GetWidth() / 2;
1013 posY
= pList
->GetYPosition() + pList
->GetHeight() / 2;
1016 CFileItemPtr pItem
= m_vecItems
[list
]->Get(item
);
1020 if (m_Directory
[list
]->IsVirtualDirectoryRoot())
1023 { //! @todo We should add the option here for shares to be added if there aren't any
1027 // and do the popup menu
1028 if (CGUIDialogContextMenu::SourcesMenu("files", pItem
, posX
, posY
))
1030 m_rootDir
.SetSources(*CMediaSourceSettings::GetInstance().GetSources("files"));
1031 if (m_Directory
[1 - list
]->IsVirtualDirectoryRoot())
1037 pItem
->Select(false);
1041 const CPlayerCoreFactory
&playerCoreFactory
= CServiceBroker::GetPlayerCoreFactory();
1043 // popup the context menu
1045 bool showEntry
= false;
1046 if (item
>= m_vecItems
[list
]->Size()) item
= -1;
1048 showEntry
=(!pItem
->IsParentFolder() || (pItem
->IsParentFolder() && m_vecItems
[list
]->GetSelectedCount()>0));
1050 // determine available players
1051 std::vector
<std::string
>players
;
1052 playerCoreFactory
.GetPlayers(*pItem
, players
);
1054 // add the needed buttons
1055 CContextButtons choices
;
1058 //The ".." item is not selectable. Take that into account when figuring out if all items are selected
1059 int notSelectable
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWPARENTDIRITEMS
) ? 1 : 0;
1060 if (NumSelected(list
) < m_vecItems
[list
]->Size() - notSelectable
)
1061 choices
.Add(CONTROL_BTNSELECTALL
, 188); // SelectAll
1062 if (!pItem
->IsParentFolder())
1063 choices
.Add(CONTROL_BTNFAVOURITES
, CServiceBroker::GetFavouritesService().IsFavourited(*pItem
.get(), GetID()) ? 14077 : 14076); // Add/Remove Favourite
1064 if (players
.size() > 1)
1065 choices
.Add(CONTROL_BTNPLAYWITH
, 15213);
1066 if (CanRename(list
) && !pItem
->IsParentFolder())
1067 choices
.Add(CONTROL_BTNRENAME
, 118);
1068 if (CanDelete(list
) && showEntry
)
1069 choices
.Add(CONTROL_BTNDELETE
, 117);
1070 if (CanCopy(list
) && showEntry
)
1071 choices
.Add(CONTROL_BTNCOPY
, 115);
1072 if (CanMove(list
) && showEntry
)
1073 choices
.Add(CONTROL_BTNMOVE
, 116);
1075 if (CanNewFolder(list
))
1076 choices
.Add(CONTROL_BTNNEWFOLDER
, 20309);
1077 if (item
>= 0 && pItem
->m_bIsFolder
&& !pItem
->IsParentFolder())
1078 choices
.Add(CONTROL_BTNCALCSIZE
, 13393);
1079 choices
.Add(CONTROL_BTNSWITCHMEDIA
, 523);
1080 if (CServiceBroker::GetJobManager()->IsProcessing("filemanager"))
1081 choices
.Add(CONTROL_BTNCANCELJOB
, 167);
1083 if (!pItem
->m_bIsFolder
)
1084 choices
.Add(CONTROL_BTNVIEW
, 39104);
1086 int btnid
= CGUIDialogContextMenu::ShowAndGetChoice(choices
);
1087 if (btnid
== CONTROL_BTNSELECTALL
)
1092 if (btnid
== CONTROL_BTNFAVOURITES
)
1094 CServiceBroker::GetFavouritesService().AddOrRemove(*pItem
.get(), GetID());
1097 if (btnid
== CONTROL_BTNPLAYWITH
)
1099 std::vector
<std::string
>players
;
1100 playerCoreFactory
.GetPlayers(*pItem
, players
);
1101 std::string player
= playerCoreFactory
.SelectPlayerDialog(players
);
1102 if (!player
.empty())
1103 OnStart(pItem
.get(), player
);
1105 if (btnid
== CONTROL_BTNRENAME
)
1107 if (btnid
== CONTROL_BTNDELETE
)
1109 if (btnid
== CONTROL_BTNCOPY
)
1111 if (btnid
== CONTROL_BTNMOVE
)
1113 if (btnid
== CONTROL_BTNNEWFOLDER
)
1115 if (btnid
== CONTROL_BTNCALCSIZE
)
1117 // setup the progress dialog, and show it
1118 CGUIDialogProgress
*progress
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogProgress
>(WINDOW_DIALOG_PROGRESS
);
1121 progress
->SetHeading(CVariant
{13394});
1122 for (int i
=0; i
< 3; i
++)
1123 progress
->SetLine(i
, CVariant
{""});
1127 // Calculate folder size for each selected item
1128 for (int i
=0; i
<m_vecItems
[list
]->Size(); ++i
)
1130 CFileItemPtr pItem2
=m_vecItems
[list
]->Get(i
);
1131 if (pItem2
->m_bIsFolder
&& pItem2
->IsSelected())
1133 int64_t folderSize
= CalculateFolderSize(pItem2
->GetPath(), progress
);
1134 if (folderSize
>= 0)
1136 pItem2
->m_dwSize
= folderSize
;
1137 if (folderSize
== 0)
1138 pItem2
->SetLabel2(StringUtils::SizeToString(folderSize
));
1140 pItem2
->SetFileSizeLabel();
1147 if (btnid
== CONTROL_BTNSWITCHMEDIA
)
1149 CGUIDialogContextMenu::SwitchMedia("files", m_vecItems
[list
]->GetPath());
1152 if (btnid
== CONTROL_BTNCANCELJOB
)
1154 if (btnid
== CONTROL_BTNVIEW
)
1155 CGUIDialogTextViewer::ShowForFile(pItem
->GetPath(), true);
1157 if (bDeselect
&& item
>= 0 && item
< m_vecItems
[list
]->Size())
1158 { // deselect item as we didn't do anything
1159 pItem
->Select(false);
1163 // Highlights the item in the list under the cursor
1164 // returns true if we should deselect the item, false otherwise
1165 bool CGUIWindowFileManager::SelectItem(int list
, int &item
)
1167 // get the currently selected item in the list
1168 item
= GetSelectedItem(list
);
1170 // select the item if we need to
1171 if (item
> -1 && !NumSelected(list
) && !m_vecItems
[list
]->Get(item
)->IsParentFolder())
1173 m_vecItems
[list
]->Get(item
)->Select(true);
1179 // recursively calculates the selected folder size
1180 int64_t CGUIWindowFileManager::CalculateFolderSize(const std::string
&strDirectory
, CGUIDialogProgress
*pProgress
)
1182 const CURL
pathToUrl(strDirectory
);
1184 { // update our progress control
1185 pProgress
->Progress();
1186 pProgress
->SetLine(1, strDirectory
);
1187 if (pProgress
->IsCanceled())
1190 // start by calculating the size of the files in this folder...
1191 int64_t totalSize
= 0;
1192 CFileItemList items
;
1193 CVirtualDirectory rootDir
;
1194 rootDir
.SetSources(*CMediaSourceSettings::GetInstance().GetSources("files"));
1195 rootDir
.GetDirectory(pathToUrl
, items
, false, false);
1196 for (int i
=0; i
< items
.Size(); i
++)
1198 if (items
[i
]->m_bIsFolder
&& !items
[i
]->IsParentFolder()) // folder
1200 int64_t folderSize
= CalculateFolderSize(items
[i
]->GetPath(), pProgress
);
1201 if (folderSize
< 0) return -1;
1202 totalSize
+= folderSize
;
1205 totalSize
+= items
[i
]->m_dwSize
;
1210 void CGUIWindowFileManager::OnJobComplete(unsigned int jobID
, bool success
, CJob
*job
)
1214 CFileOperationJob
* fileJob
= static_cast<CFileOperationJob
*>(job
);
1215 HELPERS::ShowOKDialogLines(CVariant
{fileJob
->GetHeading()},
1216 CVariant
{fileJob
->GetLine()}, CVariant
{16200}, CVariant
{0});
1221 CGUIMessage
msg(GUI_MSG_NOTIFY_ALL
, GetID(), 0, GUI_MSG_UPDATE
);
1222 CServiceBroker::GetAppMessenger()->SendGUIMessage(msg
, GetID(), false);
1225 CJobQueue::OnJobComplete(jobID
, success
, job
);
1228 void CGUIWindowFileManager::ShowShareErrorMessage(CFileItem
* pItem
)
1230 int idMessageText
= 0;
1231 CURL
url(pItem
->GetPath());
1233 if (url
.IsProtocol("smb") && url
.GetHostName().empty()) // smb workgroup
1234 idMessageText
= 15303; // Workgroup not found
1235 else if (pItem
->m_iDriveType
== CMediaSource::SOURCE_TYPE_REMOTE
|| URIUtils::IsRemote(pItem
->GetPath()))
1236 idMessageText
= 15301; // Could not connect to network server
1238 idMessageText
= 15300; // Path not found or invalid
1240 HELPERS::ShowOKDialogText(CVariant
{220}, CVariant
{idMessageText
});
1243 void CGUIWindowFileManager::OnInitWindow()
1245 bool bResult0
= Update(0, m_Directory
[0]->GetPath());
1246 bool bResult1
= Update(1, m_Directory
[1]->GetPath());
1248 CGUIWindow::OnInitWindow();
1250 if (!bCheckShareConnectivity
)
1252 bCheckShareConnectivity
= true; //reset
1253 CFileItem
pItem(strCheckSharePath
, true);
1254 ShowShareErrorMessage(&pItem
); //show the error message after window is loaded!
1255 Update(0,""); // reset view to root
1259 ShowShareErrorMessage(m_Directory
[0]); //show the error message after window is loaded!
1260 Update(0, ""); // reset view to root
1265 ShowShareErrorMessage(m_Directory
[1]); //show the error message after window is loaded!
1266 Update(1, ""); // reset view to root
1270 void CGUIWindowFileManager::SetInitialPath(const std::string
&path
)
1272 // check for a passed destination path
1273 std::string strDestination
= path
;
1274 m_rootDir
.SetSources(*CMediaSourceSettings::GetInstance().GetSources("files"));
1275 if (!strDestination
.empty())
1277 CLog::Log(LOGINFO
, "Attempting to quickpath to: {}", strDestination
);
1279 // otherwise, is this the first time accessing this window?
1280 else if (m_Directory
[0]->GetPath() == "?")
1282 m_Directory
[0]->SetPath(strDestination
= CMediaSourceSettings::GetInstance().GetDefaultSource("files"));
1283 CLog::Log(LOGINFO
, "Attempting to default to: {}", strDestination
);
1285 // try to open the destination path
1286 if (!strDestination
.empty())
1289 if (StringUtils::EqualsNoCase(strDestination
, "$ROOT"))
1291 m_Directory
[0]->SetPath("");
1292 CLog::Log(LOGINFO
, " Success! Opening root listing.");
1296 // default parameters if the jump fails
1297 m_Directory
[0]->SetPath("");
1299 bool bIsSourceName
= false;
1301 m_rootDir
.GetSources(shares
);
1302 int iIndex
= CUtil::GetMatchingSource(strDestination
, shares
, bIsSourceName
);
1304 #if defined(TARGET_DARWIN_EMBEDDED)
1305 || URIUtils::PathHasParent(strDestination
, "special://envhome/Documents/Inbox/")
1307 || URIUtils::PathHasParent(strDestination
, "special://profile/"))
1309 // set current directory to matching share
1311 if (bIsSourceName
&& iIndex
< (int)shares
.size())
1312 path
= shares
[iIndex
].strPath
;
1314 path
= strDestination
;
1315 URIUtils::RemoveSlashAtEnd(path
);
1316 m_Directory
[0]->SetPath(path
);
1317 CLog::Log(LOGINFO
, " Success! Opened destination path: {}", strDestination
);
1319 // outside call: check the share for connectivity
1320 bCheckShareConnectivity
= Update(0, m_Directory
[0]->GetPath());
1321 if(!bCheckShareConnectivity
)
1322 strCheckSharePath
= m_Directory
[0]->GetPath();
1326 CLog::Log(LOGERROR
, " Failed! Destination parameter ({}) does not match a valid share!",
1332 if (m_Directory
[1]->GetPath() == "?") m_Directory
[1]->SetPath("");
1335 const CFileItem
& CGUIWindowFileManager::CurrentDirectory(int indx
) const
1337 return *m_Directory
[indx
];