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 "music/MusicFileItemClassify.h"
43 #include "network/Network.h"
44 #include "pictures/SlideShowDelegator.h"
45 #include "platform/Filesystem.h"
46 #include "playlists/PlayList.h"
47 #include "playlists/PlayListFactory.h"
48 #include "playlists/PlayListFileItemClassify.h"
49 #include "settings/MediaSourceSettings.h"
50 #include "settings/Settings.h"
51 #include "settings/SettingsComponent.h"
52 #include "storage/MediaManager.h"
53 #include "threads/IRunnable.h"
54 #include "utils/FileOperationJob.h"
55 #include "utils/FileUtils.h"
56 #include "utils/JobManager.h"
57 #include "utils/StringUtils.h"
58 #include "utils/URIUtils.h"
59 #include "utils/Variant.h"
60 #include "utils/log.h"
61 #include "video/VideoFileItemClassify.h"
63 using namespace XFILE
;
65 using namespace KODI::MESSAGING
;
67 #define CONTROL_BTNSELECTALL 1
68 #define CONTROL_BTNFAVOURITES 2
69 #define CONTROL_BTNPLAYWITH 3
70 #define CONTROL_BTNRENAME 4
71 #define CONTROL_BTNDELETE 5
72 #define CONTROL_BTNCOPY 6
73 #define CONTROL_BTNMOVE 7
74 #define CONTROL_BTNNEWFOLDER 8
75 #define CONTROL_BTNCALCSIZE 9
76 #define CONTROL_BTNSWITCHMEDIA 11
77 #define CONTROL_BTNCANCELJOB 12
78 #define CONTROL_BTNVIEW 13
81 #define CONTROL_NUMFILES_LEFT 12
82 #define CONTROL_NUMFILES_RIGHT 13
84 #define CONTROL_LEFT_LIST 20
85 #define CONTROL_RIGHT_LIST 21
87 #define CONTROL_CURRENTDIRLABEL_LEFT 101
88 #define CONTROL_CURRENTDIRLABEL_RIGHT 102
92 class CGetDirectoryItems
: public IRunnable
95 CGetDirectoryItems(XFILE::CVirtualDirectory
& dir
, CURL
& url
, CFileItemList
& items
)
96 : m_dir(dir
), m_url(url
), m_items(items
)
101 m_result
= m_dir
.GetDirectory(m_url
, m_items
, false, false);
103 void Cancel() override
105 m_dir
.CancelDirectory();
107 bool m_result
= false;
110 XFILE::CVirtualDirectory
&m_dir
;
112 CFileItemList
&m_items
;
116 CGUIWindowFileManager::CGUIWindowFileManager(void)
117 : CGUIWindow(WINDOW_FILES
, "FileManager.xml"),
120 m_Directory
[0] = new CFileItem
;
121 m_Directory
[1] = new CFileItem
;
122 m_vecItems
[0] = new CFileItemList
;
123 m_vecItems
[1] = new CFileItemList
;
124 m_Directory
[0]->SetPath("?");
125 m_Directory
[1]->SetPath("?");
126 m_Directory
[0]->m_bIsFolder
= true;
127 m_Directory
[1]->m_bIsFolder
= true;
128 bCheckShareConnectivity
= true;
129 m_loadType
= KEEP_IN_MEMORY
;
132 CGUIWindowFileManager::~CGUIWindowFileManager(void)
134 delete m_Directory
[0];
135 delete m_Directory
[1];
136 delete m_vecItems
[0];
137 delete m_vecItems
[1];
140 bool CGUIWindowFileManager::OnAction(const CAction
&action
)
142 int list
= GetFocusedList();
143 if (list
>= 0 && list
<= 1)
147 // the non-contextual menu can be called at any time
148 if (action
.GetID() == ACTION_CONTEXT_MENU
&& m_vecItems
[list
]->Size() == 0)
150 OnPopupMenu(list
,-1, false);
153 if (action
.GetID() == ACTION_DELETE_ITEM
)
157 bool bDeselect
= SelectItem(list
, item
);
159 if (bDeselect
) m_vecItems
[list
]->Get(item
)->Select(false);
163 if (action
.GetID() == ACTION_COPY_ITEM
)
167 bool bDeselect
= SelectItem(list
, item
);
169 if (bDeselect
) m_vecItems
[list
]->Get(item
)->Select(false);
173 if (action
.GetID() == ACTION_MOVE_ITEM
)
177 bool bDeselect
= SelectItem(list
, item
);
179 if (bDeselect
) m_vecItems
[list
]->Get(item
)->Select(false);
183 if (action
.GetID() == ACTION_RENAME_ITEM
)
187 bool bDeselect
= SelectItem(list
, item
);
189 if (bDeselect
) m_vecItems
[list
]->Get(item
)->Select(false);
193 if (action
.GetID() == ACTION_PARENT_DIR
)
195 GoParentFolder(list
);
198 if (action
.GetID() == ACTION_PLAYER_PLAY
)
200 #ifdef HAS_OPTICAL_DRIVE
201 if (m_vecItems
[list
]->Get(GetSelectedItem(list
))->IsDVD())
202 return MEDIA_DETECT::CAutorun::PlayDiscAskResume(m_vecItems
[list
]->Get(GetSelectedItem(list
))->GetPath());
206 return CGUIWindow::OnAction(action
);
209 bool CGUIWindowFileManager::OnBack(int actionID
)
211 int list
= GetFocusedList();
212 if (list
>= 0 && list
<= 1 && actionID
== ACTION_NAV_BACK
&& !m_vecItems
[list
]->IsVirtualDirectoryRoot())
214 GoParentFolder(list
);
217 return CGUIWindow::OnBack(actionID
);
220 bool CGUIWindowFileManager::OnMessage(CGUIMessage
& message
)
222 switch ( message
.GetMessage() )
224 case GUI_MSG_NOTIFY_ALL
:
225 { // Message is received even if window is inactive
226 if (message
.GetParam1() == GUI_MSG_WINDOW_RESET
)
228 m_Directory
[0]->SetPath("?");
229 m_Directory
[1]->SetPath("?");
230 m_Directory
[0]->m_bIsFolder
= true;
231 m_Directory
[1]->m_bIsFolder
= true;
235 // handle removable media
236 if (message
.GetParam1() == GUI_MSG_REMOVED_MEDIA
)
238 for (int i
= 0; i
< 2; i
++)
240 if (m_Directory
[i
]->IsVirtualDirectoryRoot() && IsActive())
242 int iItem
= GetSelectedItem(i
);
243 Update(i
, m_Directory
[i
]->GetPath());
244 CONTROL_SELECT_ITEM(CONTROL_LEFT_LIST
+ i
, iItem
);
246 else if (m_Directory
[i
]->IsRemovable() && !m_rootDir
.IsInSource(m_Directory
[i
]->GetPath()))
251 m_Directory
[i
]->SetPath("");
256 else if (message
.GetParam1()==GUI_MSG_UPDATE_SOURCES
)
257 { // State of the sources changed, so update our view
258 for (int i
= 0; i
< 2; i
++)
260 if (m_Directory
[i
]->IsVirtualDirectoryRoot() && IsActive())
262 int iItem
= GetSelectedItem(i
);
263 Update(i
, m_Directory
[i
]->GetPath());
264 CONTROL_SELECT_ITEM(CONTROL_LEFT_LIST
+ i
, iItem
);
269 else if (message
.GetParam1()==GUI_MSG_UPDATE
&& IsActive())
276 case GUI_MSG_PLAYBACK_STARTED
:
277 case GUI_MSG_PLAYBACK_ENDED
:
278 case GUI_MSG_PLAYBACK_STOPPED
:
279 case GUI_MSG_PLAYLIST_CHANGED
:
280 case GUI_MSG_PLAYLISTPLAYER_STOPPED
:
281 case GUI_MSG_PLAYLISTPLAYER_STARTED
:
282 case GUI_MSG_PLAYLISTPLAYER_CHANGED
:
283 { // send a notify all to all controls on this window
284 CGUIMessage
msg(GUI_MSG_NOTIFY_ALL
, GetID(), 0, GUI_MSG_REFRESH_LIST
);
288 case GUI_MSG_WINDOW_DEINIT
:
290 CGUIWindow::OnMessage(message
);
297 case GUI_MSG_WINDOW_INIT
:
299 SetInitialPath(message
.GetStringParam());
300 message
.SetStringParam("");
302 return CGUIWindow::OnMessage(message
);
305 case GUI_MSG_CLICKED
:
307 int iControl
= message
.GetSenderId();
309 if (iControl
== CONTROL_LEFT_LIST
|| iControl
== CONTROL_RIGHT_LIST
) // list/thumb control
312 int list
= iControl
- CONTROL_LEFT_LIST
;
313 int iItem
= GetSelectedItem(list
);
314 int iAction
= message
.GetParam1();
316 // iItem is checked for validity inside these routines
317 if (iAction
== ACTION_HIGHLIGHT_ITEM
|| iAction
== ACTION_MOUSE_LEFT_CLICK
)
320 if (!CServiceBroker::GetInputManager().IsMouseActive())
323 CGUIMessage
msg(GUI_MSG_ITEM_SELECT
, GetID(), iControl
, iItem
+ 1);
324 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg
);
327 else if (iAction
== ACTION_SELECT_ITEM
|| iAction
== ACTION_MOUSE_DOUBLE_CLICK
)
329 OnClick(list
, iItem
);
331 else if (iAction
== ACTION_CONTEXT_MENU
|| iAction
== ACTION_MOUSE_RIGHT_CLICK
)
333 OnPopupMenu(list
, iItem
);
338 // prevent touch/gesture unfocussing ..
339 case GUI_MSG_GESTURE_NOTIFY
:
340 case GUI_MSG_UNFOCUS_ALL
:
343 return CGUIWindow::OnMessage(message
);
346 void CGUIWindowFileManager::OnSort(int iList
)
348 using namespace KODI::PLATFORM::FILESYSTEM
;
349 // always sort the list by label in ascending order
350 for (int i
= 0; i
< m_vecItems
[iList
]->Size(); i
++)
352 CFileItemPtr pItem
= m_vecItems
[iList
]->Get(i
);
353 if (pItem
->m_bIsFolder
&& (!pItem
->m_dwSize
|| pItem
->IsPath("add")))
354 pItem
->SetLabel2("");
356 pItem
->SetFileSizeLabel();
358 // Set free space on disc
359 if (pItem
->m_bIsShareOrDrive
)
364 auto freeSpace
= space(pItem
->GetPath(), ec
);
367 pItem
->m_dwSize
= freeSpace
.free
;
368 pItem
->SetFileSizeLabel();
371 else if (pItem
->IsDVD() && CServiceBroker::GetMediaManager().IsDiscInDrive())
374 auto freeSpace
= space(pItem
->GetPath(), ec
);
377 pItem
->m_dwSize
= freeSpace
.capacity
;
378 pItem
->SetFileSizeLabel();
381 } // if (pItem->m_bIsShareOrDrive)
385 m_vecItems
[iList
]->Sort(SortByLabel
, SortOrderAscending
);
388 void CGUIWindowFileManager::ClearFileItems(int iList
)
390 CGUIMessage
msg(GUI_MSG_LABEL_RESET
, GetID(), iList
+ CONTROL_LEFT_LIST
);
391 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg
);
393 m_vecItems
[iList
]->Clear(); // will clean up everything
396 void CGUIWindowFileManager::UpdateButtons()
398 // update our current directory labels
399 std::string strDir
= CURL(m_Directory
[0]->GetPath()).GetWithoutUserDetails();
402 SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_LEFT
,g_localizeStrings
.Get(20108));
406 SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_LEFT
, strDir
);
408 strDir
= CURL(m_Directory
[1]->GetPath()).GetWithoutUserDetails();
411 SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_RIGHT
,g_localizeStrings
.Get(20108));
415 SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_RIGHT
, strDir
);
418 // update the number of items in each list
422 void CGUIWindowFileManager::UpdateItemCounts()
424 for (int i
= 0; i
< 2; i
++)
426 unsigned int selectedCount
= 0;
427 unsigned int totalCount
= 0;
428 int64_t selectedSize
= 0;
429 for (int j
= 0; j
< m_vecItems
[i
]->Size(); j
++)
431 CFileItemPtr item
= m_vecItems
[i
]->Get(j
);
432 if (item
->IsParentFolder()) continue;
433 if (item
->IsSelected())
436 selectedSize
+= item
->m_dwSize
;
441 if (selectedCount
> 0)
443 StringUtils::Format("{}/{} {} ({})", selectedCount
, totalCount
,
444 g_localizeStrings
.Get(127), StringUtils::SizeToString(selectedSize
));
446 items
= StringUtils::Format("{} {}", totalCount
, g_localizeStrings
.Get(127));
447 SET_CONTROL_LABEL(CONTROL_NUMFILES_LEFT
+ i
, items
);
451 bool CGUIWindowFileManager::Update(int iList
, const std::string
&strDirectory
)
455 CLog::Log(LOGWARNING
, "CGUIWindowFileManager::Update - updating in progress");
458 CUpdateGuard
ug(m_updating
);
461 int iItem
= GetSelectedItem(iList
);
462 std::string strSelectedItem
= "";
464 if (iItem
>= 0 && iItem
< m_vecItems
[iList
]->Size())
466 CFileItemPtr pItem
= m_vecItems
[iList
]->Get(iItem
);
467 if (!pItem
->IsParentFolder())
469 GetDirectoryHistoryString(pItem
.get(), strSelectedItem
);
470 m_history
[iList
].SetSelectedItem(strSelectedItem
, m_Directory
[iList
]->GetPath(), iItem
);
474 std::string strOldDirectory
=m_Directory
[iList
]->GetPath();
475 m_Directory
[iList
]->SetPath(strDirectory
);
478 if (!GetDirectory(iList
, m_Directory
[iList
]->GetPath(), items
))
480 if (strDirectory
!= strOldDirectory
&& GetDirectory(iList
, strOldDirectory
, items
))
481 m_Directory
[iList
]->SetPath(strOldDirectory
); // Fallback to old (previous) path)
483 Update(iList
, ""); // Fallback to root
488 m_history
[iList
].SetSelectedItem(strSelectedItem
, strOldDirectory
);
490 ClearFileItems(iList
);
492 m_vecItems
[iList
]->Append(items
);
493 m_vecItems
[iList
]->SetPath(items
.GetPath());
495 std::string strParentPath
;
496 URIUtils::GetParentPath(strDirectory
, strParentPath
);
497 if (strDirectory
.empty() && (m_vecItems
[iList
]->Size() == 0 || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWADDSOURCEBUTTONS
)))
498 { // add 'add source button'
499 const std::string
& strLabel
= g_localizeStrings
.Get(1026);
500 CFileItemPtr
pItem(new CFileItem(strLabel
));
501 pItem
->SetPath("add");
502 pItem
->SetArt("icon", "DefaultAddSource.png");
503 pItem
->SetLabel(strLabel
);
504 pItem
->SetLabelPreformatted(true);
505 pItem
->m_bIsFolder
= true;
506 pItem
->SetSpecialSort(SortSpecialOnBottom
);
507 m_vecItems
[iList
]->Add(pItem
);
509 else if (items
.IsEmpty() || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWPARENTDIRITEMS
))
511 CFileItemPtr
pItem(new CFileItem(".."));
512 pItem
->SetPath(m_rootDir
.IsSource(strDirectory
) ? "" : strParentPath
);
513 pItem
->m_bIsFolder
= true;
514 pItem
->m_bIsShareOrDrive
= false;
515 m_vecItems
[iList
]->AddFront(pItem
, 0);
518 m_strParentPath
[iList
] = (m_rootDir
.IsSource(strDirectory
) ? "" : strParentPath
);
520 if (strDirectory
.empty())
522 CFileItemPtr
pItem(new CFileItem("special://profile/", true));
523 pItem
->SetLabel(g_localizeStrings
.Get(20070));
524 pItem
->SetArt("thumb", "DefaultFolder.png");
525 pItem
->SetLabelPreformatted(true);
526 m_vecItems
[iList
]->Add(pItem
);
528 #ifdef TARGET_DARWIN_EMBEDDED
529 CFileItemPtr
iItem(new CFileItem("special://envhome/Documents/Inbox", true));
530 iItem
->SetLabel("Inbox");
531 iItem
->SetArt("thumb", "DefaultFolder.png");
532 iItem
->SetLabelPreformatted(true);
533 m_vecItems
[iList
]->Add(iItem
);
535 #ifdef TARGET_ANDROID
536 CFileItemPtr
iItem(new CFileItem("special://logpath", true));
537 iItem
->SetLabel("Logs");
538 iItem
->SetArt("thumb", "DefaultFolder.png");
539 iItem
->SetLabelPreformatted(true);
540 m_vecItems
[iList
]->Add(iItem
);
544 // if we have a .tbn file, use itself as the thumb
545 for (int i
= 0; i
< m_vecItems
[iList
]->Size(); i
++)
547 CFileItemPtr pItem
= m_vecItems
[iList
]->Get(i
);
549 URIUtils::HasExtension(pItem
->GetPath(), ".tbn"))
551 pItem
->SetArt("thumb", pItem
->GetPath());
554 m_vecItems
[iList
]->FillInDefaultIcons();
560 strSelectedItem
= m_history
[iList
].GetSelectedItem(m_Directory
[iList
]->GetPath());
561 for (int i
= 0; i
< m_vecItems
[iList
]->Size(); ++i
)
563 CFileItemPtr pItem
= m_vecItems
[iList
]->Get(i
);
564 std::string strHistory
;
565 GetDirectoryHistoryString(pItem
.get(), strHistory
);
566 if (strHistory
== strSelectedItem
)
572 UpdateControl(iList
, item
);
577 void CGUIWindowFileManager::OnClick(int iList
, int iItem
)
579 if ( iList
< 0 || iList
>= 2) return ;
580 if ( iItem
< 0 || iItem
>= m_vecItems
[iList
]->Size() ) return ;
582 CFileItemPtr pItem
= m_vecItems
[iList
]->Get(iItem
);
583 if (pItem
->GetPath() == "add" && pItem
->GetLabel() == g_localizeStrings
.Get(1026)) // 'add source button' in empty root
585 if (CGUIDialogMediaSource::ShowAndAddMediaSource("files"))
587 m_rootDir
.SetSources(*CMediaSourceSettings::GetInstance().GetSources("files"));
588 Update(0,m_Directory
[0]->GetPath());
589 Update(1,m_Directory
[1]->GetPath());
594 if (!pItem
->m_bIsFolder
&& pItem
->IsFileFolder(EFILEFOLDER_MASK_ALL
))
596 XFILE::IFileDirectory
*pFileDirectory
= NULL
;
597 pFileDirectory
= XFILE::CFileDirectoryFactory::Create(pItem
->GetURL(), pItem
.get(), "");
599 pItem
->m_bIsFolder
= true;
600 else if(pItem
->m_bIsFolder
)
601 pItem
->m_bIsFolder
= false;
602 delete pFileDirectory
;
605 if (pItem
->m_bIsFolder
)
607 // save path + drive type because of the possible refresh
608 std::string strPath
= pItem
->GetPath();
609 int iDriveType
= pItem
->m_iDriveType
;
610 if ( pItem
->m_bIsShareOrDrive
)
612 if ( !g_passwordManager
.IsItemUnlocked( pItem
.get(), "files" ) )
618 if ( !HaveDiscOrConnection( strPath
, iDriveType
) )
621 if (!Update(iList
, strPath
))
622 ShowShareErrorMessage(pItem
.get());
624 else if (pItem
->IsZIP() || pItem
->IsCBZ()) // mount zip archive
626 CURL pathToUrl
= URIUtils::CreateArchivePath("zip", pItem
->GetURL(), "");
627 Update(iList
, pathToUrl
.Get());
629 else if (pItem
->IsRAR() || pItem
->IsCBR())
631 CURL pathToUrl
= URIUtils::CreateArchivePath("rar", pItem
->GetURL(), "");
632 Update(iList
, pathToUrl
.Get());
636 OnStart(pItem
.get(), "");
642 //! @todo 2.0: Can this be removed, or should we run without the "special" file directories while
643 // in filemanager view.
644 void CGUIWindowFileManager::OnStart(CFileItem
*pItem
, const std::string
&player
)
646 // start playlists from file manager
647 if (PLAYLIST::IsPlayList(*pItem
))
649 const std::string
& strPlayList
= pItem
->GetPath();
650 std::unique_ptr
<PLAYLIST::CPlayList
> pPlayList(PLAYLIST::CPlayListFactory::Create(strPlayList
));
651 if (nullptr != pPlayList
)
653 if (!pPlayList
->Load(strPlayList
))
655 HELPERS::ShowOKDialogText(CVariant
{6}, CVariant
{477});
659 g_application
.ProcessAndStartPlaylist(strPlayList
, *pPlayList
, PLAYLIST::Id::TYPE_MUSIC
);
662 if (MUSIC::IsAudio(*pItem
) || VIDEO::IsVideo(*pItem
))
664 CServiceBroker::GetPlaylistPlayer().Play(std::make_shared
<CFileItem
>(*pItem
), player
);
669 g_application
.PlayFile(*pItem
, player
);
673 if (pItem
->IsPythonScript())
675 CScriptInvocationManager::GetInstance().ExecuteAsync(pItem
->GetPath());
679 if (pItem
->IsPicture())
681 const auto& components
= CServiceBroker::GetAppComponents();
682 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
683 if (appPlayer
->IsPlayingVideo())
684 g_application
.StopPlaying();
686 CSlideShowDelegator
& slideShow
= CServiceBroker::GetSlideShowDelegator();
688 slideShow
.Add(pItem
);
689 slideShow
.Select(pItem
->GetPath());
691 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW
);
694 if (pItem
->IsType(".txt") || pItem
->IsType(".xml"))
695 CGUIDialogTextViewer::ShowForFile(pItem
->GetPath(), true);
698 bool CGUIWindowFileManager::HaveDiscOrConnection( std::string
& strPath
, int iDriveType
)
700 if ( iDriveType
== CMediaSource::SOURCE_TYPE_DVD
)
702 if (!CServiceBroker::GetMediaManager().IsDiscInDrive(strPath
))
704 HELPERS::ShowOKDialogText(CVariant
{218}, CVariant
{219});
705 int iList
= GetFocusedList();
706 int iItem
= GetSelectedItem(iList
);
708 CONTROL_SELECT_ITEM(iList
+ CONTROL_LEFT_LIST
, iItem
);
712 else if ( iDriveType
== CMediaSource::SOURCE_TYPE_REMOTE
)
714 //! @todo Handle not connected to a remote share
715 if (!CServiceBroker::GetNetwork().IsConnected())
717 HELPERS::ShowOKDialogText(CVariant
{220}, CVariant
{221});
726 void CGUIWindowFileManager::UpdateControl(int iList
, int item
)
728 CGUIMessage
msg(GUI_MSG_LABEL_BIND
, GetID(), iList
+ CONTROL_LEFT_LIST
, item
, 0, m_vecItems
[iList
]);
729 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg
);
732 void CGUIWindowFileManager::OnMark(int iList
, int iItem
)
734 CFileItemPtr pItem
= m_vecItems
[iList
]->Get(iItem
);
736 if (!pItem
->m_bIsShareOrDrive
)
738 if (!pItem
->IsParentFolder())
741 pItem
->Select(!pItem
->IsSelected());
749 void CGUIWindowFileManager::OnCopy(int iList
)
751 if (!CGUIDialogYesNo::ShowAndGetInput(CVariant
{120}, CVariant
{123}))
754 AddJob(new CFileOperationJob(CFileOperationJob::ActionCopy
,
756 m_Directory
[1 - iList
]->GetPath(),
757 true, 16201, 16202));
760 void CGUIWindowFileManager::OnMove(int iList
)
762 if (!CGUIDialogYesNo::ShowAndGetInput(CVariant
{121}, CVariant
{124}))
765 AddJob(new CFileOperationJob(CFileOperationJob::ActionMove
,
767 m_Directory
[1 - iList
]->GetPath(),
768 true, 16203, 16204));
771 void CGUIWindowFileManager::OnDelete(int iList
)
773 if (!CGUIDialogYesNo::ShowAndGetInput(CVariant
{122}, CVariant
{125}))
776 AddJob(new CFileOperationJob(CFileOperationJob::ActionDelete
,
778 m_Directory
[iList
]->GetPath(),
779 true, 16205, 16206));
782 void CGUIWindowFileManager::OnRename(int iList
)
785 for (int i
= 0; i
< m_vecItems
[iList
]->Size();++i
)
787 CFileItemPtr pItem
= m_vecItems
[iList
]->Get(i
);
788 if (pItem
->IsSelected())
790 strFile
= pItem
->GetPath();
795 CFileUtils::RenameFile(strFile
);
800 void CGUIWindowFileManager::OnSelectAll(int iList
)
802 for (int i
= 0; i
< m_vecItems
[iList
]->Size();++i
)
804 CFileItemPtr pItem
= m_vecItems
[iList
]->Get(i
);
805 if (!pItem
->IsParentFolder())
812 void CGUIWindowFileManager::OnNewFolder(int iList
)
814 std::string strNewFolder
= "";
815 if (CGUIKeyboardFactory::ShowAndGetInput(strNewFolder
, CVariant
{g_localizeStrings
.Get(16014)}, false))
817 std::string strNewPath
= m_Directory
[iList
]->GetPath();
818 URIUtils::AddSlashAtEnd(strNewPath
);
819 strNewPath
+= strNewFolder
;
820 CDirectory::Create(strNewPath
);
823 // select the new folder
824 for (int i
=0; i
<m_vecItems
[iList
]->Size(); ++i
)
826 CFileItemPtr pItem
=m_vecItems
[iList
]->Get(i
);
827 std::string strPath
=pItem
->GetPath();
828 URIUtils::RemoveSlashAtEnd(strPath
);
829 if (strPath
==strNewPath
)
831 CONTROL_SELECT_ITEM(iList
+ CONTROL_LEFT_LIST
, i
);
838 void CGUIWindowFileManager::Refresh(int iList
)
840 int nSel
= GetSelectedItem(iList
);
841 // update the list views
842 Update(iList
, m_Directory
[iList
]->GetPath());
844 while (nSel
> m_vecItems
[iList
]->Size())
847 CONTROL_SELECT_ITEM(iList
+ CONTROL_LEFT_LIST
, nSel
);
851 void CGUIWindowFileManager::Refresh()
853 int iList
= GetFocusedList();
854 int nSel
= GetSelectedItem(iList
);
855 // update the list views
856 Update(0, m_Directory
[0]->GetPath());
857 Update(1, m_Directory
[1]->GetPath());
859 while (nSel
> m_vecItems
[iList
]->Size())
862 CONTROL_SELECT_ITEM(iList
+ CONTROL_LEFT_LIST
, nSel
);
865 int CGUIWindowFileManager::GetSelectedItem(int iControl
)
867 if (iControl
< 0 || iControl
> 1 || m_vecItems
[iControl
]->IsEmpty())
869 CGUIMessage
msg(GUI_MSG_ITEM_SELECTED
, GetID(), iControl
+ CONTROL_LEFT_LIST
);
871 return msg
.GetParam1();
875 void CGUIWindowFileManager::GoParentFolder(int iList
)
877 CURL
url(m_Directory
[iList
]->GetPath());
878 if (url
.IsProtocol("rar") || url
.IsProtocol("zip"))
880 // check for step-below, if, unmount rar
881 if (url
.GetFileName().empty())
882 if (url
.IsProtocol("zip"))
883 g_ZipManager
.release(m_Directory
[iList
]->GetPath()); // release resources
886 std::string
strPath(m_strParentPath
[iList
]), strOldPath(m_Directory
[iList
]->GetPath());
887 Update(iList
, strPath
);
890 /// \brief Build a directory history string
891 /// \param pItem Item to build the history string from
892 /// \param strHistoryString History string build as return value
893 void CGUIWindowFileManager::GetDirectoryHistoryString(const CFileItem
* pItem
, std::string
& strHistoryString
)
895 if (pItem
->m_bIsShareOrDrive
)
897 // We are in the virtual directory
899 // History string of the DVD drive
900 // must be handled separately
901 if (pItem
->m_iDriveType
== CMediaSource::SOURCE_TYPE_DVD
)
903 // Remove disc label from item label
904 // and use as history string, m_strPath
905 // can change for new discs
906 std::string strLabel
= pItem
->GetLabel();
907 size_t nPosOpen
= strLabel
.find('(');
908 size_t nPosClose
= strLabel
.rfind(')');
909 if (nPosOpen
!= std::string::npos
&&
910 nPosClose
!= std::string::npos
&&
911 nPosClose
> nPosOpen
)
913 strLabel
.erase(nPosOpen
+ 1, (nPosClose
) - (nPosOpen
+ 1));
914 strHistoryString
= strLabel
;
917 strHistoryString
= strLabel
;
921 // Other items in virtual directory
922 strHistoryString
= pItem
->GetLabel() + pItem
->GetPath();
923 URIUtils::RemoveSlashAtEnd(strHistoryString
);
928 // Normal directory items
929 strHistoryString
= pItem
->GetPath();
930 URIUtils::RemoveSlashAtEnd(strHistoryString
);
934 bool CGUIWindowFileManager::GetDirectory(int iList
, const std::string
&strDirectory
, CFileItemList
&items
)
936 CURL
pathToUrl(strDirectory
);
938 CGetDirectoryItems
getItems(m_rootDir
, pathToUrl
, items
);
939 if (!CGUIDialogBusy::Wait(&getItems
, 100, true))
943 return getItems
.m_result
;
946 bool CGUIWindowFileManager::CanRename(int iList
)
948 //! @todo Renaming of shares (requires writing to sources.xml)
949 //! this might be able to be done via the webserver code stuff...
950 if (m_Directory
[iList
]->IsVirtualDirectoryRoot()) return false;
951 if (m_Directory
[iList
]->IsReadOnly()) return false;
956 bool CGUIWindowFileManager::CanCopy(int iList
)
958 // can't copy if the destination is not writeable, or if the source is a share!
959 //! @todo Perhaps if the source is removeable media (DVD/CD etc.) we could
960 //! put ripping/backup in here.
961 if (!CUtil::SupportsReadFileOperations(m_Directory
[iList
]->GetPath())) return false;
962 if (m_Directory
[iList
]->IsVirtualDirectoryRoot()) return false;
963 if (m_Directory
[1 - iList
]->IsVirtualDirectoryRoot()) return false;
964 if (m_Directory
[iList
]->IsVirtualDirectoryRoot()) return false;
965 if (m_Directory
[1 -iList
]->IsReadOnly()) return false;
969 bool CGUIWindowFileManager::CanMove(int iList
)
971 // can't move if the destination is not writeable, or if the source is a share or not writeable!
972 if (m_Directory
[0]->IsVirtualDirectoryRoot() || m_Directory
[0]->IsReadOnly()) return false;
973 if (m_Directory
[1]->IsVirtualDirectoryRoot() || m_Directory
[1]->IsReadOnly()) return false;
977 bool CGUIWindowFileManager::CanDelete(int iList
)
979 if (m_Directory
[iList
]->IsVirtualDirectoryRoot()) return false;
980 if (m_Directory
[iList
]->IsReadOnly()) return false;
984 bool CGUIWindowFileManager::CanNewFolder(int iList
)
986 if (m_Directory
[iList
]->IsVirtualDirectoryRoot()) return false;
987 if (m_Directory
[iList
]->IsReadOnly()) return false;
991 int CGUIWindowFileManager::NumSelected(int iList
)
993 int iSelectedItems
= 0;
994 for (int iItem
= 0; iItem
< m_vecItems
[iList
]->Size(); ++iItem
)
996 if (m_vecItems
[iList
]->Get(iItem
)->IsSelected()) iSelectedItems
++;
998 return iSelectedItems
;
1001 int CGUIWindowFileManager::GetFocusedList() const
1003 return GetFocusedControlID() - CONTROL_LEFT_LIST
;
1006 void CGUIWindowFileManager::OnPopupMenu(int list
, int item
, bool bContextDriven
/* = true */)
1008 if (list
< 0 || list
>= 2) return ;
1009 bool bDeselect
= SelectItem(list
, item
);
1010 // calculate the position for our menu
1013 const CGUIControl
*pList
= GetControl(CONTROL_LEFT_LIST
+ list
);
1016 posX
= pList
->GetXPosition() + pList
->GetWidth() / 2;
1017 posY
= pList
->GetYPosition() + pList
->GetHeight() / 2;
1020 CFileItemPtr pItem
= m_vecItems
[list
]->Get(item
);
1024 if (m_Directory
[list
]->IsVirtualDirectoryRoot())
1027 { //! @todo We should add the option here for shares to be added if there aren't any
1031 // and do the popup menu
1032 if (CGUIDialogContextMenu::SourcesMenu("files", pItem
, posX
, posY
))
1034 m_rootDir
.SetSources(*CMediaSourceSettings::GetInstance().GetSources("files"));
1035 if (m_Directory
[1 - list
]->IsVirtualDirectoryRoot())
1041 pItem
->Select(false);
1045 const CPlayerCoreFactory
&playerCoreFactory
= CServiceBroker::GetPlayerCoreFactory();
1047 // popup the context menu
1049 bool showEntry
= false;
1050 if (item
>= m_vecItems
[list
]->Size()) item
= -1;
1052 showEntry
=(!pItem
->IsParentFolder() || (pItem
->IsParentFolder() && m_vecItems
[list
]->GetSelectedCount()>0));
1054 // determine available players
1055 std::vector
<std::string
>players
;
1056 playerCoreFactory
.GetPlayers(*pItem
, players
);
1058 // add the needed buttons
1059 CContextButtons choices
;
1062 //The ".." item is not selectable. Take that into account when figuring out if all items are selected
1063 int notSelectable
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWPARENTDIRITEMS
) ? 1 : 0;
1064 if (NumSelected(list
) < m_vecItems
[list
]->Size() - notSelectable
)
1065 choices
.Add(CONTROL_BTNSELECTALL
, 188); // SelectAll
1066 if (!pItem
->IsParentFolder())
1067 choices
.Add(CONTROL_BTNFAVOURITES
, CServiceBroker::GetFavouritesService().IsFavourited(*pItem
.get(), GetID()) ? 14077 : 14076); // Add/Remove Favourite
1068 if (players
.size() > 1)
1069 choices
.Add(CONTROL_BTNPLAYWITH
, 15213);
1070 if (CanRename(list
) && !pItem
->IsParentFolder())
1071 choices
.Add(CONTROL_BTNRENAME
, 118);
1072 if (CanDelete(list
) && showEntry
)
1073 choices
.Add(CONTROL_BTNDELETE
, 117);
1074 if (CanCopy(list
) && showEntry
)
1075 choices
.Add(CONTROL_BTNCOPY
, 115);
1076 if (CanMove(list
) && showEntry
)
1077 choices
.Add(CONTROL_BTNMOVE
, 116);
1079 if (CanNewFolder(list
))
1080 choices
.Add(CONTROL_BTNNEWFOLDER
, 20309);
1081 if (item
>= 0 && pItem
->m_bIsFolder
&& !pItem
->IsParentFolder())
1082 choices
.Add(CONTROL_BTNCALCSIZE
, 13393);
1083 choices
.Add(CONTROL_BTNSWITCHMEDIA
, 523);
1084 if (CServiceBroker::GetJobManager()->IsProcessing("filemanager"))
1085 choices
.Add(CONTROL_BTNCANCELJOB
, 167);
1087 if (!pItem
->m_bIsFolder
)
1088 choices
.Add(CONTROL_BTNVIEW
, 39104);
1090 int btnid
= CGUIDialogContextMenu::ShowAndGetChoice(choices
);
1091 if (btnid
== CONTROL_BTNSELECTALL
)
1096 if (btnid
== CONTROL_BTNFAVOURITES
)
1098 CServiceBroker::GetFavouritesService().AddOrRemove(*pItem
.get(), GetID());
1101 if (btnid
== CONTROL_BTNPLAYWITH
)
1103 std::vector
<std::string
>players
;
1104 playerCoreFactory
.GetPlayers(*pItem
, players
);
1105 std::string player
= playerCoreFactory
.SelectPlayerDialog(players
);
1106 if (!player
.empty())
1107 OnStart(pItem
.get(), player
);
1109 if (btnid
== CONTROL_BTNRENAME
)
1111 if (btnid
== CONTROL_BTNDELETE
)
1113 if (btnid
== CONTROL_BTNCOPY
)
1115 if (btnid
== CONTROL_BTNMOVE
)
1117 if (btnid
== CONTROL_BTNNEWFOLDER
)
1119 if (btnid
== CONTROL_BTNCALCSIZE
)
1121 // setup the progress dialog, and show it
1122 CGUIDialogProgress
*progress
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogProgress
>(WINDOW_DIALOG_PROGRESS
);
1125 progress
->SetHeading(CVariant
{13394});
1126 for (int i
=0; i
< 3; i
++)
1127 progress
->SetLine(i
, CVariant
{""});
1131 // Calculate folder size for each selected item
1132 for (int i
=0; i
<m_vecItems
[list
]->Size(); ++i
)
1134 CFileItemPtr pItem2
=m_vecItems
[list
]->Get(i
);
1135 if (pItem2
->m_bIsFolder
&& pItem2
->IsSelected())
1137 int64_t folderSize
= CalculateFolderSize(pItem2
->GetPath(), progress
);
1138 if (folderSize
>= 0)
1140 pItem2
->m_dwSize
= folderSize
;
1141 if (folderSize
== 0)
1142 pItem2
->SetLabel2(StringUtils::SizeToString(folderSize
));
1144 pItem2
->SetFileSizeLabel();
1151 if (btnid
== CONTROL_BTNSWITCHMEDIA
)
1153 CGUIDialogContextMenu::SwitchMedia("files", m_vecItems
[list
]->GetPath());
1156 if (btnid
== CONTROL_BTNCANCELJOB
)
1158 if (btnid
== CONTROL_BTNVIEW
)
1159 CGUIDialogTextViewer::ShowForFile(pItem
->GetPath(), true);
1161 if (bDeselect
&& item
>= 0 && item
< m_vecItems
[list
]->Size())
1162 { // deselect item as we didn't do anything
1163 pItem
->Select(false);
1167 // Highlights the item in the list under the cursor
1168 // returns true if we should deselect the item, false otherwise
1169 bool CGUIWindowFileManager::SelectItem(int list
, int &item
)
1171 // get the currently selected item in the list
1172 item
= GetSelectedItem(list
);
1174 // select the item if we need to
1175 if (item
> -1 && !NumSelected(list
) && !m_vecItems
[list
]->Get(item
)->IsParentFolder())
1177 m_vecItems
[list
]->Get(item
)->Select(true);
1183 // recursively calculates the selected folder size
1184 int64_t CGUIWindowFileManager::CalculateFolderSize(const std::string
&strDirectory
, CGUIDialogProgress
*pProgress
)
1186 const CURL
pathToUrl(strDirectory
);
1188 { // update our progress control
1189 pProgress
->Progress();
1190 pProgress
->SetLine(1, strDirectory
);
1191 if (pProgress
->IsCanceled())
1194 // start by calculating the size of the files in this folder...
1195 int64_t totalSize
= 0;
1196 CFileItemList items
;
1197 CVirtualDirectory rootDir
;
1198 rootDir
.SetSources(*CMediaSourceSettings::GetInstance().GetSources("files"));
1199 rootDir
.GetDirectory(pathToUrl
, items
, false, false);
1200 for (int i
=0; i
< items
.Size(); i
++)
1202 if (items
[i
]->m_bIsFolder
&& !items
[i
]->IsParentFolder()) // folder
1204 int64_t folderSize
= CalculateFolderSize(items
[i
]->GetPath(), pProgress
);
1205 if (folderSize
< 0) return -1;
1206 totalSize
+= folderSize
;
1209 totalSize
+= items
[i
]->m_dwSize
;
1214 void CGUIWindowFileManager::OnJobComplete(unsigned int jobID
, bool success
, CJob
*job
)
1218 CFileOperationJob
* fileJob
= static_cast<CFileOperationJob
*>(job
);
1219 HELPERS::ShowOKDialogLines(CVariant
{fileJob
->GetHeading()},
1220 CVariant
{fileJob
->GetLine()}, CVariant
{16200}, CVariant
{0});
1225 CGUIMessage
msg(GUI_MSG_NOTIFY_ALL
, GetID(), 0, GUI_MSG_UPDATE
);
1226 CServiceBroker::GetAppMessenger()->SendGUIMessage(msg
, GetID(), false);
1229 CJobQueue::OnJobComplete(jobID
, success
, job
);
1232 void CGUIWindowFileManager::ShowShareErrorMessage(CFileItem
* pItem
)
1234 int idMessageText
= 0;
1235 CURL
url(pItem
->GetPath());
1237 if (url
.IsProtocol("smb") && url
.GetHostName().empty()) // smb workgroup
1238 idMessageText
= 15303; // Workgroup not found
1239 else if (pItem
->m_iDriveType
== CMediaSource::SOURCE_TYPE_REMOTE
|| URIUtils::IsRemote(pItem
->GetPath()))
1240 idMessageText
= 15301; // Could not connect to network server
1242 idMessageText
= 15300; // Path not found or invalid
1244 HELPERS::ShowOKDialogText(CVariant
{220}, CVariant
{idMessageText
});
1247 void CGUIWindowFileManager::OnInitWindow()
1249 bool bResult0
= Update(0, m_Directory
[0]->GetPath());
1250 bool bResult1
= Update(1, m_Directory
[1]->GetPath());
1252 CGUIWindow::OnInitWindow();
1254 if (!bCheckShareConnectivity
)
1256 bCheckShareConnectivity
= true; //reset
1257 CFileItem
pItem(strCheckSharePath
, true);
1258 ShowShareErrorMessage(&pItem
); //show the error message after window is loaded!
1259 Update(0,""); // reset view to root
1263 ShowShareErrorMessage(m_Directory
[0]); //show the error message after window is loaded!
1264 Update(0, ""); // reset view to root
1269 ShowShareErrorMessage(m_Directory
[1]); //show the error message after window is loaded!
1270 Update(1, ""); // reset view to root
1274 void CGUIWindowFileManager::SetInitialPath(const std::string
&path
)
1276 // check for a passed destination path
1277 std::string strDestination
= path
;
1278 m_rootDir
.SetSources(*CMediaSourceSettings::GetInstance().GetSources("files"));
1279 if (!strDestination
.empty())
1281 CLog::Log(LOGINFO
, "Attempting to quickpath to: {}", strDestination
);
1283 // otherwise, is this the first time accessing this window?
1284 else if (m_Directory
[0]->GetPath() == "?")
1286 m_Directory
[0]->SetPath(strDestination
= CMediaSourceSettings::GetInstance().GetDefaultSource("files"));
1287 CLog::Log(LOGINFO
, "Attempting to default to: {}", strDestination
);
1289 // try to open the destination path
1290 if (!strDestination
.empty())
1293 if (StringUtils::EqualsNoCase(strDestination
, "$ROOT"))
1295 m_Directory
[0]->SetPath("");
1296 CLog::Log(LOGINFO
, " Success! Opening root listing.");
1300 // default parameters if the jump fails
1301 m_Directory
[0]->SetPath("");
1303 bool bIsSourceName
= false;
1305 m_rootDir
.GetSources(shares
);
1306 int iIndex
= CUtil::GetMatchingSource(strDestination
, shares
, bIsSourceName
);
1308 #if defined(TARGET_DARWIN_EMBEDDED)
1309 || URIUtils::PathHasParent(strDestination
, "special://envhome/Documents/Inbox/")
1311 || URIUtils::PathHasParent(strDestination
, "special://profile/"))
1313 // set current directory to matching share
1315 if (bIsSourceName
&& iIndex
< (int)shares
.size())
1316 path
= shares
[iIndex
].strPath
;
1318 path
= strDestination
;
1319 URIUtils::RemoveSlashAtEnd(path
);
1320 m_Directory
[0]->SetPath(path
);
1321 CLog::Log(LOGINFO
, " Success! Opened destination path: {}", strDestination
);
1323 // outside call: check the share for connectivity
1324 bCheckShareConnectivity
= Update(0, m_Directory
[0]->GetPath());
1325 if(!bCheckShareConnectivity
)
1326 strCheckSharePath
= m_Directory
[0]->GetPath();
1330 CLog::Log(LOGERROR
, " Failed! Destination parameter ({}) does not match a valid share!",
1336 if (m_Directory
[1]->GetPath() == "?") m_Directory
[1]->SetPath("");
1339 const CFileItem
& CGUIWindowFileManager::CurrentDirectory(int indx
) const
1341 return *m_Directory
[indx
];