1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "GUIDialogFileBrowser.h"
10 
11 #include "AutoSwitch.h"
12 #include "FileItem.h"
13 #include "GUIDialogContextMenu.h"
14 #include "GUIDialogMediaSource.h"
15 #include "GUIDialogYesNo.h"
16 #include "GUIPassword.h"
17 #include "GUIUserMessages.h"
18 #include "ServiceBroker.h"
19 #include "URL.h"
20 #include "Util.h"
21 #include "filesystem/Directory.h"
22 #include "filesystem/File.h"
23 #include "filesystem/MultiPathDirectory.h"
24 #include "guilib/GUIComponent.h"
25 #include "guilib/GUIKeyboardFactory.h"
26 #include "guilib/GUIWindowManager.h"
27 #include "guilib/LocalizeStrings.h"
28 #include "input/Key.h"
29 #include "messaging/helpers/DialogOKHelper.h"
30 #include "network/GUIDialogNetworkSetup.h"
31 #include "network/Network.h"
32 #include "profiles/ProfileManager.h"
33 #include "settings/MediaSourceSettings.h"
34 #include "settings/SettingsComponent.h"
35 #include "storage/MediaManager.h"
36 #include "utils/FileExtensionProvider.h"
37 #include "utils/StringUtils.h"
38 #include "utils/URIUtils.h"
39 #include "utils/Variant.h"
40 #include "utils/log.h"
41 
42 using namespace KODI::MESSAGING;
43 using namespace XFILE;
44 
45 #define CONTROL_LIST          450
46 #define CONTROL_THUMBS        451
47 #define CONTROL_HEADING_LABEL 411
48 #define CONTROL_LABEL_PATH    412
49 #define CONTROL_OK            413
50 #define CONTROL_CANCEL        414
51 #define CONTROL_NEWFOLDER     415
52 #define CONTROL_FLIP          416
53 
CGUIDialogFileBrowser()54 CGUIDialogFileBrowser::CGUIDialogFileBrowser()
55     : CGUIDialog(WINDOW_DIALOG_FILE_BROWSER, "FileBrowser.xml")
56 {
57   m_Directory = new CFileItem;
58   m_vecItems = new CFileItemList;
59   m_bConfirmed = false;
60   m_Directory->m_bIsFolder = true;
61   m_browsingForFolders = 0;
62   m_browsingForImages = false;
63   m_useFileDirectories = false;
64   m_addNetworkShareEnabled = false;
65   m_singleList = false;
66   m_thumbLoader.SetObserver(this);
67   m_flipEnabled = false;
68   m_bFlip = false;
69   m_multipleSelection = false;
70   m_loadType = KEEP_IN_MEMORY;
71 }
72 
~CGUIDialogFileBrowser()73 CGUIDialogFileBrowser::~CGUIDialogFileBrowser()
74 {
75   delete m_Directory;
76   delete m_vecItems;
77 }
78 
OnAction(const CAction & action)79 bool CGUIDialogFileBrowser::OnAction(const CAction &action)
80 {
81   if (action.GetID() == ACTION_PARENT_DIR)
82   {
83     GoParentFolder();
84     return true;
85   }
86   if ((action.GetID() == ACTION_CONTEXT_MENU || action.GetID() == ACTION_MOUSE_RIGHT_CLICK) && m_Directory->GetPath().empty())
87   {
88     int iItem = m_viewControl.GetSelectedItem();
89     if ((!m_addSourceType.empty() && iItem != m_vecItems->Size()-1))
90       return OnPopupMenu(iItem);
91     if (m_addNetworkShareEnabled && CServiceBroker::GetMediaManager().HasLocation(m_selectedPath))
92     {
93       // need to make sure this source is not an auto added location
94       // as users locations might have the same paths
95       CFileItemPtr pItem = (*m_vecItems)[iItem];
96       for (unsigned int i=0;i<m_shares.size();++i)
97       {
98         if (StringUtils::EqualsNoCase(m_shares[i].strName, pItem->GetLabel()) && m_shares[i].m_ignore)
99           return false;
100       }
101 
102       return OnPopupMenu(iItem);
103     }
104 
105     return false;
106   }
107 
108   return CGUIDialog::OnAction(action);
109 }
110 
OnBack(int actionID)111 bool CGUIDialogFileBrowser::OnBack(int actionID)
112 {
113   if (actionID == ACTION_NAV_BACK && !m_vecItems->IsVirtualDirectoryRoot())
114   {
115     GoParentFolder();
116     return true;
117   }
118   return CGUIDialog::OnBack(actionID);
119 }
120 
OnMessage(CGUIMessage & message)121 bool CGUIDialogFileBrowser::OnMessage(CGUIMessage& message)
122 {
123   switch ( message.GetMessage() )
124   {
125   case GUI_MSG_WINDOW_DEINIT:
126     {
127       if (m_thumbLoader.IsLoading())
128         m_thumbLoader.StopThread();
129       CGUIDialog::OnMessage(message);
130       ClearFileItems();
131       m_addNetworkShareEnabled = false;
132       return true;
133     }
134     break;
135 
136   case GUI_MSG_WINDOW_INIT:
137     {
138       m_bConfirmed = false;
139       m_bFlip = false;
140       bool bIsDir = false;
141       // this code allows two different selection modes for directories
142       // end the path with a slash to start inside the directory
143       if (URIUtils::HasSlashAtEnd(m_selectedPath))
144       {
145         bIsDir = true;
146         bool bFool;
147         int iSource = CUtil::GetMatchingSource(m_selectedPath,m_shares,bFool);
148         bFool = true;
149         if (iSource > -1 && iSource < (int)m_shares.size())
150         {
151           if (URIUtils::PathEquals(m_shares[iSource].strPath, m_selectedPath))
152             bFool = false;
153         }
154 
155         if (bFool && !CDirectory::Exists(m_selectedPath))
156           m_selectedPath.clear();
157       }
158       else
159       {
160         if (!CFile::Exists(m_selectedPath) && !CDirectory::Exists(m_selectedPath))
161             m_selectedPath.clear();
162       }
163 
164       // find the parent folder if we are a file browser (don't do this for folders)
165       m_Directory->SetPath(m_selectedPath);
166       if (!m_browsingForFolders && !bIsDir)
167         m_Directory->SetPath(URIUtils::GetParentPath(m_selectedPath));
168       Update(m_Directory->GetPath());
169       m_viewControl.SetSelectedItem(m_selectedPath);
170       return CGUIDialog::OnMessage(message);
171     }
172     break;
173 
174   case GUI_MSG_CLICKED:
175     {
176       if (m_viewControl.HasControl(message.GetSenderId()))  // list control
177       {
178         int iItem = m_viewControl.GetSelectedItem();
179         int iAction = message.GetParam1();
180         if (iItem < 0) break;
181         CFileItemPtr pItem = (*m_vecItems)[iItem];
182         if ((iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK) &&
183            (!m_multipleSelection || pItem->m_bIsShareOrDrive || pItem->m_bIsFolder))
184         {
185           OnClick(iItem);
186           return true;
187         }
188         else if ((iAction == ACTION_HIGHLIGHT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK || iAction == ACTION_SELECT_ITEM) &&
189                 (m_multipleSelection && !pItem->m_bIsShareOrDrive && !pItem->m_bIsFolder))
190         {
191           pItem->Select(!pItem->IsSelected());
192           CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), message.GetSenderId(), iItem + 1);
193           OnMessage(msg);
194         }
195       }
196       else if (message.GetSenderId() == CONTROL_OK)
197       {
198         if (m_browsingForFolders == 2)
199         {
200           int iItem = m_viewControl.GetSelectedItem();
201 
202           std::string strPath;
203           if (iItem == 0)
204             strPath = m_selectedPath;
205           else
206             strPath = (*m_vecItems)[iItem]->GetPath();
207 
208           std::string strTest = URIUtils::AddFileToFolder(strPath, "1");
209           CFile file;
210           if (file.OpenForWrite(strTest,true))
211           {
212             file.Close();
213             CFile::Delete(strTest);
214             m_bConfirmed = true;
215             Close();
216           }
217           else
218             HELPERS::ShowOKDialogText(CVariant{257}, CVariant{20072});
219         }
220         else
221         {
222           if (m_multipleSelection)
223           {
224             for (int iItem = 0; iItem < m_vecItems->Size(); ++iItem)
225             {
226               CFileItemPtr pItem = (*m_vecItems)[iItem];
227               if (pItem->IsSelected())
228                 m_markedPath.push_back(pItem->GetPath());
229             }
230           }
231           m_bConfirmed = true;
232           Close();
233         }
234         return true;
235       }
236       else if (message.GetSenderId() == CONTROL_CANCEL)
237       {
238         Close();
239         return true;
240       }
241       else if (message.GetSenderId() == CONTROL_NEWFOLDER)
242       {
243         std::string strInput;
244         if (CGUIKeyboardFactory::ShowAndGetInput(strInput, CVariant{g_localizeStrings.Get(119)}, false))
245         {
246           std::string strPath = URIUtils::AddFileToFolder(m_vecItems->GetPath(), strInput);
247           if (CDirectory::Create(strPath))
248             Update(m_vecItems->GetPath());
249           else
250             HELPERS::ShowOKDialogText(CVariant{20069}, CVariant{20072});
251         }
252       }
253       else if (message.GetSenderId() == CONTROL_FLIP)
254         m_bFlip = !m_bFlip;
255     }
256     break;
257   case GUI_MSG_SETFOCUS:
258     {
259       if (m_viewControl.HasControl(message.GetControlId()) && m_viewControl.GetCurrentControl() != message.GetControlId())
260       {
261         m_viewControl.SetFocused();
262         return true;
263       }
264     }
265     break;
266   case GUI_MSG_NOTIFY_ALL:
267     { // Message is received only if this window is active
268       if (message.GetParam1() == GUI_MSG_REMOVED_MEDIA)
269       {
270         if (m_Directory->IsVirtualDirectoryRoot() && IsActive())
271         {
272           int iItem = m_viewControl.GetSelectedItem();
273           Update(m_Directory->GetPath());
274           m_viewControl.SetSelectedItem(iItem);
275         }
276         else if (m_Directory->IsRemovable())
277         { // check that we have this removable share still
278           if (!m_rootDir.IsInSource(m_Directory->GetPath()))
279           { // don't have this share any more
280             if (IsActive()) Update("");
281             else
282             {
283               m_history.ClearPathHistory();
284               m_Directory->SetPath("");
285             }
286           }
287         }
288         return true;
289       }
290       else if (message.GetParam1()==GUI_MSG_UPDATE_SOURCES)
291       { // State of the sources changed, so update our view
292         if (m_Directory->IsVirtualDirectoryRoot() && IsActive())
293         {
294           int iItem = m_viewControl.GetSelectedItem();
295           Update(m_Directory->GetPath());
296           m_viewControl.SetSelectedItem(iItem);
297         }
298         return true;
299       }
300       else if (message.GetParam1()==GUI_MSG_UPDATE_PATH)
301       {
302         if (IsActive())
303         {
304           if((message.GetStringParam() == m_Directory->GetPath()) ||
305              (m_Directory->IsMultiPath() && XFILE::CMultiPathDirectory::HasPath(m_Directory->GetPath(), message.GetStringParam())))
306           {
307             int iItem = m_viewControl.GetSelectedItem();
308             Update(m_Directory->GetPath());
309             m_viewControl.SetSelectedItem(iItem);
310           }
311         }
312       }
313     }
314     break;
315 
316   }
317   return CGUIDialog::OnMessage(message);
318 }
319 
ClearFileItems()320 void CGUIDialogFileBrowser::ClearFileItems()
321 {
322   m_viewControl.Clear();
323   m_vecItems->Clear(); // will clean up everything
324 }
325 
OnSort()326 void CGUIDialogFileBrowser::OnSort()
327 {
328   if (!m_singleList)
329     m_vecItems->Sort(SortByLabel, SortOrderAscending);
330 }
331 
Update(const std::string & strDirectory)332 void CGUIDialogFileBrowser::Update(const std::string &strDirectory)
333 {
334   const CURL pathToUrl(strDirectory);
335 
336   if (m_browsingForImages && m_thumbLoader.IsLoading())
337     m_thumbLoader.StopThread();
338   // get selected item
339   int iItem = m_viewControl.GetSelectedItem();
340   std::string strSelectedItem;
341   if (iItem >= 0 && iItem < m_vecItems->Size())
342   {
343     CFileItemPtr pItem = (*m_vecItems)[iItem];
344     if (!pItem->IsParentFolder())
345     {
346       strSelectedItem = pItem->GetPath();
347       URIUtils::RemoveSlashAtEnd(strSelectedItem);
348       m_history.SetSelectedItem(strSelectedItem, m_Directory->GetPath().empty()?"empty":m_Directory->GetPath());
349     }
350   }
351 
352   if (!m_singleList)
353   {
354     CFileItemList items;
355     std::string strParentPath;
356 
357     if (!m_rootDir.GetDirectory(pathToUrl, items, m_useFileDirectories, false))
358     {
359       CLog::Log(LOGERROR,"CGUIDialogFileBrowser::GetDirectory(%s) failed", pathToUrl.GetRedacted().c_str());
360 
361       // We assume, we can get the parent
362       // directory again
363       std::string strParentPath = m_history.GetParentPath();
364       m_history.RemoveParentPath();
365       Update(strParentPath);
366       return;
367     }
368 
369     // check if current directory is a root share
370     if (!m_rootDir.IsSource(strDirectory))
371     {
372       if (URIUtils::GetParentPath(strDirectory, strParentPath))
373       {
374         CFileItemPtr pItem(new CFileItem(".."));
375         pItem->SetPath(strParentPath);
376         pItem->m_bIsFolder = true;
377         pItem->m_bIsShareOrDrive = false;
378         items.AddFront(pItem, 0);
379       }
380     }
381     else
382     {
383       // yes, this is the root of a share
384       // add parent path to the virtual directory
385       CFileItemPtr pItem(new CFileItem(".."));
386       pItem->SetPath("");
387       pItem->m_bIsShareOrDrive = false;
388       pItem->m_bIsFolder = true;
389       items.AddFront(pItem, 0);
390       strParentPath = "";
391     }
392 
393     ClearFileItems();
394     m_vecItems->Copy(items);
395     m_Directory->SetPath(strDirectory);
396     m_strParentPath = strParentPath;
397   }
398 
399   // if we're getting the root source listing
400   // make sure the path history is clean
401   if (strDirectory.empty())
402     m_history.ClearPathHistory();
403 
404   // some evil stuff don't work with the '/' mask, e.g. shoutcast directory - make sure no files are in there
405   if (m_browsingForFolders)
406   {
407     for (int i=0;i<m_vecItems->Size();++i)
408       if (!(*m_vecItems)[i]->m_bIsFolder)
409       {
410         m_vecItems->Remove(i);
411         i--;
412       }
413   }
414 
415   // No need to set thumbs
416 
417   m_vecItems->FillInDefaultIcons();
418 
419   OnSort();
420 
421   if (m_Directory->GetPath().empty() && m_addNetworkShareEnabled &&
422      (CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
423       CServiceBroker::GetSettingsComponent()->GetProfileManager()->IsMasterProfile() || g_passwordManager.bMasterUser))
424   { // we are in the virtual directory - add the "Add Network Location" item
425     CFileItemPtr pItem(new CFileItem(g_localizeStrings.Get(1032)));
426     pItem->SetPath("net://");
427     pItem->m_bIsFolder = true;
428     m_vecItems->Add(pItem);
429   }
430   if (m_Directory->GetPath().empty() && !m_addSourceType.empty())
431   {
432     CFileItemPtr pItem(new CFileItem(g_localizeStrings.Get(21359)));
433     pItem->SetPath("source://");
434     pItem->m_bIsFolder = true;
435     m_vecItems->Add(pItem);
436   }
437 
438   m_viewControl.SetItems(*m_vecItems);
439   m_viewControl.SetCurrentView((m_browsingForImages && CAutoSwitch::ByFileCount(*m_vecItems)) ? CONTROL_THUMBS : CONTROL_LIST);
440 
441   std::string strPath2 = m_Directory->GetPath();
442   URIUtils::RemoveSlashAtEnd(strPath2);
443   strSelectedItem = m_history.GetSelectedItem(strPath2==""?"empty":strPath2);
444 
445   bool bSelectedFound = false;
446   for (int i = 0; i < m_vecItems->Size(); ++i)
447   {
448     CFileItemPtr pItem = (*m_vecItems)[i];
449     strPath2 = pItem->GetPath();
450     URIUtils::RemoveSlashAtEnd(strPath2);
451     if (strPath2 == strSelectedItem)
452     {
453       m_viewControl.SetSelectedItem(i);
454       bSelectedFound = true;
455       break;
456     }
457   }
458 
459   // if we haven't found the selected item, select the first item
460   if (!bSelectedFound)
461     m_viewControl.SetSelectedItem(0);
462 
463   m_history.AddPath(m_Directory->GetPath());
464 
465   if (m_browsingForImages)
466     m_thumbLoader.Load(*m_vecItems);
467 }
468 
FrameMove()469 void CGUIDialogFileBrowser::FrameMove()
470 {
471   int item = m_viewControl.GetSelectedItem();
472   if (item >= 0)
473   {
474     // if we are browsing for folders, and not in the root directory, then we use the parent path,
475     // else we use the current file's path
476     if (m_browsingForFolders && !m_Directory->IsVirtualDirectoryRoot())
477       m_selectedPath = m_Directory->GetPath();
478     else
479       m_selectedPath = (*m_vecItems)[item]->GetPath();
480     if (m_selectedPath == "net://")
481     {
482       SET_CONTROL_LABEL(CONTROL_LABEL_PATH, g_localizeStrings.Get(1032)); // "Add Network Location..."
483     }
484     else
485     {
486       // Update the current path label
487       CURL url(m_selectedPath);
488       std::string safePath = url.GetWithoutUserDetails();
489       SET_CONTROL_LABEL(CONTROL_LABEL_PATH, safePath);
490     }
491     if ((!m_browsingForFolders && (*m_vecItems)[item]->m_bIsFolder) || ((*m_vecItems)[item]->GetPath() == "image://Browse"))
492     {
493       CONTROL_DISABLE(CONTROL_OK);
494     }
495     else
496     {
497       CONTROL_ENABLE(CONTROL_OK);
498     }
499     if (m_browsingForFolders == 2)
500     {
501       CONTROL_ENABLE(CONTROL_NEWFOLDER);
502     }
503     else
504     {
505       CONTROL_DISABLE(CONTROL_NEWFOLDER);
506     }
507     if (m_flipEnabled)
508     {
509       CONTROL_ENABLE(CONTROL_FLIP);
510     }
511     else
512     {
513       CONTROL_DISABLE(CONTROL_FLIP);
514     }
515   }
516   CGUIDialog::FrameMove();
517 }
518 
OnClick(int iItem)519 void CGUIDialogFileBrowser::OnClick(int iItem)
520 {
521   if ( iItem < 0 || iItem >= m_vecItems->Size() ) return ;
522   CFileItemPtr pItem = (*m_vecItems)[iItem];
523   std::string strPath = pItem->GetPath();
524 
525   if (pItem->m_bIsFolder)
526   {
527     if (pItem->GetPath() == "net://")
528     { // special "Add Network Location" item
529       OnAddNetworkLocation();
530       return;
531     }
532     if (pItem->GetPath() == "source://")
533     { // special "Add Source" item
534       OnAddMediaSource();
535       return;
536     }
537     if (!m_addSourceType.empty())
538     {
539       OnEditMediaSource(pItem.get());
540       return;
541     }
542     if ( pItem->m_bIsShareOrDrive )
543     {
544       if ( !HaveDiscOrConnection( pItem->m_iDriveType ) )
545         return ;
546     }
547     Update(strPath);
548   }
549   else if (!m_browsingForFolders)
550   {
551     m_selectedPath = pItem->GetPath();
552     m_bConfirmed = true;
553     Close();
554   }
555 }
556 
HaveDiscOrConnection(int iDriveType)557 bool CGUIDialogFileBrowser::HaveDiscOrConnection( int iDriveType )
558 {
559   if ( iDriveType == CMediaSource::SOURCE_TYPE_DVD )
560   {
561     if (!CServiceBroker::GetMediaManager().IsDiscInDrive())
562     {
563       HELPERS::ShowOKDialogText(CVariant{218}, CVariant{219});
564       return false;
565     }
566   }
567   else if ( iDriveType == CMediaSource::SOURCE_TYPE_REMOTE )
568   {
569     //! @todo Handle not connected to a remote share
570     if ( !CServiceBroker::GetNetwork().IsConnected() )
571     {
572       HELPERS::ShowOKDialogText(CVariant{220}, CVariant{221});
573       return false;
574     }
575   }
576 
577   return true;
578 }
579 
GoParentFolder()580 void CGUIDialogFileBrowser::GoParentFolder()
581 {
582   std::string strPath(m_strParentPath);
583   if (strPath.size() == 2)
584     if (strPath[1] == ':')
585       URIUtils::AddSlashAtEnd(strPath);
586   Update(strPath);
587 }
588 
OnWindowLoaded()589 void CGUIDialogFileBrowser::OnWindowLoaded()
590 {
591   CGUIDialog::OnWindowLoaded();
592   m_viewControl.Reset();
593   m_viewControl.SetParentWindow(GetID());
594   m_viewControl.AddView(GetControl(CONTROL_LIST));
595   m_viewControl.AddView(GetControl(CONTROL_THUMBS));
596 }
597 
OnWindowUnload()598 void CGUIDialogFileBrowser::OnWindowUnload()
599 {
600   CGUIDialog::OnWindowUnload();
601   m_viewControl.Reset();
602 }
603 
ShowAndGetImage(const CFileItemList & items,const VECSOURCES & shares,const std::string & heading,std::string & result,bool * flip,int label)604 bool CGUIDialogFileBrowser::ShowAndGetImage(const CFileItemList &items, const VECSOURCES &shares, const std::string &heading, std::string &result, bool* flip, int label)
605 {
606   CGUIDialogFileBrowser *browser = new CGUIDialogFileBrowser();
607   if (!browser)
608     return false;
609   CServiceBroker::GetGUI()->GetWindowManager().AddUniqueInstance(browser);
610 
611   browser->m_browsingForImages = true;
612   browser->m_singleList = true;
613   browser->m_vecItems->Clear();
614   browser->m_vecItems->Append(items);
615   if (true)
616   {
617     CFileItemPtr item(new CFileItem("image://Browse", false));
618     item->SetLabel(g_localizeStrings.Get(20153));
619     item->SetArt("icon", "DefaultFolder.png");
620     browser->m_vecItems->Add(item);
621   }
622   browser->SetHeading(heading);
623   browser->m_flipEnabled = flip?true:false;
624   browser->Open();
625   bool confirmed(browser->IsConfirmed());
626   if (confirmed)
627   {
628     result = browser->m_selectedPath;
629     if (result == "image://Browse")
630     { // "Browse for thumb"
631       CServiceBroker::GetGUI()->GetWindowManager().Remove(browser->GetID());
632       delete browser;
633       return ShowAndGetImage(shares, g_localizeStrings.Get(label), result);
634     }
635   }
636 
637   if (flip)
638     *flip = browser->m_bFlip != 0;
639 
640   CServiceBroker::GetGUI()->GetWindowManager().Remove(browser->GetID());
641   delete browser;
642 
643   return confirmed;
644 }
645 
ShowAndGetImage(const VECSOURCES & shares,const std::string & heading,std::string & path)646 bool CGUIDialogFileBrowser::ShowAndGetImage(const VECSOURCES &shares, const std::string &heading, std::string &path)
647 {
648   return ShowAndGetFile(shares, CServiceBroker::GetFileExtensionProvider().GetPictureExtensions(), heading, path, true); // true for use thumbs
649 }
650 
ShowAndGetImageList(const VECSOURCES & shares,const std::string & heading,std::vector<std::string> & path)651 bool CGUIDialogFileBrowser::ShowAndGetImageList(const VECSOURCES &shares, const std::string &heading, std::vector<std::string> &path)
652 {
653   return ShowAndGetFileList(shares, CServiceBroker::GetFileExtensionProvider().GetPictureExtensions(), heading, path, true); // true for use thumbs
654 }
655 
ShowAndGetDirectory(const VECSOURCES & shares,const std::string & heading,std::string & path,bool bWriteOnly)656 bool CGUIDialogFileBrowser::ShowAndGetDirectory(const VECSOURCES &shares, const std::string &heading, std::string &path, bool bWriteOnly)
657 {
658   // an extension mask of "/" ensures that no files are shown
659   if (bWriteOnly)
660   {
661     VECSOURCES shareWritable;
662     for (unsigned int i=0;i<shares.size();++i)
663     {
664       if (shares[i].IsWritable())
665         shareWritable.push_back(shares[i]);
666     }
667 
668     return ShowAndGetFile(shareWritable, "/w", heading, path);
669   }
670 
671   return ShowAndGetFile(shares, "/", heading, path);
672 }
673 
ShowAndGetFile(const VECSOURCES & shares,const std::string & mask,const std::string & heading,std::string & path,bool useThumbs,bool useFileDirectories)674 bool CGUIDialogFileBrowser::ShowAndGetFile(const VECSOURCES &shares, const std::string &mask, const std::string &heading, std::string &path, bool useThumbs /* = false */, bool useFileDirectories /* = false */)
675 {
676   CGUIDialogFileBrowser *browser = new CGUIDialogFileBrowser();
677   if (!browser)
678     return false;
679   CServiceBroker::GetGUI()->GetWindowManager().AddUniqueInstance(browser);
680 
681   browser->m_useFileDirectories = useFileDirectories;
682 
683   browser->m_browsingForImages = useThumbs;
684   browser->SetHeading(heading);
685   browser->SetSources(shares);
686   std::string strMask = mask;
687   if (mask == "/")
688     browser->m_browsingForFolders=1;
689   else
690   if (mask == "/w")
691   {
692     browser->m_browsingForFolders=2;
693     strMask = "/";
694   }
695   else
696     browser->m_browsingForFolders = 0;
697 
698   browser->m_rootDir.SetMask(strMask);
699   browser->m_selectedPath = path;
700   browser->m_addNetworkShareEnabled = false;
701   browser->Open();
702   bool confirmed(browser->IsConfirmed());
703   if (confirmed)
704     path = browser->m_selectedPath;
705   CServiceBroker::GetGUI()->GetWindowManager().Remove(browser->GetID());
706   delete browser;
707   return confirmed;
708 }
709 
710 // same as above, starting in a single directory
ShowAndGetFile(const std::string & directory,const std::string & mask,const std::string & heading,std::string & path,bool useThumbs,bool useFileDirectories,bool singleList)711 bool CGUIDialogFileBrowser::ShowAndGetFile(const std::string &directory, const std::string &mask, const std::string &heading, std::string &path, bool useThumbs /* = false */, bool useFileDirectories /* = false */, bool singleList /* = false */)
712 {
713   CGUIDialogFileBrowser *browser = new CGUIDialogFileBrowser();
714   if (!browser)
715     return false;
716   CServiceBroker::GetGUI()->GetWindowManager().AddUniqueInstance(browser);
717 
718   browser->m_useFileDirectories = useFileDirectories;
719   browser->m_browsingForImages = useThumbs;
720   browser->SetHeading(heading);
721 
722   // add a single share for this directory
723   if (!singleList)
724   {
725     VECSOURCES shares;
726     CMediaSource share;
727     share.strPath = directory;
728     URIUtils::RemoveSlashAtEnd(share.strPath); // this is needed for the dodgy code in WINDOW_INIT
729     shares.push_back(share);
730     browser->SetSources(shares);
731   }
732   else
733   {
734     browser->m_vecItems->Clear();
735     CDirectory::GetDirectory(directory,*browser->m_vecItems, "", DIR_FLAG_DEFAULTS);
736     CFileItemPtr item(new CFileItem("file://Browse", false));
737     item->SetLabel(g_localizeStrings.Get(20153));
738     item->SetArt("icon", "DefaultFolder.png");
739     browser->m_vecItems->Add(item);
740     browser->m_singleList = true;
741   }
742   std::string strMask = mask;
743   if (mask == "/")
744     browser->m_browsingForFolders=1;
745   else
746   if (mask == "/w")
747   {
748     browser->m_browsingForFolders=2;
749     strMask = "/";
750   }
751   else
752     browser->m_browsingForFolders = 0;
753 
754   browser->m_rootDir.SetMask(strMask);
755   browser->m_selectedPath = directory;
756   browser->m_addNetworkShareEnabled = false;
757   browser->Open();
758   bool confirmed(browser->IsConfirmed());
759   if (confirmed)
760     path = browser->m_selectedPath;
761   if (path == "file://Browse")
762   { // "Browse for thumb"
763     CServiceBroker::GetGUI()->GetWindowManager().Remove(browser->GetID());
764     delete browser;
765     VECSOURCES shares;
766     CServiceBroker::GetMediaManager().GetLocalDrives(shares);
767 
768     return ShowAndGetFile(shares, mask, heading, path, useThumbs,useFileDirectories);
769   }
770   CServiceBroker::GetGUI()->GetWindowManager().Remove(browser->GetID());
771   delete browser;
772   return confirmed;
773 }
774 
ShowAndGetFileList(const VECSOURCES & shares,const std::string & mask,const std::string & heading,std::vector<std::string> & path,bool useThumbs,bool useFileDirectories)775 bool CGUIDialogFileBrowser::ShowAndGetFileList(const VECSOURCES &shares, const std::string &mask, const std::string &heading, std::vector<std::string> &path, bool useThumbs /* = false */, bool useFileDirectories /* = false */)
776 {
777   CGUIDialogFileBrowser *browser = new CGUIDialogFileBrowser();
778   if (!browser)
779     return false;
780   CServiceBroker::GetGUI()->GetWindowManager().AddUniqueInstance(browser);
781 
782   browser->m_useFileDirectories = useFileDirectories;
783   browser->m_multipleSelection = true;
784   browser->m_browsingForImages = useThumbs;
785   browser->SetHeading(heading);
786   browser->SetSources(shares);
787   browser->m_browsingForFolders = 0;
788   browser->m_rootDir.SetMask(mask);
789   browser->m_addNetworkShareEnabled = false;
790   browser->Open();
791   bool confirmed(browser->IsConfirmed());
792   if (confirmed)
793   {
794     if (browser->m_markedPath.size())
795       path = browser->m_markedPath;
796     else
797       path.push_back(browser->m_selectedPath);
798   }
799   CServiceBroker::GetGUI()->GetWindowManager().Remove(browser->GetID());
800   delete browser;
801   return confirmed;
802 }
803 
SetHeading(const std::string & heading)804 void CGUIDialogFileBrowser::SetHeading(const std::string &heading)
805 {
806   Initialize();
807   SET_CONTROL_LABEL(CONTROL_HEADING_LABEL, heading);
808 }
809 
ShowAndGetSource(std::string & path,bool allowNetworkShares,VECSOURCES * additionalShare,const std::string & strType)810 bool CGUIDialogFileBrowser::ShowAndGetSource(std::string &path, bool allowNetworkShares, VECSOURCES* additionalShare /* = NULL */, const std::string& strType /* = "" */)
811 {
812   // Technique is
813   // 1.  Show Filebrowser with currently defined local, and optionally the network locations.
814   // 2.  Have the "Add Network Location" option in addition.
815   // 3a. If the "Add Network Location" is pressed, then:
816   //     a) Fire up the network location dialog to grab the new location
817   //     b) Check the location by doing a GetDirectory() - if it fails, prompt the user
818   //        to allow them to add currently disconnected network shares.
819   //     c) Save this location to our xml file (network.xml)
820   //     d) Return to 1.
821   // 3b. If the "Add Source" is pressed, then:
822   //     a) Fire up the media source dialog to add the new location
823   // 4.  Optionally allow user to browse the local and network locations for their share.
824   // 5.  On OK, return.
825 
826   // Create a new filebrowser window
827   CGUIDialogFileBrowser *browser = new CGUIDialogFileBrowser();
828   if (!browser) return false;
829 
830   // Add it to our window manager
831   CServiceBroker::GetGUI()->GetWindowManager().AddUniqueInstance(browser);
832 
833   VECSOURCES shares;
834   if (!strType.empty())
835   {
836     if (additionalShare)
837       shares = *additionalShare;
838     browser->m_addSourceType = strType;
839   }
840   else
841   {
842     browser->SetHeading(g_localizeStrings.Get(1023));
843 
844     CServiceBroker::GetMediaManager().GetLocalDrives(shares);
845 
846     // Now the additional share if appropriate
847     if (additionalShare)
848     {
849       shares.insert(shares.end(),additionalShare->begin(),additionalShare->end());
850     }
851 
852     // Now add the network shares...
853     if (allowNetworkShares)
854     {
855       CServiceBroker::GetMediaManager().GetNetworkLocations(shares);
856     }
857   }
858 
859   browser->SetSources(shares);
860   browser->m_rootDir.SetMask("/");
861   browser->m_rootDir.AllowNonLocalSources(false);  // don't allow plug n play shares
862   browser->m_browsingForFolders = 1;
863   browser->m_addNetworkShareEnabled = allowNetworkShares;
864   browser->m_selectedPath = "";
865   browser->Open();
866   bool confirmed = browser->IsConfirmed();
867   if (confirmed)
868     path = browser->m_selectedPath;
869 
870   CServiceBroker::GetGUI()->GetWindowManager().Remove(browser->GetID());
871   delete browser;
872   return confirmed;
873 }
874 
SetSources(const VECSOURCES & shares)875 void CGUIDialogFileBrowser::SetSources(const VECSOURCES &shares)
876 {
877   m_shares = shares;
878   if (!m_shares.size() && m_addSourceType.empty())
879     CServiceBroker::GetMediaManager().GetLocalDrives(m_shares);
880   m_rootDir.SetSources(m_shares);
881 }
882 
OnAddNetworkLocation()883 void CGUIDialogFileBrowser::OnAddNetworkLocation()
884 {
885   // ok, fire up the network location dialog
886   std::string path;
887   if (CGUIDialogNetworkSetup::ShowAndGetNetworkAddress(path))
888   {
889     // verify the path by doing a GetDirectory.
890     CFileItemList items;
891     if (CDirectory::GetDirectory(path, items, "", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_ALLOW_PROMPT) || CGUIDialogYesNo::ShowAndGetInput(CVariant{1001}, CVariant{1002}))
892     { // add the network location to the shares list
893       CMediaSource share;
894       share.strPath = path; //setPath(path);
895       CURL url(path);
896       share.strName = url.GetWithoutUserDetails();
897       URIUtils::RemoveSlashAtEnd(share.strName);
898       m_shares.push_back(share);
899       // add to our location manager...
900       CServiceBroker::GetMediaManager().AddNetworkLocation(path);
901     }
902   }
903   m_rootDir.SetSources(m_shares);
904   Update(m_vecItems->GetPath());
905 }
906 
OnAddMediaSource()907 void CGUIDialogFileBrowser::OnAddMediaSource()
908 {
909   if (CGUIDialogMediaSource::ShowAndAddMediaSource(m_addSourceType))
910   {
911     SetSources(*CMediaSourceSettings::GetInstance().GetSources(m_addSourceType));
912     Update("");
913   }
914 }
915 
OnEditMediaSource(CFileItem * pItem)916 void CGUIDialogFileBrowser::OnEditMediaSource(CFileItem* pItem)
917 {
918   if (CGUIDialogMediaSource::ShowAndEditMediaSource(m_addSourceType,pItem->GetLabel()))
919   {
920     SetSources(*CMediaSourceSettings::GetInstance().GetSources(m_addSourceType));
921     Update("");
922   }
923 }
924 
OnPopupMenu(int iItem)925 bool CGUIDialogFileBrowser::OnPopupMenu(int iItem)
926 {
927   CContextButtons choices;
928   choices.Add(1, m_addSourceType.empty() ? 20133 : 21364);
929   choices.Add(2, m_addSourceType.empty() ? 20134 : 21365);
930 
931   int btnid = CGUIDialogContextMenu::ShowAndGetChoice(choices);
932   if (btnid == 1)
933   {
934     if (m_addNetworkShareEnabled)
935     {
936       std::string strOldPath=m_selectedPath,newPath=m_selectedPath;
937       VECSOURCES shares=m_shares;
938       if (CGUIDialogNetworkSetup::ShowAndGetNetworkAddress(newPath))
939       {
940         CServiceBroker::GetMediaManager().SetLocationPath(strOldPath, newPath);
941         CURL url(newPath);
942         for (unsigned int i=0;i<shares.size();++i)
943         {
944           if (URIUtils::CompareWithoutSlashAtEnd(shares[i].strPath, strOldPath))//getPath().Equals(strOldPath))
945           {
946             shares[i].strName = url.GetWithoutUserDetails();
947             shares[i].strPath = newPath;
948             URIUtils::RemoveSlashAtEnd(shares[i].strName);
949             break;
950           }
951         }
952         // refresh dialog content
953         SetSources(shares);
954         m_rootDir.SetMask("/");
955         m_browsingForFolders = 1;
956         m_addNetworkShareEnabled = true;
957         m_selectedPath = url.GetWithoutUserDetails();
958         Update(m_Directory->GetPath());
959         m_viewControl.SetSelectedItem(iItem);
960       }
961     }
962     else
963     {
964       CFileItemPtr item = m_vecItems->Get(iItem);
965       OnEditMediaSource(item.get());
966     }
967   }
968   if (btnid == 2)
969   {
970     if (m_addNetworkShareEnabled)
971     {
972       CServiceBroker::GetMediaManager().RemoveLocation(m_selectedPath);
973 
974       for (unsigned int i=0;i<m_shares.size();++i)
975       {
976         if (URIUtils::CompareWithoutSlashAtEnd(m_shares[i].strPath, m_selectedPath) && !m_shares[i].m_ignore) // getPath().Equals(m_selectedPath))
977         {
978           m_shares.erase(m_shares.begin()+i);
979           break;
980         }
981       }
982       m_rootDir.SetSources(m_shares);
983       m_rootDir.SetMask("/");
984 
985       m_browsingForFolders = 1;
986       m_addNetworkShareEnabled = true;
987       m_selectedPath = "";
988 
989       Update(m_Directory->GetPath());
990     }
991     else
992     {
993       CMediaSourceSettings::GetInstance().DeleteSource(m_addSourceType,(*m_vecItems)[iItem]->GetLabel(),(*m_vecItems)[iItem]->GetPath());
994       SetSources(*CMediaSourceSettings::GetInstance().GetSources(m_addSourceType));
995       Update("");
996     }
997   }
998 
999   return true;
1000 }
1001 
GetCurrentListItem(int offset)1002 CFileItemPtr CGUIDialogFileBrowser::GetCurrentListItem(int offset)
1003 {
1004   int item = m_viewControl.GetSelectedItem();
1005   if (item < 0 || !m_vecItems->Size()) return CFileItemPtr();
1006 
1007   item = (item + offset) % m_vecItems->Size();
1008   if (item < 0) item += m_vecItems->Size();
1009   return (*m_vecItems)[item];
1010 }
1011 
GetFirstFocusableControl(int id)1012 CGUIControl *CGUIDialogFileBrowser::GetFirstFocusableControl(int id)
1013 {
1014   if (m_viewControl.HasControl(id))
1015     id = m_viewControl.GetCurrentControl();
1016   return CGUIWindow::GetFirstFocusableControl(id);
1017 }
1018 
1019 
1020