2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
22 #include "GUIWindowFileManager.h"
23 #include "Application.h"
24 #include "ApplicationMessenger.h"
26 #include "filesystem/Directory.h"
27 #include "filesystem/ZipManager.h"
28 #include "filesystem/FileDirectoryFactory.h"
29 #include "dialogs/GUIDialogContextMenu.h"
30 #include "guilib/GUIListContainer.h"
31 #include "dialogs/GUIDialogMediaSource.h"
32 #include "GUIPassword.h"
33 #include "GUIUserMessages.h"
35 #include "interfaces/python/XBPython.h"
37 #include "pictures/GUIWindowSlideShow.h"
38 #include "playlists/PlayListFactory.h"
39 #include "network/Network.h"
40 #include "guilib/GUIWindowManager.h"
41 #include "guilib/Key.h"
42 #include "dialogs/GUIDialogOK.h"
43 #include "dialogs/GUIDialogYesNo.h"
44 #include "guilib/GUIKeyboardFactory.h"
45 #include "dialogs/GUIDialogProgress.h"
46 #include "dialogs/GUIDialogExtendedProgressBar.h"
47 #include "filesystem/FavouritesDirectory.h"
48 #include "playlists/PlayList.h"
49 #include "utils/AsyncFileCopy.h"
50 #include "storage/MediaManager.h"
51 #include "settings/AdvancedSettings.h"
52 #include "settings/MediaSourceSettings.h"
53 #include "settings/Settings.h"
54 #include "input/MouseStat.h"
55 #include "guilib/LocalizeStrings.h"
56 #include "utils/StringUtils.h"
57 #include "utils/log.h"
59 #include "utils/JobManager.h"
60 #include "utils/FileOperationJob.h"
61 #include "utils/FileUtils.h"
62 #include "utils/URIUtils.h"
67 using namespace XFILE;
68 using namespace PLAYLIST;
72 #define ACTION_DELETE 3
73 #define ACTION_CREATEFOLDER 4
74 #define ACTION_DELETEFOLDER 5
76 #define CONTROL_BTNVIEWASICONS 2
77 #define CONTROL_BTNSORTBY 3
78 #define CONTROL_BTNSORTASC 4
79 #define CONTROL_BTNNEWFOLDER 6
80 #define CONTROL_BTNSELECTALL 7
81 #define CONTROL_BTNCOPY 10
82 #define CONTROL_BTNMOVE 11
83 #define CONTROL_BTNDELETE 8
84 #define CONTROL_BTNRENAME 9
86 #define CONTROL_NUMFILES_LEFT 12
87 #define CONTROL_NUMFILES_RIGHT 13
89 #define CONTROL_LEFT_LIST 20
90 #define CONTROL_RIGHT_LIST 21
92 #define CONTROL_CURRENTDIRLABEL_LEFT 101
93 #define CONTROL_CURRENTDIRLABEL_RIGHT 102
95 CGUIWindowFileManager::CGUIWindowFileManager(void)
96 : CGUIWindow(WINDOW_FILES, "FileManager.xml"),
99 m_Directory[0] = new CFileItem;
100 m_Directory[1] = new CFileItem;
101 m_vecItems[0] = new CFileItemList;
102 m_vecItems[1] = new CFileItemList;
103 m_Directory[0]->SetPath("?");
104 m_Directory[1]->SetPath("?");
105 m_Directory[0]->m_bIsFolder = true;
106 m_Directory[1]->m_bIsFolder = true;
107 bCheckShareConnectivity = true;
108 m_loadType = KEEP_IN_MEMORY;
111 CGUIWindowFileManager::~CGUIWindowFileManager(void)
113 delete m_Directory[0];
114 delete m_Directory[1];
115 delete m_vecItems[0];
116 delete m_vecItems[1];
119 bool CGUIWindowFileManager::OnAction(const CAction &action)
121 int list = GetFocusedList();
122 if (list >= 0 && list <= 1)
126 // the non-contextual menu can be called at any time
127 if (action.GetID() == ACTION_CONTEXT_MENU && m_vecItems[list]->Size() == 0)
129 OnPopupMenu(list,-1, false);
132 if (action.GetID() == ACTION_DELETE_ITEM)
136 bool bDeselect = SelectItem(list, item);
138 if (bDeselect) m_vecItems[list]->Get(item)->Select(false);
142 if (action.GetID() == ACTION_COPY_ITEM)
146 bool bDeselect = SelectItem(list, item);
148 if (bDeselect) m_vecItems[list]->Get(item)->Select(false);
152 if (action.GetID() == ACTION_MOVE_ITEM)
156 bool bDeselect = SelectItem(list, item);
158 if (bDeselect) m_vecItems[list]->Get(item)->Select(false);
162 if (action.GetID() == ACTION_RENAME_ITEM)
166 bool bDeselect = SelectItem(list, item);
168 if (bDeselect) m_vecItems[list]->Get(item)->Select(false);
172 if (action.GetID() == ACTION_PARENT_DIR)
174 GoParentFolder(list);
177 if (action.GetID() == ACTION_PLAYER_PLAY)
180 if (m_vecItems[list]->Get(GetSelectedItem(list))->IsDVD())
181 return MEDIA_DETECT::CAutorun::PlayDiscAskResume(m_vecItems[list]->Get(GetSelectedItem(list))->GetPath());
185 return CGUIWindow::OnAction(action);
188 bool CGUIWindowFileManager::OnBack(int actionID)
190 int list = GetFocusedList();
191 if (list >= 0 && list <= 1 && actionID == ACTION_NAV_BACK && !m_vecItems[list]->IsVirtualDirectoryRoot())
193 GoParentFolder(list);
196 return CGUIWindow::OnBack(actionID);
199 bool CGUIWindowFileManager::OnMessage(CGUIMessage& message)
201 switch ( message.GetMessage() )
203 case GUI_MSG_NOTIFY_ALL:
204 { // Message is received even if window is inactive
205 if (message.GetParam1() == GUI_MSG_WINDOW_RESET)
207 m_Directory[0]->SetPath("?");
208 m_Directory[1]->SetPath("?");
209 m_Directory[0]->m_bIsFolder = true;
210 m_Directory[1]->m_bIsFolder = true;
214 // handle removable media
215 if (message.GetParam1() == GUI_MSG_REMOVED_MEDIA)
217 for (int i = 0; i < 2; i++)
219 if (m_Directory[i]->IsVirtualDirectoryRoot() && IsActive())
221 int iItem = GetSelectedItem(i);
222 Update(i, m_Directory[i]->GetPath());
223 CONTROL_SELECT_ITEM(CONTROL_LEFT_LIST + i, iItem);
225 else if (m_Directory[i]->IsRemovable() && !m_rootDir.IsInSource(m_Directory[i]->GetPath()))
230 m_Directory[i]->SetPath("");
235 else if (message.GetParam1()==GUI_MSG_UPDATE_SOURCES)
236 { // State of the sources changed, so update our view
237 for (int i = 0; i < 2; i++)
239 if (m_Directory[i]->IsVirtualDirectoryRoot() && IsActive())
241 int iItem = GetSelectedItem(i);
242 Update(i, m_Directory[i]->GetPath());
243 CONTROL_SELECT_ITEM(CONTROL_LEFT_LIST + i, iItem);
248 else if (message.GetParam1()==GUI_MSG_UPDATE && IsActive())
255 case GUI_MSG_PLAYBACK_STARTED:
256 case GUI_MSG_PLAYBACK_ENDED:
257 case GUI_MSG_PLAYBACK_STOPPED:
258 case GUI_MSG_PLAYLIST_CHANGED:
259 case GUI_MSG_PLAYLISTPLAYER_STOPPED:
260 case GUI_MSG_PLAYLISTPLAYER_STARTED:
261 case GUI_MSG_PLAYLISTPLAYER_CHANGED:
262 { // send a notify all to all controls on this window
263 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_REFRESH_LIST);
267 case GUI_MSG_WINDOW_DEINIT:
269 CGUIWindow::OnMessage(message);
276 case GUI_MSG_WINDOW_INIT:
278 SetInitialPath(message.GetStringParam());
279 message.SetStringParam("");
281 return CGUIWindow::OnMessage(message);
284 case GUI_MSG_CLICKED:
286 int iControl = message.GetSenderId();
288 if (iControl == CONTROL_LEFT_LIST || iControl == CONTROL_RIGHT_LIST) // list/thumb control
291 int list = iControl - CONTROL_LEFT_LIST;
292 int iItem = GetSelectedItem(list);
293 int iAction = message.GetParam1();
295 // iItem is checked for validity inside these routines
296 if (iAction == ACTION_HIGHLIGHT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
299 if (!g_Mouse.IsActive())
302 CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), iControl, iItem + 1);
303 g_windowManager.SendMessage(msg);
306 else if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_DOUBLE_CLICK)
308 OnClick(list, iItem);
310 else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
312 OnPopupMenu(list, iItem);
318 return CGUIWindow::OnMessage(message);
321 void CGUIWindowFileManager::OnSort(int iList)
323 // always sort the list by label in ascending order
324 for (int i = 0; i < m_vecItems[iList]->Size(); i++)
326 CFileItemPtr pItem = m_vecItems[iList]->Get(i);
327 if (pItem->m_bIsFolder && (!pItem->m_dwSize || pItem->GetPath().Equals("add")))
328 pItem->SetLabel2("");
330 pItem->SetFileSizeLabel();
332 // Set free space on disc
333 if (pItem->m_bIsShareOrDrive)
337 ULARGE_INTEGER ulBytesFree;
338 if (GetDiskFreeSpaceEx(pItem->GetPath().c_str(), &ulBytesFree, NULL, NULL))
340 pItem->m_dwSize = ulBytesFree.QuadPart;
341 pItem->SetFileSizeLabel();
344 else if (pItem->IsDVD() && g_mediaManager.IsDiscInDrive())
346 ULARGE_INTEGER ulBytesTotal;
347 if (GetDiskFreeSpaceEx(pItem->GetPath().c_str(), NULL, &ulBytesTotal, NULL))
349 pItem->m_dwSize = ulBytesTotal.QuadPart;
350 pItem->SetFileSizeLabel();
353 } // if (pItem->m_bIsShareOrDrive)
357 m_vecItems[iList]->Sort(SORT_METHOD_LABEL, SortOrderAscending);
360 void CGUIWindowFileManager::ClearFileItems(int iList)
362 CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), iList + CONTROL_LEFT_LIST);
363 g_windowManager.SendMessage(msg);
365 m_vecItems[iList]->Clear(); // will clean up everything
368 void CGUIWindowFileManager::UpdateButtons()
370 // update our current directory labels
371 CStdString strDir = CURL(m_Directory[0]->GetPath()).GetWithoutUserDetails();
372 if (strDir.IsEmpty())
374 SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_LEFT,g_localizeStrings.Get(20108));
378 SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_LEFT, strDir);
380 strDir = CURL(m_Directory[1]->GetPath()).GetWithoutUserDetails();
381 if (strDir.IsEmpty())
383 SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_RIGHT,g_localizeStrings.Get(20108));
387 SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_RIGHT, strDir);
390 // update the number of items in each list
394 void CGUIWindowFileManager::UpdateItemCounts()
396 for (int i = 0; i < 2; i++)
398 unsigned int selectedCount = 0;
399 unsigned int totalCount = 0;
400 int64_t selectedSize = 0;
401 int64_t totalSize = 0;
402 for (int j = 0; j < m_vecItems[i]->Size(); j++)
404 CFileItemPtr item = m_vecItems[i]->Get(j);
405 if (item->IsParentFolder()) continue;
406 if (item->IsSelected())
409 selectedSize += item->m_dwSize;
412 totalSize += item->m_dwSize;
415 if (selectedCount > 0)
416 items.Format("%i/%i %s (%s)", selectedCount, totalCount, g_localizeStrings.Get(127).c_str(), StringUtils::SizeToString(selectedSize).c_str());
418 items.Format("%i %s", totalCount, g_localizeStrings.Get(127).c_str());
419 SET_CONTROL_LABEL(CONTROL_NUMFILES_LEFT + i, items);
423 bool CGUIWindowFileManager::Update(int iList, const CStdString &strDirectory)
426 int iItem = GetSelectedItem(iList);
427 CStdString strSelectedItem = "";
429 if (iItem >= 0 && iItem < (int)m_vecItems[iList]->Size())
431 CFileItemPtr pItem = m_vecItems[iList]->Get(iItem);
432 if (!pItem->IsParentFolder())
434 GetDirectoryHistoryString(pItem.get(), strSelectedItem);
435 m_history[iList].SetSelectedItem(strSelectedItem, m_Directory[iList]->GetPath());
439 CStdString strOldDirectory=m_Directory[iList]->GetPath();
440 m_Directory[iList]->SetPath(strDirectory);
443 if (!GetDirectory(iList, m_Directory[iList]->GetPath(), items))
445 if (strDirectory != strOldDirectory && GetDirectory(iList, strOldDirectory, items))
446 m_Directory[iList]->SetPath(strOldDirectory); // Fallback to old (previous) path)
448 Update(iList, ""); // Fallback to root
453 m_history[iList].SetSelectedItem(strSelectedItem, strOldDirectory);
455 ClearFileItems(iList);
457 m_vecItems[iList]->Append(items);
458 m_vecItems[iList]->SetPath(items.GetPath());
460 CStdString strParentPath;
461 URIUtils::GetParentPath(strDirectory, strParentPath);
462 if (strDirectory.IsEmpty() && (m_vecItems[iList]->Size() == 0 || CSettings::Get().GetBool("filelists.showaddsourcebuttons")))
463 { // add 'add source button'
464 CStdString strLabel = g_localizeStrings.Get(1026);
465 CFileItemPtr pItem(new CFileItem(strLabel));
466 pItem->SetPath("add");
467 pItem->SetIconImage("DefaultAddSource.png");
468 pItem->SetLabel(strLabel);
469 pItem->SetLabelPreformated(true);
470 pItem->m_bIsFolder = true;
471 pItem->SetSpecialSort(SortSpecialOnBottom);
472 m_vecItems[iList]->Add(pItem);
474 else if (items.IsEmpty() || CSettings::Get().GetBool("filelists.showparentdiritems"))
476 CFileItemPtr pItem(new CFileItem(".."));
477 pItem->SetPath(m_rootDir.IsSource(strDirectory) ? "" : strParentPath);
478 pItem->m_bIsFolder = true;
479 pItem->m_bIsShareOrDrive = false;
480 m_vecItems[iList]->AddFront(pItem, 0);
483 m_strParentPath[iList] = (m_rootDir.IsSource(strDirectory) ? "" : strParentPath);
485 if (strDirectory.IsEmpty())
487 CFileItemPtr pItem(new CFileItem("special://profile/", true));
488 pItem->SetLabel(g_localizeStrings.Get(20070));
489 pItem->SetArt("thumb", "DefaultFolder.png");
490 pItem->SetLabelPreformated(true);
491 m_vecItems[iList]->Add(pItem);
494 // if we have a .tbn file, use itself as the thumb
495 for (int i = 0; i < (int)m_vecItems[iList]->Size(); i++)
497 CFileItemPtr pItem = m_vecItems[iList]->Get(i);
499 URIUtils::HasExtension(pItem->GetPath(), ".tbn"))
501 pItem->SetArt("thumb", pItem->GetPath());
504 m_vecItems[iList]->FillInDefaultIcons();
510 strSelectedItem = m_history[iList].GetSelectedItem(m_Directory[iList]->GetPath());
511 for (int i = 0; i < m_vecItems[iList]->Size(); ++i)
513 CFileItemPtr pItem = m_vecItems[iList]->Get(i);
514 CStdString strHistory;
515 GetDirectoryHistoryString(pItem.get(), strHistory);
516 if (strHistory == strSelectedItem)
522 UpdateControl(iList, item);
527 void CGUIWindowFileManager::OnClick(int iList, int iItem)
529 if ( iList < 0 || iList >= 2) return ;
530 if ( iItem < 0 || iItem >= m_vecItems[iList]->Size() ) return ;
532 CFileItemPtr pItem = m_vecItems[iList]->Get(iItem);
533 if (pItem->GetPath() == "add" && pItem->GetLabel() == g_localizeStrings.Get(1026)) // 'add source button' in empty root
535 if (CGUIDialogMediaSource::ShowAndAddMediaSource("files"))
537 m_rootDir.SetSources(*CMediaSourceSettings::Get().GetSources("files"));
538 Update(0,m_Directory[0]->GetPath());
539 Update(1,m_Directory[1]->GetPath());
544 if (!pItem->m_bIsFolder && pItem->IsFileFolder(EFILEFOLDER_MASK_ALL))
546 XFILE::IFileDirectory *pFileDirectory = NULL;
547 pFileDirectory = XFILE::CFileDirectoryFactory::Create(pItem->GetPath(), pItem.get(), "");
549 pItem->m_bIsFolder = true;
550 else if(pItem->m_bIsFolder)
551 pItem->m_bIsFolder = false;
552 delete pFileDirectory;
555 if (pItem->m_bIsFolder)
557 // save path + drive type because of the possible refresh
558 CStdString strPath = pItem->GetPath();
559 int iDriveType = pItem->m_iDriveType;
560 if ( pItem->m_bIsShareOrDrive )
562 if ( !g_passwordManager.IsItemUnlocked( pItem.get(), "files" ) )
568 if ( !HaveDiscOrConnection( strPath, iDriveType ) )
571 if (!Update(iList, strPath))
572 ShowShareErrorMessage(pItem.get());
574 else if (pItem->IsZIP() || pItem->IsCBZ()) // mount zip archive
576 CStdString strArcivedPath;
577 URIUtils::CreateArchivePath(strArcivedPath, "zip", pItem->GetPath(), "");
578 Update(iList, strArcivedPath);
580 else if (pItem->IsRAR() || pItem->IsCBR())
582 CStdString strArcivedPath;
583 URIUtils::CreateArchivePath(strArcivedPath, "rar", pItem->GetPath(), "");
584 Update(iList, strArcivedPath);
588 OnStart(pItem.get());
594 // TODO 2.0: Can this be removed, or should we run without the "special" file directories while
595 // in filemanager view.
596 void CGUIWindowFileManager::OnStart(CFileItem *pItem)
598 // start playlists from file manager
599 if (pItem->IsPlayList())
601 CStdString strPlayList = pItem->GetPath();
602 auto_ptr<CPlayList> pPlayList (CPlayListFactory::Create(strPlayList));
603 if (NULL != pPlayList.get())
605 if (!pPlayList->Load(strPlayList))
607 CGUIDialogOK::ShowAndGetInput(6, 0, 477, 0);
611 g_application.ProcessAndStartPlaylist(strPlayList, *pPlayList, PLAYLIST_MUSIC);
614 if (pItem->IsAudio() || pItem->IsVideo())
616 g_application.PlayFile(*pItem);
620 if (pItem->IsPythonScript())
622 g_pythonParser.evalFile(pItem->GetPath().c_str(),ADDON::AddonPtr());
626 if (pItem->IsPicture())
628 CGUIWindowSlideShow *pSlideShow = (CGUIWindowSlideShow *)g_windowManager.GetWindow(WINDOW_SLIDESHOW);
631 if (g_application.IsPlayingVideo())
632 g_application.StopPlaying();
635 pSlideShow->Add(pItem);
636 pSlideShow->Select(pItem->GetPath());
638 g_windowManager.ActivateWindow(WINDOW_SLIDESHOW);
642 bool CGUIWindowFileManager::HaveDiscOrConnection( CStdString& strPath, int iDriveType )
644 if ( iDriveType == CMediaSource::SOURCE_TYPE_DVD )
646 if ( !g_mediaManager.IsDiscInDrive(strPath) )
648 CGUIDialogOK::ShowAndGetInput(218, 219, 0, 0);
649 int iList = GetFocusedList();
650 int iItem = GetSelectedItem(iList);
652 CONTROL_SELECT_ITEM(iList + CONTROL_LEFT_LIST, iItem);
656 else if ( iDriveType == CMediaSource::SOURCE_TYPE_REMOTE )
658 // TODO: Handle not connected to a remote share
659 if ( !g_application.getNetwork().IsConnected() )
661 CGUIDialogOK::ShowAndGetInput(220, 221, 0, 0);
670 void CGUIWindowFileManager::UpdateControl(int iList, int item)
672 CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), iList + CONTROL_LEFT_LIST, item, 0, m_vecItems[iList]);
673 g_windowManager.SendMessage(msg);
676 void CGUIWindowFileManager::OnMark(int iList, int iItem)
678 CFileItemPtr pItem = m_vecItems[iList]->Get(iItem);
680 if (!pItem->m_bIsShareOrDrive)
682 if (!pItem->IsParentFolder())
685 pItem->Select(!pItem->IsSelected());
693 void CGUIWindowFileManager::OnCopy(int iList)
695 if (!CGUIDialogYesNo::ShowAndGetInput(120, 123, 0, 0))
698 AddJob(new CFileOperationJob(CFileOperationJob::ActionCopy,
700 m_Directory[1 - iList]->GetPath(),
701 true, 16201, 16202));
704 void CGUIWindowFileManager::OnMove(int iList)
706 if (!CGUIDialogYesNo::ShowAndGetInput(121, 124, 0, 0))
709 AddJob(new CFileOperationJob(CFileOperationJob::ActionMove,
711 m_Directory[1 - iList]->GetPath(),
712 true, 16203, 16204));
715 void CGUIWindowFileManager::OnDelete(int iList)
717 if (!CGUIDialogYesNo::ShowAndGetInput(122, 125, 0, 0))
720 AddJob(new CFileOperationJob(CFileOperationJob::ActionDelete,
722 m_Directory[iList]->GetPath(),
723 true, 16205, 16206));
726 void CGUIWindowFileManager::OnRename(int iList)
729 for (int i = 0; i < m_vecItems[iList]->Size();++i)
731 CFileItemPtr pItem = m_vecItems[iList]->Get(i);
732 if (pItem->IsSelected())
734 strFile = pItem->GetPath();
739 CFileUtils::RenameFile(strFile);
744 void CGUIWindowFileManager::OnSelectAll(int iList)
746 for (int i = 0; i < m_vecItems[iList]->Size();++i)
748 CFileItemPtr pItem = m_vecItems[iList]->Get(i);
749 if (!pItem->IsParentFolder())
756 void CGUIWindowFileManager::OnNewFolder(int iList)
758 CStdString strNewFolder = "";
759 if (CGUIKeyboardFactory::ShowAndGetInput(strNewFolder, g_localizeStrings.Get(16014), false))
761 CStdString strNewPath = m_Directory[iList]->GetPath();
762 URIUtils::AddSlashAtEnd(strNewPath);
763 strNewPath += strNewFolder;
764 CDirectory::Create(strNewPath);
767 // select the new folder
768 for (int i=0; i<m_vecItems[iList]->Size(); ++i)
770 CFileItemPtr pItem=m_vecItems[iList]->Get(i);
771 CStdString strPath=pItem->GetPath();
772 URIUtils::RemoveSlashAtEnd(strPath);
773 if (strPath==strNewPath)
775 CONTROL_SELECT_ITEM(iList + CONTROL_LEFT_LIST, i);
782 void CGUIWindowFileManager::Refresh(int iList)
784 int nSel = GetSelectedItem(iList);
785 // update the list views
786 Update(iList, m_Directory[iList]->GetPath());
788 while (nSel > m_vecItems[iList]->Size())
791 CONTROL_SELECT_ITEM(iList + CONTROL_LEFT_LIST, nSel);
795 void CGUIWindowFileManager::Refresh()
797 int iList = GetFocusedList();
798 int nSel = GetSelectedItem(iList);
799 // update the list views
800 Update(0, m_Directory[0]->GetPath());
801 Update(1, m_Directory[1]->GetPath());
803 while (nSel > (int)m_vecItems[iList]->Size())
806 CONTROL_SELECT_ITEM(iList + CONTROL_LEFT_LIST, nSel);
809 int CGUIWindowFileManager::GetSelectedItem(int iControl)
811 if (iControl < 0 || iControl > 1) return -1;
812 CGUIListContainer *pControl = (CGUIListContainer *)GetControl(iControl + CONTROL_LEFT_LIST);
813 if (!pControl || !m_vecItems[iControl]->Size()) return -1;
814 return pControl->GetSelectedItem();
817 void CGUIWindowFileManager::GoParentFolder(int iList)
819 CURL url(m_Directory[iList]->GetPath());
820 if ((url.GetProtocol() == "rar") || (url.GetProtocol() == "zip"))
822 // check for step-below, if, unmount rar
823 if (url.GetFileName().IsEmpty())
824 if (url.GetProtocol() == "zip")
825 g_ZipManager.release(m_Directory[iList]->GetPath()); // release resources
828 CStdString strPath(m_strParentPath[iList]), strOldPath(m_Directory[iList]->GetPath());
829 Update(iList, strPath);
832 /// \brief Build a directory history string
833 /// \param pItem Item to build the history string from
834 /// \param strHistoryString History string build as return value
835 void CGUIWindowFileManager::GetDirectoryHistoryString(const CFileItem* pItem, CStdString& strHistoryString)
837 if (pItem->m_bIsShareOrDrive)
839 // We are in the virtual directory
841 // History string of the DVD drive
842 // must be handel separately
843 if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_DVD)
845 // Remove disc label from item label
846 // and use as history string, m_strPath
847 // can change for new discs
848 CStdString strLabel = pItem->GetLabel();
849 int nPosOpen = strLabel.Find('(');
850 int nPosClose = strLabel.ReverseFind(')');
851 if (nPosOpen > -1 && nPosClose > -1 && nPosClose > nPosOpen)
853 strLabel.Delete(nPosOpen + 1, (nPosClose) - (nPosOpen + 1));
854 strHistoryString = strLabel;
857 strHistoryString = strLabel;
861 // Other items in virtual directory
862 strHistoryString = pItem->GetLabel() + pItem->GetPath();
863 URIUtils::RemoveSlashAtEnd(strHistoryString);
868 // Normal directory items
869 strHistoryString = pItem->GetPath();
870 URIUtils::RemoveSlashAtEnd(strHistoryString);
874 bool CGUIWindowFileManager::GetDirectory(int iList, const CStdString &strDirectory, CFileItemList &items)
876 return m_rootDir.GetDirectory(strDirectory,items,false);
879 bool CGUIWindowFileManager::CanRename(int iList)
881 // TODO: Renaming of shares (requires writing to xboxmediacenter.xml)
882 // this might be able to be done via the webserver code stuff...
883 if (m_Directory[iList]->IsVirtualDirectoryRoot()) return false;
884 if (m_Directory[iList]->IsReadOnly()) return false;
889 bool CGUIWindowFileManager::CanCopy(int iList)
891 // can't copy if the destination is not writeable, or if the source is a share!
892 // TODO: Perhaps if the source is removeable media (DVD/CD etc.) we could
893 // put ripping/backup in here.
894 if (!CUtil::SupportsReadFileOperations(m_Directory[iList]->GetPath())) return false;
895 if (m_Directory[iList]->IsVirtualDirectoryRoot()) return false;
896 if (m_Directory[1 - iList]->IsVirtualDirectoryRoot()) return false;
897 if (m_Directory[iList]->IsVirtualDirectoryRoot()) return false;
898 if (m_Directory[1 -iList]->IsReadOnly()) return false;
902 bool CGUIWindowFileManager::CanMove(int iList)
904 // can't move if the destination is not writeable, or if the source is a share or not writeable!
905 if (m_Directory[0]->IsVirtualDirectoryRoot() || m_Directory[0]->IsReadOnly()) return false;
906 if (m_Directory[1]->IsVirtualDirectoryRoot() || m_Directory[1]->IsReadOnly()) return false;
910 bool CGUIWindowFileManager::CanDelete(int iList)
912 if (m_Directory[iList]->IsVirtualDirectoryRoot()) return false;
913 if (m_Directory[iList]->IsReadOnly()) return false;
917 bool CGUIWindowFileManager::CanNewFolder(int iList)
919 if (m_Directory[iList]->IsVirtualDirectoryRoot()) return false;
920 if (m_Directory[iList]->IsReadOnly()) return false;
924 int CGUIWindowFileManager::NumSelected(int iList)
926 int iSelectedItems = 0;
927 for (int iItem = 0; iItem < m_vecItems[iList]->Size(); ++iItem)
929 if (m_vecItems[iList]->Get(iItem)->IsSelected()) iSelectedItems++;
931 return iSelectedItems;
934 int CGUIWindowFileManager::GetFocusedList() const
936 return GetFocusedControlID() - CONTROL_LEFT_LIST;
939 void CGUIWindowFileManager::OnPopupMenu(int list, int item, bool bContextDriven /* = true */)
941 if (list < 0 || list >= 2) return ;
942 bool bDeselect = SelectItem(list, item);
943 // calculate the position for our menu
946 const CGUIControl *pList = GetControl(CONTROL_LEFT_LIST + list);
949 posX = pList->GetXPosition() + pList->GetWidth() / 2;
950 posY = pList->GetYPosition() + pList->GetHeight() / 2;
953 CFileItemPtr pItem = m_vecItems[list]->Get(item);
957 if (m_Directory[list]->IsVirtualDirectoryRoot())
960 { // TODO: We should add the option here for shares to be added if there aren't any
964 // and do the popup menu
965 if (CGUIDialogContextMenu::SourcesMenu("files", pItem, posX, posY))
967 m_rootDir.SetSources(*CMediaSourceSettings::Get().GetSources("files"));
968 if (m_Directory[1 - list]->IsVirtualDirectoryRoot())
974 pItem->Select(false);
977 // popup the context menu
979 bool showEntry = false;
980 if (item >= m_vecItems[list]->Size()) item = -1;
982 showEntry=(!pItem->IsParentFolder() || (pItem->IsParentFolder() && m_vecItems[list]->GetSelectedCount()>0));
984 // determine available players
985 VECPLAYERCORES vecCores;
986 CPlayerCoreFactory::Get().GetPlayers(*pItem, vecCores);
988 // add the needed buttons
989 CContextButtons choices;
992 choices.Add(1, 188); // SelectAll
993 if (!pItem->IsParentFolder())
994 choices.Add(2, XFILE::CFavouritesDirectory::IsFavourite(pItem.get(), GetID()) ? 14077 : 14076); // Add/Remove Favourite
995 if (vecCores.size() > 1)
996 choices.Add(3, 15213); // Play Using...
997 if (CanRename(list) && !pItem->IsParentFolder())
998 choices.Add(4, 118); // Rename
999 if (CanDelete(list) && showEntry)
1000 choices.Add(5, 117); // Delete
1001 if (CanCopy(list) && showEntry)
1002 choices.Add(6, 115); // Copy
1003 if (CanMove(list) && showEntry)
1004 choices.Add(7, 116); // Move
1006 if (CanNewFolder(list))
1007 choices.Add(8, 20309); // New Folder
1008 if (item >= 0 && pItem->m_bIsFolder && !pItem->IsParentFolder())
1009 choices.Add(9, 13393); // Calculate Size
1010 choices.Add(10, 5); // Settings
1011 choices.Add(11, 20128); // Go To Root
1012 choices.Add(12, 523); // switch media
1013 if (CJobManager::GetInstance().IsProcessing("filemanager"))
1014 choices.Add(13, 167);
1016 int btnid = CGUIDialogContextMenu::ShowAndGetChoice(choices);
1024 XFILE::CFavouritesDirectory::AddOrRemove(pItem.get(), GetID());
1029 VECPLAYERCORES vecCores;
1030 CPlayerCoreFactory::Get().GetPlayers(*pItem, vecCores);
1031 g_application.m_eForcedNextPlayer = CPlayerCoreFactory::Get().SelectPlayerDialog(vecCores);
1032 if (g_application.m_eForcedNextPlayer != EPC_NONE)
1033 OnStart(pItem.get());
1047 // setup the progress dialog, and show it
1048 CGUIDialogProgress *progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
1051 progress->SetHeading(13394);
1052 for (int i=0; i < 3; i++)
1053 progress->SetLine(i, "");
1054 progress->StartModal();
1057 // Calculate folder size for each selected item
1058 for (int i=0; i<m_vecItems[list]->Size(); ++i)
1060 CFileItemPtr pItem2=m_vecItems[list]->Get(i);
1061 if (pItem2->m_bIsFolder && pItem2->IsSelected())
1063 int64_t folderSize = CalculateFolderSize(pItem2->GetPath(), progress);
1064 if (folderSize >= 0)
1066 pItem2->m_dwSize = folderSize;
1067 if (folderSize == 0)
1068 pItem2->SetLabel2(StringUtils::SizeToString(folderSize));
1070 pItem2->SetFileSizeLabel();
1079 g_windowManager.ActivateWindow(WINDOW_SETTINGS_MENU);
1089 CGUIDialogContextMenu::SwitchMedia("files", m_vecItems[list]->GetPath());
1095 if (bDeselect && item >= 0 && item < m_vecItems[list]->Size())
1096 { // deselect item as we didn't do anything
1097 pItem->Select(false);
1101 // Highlights the item in the list under the cursor
1102 // returns true if we should deselect the item, false otherwise
1103 bool CGUIWindowFileManager::SelectItem(int list, int &item)
1105 // get the currently selected item in the list
1106 item = GetSelectedItem(list);
1108 // select the item if we need to
1109 if (item > -1 && !NumSelected(list) && !m_vecItems[list]->Get(item)->IsParentFolder())
1111 m_vecItems[list]->Get(item)->Select(true);
1117 // recursively calculates the selected folder size
1118 int64_t CGUIWindowFileManager::CalculateFolderSize(const CStdString &strDirectory, CGUIDialogProgress *pProgress)
1121 { // update our progress control
1122 pProgress->Progress();
1123 pProgress->SetLine(1, strDirectory);
1124 if (pProgress->IsCanceled())
1127 // start by calculating the size of the files in this folder...
1128 int64_t totalSize = 0;
1129 CFileItemList items;
1130 CVirtualDirectory rootDir;
1131 rootDir.SetSources(*CMediaSourceSettings::Get().GetSources("files"));
1132 rootDir.GetDirectory(strDirectory, items, false);
1133 for (int i=0; i < items.Size(); i++)
1135 if (items[i]->m_bIsFolder && !items[i]->IsParentFolder()) // folder
1137 int64_t folderSize = CalculateFolderSize(items[i]->GetPath(), pProgress);
1138 if (folderSize < 0) return -1;
1139 totalSize += folderSize;
1142 totalSize += items[i]->m_dwSize;
1147 void CGUIWindowFileManager::OnJobComplete(unsigned int jobID, bool success, CJob *job)
1151 CFileOperationJob* fileJob = (CFileOperationJob*)job;
1152 CGUIDialogOK::ShowAndGetInput(fileJob->GetHeading(),
1153 fileJob->GetLine(), 16200, 0);
1158 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_UPDATE);
1159 CApplicationMessenger::Get().SendGUIMessage(msg, GetID(), false);
1162 CJobQueue::OnJobComplete(jobID, success, job);
1165 void CGUIWindowFileManager::ShowShareErrorMessage(CFileItem* pItem)
1167 int idMessageText = 0;
1168 CURL url(pItem->GetPath());
1169 const CStdString& strHostName = url.GetHostName();
1171 if (url.GetProtocol() == "smb" && strHostName.IsEmpty()) // smb workgroup
1172 idMessageText = 15303; // Workgroup not found
1173 else if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_REMOTE || URIUtils::IsRemote(pItem->GetPath()))
1174 idMessageText = 15301; // Could not connect to network server
1176 idMessageText = 15300; // Path not found or invalid
1178 CGUIDialogOK::ShowAndGetInput(220, idMessageText, 0, 0);
1181 void CGUIWindowFileManager::OnInitWindow()
1183 bool bResult0 = Update(0, m_Directory[0]->GetPath());
1184 bool bResult1 = Update(1, m_Directory[1]->GetPath());
1186 CGUIWindow::OnInitWindow();
1188 if (!bCheckShareConnectivity)
1190 bCheckShareConnectivity = true; //reset
1191 CFileItem pItem(strCheckSharePath, true);
1192 ShowShareErrorMessage(&pItem); //show the error message after window is loaded!
1193 Update(0,""); // reset view to root
1197 ShowShareErrorMessage(m_Directory[0]); //show the error message after window is loaded!
1202 ShowShareErrorMessage(m_Directory[1]); //show the error message after window is loaded!
1206 void CGUIWindowFileManager::SetInitialPath(const CStdString &path)
1208 // check for a passed destination path
1209 CStdString strDestination = path;
1210 m_rootDir.SetSources(*CMediaSourceSettings::Get().GetSources("files"));
1211 if (!strDestination.IsEmpty())
1213 CLog::Log(LOGINFO, "Attempting to quickpath to: %s", strDestination.c_str());
1215 // otherwise, is this the first time accessing this window?
1216 else if (m_Directory[0]->GetPath() == "?")
1218 m_Directory[0]->SetPath(strDestination = CMediaSourceSettings::Get().GetDefaultSource("files"));
1219 CLog::Log(LOGINFO, "Attempting to default to: %s", strDestination.c_str());
1221 // try to open the destination path
1222 if (!strDestination.IsEmpty())
1225 if (strDestination.Equals("$ROOT"))
1227 m_Directory[0]->SetPath("");
1228 CLog::Log(LOGINFO, " Success! Opening root listing.");
1232 // default parameters if the jump fails
1233 m_Directory[0]->SetPath("");
1235 bool bIsSourceName = false;
1237 m_rootDir.GetSources(shares);
1238 int iIndex = CUtil::GetMatchingSource(strDestination, shares, bIsSourceName);
1241 // set current directory to matching share
1243 if (bIsSourceName && iIndex < (int)shares.size())
1244 path = shares[iIndex].strPath;
1246 path = strDestination;
1247 URIUtils::RemoveSlashAtEnd(path);
1248 m_Directory[0]->SetPath(path);
1249 CLog::Log(LOGINFO, " Success! Opened destination path: %s", strDestination.c_str());
1251 // outside call: check the share for connectivity
1252 bCheckShareConnectivity = Update(0, m_Directory[0]->GetPath());
1253 if(!bCheckShareConnectivity)
1254 strCheckSharePath = m_Directory[0]->GetPath();
1258 CLog::Log(LOGERROR, " Failed! Destination parameter (%s) does not match a valid share!", strDestination.c_str());
1263 if (m_Directory[1]->GetPath() == "?") m_Directory[1]->SetPath("");
1266 const CFileItem& CGUIWindowFileManager::CurrentDirectory(int indx) const
1268 return *m_Directory[indx];