[video] fix selection after changing video or extra art
[xbmc.git] / xbmc / windows / GUIWindowFileManager.cpp
blob2093101d080051d8cfd2b838ff308dd2048e69e7
1 /*
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.
7 */
9 #include "GUIWindowFileManager.h"
11 #include "Autorun.h"
12 #include "GUIPassword.h"
13 #include "GUIUserMessages.h"
14 #include "PlayListPlayer.h"
15 #include "ServiceBroker.h"
16 #include "URL.h"
17 #include "Util.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
86 namespace
88 class CGetDirectoryItems : public IRunnable
90 public:
91 CGetDirectoryItems(XFILE::CVirtualDirectory& dir, CURL& url, CFileItemList& items)
92 : m_dir(dir), m_url(url), m_items(items)
95 void Run() override
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;
105 protected:
106 XFILE::CVirtualDirectory &m_dir;
107 CURL m_url;
108 CFileItemList &m_items;
112 CGUIWindowFileManager::CGUIWindowFileManager(void)
113 : CGUIWindow(WINDOW_FILES, "FileManager.xml"),
114 CJobQueue(false,2)
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)
141 int item;
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);
147 return true;
149 if (action.GetID() == ACTION_DELETE_ITEM)
151 if (CanDelete(list))
153 bool bDeselect = SelectItem(list, item);
154 OnDelete(list);
155 if (bDeselect) m_vecItems[list]->Get(item)->Select(false);
157 return true;
159 if (action.GetID() == ACTION_COPY_ITEM)
161 if (CanCopy(list))
163 bool bDeselect = SelectItem(list, item);
164 OnCopy(list);
165 if (bDeselect) m_vecItems[list]->Get(item)->Select(false);
167 return true;
169 if (action.GetID() == ACTION_MOVE_ITEM)
171 if (CanMove(list))
173 bool bDeselect = SelectItem(list, item);
174 OnMove(list);
175 if (bDeselect) m_vecItems[list]->Get(item)->Select(false);
177 return true;
179 if (action.GetID() == ACTION_RENAME_ITEM)
181 if (CanRename(list))
183 bool bDeselect = SelectItem(list, item);
184 OnRename(list);
185 if (bDeselect) m_vecItems[list]->Get(item)->Select(false);
187 return true;
189 if (action.GetID() == ACTION_PARENT_DIR)
191 GoParentFolder(list);
192 return true;
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());
199 #endif
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);
211 return true;
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;
228 return 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()))
243 { //
244 if (IsActive())
245 Update(i, "");
246 else
247 m_Directory[i]->SetPath("");
250 return true;
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);
263 return true;
265 else if (message.GetParam1()==GUI_MSG_UPDATE && IsActive())
267 Refresh();
268 return true;
271 break;
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);
281 OnMessage(msg);
282 break;
284 case GUI_MSG_WINDOW_DEINIT:
286 CGUIWindow::OnMessage(message);
287 ClearFileItems(0);
288 ClearFileItems(1);
289 return true;
291 break;
293 case GUI_MSG_WINDOW_INIT:
295 SetInitialPath(message.GetStringParam());
296 message.SetStringParam("");
298 return CGUIWindow::OnMessage(message);
300 break;
301 case GUI_MSG_CLICKED:
303 int iControl = message.GetSenderId();
305 if (iControl == CONTROL_LEFT_LIST || iControl == CONTROL_RIGHT_LIST) // list/thumb control
307 // get selected item
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)
315 OnMark(list, iItem);
316 if (!CServiceBroker::GetInputManager().IsMouseActive())
318 //move to next item
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);
333 break;
334 // prevent touch/gesture unfocussing ..
335 case GUI_MSG_GESTURE_NOTIFY:
336 case GUI_MSG_UNFOCUS_ALL:
337 return true;
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("");
351 else
352 pItem->SetFileSizeLabel();
354 // Set free space on disc
355 if (pItem->m_bIsShareOrDrive)
357 if (pItem->IsHD())
359 std::error_code ec;
360 auto freeSpace = space(pItem->GetPath(), ec);
361 if (ec.value() == 0)
363 pItem->m_dwSize = freeSpace.free;
364 pItem->SetFileSizeLabel();
367 else if (pItem->IsDVD() && CServiceBroker::GetMediaManager().IsDiscInDrive())
369 std::error_code ec;
370 auto freeSpace = space(pItem->GetPath(), ec);
371 if (ec.value() == 0)
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();
396 if (strDir.empty())
398 SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_LEFT,g_localizeStrings.Get(20108));
400 else
402 SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_LEFT, strDir);
404 strDir = CURL(m_Directory[1]->GetPath()).GetWithoutUserDetails();
405 if (strDir.empty())
407 SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_RIGHT,g_localizeStrings.Get(20108));
409 else
411 SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_RIGHT, strDir);
414 // update the number of items in each list
415 UpdateItemCounts();
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())
431 selectedCount++;
432 selectedSize += item->m_dwSize;
434 totalCount++;
436 std::string items;
437 if (selectedCount > 0)
438 items =
439 StringUtils::Format("{}/{} {} ({})", selectedCount, totalCount,
440 g_localizeStrings.Get(127), StringUtils::SizeToString(selectedSize));
441 else
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)
449 if (m_updating)
451 CLog::Log(LOGWARNING, "CGUIWindowFileManager::Update - updating in progress");
452 return true;
454 CUpdateGuard ug(m_updating);
456 // get selected item
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);
473 CFileItemList items;
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)
478 else
479 Update(iList, ""); // Fallback to root
481 return false;
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);
530 #endif
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);
537 #endif
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);
544 if (pItem->IsHD() &&
545 URIUtils::HasExtension(pItem->GetPath(), ".tbn"))
547 pItem->SetArt("thumb", pItem->GetPath());
550 m_vecItems[iList]->FillInDefaultIcons();
552 OnSort(iList);
553 UpdateButtons();
555 int item = 0;
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)
564 item = i;
565 break;
568 UpdateControl(iList, item);
569 return true;
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());
587 return;
590 if (!pItem->m_bIsFolder && pItem->IsFileFolder(EFILEFOLDER_MASK_ALL))
592 XFILE::IFileDirectory *pFileDirectory = NULL;
593 pFileDirectory = XFILE::CFileDirectoryFactory::Create(pItem->GetURL(), pItem.get(), "");
594 if(pFileDirectory)
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" ) )
610 Refresh();
611 return ;
614 if ( !HaveDiscOrConnection( strPath, iDriveType ) )
615 return ;
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());
630 else
632 OnStart(pItem.get(), "");
633 return ;
635 // UpdateButtons();
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});
652 return;
655 g_application.ProcessAndStartPlaylist(strPlayList, *pPlayList, PLAYLIST::TYPE_MUSIC);
656 return;
658 if (pItem->IsAudio() || pItem->IsVideo())
660 CServiceBroker::GetPlaylistPlayer().Play(std::make_shared<CFileItem>(*pItem), player);
661 return;
663 if (pItem->IsGame())
665 g_application.PlayFile(*pItem, player);
666 return ;
668 #ifdef HAS_PYTHON
669 if (pItem->IsPythonScript())
671 CScriptInvocationManager::GetInstance().ExecuteAsync(pItem->GetPath());
672 return ;
674 #endif
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();
683 slideShow.Reset();
684 slideShow.Add(pItem);
685 slideShow.Select(pItem->GetPath());
687 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW);
688 return;
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);
703 Update(iList, "");
704 CONTROL_SELECT_ITEM(iList + CONTROL_LEFT_LIST, iItem);
705 return false;
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});
714 return false;
717 else
718 return true;
719 return true;
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())
736 // MARK file
737 pItem->Select(!pItem->IsSelected());
741 UpdateItemCounts();
742 // UpdateButtons();
745 void CGUIWindowFileManager::OnCopy(int iList)
747 if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{120}, CVariant{123}))
748 return;
750 AddJob(new CFileOperationJob(CFileOperationJob::ActionCopy,
751 *m_vecItems[iList],
752 m_Directory[1 - iList]->GetPath(),
753 true, 16201, 16202));
756 void CGUIWindowFileManager::OnMove(int iList)
758 if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{121}, CVariant{124}))
759 return;
761 AddJob(new CFileOperationJob(CFileOperationJob::ActionMove,
762 *m_vecItems[iList],
763 m_Directory[1 - iList]->GetPath(),
764 true, 16203, 16204));
767 void CGUIWindowFileManager::OnDelete(int iList)
769 if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{122}, CVariant{125}))
770 return;
772 AddJob(new CFileOperationJob(CFileOperationJob::ActionDelete,
773 *m_vecItems[iList],
774 m_Directory[iList]->GetPath(),
775 true, 16205, 16206));
778 void CGUIWindowFileManager::OnRename(int iList)
780 std::string strFile;
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();
787 break;
791 CFileUtils::RenameFile(strFile);
793 Refresh(iList);
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())
803 pItem->Select(true);
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);
817 Refresh(iList);
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);
828 break;
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())
841 nSel--;
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())
856 nSel--;
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())
864 return -1;
865 CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl + CONTROL_LEFT_LIST);
866 if (OnMessage(msg))
867 return msg.GetParam1();
868 return -1;
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;
912 else
913 strHistoryString = strLabel;
915 else
917 // Other items in virtual directory
918 strHistoryString = pItem->GetLabel() + pItem->GetPath();
919 URIUtils::RemoveSlashAtEnd(strHistoryString);
922 else
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))
937 return false;
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;
949 return true;
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;
962 return true;
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;
970 return true;
973 bool CGUIWindowFileManager::CanDelete(int iList)
975 if (m_Directory[iList]->IsVirtualDirectoryRoot()) return false;
976 if (m_Directory[iList]->IsReadOnly()) return false;
977 return true;
980 bool CGUIWindowFileManager::CanNewFolder(int iList)
982 if (m_Directory[iList]->IsVirtualDirectoryRoot()) return false;
983 if (m_Directory[iList]->IsReadOnly()) return false;
984 return true;
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
1007 float posX = 200;
1008 float posY = 100;
1009 const CGUIControl *pList = GetControl(CONTROL_LEFT_LIST + list);
1010 if (pList)
1012 posX = pList->GetXPosition() + pList->GetWidth() / 2;
1013 posY = pList->GetYPosition() + pList->GetHeight() / 2;
1016 CFileItemPtr pItem = m_vecItems[list]->Get(item);
1017 if (!pItem.get())
1018 return;
1020 if (m_Directory[list]->IsVirtualDirectoryRoot())
1022 if (item < 0)
1023 { //! @todo We should add the option here for shares to be added if there aren't any
1024 return ;
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())
1032 Refresh();
1033 else
1034 Refresh(list);
1035 return ;
1037 pItem->Select(false);
1038 return ;
1041 const CPlayerCoreFactory &playerCoreFactory = CServiceBroker::GetPlayerCoreFactory();
1043 // popup the context menu
1045 bool showEntry = false;
1046 if (item >= m_vecItems[list]->Size()) item = -1;
1047 if (item >= 0)
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;
1056 if (item >= 0)
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)
1089 OnSelectAll(list);
1090 bDeselect=false;
1092 if (btnid == CONTROL_BTNFAVOURITES)
1094 CServiceBroker::GetFavouritesService().AddOrRemove(*pItem.get(), GetID());
1095 return;
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)
1106 OnRename(list);
1107 if (btnid == CONTROL_BTNDELETE)
1108 OnDelete(list);
1109 if (btnid == CONTROL_BTNCOPY)
1110 OnCopy(list);
1111 if (btnid == CONTROL_BTNMOVE)
1112 OnMove(list);
1113 if (btnid == CONTROL_BTNNEWFOLDER)
1114 OnNewFolder(list);
1115 if (btnid == CONTROL_BTNCALCSIZE)
1117 // setup the progress dialog, and show it
1118 CGUIDialogProgress *progress = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS);
1119 if (progress)
1121 progress->SetHeading(CVariant{13394});
1122 for (int i=0; i < 3; i++)
1123 progress->SetLine(i, CVariant{""});
1124 progress->Open();
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));
1139 else
1140 pItem2->SetFileSizeLabel();
1144 if (progress)
1145 progress->Close();
1147 if (btnid == CONTROL_BTNSWITCHMEDIA)
1149 CGUIDialogContextMenu::SwitchMedia("files", m_vecItems[list]->GetPath());
1150 return;
1152 if (btnid == CONTROL_BTNCANCELJOB)
1153 CancelJobs();
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);
1174 return true;
1176 return false;
1179 // recursively calculates the selected folder size
1180 int64_t CGUIWindowFileManager::CalculateFolderSize(const std::string &strDirectory, CGUIDialogProgress *pProgress)
1182 const CURL pathToUrl(strDirectory);
1183 if (pProgress)
1184 { // update our progress control
1185 pProgress->Progress();
1186 pProgress->SetLine(1, strDirectory);
1187 if (pProgress->IsCanceled())
1188 return -1;
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;
1204 else // file
1205 totalSize += items[i]->m_dwSize;
1207 return totalSize;
1210 void CGUIWindowFileManager::OnJobComplete(unsigned int jobID, bool success, CJob *job)
1212 if(!success)
1214 CFileOperationJob* fileJob = static_cast<CFileOperationJob*>(job);
1215 HELPERS::ShowOKDialogLines(CVariant{fileJob->GetHeading()},
1216 CVariant{fileJob->GetLine()}, CVariant{16200}, CVariant{0});
1219 if (IsActive())
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
1237 else
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
1257 else if (!bResult0)
1259 ShowShareErrorMessage(m_Directory[0]); //show the error message after window is loaded!
1260 Update(0, ""); // reset view to root
1263 if (!bResult1)
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())
1288 // open root
1289 if (StringUtils::EqualsNoCase(strDestination, "$ROOT"))
1291 m_Directory[0]->SetPath("");
1292 CLog::Log(LOGINFO, " Success! Opening root listing.");
1294 else
1296 // default parameters if the jump fails
1297 m_Directory[0]->SetPath("");
1299 bool bIsSourceName = false;
1300 VECSOURCES shares;
1301 m_rootDir.GetSources(shares);
1302 int iIndex = CUtil::GetMatchingSource(strDestination, shares, bIsSourceName);
1303 if (iIndex > -1
1304 #if defined(TARGET_DARWIN_EMBEDDED)
1305 || URIUtils::PathHasParent(strDestination, "special://envhome/Documents/Inbox/")
1306 #endif
1307 || URIUtils::PathHasParent(strDestination, "special://profile/"))
1309 // set current directory to matching share
1310 std::string path;
1311 if (bIsSourceName && iIndex < (int)shares.size())
1312 path = shares[iIndex].strPath;
1313 else
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();
1324 else
1326 CLog::Log(LOGERROR, " Failed! Destination parameter ({}) does not match a valid share!",
1327 strDestination);
1332 if (m_Directory[1]->GetPath() == "?") m_Directory[1]->SetPath("");
1335 const CFileItem& CGUIWindowFileManager::CurrentDirectory(int indx) const
1337 return *m_Directory[indx];