Merge pull request #2531 from Montellese/settings_cleanup_4
[vuplus_xbmc] / xbmc / windows / GUIMediaWindow.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include "threads/SystemClock.h"
22 #include "GUIMediaWindow.h"
23 #include "GUIUserMessages.h"
24 #include "Util.h"
25 #include "PlayListPlayer.h"
26 #include "addons/AddonManager.h"
27 #include "addons/PluginSource.h"
28 #include "filesystem/PluginDirectory.h"
29 #include "filesystem/MultiPathDirectory.h"
30 #include "GUIPassword.h"
31 #include "Application.h"
32 #include "ApplicationMessenger.h"
33 #include "network/Network.h"
34 #include "utils/RegExp.h"
35 #include "PartyModeManager.h"
36 #include "dialogs/GUIDialogMediaSource.h"
37 #include "GUIWindowFileManager.h"
38 #include "Favourites.h"
39 #include "utils/LabelFormatter.h"
40 #include "dialogs/GUIDialogProgress.h"
41 #include "profiles/ProfilesManager.h"
42 #include "settings/AdvancedSettings.h"
43 #include "settings/GUISettings.h"
44 #include "URL.h"
45
46 #include "dialogs/GUIDialogSmartPlaylistEditor.h"
47 #include "addons/GUIDialogAddonSettings.h"
48 #include "dialogs/GUIDialogYesNo.h"
49 #include "guilib/GUIWindowManager.h"
50 #include "dialogs/GUIDialogOK.h"
51 #include "playlists/PlayList.h"
52 #include "storage/MediaManager.h"
53 #include "utils/StringUtils.h"
54 #include "utils/URIUtils.h"
55 #include "guilib/Key.h"
56 #include "guilib/LocalizeStrings.h"
57 #include "utils/TimeUtils.h"
58 #include "filesystem/File.h"
59 #include "filesystem/FileDirectoryFactory.h"
60 #include "utils/log.h"
61 #include "utils/FileUtils.h"
62 #include "guilib/GUIEditControl.h"
63 #include "guilib/GUIKeyboardFactory.h"
64 #ifdef HAS_PYTHON
65 #include "interfaces/python/XBPython.h"
66 #endif
67 #include "interfaces/Builtins.h"
68 #include "dialogs/GUIDialogKaiToast.h"
69 #include "dialogs/GUIDialogMediaFilter.h"
70 #include "filesystem/SmartPlaylistDirectory.h"
71 #if defined(TARGET_ANDROID)
72 #include "xbmc/android/activity/XBMCApp.h"
73 #endif
74
75 #define CONTROL_BTNVIEWASICONS       2
76 #define CONTROL_BTNSORTBY            3
77 #define CONTROL_BTNSORTASC           4
78 #define CONTROL_BTN_FILTER          19
79
80 #define CONTROL_LABELFILES          12
81
82 #define PROPERTY_PATH_DB            "path.db"
83 #define PROPERTY_SORT_ORDER         "sort.order"
84 #define PROPERTY_SORT_ASCENDING     "sort.ascending"
85
86 using namespace std;
87 using namespace ADDON;
88
89 CGUIMediaWindow::CGUIMediaWindow(int id, const char *xmlFile)
90     : CGUIWindow(id, xmlFile)
91 {
92   m_loadType = KEEP_IN_MEMORY;
93   m_vecItems = new CFileItemList;
94   m_unfilteredItems = new CFileItemList;
95   m_vecItems->SetPath("?");
96   m_iLastControl = -1;
97   m_iSelectedItem = -1;
98   m_canFilterAdvanced = false;
99
100   m_guiState.reset(CGUIViewState::GetViewState(GetID(), *m_vecItems));
101 }
102
103 CGUIMediaWindow::~CGUIMediaWindow()
104 {
105   delete m_vecItems;
106   delete m_unfilteredItems;
107 }
108
109 #define CONTROL_VIEW_START        50
110 #define CONTROL_VIEW_END          59
111
112 void CGUIMediaWindow::LoadAdditionalTags(TiXmlElement *root)
113 {
114   CGUIWindow::LoadAdditionalTags(root);
115   // configure our view control
116   m_viewControl.Reset();
117   m_viewControl.SetParentWindow(GetID());
118   TiXmlElement *element = root->FirstChildElement("views");
119   if (element && element->FirstChild())
120   { // format is <views>50,29,51,95</views>
121     CStdString allViews = element->FirstChild()->Value();
122     CStdStringArray views;
123     StringUtils::SplitString(allViews, ",", views);
124     for (unsigned int i = 0; i < views.size(); i++)
125     {
126       int controlID = atol(views[i].c_str());
127       CGUIControl *control = (CGUIControl *)GetControl(controlID);
128       if (control && control->IsContainer())
129         m_viewControl.AddView(control);
130     }
131   }
132   else
133   { // backward compatibility
134     vector<CGUIControl *> controls;
135     GetContainers(controls);
136     for (ciControls it = controls.begin(); it != controls.end(); it++)
137     {
138       CGUIControl *control = *it;
139       if (control->GetID() >= CONTROL_VIEW_START && control->GetID() <= CONTROL_VIEW_END)
140         m_viewControl.AddView(control);
141     }
142   }
143   m_viewControl.SetViewControlID(CONTROL_BTNVIEWASICONS);
144 }
145
146 void CGUIMediaWindow::OnWindowLoaded()
147 {
148   SendMessage(GUI_MSG_SET_TYPE, CONTROL_BTN_FILTER, CGUIEditControl::INPUT_TYPE_FILTER);
149   CGUIWindow::OnWindowLoaded();
150   SetupShares();
151 }
152
153 void CGUIMediaWindow::OnWindowUnload()
154 {
155   CGUIWindow::OnWindowUnload();
156   m_viewControl.Reset();
157 }
158
159 CFileItemPtr CGUIMediaWindow::GetCurrentListItem(int offset)
160 {
161   int item = m_viewControl.GetSelectedItem();
162   if (!m_vecItems->Size() || item < 0)
163     return CFileItemPtr();
164   item = (item + offset) % m_vecItems->Size();
165   if (item < 0) item += m_vecItems->Size();
166   return m_vecItems->Get(item);
167 }
168
169 bool CGUIMediaWindow::OnAction(const CAction &action)
170 {
171   if (action.GetID() == ACTION_PARENT_DIR)
172   {
173     GoParentFolder();
174     return true;
175   }
176
177   // the non-contextual menu can be called at any time
178   if (action.GetID() == ACTION_CONTEXT_MENU && !m_viewControl.HasControl(GetFocusedControlID()))
179   {
180     OnPopupMenu(-1);
181     return true;
182   }
183
184   if (CGUIWindow::OnAction(action))
185     return true;
186
187   if (action.GetID() == ACTION_FILTER)
188     return Filter();
189
190   // live filtering
191   if (action.GetID() == ACTION_FILTER_CLEAR)
192   {
193     CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS);
194     message.SetStringParam("");
195     OnMessage(message);
196     return true;
197   }
198
199   if (action.GetID() == ACTION_BACKSPACE)
200   {
201     CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS, 2); // 2 for delete
202     OnMessage(message);
203     return true;
204   }
205
206   if (action.GetID() >= ACTION_FILTER_SMS2 && action.GetID() <= ACTION_FILTER_SMS9)
207   {
208     CStdString filter;
209     filter.Format("%i", (int)(action.GetID() - ACTION_FILTER_SMS2 + 2));
210     CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS, 1); // 1 for append
211     message.SetStringParam(filter);
212     OnMessage(message);
213     return true;
214   }
215
216   return false;
217 }
218
219 bool CGUIMediaWindow::OnBack(int actionID)
220 {
221   CURL filterUrl(m_strFilterPath);
222   if (actionID == ACTION_NAV_BACK && !m_vecItems->IsVirtualDirectoryRoot() &&
223      (m_vecItems->GetPath() != m_startDirectory || (m_canFilterAdvanced && filterUrl.HasOption("filter"))))
224   {
225     GoParentFolder();
226     return true;
227   }
228   return CGUIWindow::OnBack(actionID);
229 }
230
231 bool CGUIMediaWindow::OnMessage(CGUIMessage& message)
232 {
233   switch ( message.GetMessage() )
234   {
235   case GUI_MSG_WINDOW_DEINIT:
236     {
237       m_iSelectedItem = m_viewControl.GetSelectedItem();
238       m_iLastControl = GetFocusedControlID();
239       CGUIWindow::OnMessage(message);
240       CGUIDialogContextMenu* pDlg = (CGUIDialogContextMenu*)g_windowManager.GetWindow(WINDOW_DIALOG_CONTEXT_MENU);
241       if (pDlg && pDlg->IsActive())
242         pDlg->Close();
243
244       // get rid of any active filtering
245       if (m_canFilterAdvanced)
246       {
247         m_canFilterAdvanced = false;
248         m_filter.Reset();
249       }
250       m_strFilterPath.clear();
251       
252       // Call ClearFileItems() after our window has finished doing any WindowClose
253       // animations
254       ClearFileItems();
255       return true;
256     }
257     break;
258
259   case GUI_MSG_CLICKED:
260     {
261       int iControl = message.GetSenderId();
262       if (iControl == CONTROL_BTNVIEWASICONS)
263       {
264         // view as control could be a select button
265         int viewMode = 0;
266         const CGUIControl *control = GetControl(CONTROL_BTNVIEWASICONS);
267         if (control && control->GetControlType() != CGUIControl::GUICONTROL_BUTTON)
268         {
269           CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTNVIEWASICONS);
270           OnMessage(msg);
271           viewMode = m_viewControl.GetViewModeNumber(msg.GetParam1());
272         }
273         else
274           viewMode = m_viewControl.GetNextViewMode();
275
276         if (m_guiState.get())
277           m_guiState->SaveViewAsControl(viewMode);
278
279         UpdateButtons();
280         return true;
281       }
282       else if (iControl == CONTROL_BTNSORTASC) // sort asc
283       {
284         if (m_guiState.get())
285           m_guiState->SetNextSortOrder();
286         UpdateFileList();
287         return true;
288       }
289       else if (iControl == CONTROL_BTNSORTBY) // sort by
290       {
291         if (m_guiState.get())
292           m_guiState->SetNextSortMethod();
293         UpdateFileList();
294         return true;
295       }
296       else if (iControl == CONTROL_BTN_FILTER)
297         return Filter(false);
298       else if (m_viewControl.HasControl(iControl))  // list/thumb control
299       {
300         int iItem = m_viewControl.GetSelectedItem();
301         int iAction = message.GetParam1();
302         if (iItem < 0) break;
303         if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
304         {
305           OnSelect(iItem);
306         }
307         else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
308         {
309           OnPopupMenu(iItem);
310           return true;
311         }
312       }
313     }
314     break;
315
316   case GUI_MSG_SETFOCUS:
317     {
318       if (m_viewControl.HasControl(message.GetControlId()) && m_viewControl.GetCurrentControl() != message.GetControlId())
319       {
320         m_viewControl.SetFocused();
321         return true;
322       }
323     }
324     break;
325
326   case GUI_MSG_NOTIFY_ALL:
327     { // Message is received even if this window is inactive
328       if (message.GetParam1() == GUI_MSG_WINDOW_RESET)
329       {
330         m_vecItems->SetPath("?");
331         return true;
332       }
333       else if ( message.GetParam1() == GUI_MSG_REFRESH_THUMBS )
334       {
335         for (int i = 0; i < m_vecItems->Size(); i++)
336           m_vecItems->Get(i)->FreeMemory(true);
337         break;  // the window will take care of any info images
338       }
339       else if (message.GetParam1() == GUI_MSG_REMOVED_MEDIA)
340       {
341         if ((m_vecItems->IsVirtualDirectoryRoot() ||
342              m_vecItems->IsSourcesPath()) && IsActive())
343         {
344           int iItem = m_viewControl.GetSelectedItem();
345           Refresh();
346           m_viewControl.SetSelectedItem(iItem);
347         }
348         else if (m_vecItems->IsRemovable())
349         { // check that we have this removable share still
350           if (!m_rootDir.IsInSource(m_vecItems->GetPath()))
351           { // don't have this share any more
352             if (IsActive()) Update("");
353             else
354             {
355               m_history.ClearPathHistory();
356               m_vecItems->SetPath("");
357             }
358           }
359         }
360
361         return true;
362       }
363       else if (message.GetParam1()==GUI_MSG_UPDATE_SOURCES)
364       { // State of the sources changed, so update our view
365         if ((m_vecItems->IsVirtualDirectoryRoot() ||
366              m_vecItems->IsSourcesPath()) && IsActive())
367         {
368           int iItem = m_viewControl.GetSelectedItem();
369           Refresh();
370           m_viewControl.SetSelectedItem(iItem);
371         }
372         return true;
373       }
374       else if (message.GetParam1()==GUI_MSG_UPDATE && IsActive())
375       {
376         if (message.GetNumStringParams())
377         {
378           if (message.GetParam2()) // param2 is used for resetting the history
379             SetHistoryForPath(message.GetStringParam());
380
381           CFileItemList list(message.GetStringParam());
382           list.RemoveDiscCache(GetID());
383           Update(message.GetStringParam());
384         }
385         else
386           Refresh(true); // refresh the listing
387       }
388       else if (message.GetParam1()==GUI_MSG_UPDATE_ITEM && message.GetItem())
389       {
390         CFileItemPtr newItem = boost::static_pointer_cast<CFileItem>(message.GetItem());
391         if (IsActive())
392         {
393           if (m_vecItems->UpdateItem(newItem.get()) && message.GetParam2() == 1)
394           { // need the list updated as well
395             UpdateFileList();
396           }
397         }
398         else if (newItem)
399         { // need to remove the disc cache
400           CFileItemList items;
401           CStdString path;
402           URIUtils::GetDirectory(newItem->GetPath(), path);
403           items.SetPath(path);
404           items.RemoveDiscCache(GetID());
405         }
406       }
407       else if (message.GetParam1()==GUI_MSG_UPDATE_PATH)
408       {
409         if (IsActive())
410         {
411           if((message.GetStringParam() == m_vecItems->GetPath()) ||
412              (m_vecItems->IsMultiPath() && XFILE::CMultiPathDirectory::HasPath(m_vecItems->GetPath(), message.GetStringParam())))
413             Refresh();
414         }
415       }
416       else if (message.GetParam1() == GUI_MSG_FILTER_ITEMS && IsActive())
417       {
418         CStdString filter = GetProperty("filter").asString();
419         // check if this is meant for advanced filtering
420         if (message.GetParam2() != 10)
421         {
422           if (message.GetParam2() == 1) // append
423             filter += message.GetStringParam();
424           else if (message.GetParam2() == 2)
425           { // delete
426             if (filter.size())
427               filter = filter.Left(filter.size() - 1);
428           }
429           else
430             filter = message.GetStringParam();
431         }
432         OnFilterItems(filter);
433         return true;
434       }
435       else
436         return CGUIWindow::OnMessage(message);
437
438       return true;
439     }
440     break;
441   case GUI_MSG_PLAYBACK_STARTED:
442   case GUI_MSG_PLAYBACK_ENDED:
443   case GUI_MSG_PLAYBACK_STOPPED:
444   case GUI_MSG_PLAYLIST_CHANGED:
445   case GUI_MSG_PLAYLISTPLAYER_STOPPED:
446   case GUI_MSG_PLAYLISTPLAYER_STARTED:
447   case GUI_MSG_PLAYLISTPLAYER_CHANGED:
448     { // send a notify all to all controls on this window
449       CGUIMessage msg(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_REFRESH_LIST);
450       OnMessage(msg);
451       break;
452     }
453   case GUI_MSG_CHANGE_VIEW_MODE:
454     {
455       int viewMode = 0;
456       if (message.GetParam1())  // we have an id
457         viewMode = m_viewControl.GetViewModeByID(message.GetParam1());
458       else if (message.GetParam2())
459         viewMode = m_viewControl.GetNextViewMode((int)message.GetParam2());
460
461       if (m_guiState.get())
462         m_guiState->SaveViewAsControl(viewMode);
463       UpdateButtons();
464       return true;
465     }
466     break;
467   case GUI_MSG_CHANGE_SORT_METHOD:
468     {
469       if (m_guiState.get())
470       {
471         if (message.GetParam1())
472           m_guiState->SetCurrentSortMethod((int)message.GetParam1());
473         else if (message.GetParam2())
474           m_guiState->SetNextSortMethod((int)message.GetParam2());
475       }
476       UpdateFileList();
477       return true;
478     }
479     break;
480   case GUI_MSG_CHANGE_SORT_DIRECTION:
481     {
482       if (m_guiState.get())
483         m_guiState->SetNextSortOrder();
484       UpdateFileList();
485       return true;
486     }
487     break;
488   case GUI_MSG_WINDOW_INIT:
489     {
490       if (m_vecItems->GetPath() == "?")
491         m_vecItems->SetPath("");
492       CStdString dir = message.GetStringParam(0);
493       const CStdString &ret = message.GetStringParam(1);
494       bool returning = ret.CompareNoCase("return") == 0;
495       if (!dir.IsEmpty())
496       {
497         m_history.ClearPathHistory();
498         // ensure our directory is valid
499         dir = GetStartFolder(dir);
500         if (!returning || m_vecItems->GetPath().Left(dir.GetLength()) != dir)
501         { // we're not returning to the same path, so set our directory to the requested path
502           m_vecItems->SetPath(dir);
503         }
504         // check for network up
505         if (URIUtils::IsRemote(m_vecItems->GetPath()) && !WaitForNetwork())
506           m_vecItems->SetPath("");
507         SetHistoryForPath(m_vecItems->GetPath());
508       }
509       if (message.GetParam1() != WINDOW_INVALID)
510       { // first time to this window - make sure we set the root path
511         m_startDirectory = returning ? dir : "";
512       }
513     }
514     break;
515   }
516
517   return CGUIWindow::OnMessage(message);
518 }
519
520 // \brief Updates the states (enable, disable, visible...)
521 // of the controls defined by this window
522 // Override this function in a derived class to add new controls
523 void CGUIMediaWindow::UpdateButtons()
524 {
525   if (m_guiState.get())
526   {
527     // Update sorting controls
528     if (m_guiState->GetDisplaySortOrder() == SortOrderNone)
529     {
530       CONTROL_DISABLE(CONTROL_BTNSORTASC);
531     }
532     else
533     {
534       CONTROL_ENABLE(CONTROL_BTNSORTASC);
535       if (m_guiState->GetDisplaySortOrder() == SortOrderAscending)
536       {
537         CGUIMessage msg(GUI_MSG_DESELECTED, GetID(), CONTROL_BTNSORTASC);
538         g_windowManager.SendMessage(msg);
539       }
540       else
541       {
542         CGUIMessage msg(GUI_MSG_SELECTED, GetID(), CONTROL_BTNSORTASC);
543         g_windowManager.SendMessage(msg);
544       }
545     }
546
547     // Update list/thumb control
548     m_viewControl.SetCurrentView(m_guiState->GetViewAsControl());
549
550     // Update sort by button
551     if (m_guiState->GetSortMethod()==SORT_METHOD_NONE)
552     {
553       CONTROL_DISABLE(CONTROL_BTNSORTBY);
554     }
555     else
556     {
557       CONTROL_ENABLE(CONTROL_BTNSORTBY);
558     }
559     CStdString sortLabel;
560     sortLabel.Format(g_localizeStrings.Get(550).c_str(), g_localizeStrings.Get(m_guiState->GetSortMethodLabel()).c_str());
561     SET_CONTROL_LABEL(CONTROL_BTNSORTBY, sortLabel);
562   }
563
564   CStdString items;
565   items.Format("%i %s", m_vecItems->GetObjectCount(), g_localizeStrings.Get(127).c_str());
566   SET_CONTROL_LABEL(CONTROL_LABELFILES, items);
567
568   SET_CONTROL_LABEL2(CONTROL_BTN_FILTER, GetProperty("filter").asString());
569 }
570
571 void CGUIMediaWindow::ClearFileItems()
572 {
573   m_viewControl.Clear();
574   m_vecItems->Clear();
575   m_unfilteredItems->Clear();
576 }
577
578 // \brief Sorts Fileitems based on the sort method and sort oder provided by guiViewState
579 void CGUIMediaWindow::SortItems(CFileItemList &items)
580 {
581   auto_ptr<CGUIViewState> guiState(CGUIViewState::GetViewState(GetID(), items));
582
583   if (guiState.get())
584   {
585     bool sorted = false;
586     SORT_METHOD sortMethod = guiState->GetSortMethod();
587     // If the sort method is "sort by playlist" and we have a specific
588     // sort order available we can use the specified sort order to do the sorting
589     // We do this as the new SortBy methods are a superset of the SORT_METHOD methods, thus
590     // not all are available. This may be removed once SORT_METHOD_* have been replaced by
591     // SortBy.
592     if ((sortMethod == SORT_METHOD_PLAYLIST_ORDER) && items.HasProperty(PROPERTY_SORT_ORDER))
593     {
594       SortBy sortBy = (SortBy)items.GetProperty(PROPERTY_SORT_ORDER).asInteger();
595       if (sortBy != SortByNone && sortBy != SortByPlaylistOrder && sortBy != SortByProgramCount)
596       {
597         SortDescription sorting;
598         sorting.sortBy = sortBy;
599         sorting.sortOrder = items.GetProperty(PROPERTY_SORT_ASCENDING).asBoolean() ? SortOrderAscending : SortOrderDescending;
600         sorting.sortAttributes = g_guiSettings.GetBool("filelists.ignorethewhensorting") ? SortAttributeIgnoreArticle : SortAttributeNone;
601
602         // if the sort order is descending, we need to switch the original sort order, as we assume
603         // in CGUIViewState::AddPlaylistOrder that SORT_METHOD_PLAYLIST_ORDER is ascending.
604         if (guiState->GetDisplaySortOrder() == SortOrderDescending)
605           sorting.sortOrder = sorting.sortOrder == SortOrderDescending ? SortOrderAscending : SortOrderDescending;
606
607         items.Sort(sorting);
608         sorted = true;
609       }
610     }
611
612     if (!sorted)
613       items.Sort(sortMethod, guiState->GetDisplaySortOrder());
614   }
615 }
616
617 // \brief Formats item labels based on the formatting provided by guiViewState
618 void CGUIMediaWindow::FormatItemLabels(CFileItemList &items, const LABEL_MASKS &labelMasks)
619 {
620   CLabelFormatter fileFormatter(labelMasks.m_strLabelFile, labelMasks.m_strLabel2File);
621   CLabelFormatter folderFormatter(labelMasks.m_strLabelFolder, labelMasks.m_strLabel2Folder);
622   for (int i=0; i<items.Size(); ++i)
623   {
624     CFileItemPtr pItem=items[i];
625
626     if (pItem->IsLabelPreformated())
627       continue;
628
629     if (pItem->m_bIsFolder)
630       folderFormatter.FormatLabels(pItem.get());
631     else
632       fileFormatter.FormatLabels(pItem.get());
633   }
634
635   if(items.GetSortMethod() == SORT_METHOD_LABEL_IGNORE_THE
636   || items.GetSortMethod() == SORT_METHOD_LABEL)
637     items.ClearSortState();
638 }
639
640 // \brief Prepares and adds the fileitems list/thumb panel
641 void CGUIMediaWindow::FormatAndSort(CFileItemList &items)
642 {
643   auto_ptr<CGUIViewState> viewState(CGUIViewState::GetViewState(GetID(), items));
644
645   if (viewState.get())
646   {
647     LABEL_MASKS labelMasks;
648     viewState->GetSortMethodLabelMasks(labelMasks);
649     FormatItemLabels(items, labelMasks);
650
651     items.Sort(viewState->GetSortMethod(), viewState->GetDisplaySortOrder());
652   }
653 }
654
655 /*!
656   \brief Overwrite to fill fileitems from a source
657   \param strDirectory Path to read
658   \param items Fill with items specified in \e strDirectory
659   */
660 bool CGUIMediaWindow::GetDirectory(const CStdString &strDirectory, CFileItemList &items)
661 {
662   // cleanup items
663   if (items.Size())
664     items.Clear();
665
666   CStdString strParentPath = m_history.GetParentPath();
667
668   CLog::Log(LOGDEBUG,"CGUIMediaWindow::GetDirectory (%s)", strDirectory.c_str());
669   CLog::Log(LOGDEBUG,"  ParentPath = [%s]", strParentPath.c_str());
670
671   // see if we can load a previously cached folder
672   CFileItemList cachedItems(strDirectory);
673   if (!strDirectory.IsEmpty() && cachedItems.Load(GetID()))
674   {
675     items.Assign(cachedItems);
676   }
677   else
678   {
679     unsigned int time = XbmcThreads::SystemClockMillis();
680
681     if (strDirectory.IsEmpty())
682       SetupShares();
683
684     if (!m_rootDir.GetDirectory(strDirectory, items))
685       return false;
686
687     // took over a second, and not normally cached, so cache it
688     if ((XbmcThreads::SystemClockMillis() - time) > 1000  && items.CacheToDiscIfSlow())
689       items.Save(GetID());
690
691     // if these items should replace the current listing, then pop it off the top
692     if (items.GetReplaceListing())
693       m_history.RemoveParentPath();
694   }
695
696   if (m_guiState.get() && !m_guiState->HideParentDirItems() && !items.GetPath().IsEmpty())
697   {
698     CFileItemPtr pItem(new CFileItem(".."));
699     pItem->SetPath(strParentPath);
700     pItem->m_bIsFolder = true;
701     pItem->m_bIsShareOrDrive = false;
702     items.AddFront(pItem, 0);
703   }
704
705   int iWindow = GetID();
706   CStdStringArray regexps;
707
708   // TODO: Do we want to limit the directories we apply the video ones to?
709   if (iWindow == WINDOW_VIDEO_NAV)
710     regexps = g_advancedSettings.m_videoExcludeFromListingRegExps;
711   if (iWindow == WINDOW_MUSIC_FILES)
712     regexps = g_advancedSettings.m_audioExcludeFromListingRegExps;
713   if (iWindow == WINDOW_PICTURES)
714     regexps = g_advancedSettings.m_pictureExcludeFromListingRegExps;
715
716   if (regexps.size())
717   {
718     for (int i=0; i < items.Size();)
719     {
720       if (CUtil::ExcludeFileOrFolder(items[i]->GetPath(), regexps))
721         items.Remove(i);
722       else
723         i++;
724     }
725   }
726
727   // clear the filter
728   SetProperty("filter", "");
729   m_canFilterAdvanced = false;
730   m_filter.Reset();
731   return true;
732 }
733
734 // \brief Set window to a specific directory
735 // \param strDirectory The directory to be displayed in list/thumb control
736 // This function calls OnPrepareFileItems() and OnFinalizeFileItems()
737 bool CGUIMediaWindow::Update(const CStdString &strDirectory, bool updateFilterPath /* = true */)
738 {
739   // TODO: OnInitWindow calls Update() before window path has been set properly.
740   if (strDirectory == "?")
741     return false;
742
743   // get selected item
744   int iItem = m_viewControl.GetSelectedItem();
745   CStdString strSelectedItem = "";
746   if (iItem >= 0 && iItem < m_vecItems->Size())
747   {
748     CFileItemPtr pItem = m_vecItems->Get(iItem);
749     if (!pItem->IsParentFolder())
750     {
751       GetDirectoryHistoryString(pItem.get(), strSelectedItem);
752     }
753   }
754   
755   CStdString strCurrentDirectory = m_vecItems->GetPath();
756   m_history.SetSelectedItem(strSelectedItem, strCurrentDirectory);
757
758   CStdString directory = strDirectory;
759   // check if the path contains a filter and temporarily remove it
760   // so that the retrieved list of items is unfiltered
761   bool canfilter = CanContainFilter(directory);
762   CStdString filter;
763   CURL url(directory);
764   if (canfilter && url.HasOption("filter"))
765   {
766     filter = url.GetOption("filter");
767     directory = RemoveParameterFromPath(directory, "filter");
768   }
769
770   CFileItemList items;
771   if (!GetDirectory(directory, items))
772   {
773     CLog::Log(LOGERROR,"CGUIMediaWindow::GetDirectory(%s) failed", strDirectory.c_str());
774     // Try to return to the previous directory, if not the same
775     // else fallback to root
776     if (strDirectory.Equals(strCurrentDirectory) || !Update(m_history.RemoveParentPath()))
777       Update(""); // Fallback to root
778
779     // Return false to be able to eg. show
780     // an error message.
781     return false;
782   }
783
784   if (items.GetLabel().IsEmpty())
785     items.SetLabel(CUtil::GetTitleFromPath(items.GetPath(), true));
786   
787   ClearFileItems();
788   m_vecItems->Copy(items);
789
790   // only set the filter path if it hasn't been marked
791   // as preset or if it's empty
792   if (updateFilterPath || m_strFilterPath.empty())
793   {
794     if (items.HasProperty(PROPERTY_PATH_DB))
795       m_strFilterPath = items.GetProperty(PROPERTY_PATH_DB).asString();
796     else
797       m_strFilterPath = items.GetPath();
798   }
799   
800   // maybe the filter path can contain a filter
801   if (!canfilter && CanContainFilter(m_strFilterPath))
802     canfilter = true;
803
804   // check if the filter path contains a filter
805   CURL filterPathUrl(m_strFilterPath);
806   if (canfilter && filter.empty())
807   {
808     if (filterPathUrl.HasOption("filter"))
809       filter = filterPathUrl.GetOption("filter");
810   }
811
812   // check if there is a filter and re-apply it
813   if (canfilter && !filter.empty())
814   {
815     if (!m_filter.LoadFromJson(filter))
816     {
817       CLog::Log(LOGWARNING, "CGUIMediaWindow::Update: unable to load existing filter (%s)", filter.c_str());
818       m_filter.Reset();
819       m_strFilterPath = m_vecItems->GetPath();
820     }
821     else
822     {
823       // add the filter to the filter path
824       filterPathUrl.SetOption("filter", filter);
825       m_strFilterPath = filterPathUrl.Get();
826     }
827   }
828     
829   // if we're getting the root source listing
830   // make sure the path history is clean
831   if (strDirectory.IsEmpty())
832     m_history.ClearPathHistory();
833
834   int iWindow = GetID();
835   int showLabel = 0;
836   if (strDirectory.IsEmpty())
837   {
838     if (iWindow == WINDOW_PICTURES)
839       showLabel = 997;
840     else if (iWindow == WINDOW_MUSIC_FILES)
841       showLabel = 998;
842     else if (iWindow == WINDOW_FILES || iWindow == WINDOW_PROGRAMS)
843       showLabel = 1026;
844   }
845   if (strDirectory.Equals("sources://video/"))
846     showLabel = 999;
847   if (showLabel && (m_vecItems->Size() == 0 || !m_guiState->DisableAddSourceButtons())) // add 'add source button'
848   {
849     CStdString strLabel = g_localizeStrings.Get(showLabel);
850     CFileItemPtr pItem(new CFileItem(strLabel));
851     pItem->SetPath("add");
852     pItem->SetIconImage("DefaultAddSource.png");
853     pItem->SetLabel(strLabel);
854     pItem->SetLabelPreformated(true);
855     pItem->m_bIsFolder = true;
856     pItem->SetSpecialSort(SortSpecialOnBottom);
857     m_vecItems->Add(pItem);
858   }
859   m_iLastControl = GetFocusedControlID();
860
861   // Check whether to enabled advanced filtering based on the content type
862   m_canFilterAdvanced = CheckFilterAdvanced(*m_vecItems);
863   if (m_canFilterAdvanced)
864     m_filter.SetType(m_vecItems->GetContent());
865
866   //  Ask the derived class if it wants to load additional info
867   //  for the fileitems like media info or additional
868   //  filtering on the items, setting thumbs.
869   OnPrepareFileItems(*m_vecItems);
870
871   m_vecItems->FillInDefaultIcons();
872
873   m_guiState.reset(CGUIViewState::GetViewState(GetID(), *m_vecItems));
874
875   // remember the original (untouched) list of items (for filtering etc)
876   m_unfilteredItems->SetPath(m_vecItems->GetPath()); // use the original path - it'll likely be relied on for other things later.
877   m_unfilteredItems->Append(*m_vecItems);
878
879   // Cache the list of items if possible
880   OnCacheFileItems(*m_vecItems);
881
882   // Filter and group the items if necessary
883   OnFilterItems(GetProperty("filter").asString());
884
885   // Ask the devived class if it wants to do custom list operations,
886   // eg. changing the label
887   OnFinalizeFileItems(*m_vecItems);
888   UpdateButtons();
889
890   strSelectedItem = m_history.GetSelectedItem(m_vecItems->GetPath());
891
892   bool bSelectedFound = false;
893   //int iSongInDirectory = -1;
894   for (int i = 0; i < m_vecItems->Size(); ++i)
895   {
896     CFileItemPtr pItem = m_vecItems->Get(i);
897
898     // Update selected item
899     CStdString strHistory;
900     GetDirectoryHistoryString(pItem.get(), strHistory);
901     if (strHistory == strSelectedItem)
902     {
903       m_viewControl.SetSelectedItem(i);
904       bSelectedFound = true;
905       break;
906     }
907   }
908
909   // if we haven't found the selected item, select the first item
910   if (!bSelectedFound)
911     m_viewControl.SetSelectedItem(0);
912
913   if (iWindow != WINDOW_PVR || (iWindow == WINDOW_PVR && m_vecItems->GetPath().Left(17) == "pvr://recordings/"))
914     m_history.AddPath(m_vecItems->GetPath(), m_strFilterPath);
915
916   //m_history.DumpPathHistory();
917
918   return true;
919 }
920
921 bool CGUIMediaWindow::Refresh(bool clearCache /* = false */)
922 {
923   CStdString strCurrentDirectory = m_vecItems->GetPath();
924   if (strCurrentDirectory.Equals("?"))
925     return false;
926
927   if (clearCache)
928     m_vecItems->RemoveDiscCache(GetID());
929
930   // get the original number of items
931   if (!Update(strCurrentDirectory, false))
932     return false;
933
934   return true;
935 }
936
937 // \brief This function will be called by Update() before the
938 // labels of the fileitems are formatted. Override this function
939 // to set custom thumbs or load additional media info.
940 // It's used to load tag info for music.
941 void CGUIMediaWindow::OnPrepareFileItems(CFileItemList &items)
942 {
943
944 }
945
946 // \brief This function will be called by Update() before
947 // any additional formatting, filtering or sorting is applied.
948 // Override this function to define a custom caching behaviour.
949 void CGUIMediaWindow::OnCacheFileItems(CFileItemList &items)
950 {
951   // Should these items be saved to the hdd
952   if (items.CacheToDiscAlways() && !IsFiltered())
953     items.Save(GetID());
954 }
955
956 // \brief This function will be called by Update() after the
957 // labels of the fileitems are formatted. Override this function
958 // to modify the fileitems. Eg. to modify the item label
959 void CGUIMediaWindow::OnFinalizeFileItems(CFileItemList &items)
960 {
961
962 }
963
964 // \brief With this function you can react on a users click in the list/thumb panel.
965 // It returns true, if the click is handled.
966 // This function calls OnPlayMedia()
967 bool CGUIMediaWindow::OnClick(int iItem)
968 {
969   if ( iItem < 0 || iItem >= (int)m_vecItems->Size() ) return true;
970   CFileItemPtr pItem = m_vecItems->Get(iItem);
971
972   if (pItem->IsParentFolder())
973   {
974     GoParentFolder();
975     return true;
976   }
977   if (pItem->GetPath() == "add" || pItem->GetPath() == "sources://add/") // 'add source button' in empty root
978   {
979     OnContextButton(iItem, CONTEXT_BUTTON_ADD_SOURCE);
980     return true;
981   }
982
983   if (!pItem->m_bIsFolder && pItem->IsFileFolder())
984   {
985     XFILE::IFileDirectory *pFileDirectory = NULL;
986     pFileDirectory = XFILE::CFileDirectoryFactory::Create(pItem->GetPath(), pItem.get(), "");
987     if(pFileDirectory)
988       pItem->m_bIsFolder = true;
989     else if(pItem->m_bIsFolder)
990       pItem->m_bIsFolder = false;
991     delete pFileDirectory;
992   }
993
994   if (pItem->IsScript())
995   {
996     // execute the script
997     CURL url(pItem->GetPath());
998     AddonPtr addon;
999     if (CAddonMgr::Get().GetAddon(url.GetHostName(), addon, ADDON_SCRIPT))
1000     {
1001 #ifdef HAS_PYTHON
1002       if (!g_pythonParser.StopScript(addon->LibPath()))
1003         g_pythonParser.evalFile(addon->LibPath(),addon);
1004 #endif
1005       return true;
1006     }
1007   }
1008
1009   if (pItem->m_bIsFolder)
1010   {
1011     if ( pItem->m_bIsShareOrDrive )
1012     {
1013       const CStdString& strLockType=m_guiState->GetLockType();
1014       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE)
1015         if (!strLockType.IsEmpty() && !g_passwordManager.IsItemUnlocked(pItem.get(), strLockType))
1016             return true;
1017
1018       if (!HaveDiscOrConnection(pItem->GetPath(), pItem->m_iDriveType))
1019         return true;
1020     }
1021
1022     // check for the partymode playlist items - they may not exist yet
1023     if ((pItem->GetPath() == CProfilesManager::Get().GetUserDataItem("PartyMode.xsp")) ||
1024         (pItem->GetPath() == CProfilesManager::Get().GetUserDataItem("PartyMode-Video.xsp")))
1025     {
1026       // party mode playlist item - if it doesn't exist, prompt for user to define it
1027       if (!XFILE::CFile::Exists(pItem->GetPath()))
1028       {
1029         m_vecItems->RemoveDiscCache(GetID());
1030         if (CGUIDialogSmartPlaylistEditor::EditPlaylist(pItem->GetPath()))
1031           Refresh();
1032         return true;
1033       }
1034     }
1035
1036     // remove the directory cache if the folder is not normally cached
1037     CFileItemList items(pItem->GetPath());
1038     if (!items.AlwaysCache())
1039       items.RemoveDiscCache(GetID());
1040
1041     // if we have a filtered list, we need to add the filtered
1042     // path to be able to come back to the filtered view
1043     CStdString strCurrentDirectory = m_vecItems->GetPath();
1044     if (m_canFilterAdvanced && !m_filter.IsEmpty() &&
1045       !m_strFilterPath.Equals(strCurrentDirectory))
1046     {
1047       m_history.RemoveParentPath();
1048       m_history.AddPath(strCurrentDirectory, m_strFilterPath);
1049     }
1050
1051     CFileItem directory(*pItem);
1052     if (!Update(directory.GetPath()))
1053       ShowShareErrorMessage(&directory);
1054
1055     return true;
1056   }
1057   else if (pItem->IsPlugin() && !pItem->GetProperty("isplayable").asBoolean())
1058   {
1059     return XFILE::CPluginDirectory::RunScriptWithParams(pItem->GetPath());
1060   }
1061 #if defined(TARGET_ANDROID)
1062   else if (pItem->IsAndroidApp())
1063   {
1064     CStdString appName = URIUtils::GetFileName(pItem->GetPath());
1065     CLog::Log(LOGDEBUG, "CGUIMediaWindow::OnClick Trying to run: %s",appName.c_str());
1066     return CXBMCApp::StartActivity(appName);
1067   }
1068 #endif
1069   else
1070   {
1071     m_iSelectedItem = m_viewControl.GetSelectedItem();
1072
1073     if (pItem->GetPath() == "newplaylist://")
1074     {
1075       m_vecItems->RemoveDiscCache(GetID());
1076       g_windowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST_EDITOR,"newplaylist://");
1077       return true;
1078     }
1079     else if (pItem->GetPath().Left(19).Equals("newsmartplaylist://"))
1080     {
1081       m_vecItems->RemoveDiscCache(GetID());
1082       if (CGUIDialogSmartPlaylistEditor::NewPlaylist(pItem->GetPath().Mid(19)))
1083         Refresh();
1084       return true;
1085     }
1086     else if (pItem->GetPath().Left(14).Equals("addons://more/"))
1087     {
1088       CBuiltins::Execute("ActivateWindow(AddonBrowser,addons://all/xbmc.addon." + pItem->GetPath().Mid(14) + ",return)");
1089       return true;
1090     }
1091
1092     // If karaoke song is being played AND popup autoselector is enabled, the playlist should not be added
1093     bool do_not_add_karaoke = g_guiSettings.GetBool("karaoke.enabled") &&
1094       g_guiSettings.GetBool("karaoke.autopopupselector") && pItem->IsKaraoke();
1095     bool autoplay = m_guiState.get() && m_guiState->AutoPlayNextItem();
1096
1097     if (m_vecItems->IsPlugin())
1098     {
1099       CURL url(m_vecItems->GetPath());
1100       AddonPtr addon;
1101       if (CAddonMgr::Get().GetAddon(url.GetHostName(),addon))
1102       {
1103         PluginPtr plugin = boost::dynamic_pointer_cast<CPluginSource>(addon);
1104         if (plugin && plugin->Provides(CPluginSource::AUDIO))
1105         {
1106           CFileItemList items;
1107           auto_ptr<CGUIViewState> state(CGUIViewState::GetViewState(GetID(), items));
1108           autoplay = state.get() && state->AutoPlayNextItem();
1109         }
1110       }
1111     }
1112
1113     if (autoplay && !g_partyModeManager.IsEnabled() && 
1114         !pItem->IsPlayList() && !do_not_add_karaoke)
1115     {
1116       return OnPlayAndQueueMedia(pItem);
1117     }
1118     else
1119     {
1120       return OnPlayMedia(iItem);
1121     }
1122   }
1123
1124   return false;
1125 }
1126
1127 bool CGUIMediaWindow::OnSelect(int item)
1128 {
1129   return OnClick(item);
1130 }
1131
1132 // \brief Checks if there is a disc in the dvd drive and whether the
1133 // network is connected or not.
1134 bool CGUIMediaWindow::HaveDiscOrConnection(const CStdString& strPath, int iDriveType)
1135 {
1136   if (iDriveType==CMediaSource::SOURCE_TYPE_DVD)
1137   {
1138     if (!g_mediaManager.IsDiscInDrive(strPath))
1139     {
1140       CGUIDialogOK::ShowAndGetInput(218, 219, 0, 0);
1141       return false;
1142     }
1143   }
1144   else if (iDriveType==CMediaSource::SOURCE_TYPE_REMOTE)
1145   {
1146     // TODO: Handle not connected to a remote share
1147     if ( !g_application.getNetwork().IsConnected() )
1148     {
1149       CGUIDialogOK::ShowAndGetInput(220, 221, 0, 0);
1150       return false;
1151     }
1152   }
1153
1154   return true;
1155 }
1156
1157 // \brief Shows a standard errormessage for a given pItem.
1158 void CGUIMediaWindow::ShowShareErrorMessage(CFileItem* pItem)
1159 {
1160   int idMessageText = 0;
1161   CURL url(pItem->GetPath());
1162   const CStdString& strHostName = url.GetHostName();
1163
1164   if (url.GetProtocol() == "smb" && strHostName.IsEmpty()) //  smb workgroup
1165     idMessageText = 15303; // Workgroup not found
1166   else if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_REMOTE || URIUtils::IsRemote(pItem->GetPath()))
1167     idMessageText = 15301; // Could not connect to network server
1168   else
1169     idMessageText = 15300; // Path not found or invalid
1170
1171   CGUIDialogOK::ShowAndGetInput(220, idMessageText, 0, 0);
1172 }
1173
1174 // \brief The functon goes up one level in the directory tree
1175 void CGUIMediaWindow::GoParentFolder()
1176 {
1177   //m_history.DumpPathHistory();
1178
1179   // remove current directory if its on the stack
1180   // there were some issues due some folders having a trailing slash and some not
1181   // so just add a trailing slash to all of them for comparison.
1182   CStdString strPath = m_vecItems->GetPath();
1183   URIUtils::AddSlashAtEnd(strPath);
1184   CStdString strParent = m_history.GetParentPath();
1185   // in case the path history is messed up and the current folder is on
1186   // the stack more than once, keep going until there's nothing left or they
1187   // dont match anymore.
1188   while (!strParent.IsEmpty())
1189   {
1190     URIUtils::AddSlashAtEnd(strParent);
1191     if (strParent.Equals(strPath))
1192       m_history.RemoveParentPath();
1193     else
1194       break;
1195     strParent = m_history.GetParentPath();
1196   }
1197
1198   // remove the current filter but only if the parent
1199   // item doesn't have a filter as well
1200   CURL filterUrl(m_strFilterPath);
1201   if (filterUrl.HasOption("filter"))
1202   {
1203     CURL parentUrl(m_history.GetParentPath(true));
1204     if (!parentUrl.HasOption("filter"))
1205     {
1206       // we need to overwrite m_strFilterPath because
1207       // Refresh() will set updateFilterPath to false
1208       m_strFilterPath.clear();
1209       Refresh();
1210       return;
1211     }
1212   }
1213
1214   // if vector is not empty, pop parent
1215   // if vector is empty, parent is root source listing
1216   m_strFilterPath = m_history.GetParentPath(true);
1217   strParent = m_history.RemoveParentPath();
1218   if (!Update(strParent, false))
1219     return;
1220
1221   // No items to show so go another level up
1222   if (!m_vecItems->GetPath().empty() && (m_filter.IsEmpty() ? m_vecItems->Size() : m_unfilteredItems->Size()) <= 0)
1223   {
1224     CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(2080), g_localizeStrings.Get(2081));
1225     GoParentFolder();
1226   }
1227 }
1228
1229 // \brief Override the function to change the default behavior on how
1230 // a selected item history should look like
1231 void CGUIMediaWindow::GetDirectoryHistoryString(const CFileItem* pItem, CStdString& strHistoryString)
1232 {
1233   if (pItem->m_bIsShareOrDrive)
1234   {
1235     // We are in the virual directory
1236
1237     // History string of the DVD drive
1238     // must be handel separately
1239     if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_DVD)
1240     {
1241       // Remove disc label from item label
1242       // and use as history string, m_strPath
1243       // can change for new discs
1244       CStdString strLabel = pItem->GetLabel();
1245       int nPosOpen = strLabel.Find('(');
1246       int nPosClose = strLabel.ReverseFind(')');
1247       if (nPosOpen > -1 && nPosClose > -1 && nPosClose > nPosOpen)
1248       {
1249         strLabel.Delete(nPosOpen + 1, (nPosClose) - (nPosOpen + 1));
1250         strHistoryString = strLabel;
1251       }
1252       else
1253         strHistoryString = strLabel;
1254     }
1255     else
1256     {
1257       // Other items in virual directory
1258       CStdString strPath = pItem->GetPath();
1259       URIUtils::RemoveSlashAtEnd(strPath);
1260
1261       strHistoryString = pItem->GetLabel() + strPath;
1262     }
1263   }
1264   else if (pItem->m_lEndOffset>pItem->m_lStartOffset && pItem->m_lStartOffset != -1)
1265   {
1266     // Could be a cue item, all items of a cue share the same filename
1267     // so add the offsets to build the history string
1268     strHistoryString.Format("%ld%ld", pItem->m_lStartOffset, pItem->m_lEndOffset);
1269     strHistoryString += pItem->GetPath();
1270   }
1271   else
1272   {
1273     // Normal directory items
1274     strHistoryString = pItem->GetPath();
1275   }
1276
1277   // remove any filter
1278   if (CanContainFilter(strHistoryString))
1279     strHistoryString = RemoveParameterFromPath(strHistoryString, "filter");
1280
1281   URIUtils::RemoveSlashAtEnd(strHistoryString);
1282   strHistoryString.ToLower();
1283 }
1284
1285 // \brief Call this function to create a directory history for the
1286 // path given by strDirectory.
1287 void CGUIMediaWindow::SetHistoryForPath(const CStdString& strDirectory)
1288 {
1289   // Make sure our shares are configured
1290   SetupShares();
1291   if (!strDirectory.IsEmpty())
1292   {
1293     // Build the directory history for default path
1294     CStdString strPath, strParentPath;
1295     strPath = strDirectory;
1296     URIUtils::RemoveSlashAtEnd(strPath);
1297
1298     CFileItemList items;
1299     m_rootDir.GetDirectory("", items);
1300
1301     m_history.ClearPathHistory();
1302
1303     while (URIUtils::GetParentPath(strPath, strParentPath))
1304     {
1305       for (int i = 0; i < (int)items.Size(); ++i)
1306       {
1307         CFileItemPtr pItem = items[i];
1308         CStdString path(pItem->GetPath());
1309         URIUtils::RemoveSlashAtEnd(path);
1310         if (path == strPath)
1311         {
1312           CStdString strHistory;
1313           GetDirectoryHistoryString(pItem.get(), strHistory);
1314           m_history.SetSelectedItem(strHistory, "");
1315           URIUtils::AddSlashAtEnd(strPath);
1316           m_history.AddPathFront(strPath);
1317           m_history.AddPathFront("");
1318
1319           //m_history.DumpPathHistory();
1320           return ;
1321         }
1322       }
1323
1324       if (URIUtils::IsVideoDb(strPath))
1325       {
1326         CURL url(strParentPath);
1327         url.SetOptions(""); // clear any URL options from recreated parent path
1328         strParentPath = url.Get();
1329       }
1330
1331       URIUtils::AddSlashAtEnd(strPath);
1332       m_history.AddPathFront(strPath);
1333       m_history.SetSelectedItem(strPath, strParentPath);
1334       strPath = strParentPath;
1335       URIUtils::RemoveSlashAtEnd(strPath);
1336     }
1337   }
1338   else
1339     m_history.ClearPathHistory();
1340
1341   //m_history.DumpPathHistory();
1342 }
1343
1344 // \brief Override if you want to change the default behavior, what is done
1345 // when the user clicks on a file.
1346 // This function is called by OnClick()
1347 bool CGUIMediaWindow::OnPlayMedia(int iItem)
1348 {
1349   // Reset Playlistplayer, playback started now does
1350   // not use the playlistplayer.
1351   g_playlistPlayer.Reset();
1352   g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_NONE);
1353   CFileItemPtr pItem=m_vecItems->Get(iItem);
1354
1355   CLog::Log(LOGDEBUG, "%s %s", __FUNCTION__, pItem->GetPath().c_str());
1356
1357   bool bResult = false;
1358   if (pItem->IsInternetStream() || pItem->IsPlayList())
1359     bResult = g_application.PlayMedia(*pItem, m_guiState->GetPlaylist());
1360   else
1361     bResult = g_application.PlayFile(*pItem);
1362
1363   if (pItem->m_lStartOffset == STARTOFFSET_RESUME)
1364     pItem->m_lStartOffset = 0;
1365
1366   return bResult;
1367 }
1368
1369 // \brief Override if you want to change the default behavior of what is done
1370 // when the user clicks on a file in a "folder" with similar files.
1371 // This function is called by OnClick()
1372 bool CGUIMediaWindow::OnPlayAndQueueMedia(const CFileItemPtr &item)
1373 {
1374   //play and add current directory to temporary playlist
1375   int iPlaylist = m_guiState->GetPlaylist();
1376   if (iPlaylist != PLAYLIST_NONE)
1377   {
1378     g_playlistPlayer.ClearPlaylist(iPlaylist);
1379     g_playlistPlayer.Reset();
1380     int mediaToPlay = 0;
1381     for ( int i = 0; i < m_vecItems->Size(); i++ )
1382     {
1383       CFileItemPtr nItem = m_vecItems->Get(i);
1384
1385       if (nItem->m_bIsFolder)
1386         continue;
1387
1388       if (!nItem->IsPlayList() && !nItem->IsZIP() && !nItem->IsRAR())
1389         g_playlistPlayer.Add(iPlaylist, nItem);
1390
1391       if (item->IsSamePath(nItem.get()))
1392       { // item that was clicked
1393         mediaToPlay = g_playlistPlayer.GetPlaylist(iPlaylist).size() - 1;
1394       }
1395     }
1396
1397     // Save current window and directory to know where the selected item was
1398     if (m_guiState.get())
1399       m_guiState->SetPlaylistDirectory(m_vecItems->GetPath());
1400
1401     // figure out where we start playback
1402     if (g_playlistPlayer.IsShuffled(iPlaylist))
1403     {
1404       int iIndex = g_playlistPlayer.GetPlaylist(iPlaylist).FindOrder(mediaToPlay);
1405       g_playlistPlayer.GetPlaylist(iPlaylist).Swap(0, iIndex);
1406       mediaToPlay = 0;
1407     }
1408
1409     // play
1410     g_playlistPlayer.SetCurrentPlaylist(iPlaylist);
1411     g_playlistPlayer.Play(mediaToPlay);
1412   }
1413   return true;
1414 }
1415
1416 // \brief Synchonize the fileitems with the playlistplayer
1417 // It recreated the playlist of the playlistplayer based
1418 // on the fileitems of the window
1419 void CGUIMediaWindow::UpdateFileList()
1420 {
1421   int nItem = m_viewControl.GetSelectedItem();
1422   CStdString strSelected;
1423   if (nItem >= 0)
1424     strSelected = m_vecItems->Get(nItem)->GetPath();
1425
1426   FormatAndSort(*m_vecItems);
1427   UpdateButtons();
1428
1429   m_viewControl.SetItems(*m_vecItems);
1430   m_viewControl.SetSelectedItem(strSelected);
1431
1432   //  set the currently playing item as selected, if its in this directory
1433   if (m_guiState.get() && m_guiState->IsCurrentPlaylistDirectory(m_vecItems->GetPath()))
1434   {
1435     int iPlaylist=m_guiState->GetPlaylist();
1436     int nSong = g_playlistPlayer.GetCurrentSong();
1437     CFileItem playlistItem;
1438     if (nSong > -1 && iPlaylist > -1)
1439       playlistItem=*g_playlistPlayer.GetPlaylist(iPlaylist)[nSong];
1440
1441     g_playlistPlayer.ClearPlaylist(iPlaylist);
1442     g_playlistPlayer.Reset();
1443
1444     for (int i = 0; i < m_vecItems->Size(); i++)
1445     {
1446       CFileItemPtr pItem = m_vecItems->Get(i);
1447       if (pItem->m_bIsFolder)
1448         continue;
1449
1450       if (!pItem->IsPlayList() && !pItem->IsZIP() && !pItem->IsRAR())
1451         g_playlistPlayer.Add(iPlaylist, pItem);
1452
1453       if (pItem->GetPath() == playlistItem.GetPath() &&
1454           pItem->m_lStartOffset == playlistItem.m_lStartOffset)
1455         g_playlistPlayer.SetCurrentSong(g_playlistPlayer.GetPlaylist(iPlaylist).size() - 1);
1456     }
1457   }
1458 }
1459
1460 void CGUIMediaWindow::OnDeleteItem(int iItem)
1461 {
1462   if ( iItem < 0 || iItem >= m_vecItems->Size()) return;
1463   CFileItemPtr item = m_vecItems->Get(iItem);
1464
1465   if (item->IsPlayList())
1466     item->m_bIsFolder = false;
1467
1468   if (CProfilesManager::Get().GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE && CProfilesManager::Get().GetCurrentProfile().filesLocked())
1469     if (!g_passwordManager.IsMasterLockUnlocked(true))
1470       return;
1471
1472   if (!CFileUtils::DeleteItem(item))
1473     return;
1474   Refresh(true);
1475   m_viewControl.SetSelectedItem(iItem);
1476 }
1477
1478 void CGUIMediaWindow::OnRenameItem(int iItem)
1479 {
1480   if ( iItem < 0 || iItem >= m_vecItems->Size()) return;
1481
1482   if (CProfilesManager::Get().GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE && CProfilesManager::Get().GetCurrentProfile().filesLocked())
1483     if (!g_passwordManager.IsMasterLockUnlocked(true))
1484       return;
1485
1486   if (!CFileUtils::RenameFile(m_vecItems->Get(iItem)->GetPath()))
1487     return;
1488   Refresh(true);
1489   m_viewControl.SetSelectedItem(iItem);
1490 }
1491
1492 void CGUIMediaWindow::OnInitWindow()
1493 {
1494   // initial fetch is done unthreaded to ensure the items are setup prior to skin animations kicking off
1495   m_rootDir.SetAllowThreads(false);
1496   Refresh();
1497   m_rootDir.SetAllowThreads(true);
1498
1499   if (m_iSelectedItem > -1)
1500     m_viewControl.SetSelectedItem(m_iSelectedItem);
1501
1502   CGUIWindow::OnInitWindow();
1503 }
1504
1505 CGUIControl *CGUIMediaWindow::GetFirstFocusableControl(int id)
1506 {
1507   if (m_viewControl.HasControl(id))
1508     id = m_viewControl.GetCurrentControl();
1509   return CGUIWindow::GetFirstFocusableControl(id);
1510 }
1511
1512 void CGUIMediaWindow::SetupShares()
1513 {
1514   // Setup shares and filemasks for this window
1515   CFileItemList items;
1516   CGUIViewState* viewState=CGUIViewState::GetViewState(GetID(), items);
1517   if (viewState)
1518   {
1519     m_rootDir.SetMask(viewState->GetExtensions());
1520     m_rootDir.SetSources(viewState->GetSources());
1521     delete viewState;
1522   }
1523 }
1524
1525 bool CGUIMediaWindow::OnPopupMenu(int iItem)
1526 {
1527   // popup the context menu
1528   // grab our context menu
1529   CContextButtons buttons;
1530   GetContextButtons(iItem, buttons);
1531
1532   if (buttons.size())
1533   {
1534     // mark the item
1535     if (iItem >= 0 && iItem < m_vecItems->Size())
1536       m_vecItems->Get(iItem)->Select(true);
1537
1538     int choice = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
1539
1540     // deselect our item
1541     if (iItem >= 0 && iItem < m_vecItems->Size())
1542       m_vecItems->Get(iItem)->Select(false);
1543
1544     if (choice >= 0)
1545       return OnContextButton(iItem, (CONTEXT_BUTTON)choice);
1546   }
1547   return false;
1548 }
1549
1550 void CGUIMediaWindow::GetContextButtons(int itemNumber, CContextButtons &buttons)
1551 {
1552   CFileItemPtr item = (itemNumber >= 0 && itemNumber < m_vecItems->Size()) ? m_vecItems->Get(itemNumber) : CFileItemPtr();
1553
1554   if (!item)
1555     return;
1556
1557   // user added buttons
1558   CStdString label;
1559   CStdString action;
1560   for (int i = CONTEXT_BUTTON_USER1; i <= CONTEXT_BUTTON_USER10; i++)
1561   {
1562     label.Format("contextmenulabel(%i)", i - CONTEXT_BUTTON_USER1);
1563     if (item->GetProperty(label).empty())
1564       break;
1565
1566     action.Format("contextmenuaction(%i)", i - CONTEXT_BUTTON_USER1);
1567     if (item->GetProperty(action).empty())
1568       break;
1569
1570     buttons.Add((CONTEXT_BUTTON)i, item->GetProperty(label).asString());
1571   }
1572
1573   if (item->GetProperty("pluginreplacecontextitems").asBoolean())
1574     return;
1575
1576   // TODO: FAVOURITES Conditions on masterlock and localisation
1577   if (!item->IsParentFolder() && !item->GetPath().Equals("add") && !item->GetPath().Equals("newplaylist://") &&
1578       !item->GetPath().Left(19).Equals("newsmartplaylist://") && !item->GetPath().Left(9).Equals("newtag://"))
1579   {
1580     if (CFavourites::IsFavourite(item.get(), GetID()))
1581       buttons.Add(CONTEXT_BUTTON_ADD_FAVOURITE, 14077);     // Remove Favourite
1582     else
1583       buttons.Add(CONTEXT_BUTTON_ADD_FAVOURITE, 14076);     // Add To Favourites;
1584   }
1585 }
1586
1587 bool CGUIMediaWindow::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
1588 {
1589   switch (button)
1590   {
1591   case CONTEXT_BUTTON_ADD_FAVOURITE:
1592     {
1593       CFileItemPtr item = m_vecItems->Get(itemNumber);
1594       CFavourites::AddOrRemove(item.get(), GetID());
1595       return true;
1596     }
1597   case CONTEXT_BUTTON_PLUGIN_SETTINGS:
1598     {
1599       CFileItemPtr item = m_vecItems->Get(itemNumber);
1600       // CONTEXT_BUTTON_PLUGIN_SETTINGS can be called for plugin item
1601       // or script item; or for the plugin directory current listing.
1602       bool isPluginOrScriptItem = (item && (item->IsPlugin() || item->IsScript()));
1603       CURL plugin(isPluginOrScriptItem ? item->GetPath() : m_vecItems->GetPath());
1604       ADDON::AddonPtr addon;
1605       if (CAddonMgr::Get().GetAddon(plugin.GetHostName(), addon))
1606         if (CGUIDialogAddonSettings::ShowAndGetInput(addon))
1607           Refresh();
1608       return true;
1609     }
1610   case CONTEXT_BUTTON_USER1:
1611   case CONTEXT_BUTTON_USER2:
1612   case CONTEXT_BUTTON_USER3:
1613   case CONTEXT_BUTTON_USER4:
1614   case CONTEXT_BUTTON_USER5:
1615   case CONTEXT_BUTTON_USER6:
1616   case CONTEXT_BUTTON_USER7:
1617   case CONTEXT_BUTTON_USER8:
1618   case CONTEXT_BUTTON_USER9:
1619   case CONTEXT_BUTTON_USER10:
1620     {
1621       CStdString action;
1622       action.Format("contextmenuaction(%i)", button - CONTEXT_BUTTON_USER1);
1623       CApplicationMessenger::Get().ExecBuiltIn(m_vecItems->Get(itemNumber)->GetProperty(action).asString());
1624       return true;
1625     }
1626   default:
1627     break;
1628   }
1629   return false;
1630 }
1631
1632 const CGUIViewState *CGUIMediaWindow::GetViewState() const
1633 {
1634   return m_guiState.get();
1635 }
1636
1637 const CFileItemList& CGUIMediaWindow::CurrentDirectory() const
1638 {
1639   return *m_vecItems;
1640 }
1641
1642 bool CGUIMediaWindow::WaitForNetwork() const
1643 {
1644   if (g_application.getNetwork().IsAvailable())
1645     return true;
1646
1647   CGUIDialogProgress *progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
1648   if (!progress)
1649     return true;
1650
1651   CURL url(m_vecItems->GetPath());
1652   progress->SetHeading(1040); // Loading Directory
1653   progress->SetLine(1, url.GetWithoutUserDetails());
1654   progress->ShowProgressBar(false);
1655   progress->StartModal();
1656   while (!g_application.getNetwork().IsAvailable())
1657   {
1658     progress->Progress();
1659     if (progress->IsCanceled())
1660     {
1661       progress->Close();
1662       return false;
1663     }
1664   }
1665   progress->Close();
1666   return true;
1667 }
1668
1669 void CGUIMediaWindow::OnFilterItems(const CStdString &filter)
1670 {
1671   CFileItemPtr currentItem;
1672   CStdString currentItemPath;
1673   int item = m_viewControl.GetSelectedItem();
1674   if (item >= 0 && item < m_vecItems->Size())
1675   {
1676     currentItem = m_vecItems->Get(item);
1677     currentItemPath = currentItem->GetPath();
1678   }
1679   
1680   m_viewControl.Clear();
1681   
1682   CFileItemList items;
1683   items.Copy(*m_vecItems, false); // use the original path - it'll likely be relied on for other things later.
1684   items.Append(*m_unfilteredItems);
1685   bool filtered = GetFilteredItems(filter, items);
1686
1687   m_vecItems->ClearItems();
1688   // we need to clear the sort state and re-sort the items
1689   m_vecItems->ClearSortState();
1690   m_vecItems->Append(items);
1691   
1692   // if the filter has changed, get the new filter path
1693   if (filtered && m_canFilterAdvanced)
1694   {
1695     if (items.HasProperty(PROPERTY_PATH_DB))
1696       m_strFilterPath = items.GetProperty(PROPERTY_PATH_DB).asString();
1697     // only set m_strFilterPath if it hasn't been set before
1698     // otherwise we might overwrite it with a non-filter path
1699     // in case GetFilteredItems() returns true even though no
1700     // db-based filter (e.g. watched filter) has been applied
1701     else if (m_strFilterPath.empty())
1702       m_strFilterPath = items.GetPath();
1703   }
1704   
1705   GetGroupedItems(*m_vecItems);
1706   FormatAndSort(*m_vecItems);
1707
1708   // get the "filter" option
1709   CStdString filterOption;
1710   CURL filterUrl(m_strFilterPath);
1711   if (filterUrl.HasOption("filter"))
1712     filterOption = filterUrl.GetOption("filter");
1713
1714   // apply the "filter" option to any folder item so that
1715   // the filter can be passed down to the sub-directory
1716   for (int index = 0; index < m_vecItems->Size(); index++)
1717   {
1718     CFileItemPtr pItem = m_vecItems->Get(index);
1719     // if the item is a folder we need to copy the path of
1720     // the filtered item to be able to keep the applied filters
1721     if (pItem->m_bIsFolder)
1722     {
1723       CURL itemUrl(pItem->GetPath());
1724       if (!filterOption.empty())
1725         itemUrl.SetOption("filter", filterOption);
1726       else
1727         itemUrl.RemoveOption("filter");
1728       pItem->SetPath(itemUrl.Get());
1729     }
1730   }
1731
1732   SetProperty("filter", filter);
1733   if (filtered && m_canFilterAdvanced)
1734   {
1735     // to be able to select the same item as before we need to adjust
1736     // the path of the item i.e. add or remove the "filter=" URL option
1737     // but that's only necessary for folder items
1738     if (currentItem.get() != NULL && currentItem->m_bIsFolder)
1739     {
1740       CURL curUrl(currentItemPath), newUrl(m_strFilterPath);
1741       if (newUrl.HasOption("filter"))
1742         curUrl.SetOption("filter", newUrl.GetOption("filter"));
1743       else if (curUrl.HasOption("filter"))
1744         curUrl.RemoveOption("filter");
1745
1746       currentItemPath = curUrl.Get();
1747     }
1748   }
1749
1750   // The idea here is to ensure we have something to focus if our file list
1751   // is empty.  As such, this check MUST be last and ignore the hide parent
1752   // fileitems settings.
1753   if (m_vecItems->IsEmpty())
1754   {
1755     CFileItemPtr pItem(new CFileItem(".."));
1756     pItem->SetPath(m_history.GetParentPath());
1757     pItem->m_bIsFolder = true;
1758     pItem->m_bIsShareOrDrive = false;
1759     m_vecItems->AddFront(pItem, 0);
1760   }
1761
1762   // and update our view control + buttons
1763   m_viewControl.SetItems(*m_vecItems);
1764   m_viewControl.SetSelectedItem(currentItemPath);
1765   UpdateButtons();
1766 }
1767
1768 bool CGUIMediaWindow::GetFilteredItems(const CStdString &filter, CFileItemList &items)
1769 {
1770   bool result = false;
1771   if (m_canFilterAdvanced)
1772     result = GetAdvanceFilteredItems(items);
1773
1774   CStdString trimmedFilter(filter);
1775   trimmedFilter.TrimLeft().ToLower();
1776   
1777   if (trimmedFilter.IsEmpty())
1778     return result;
1779
1780   CFileItemList filteredItems(items.GetPath()); // use the original path - it'll likely be relied on for other things later.
1781   bool numericMatch = StringUtils::IsNaturalNumber(trimmedFilter);
1782   for (int i = 0; i < items.Size(); i++)
1783   {
1784     CFileItemPtr item = items.Get(i);
1785     if (item->IsParentFolder())
1786     {
1787       filteredItems.Add(item);
1788       continue;
1789     }
1790     // TODO: Need to update this to get all labels, ideally out of the displayed info (ie from m_layout and m_focusedLayout)
1791     // though that isn't practical.  Perhaps a better idea would be to just grab the info that we should filter on based on
1792     // where we are in the library tree.
1793     // Another idea is tying the filter string to the current level of the tree, so that going deeper disables the filter,
1794     // but it's re-enabled on the way back out.
1795     CStdString match;
1796     /*    if (item->GetFocusedLayout())
1797      match = item->GetFocusedLayout()->GetAllText();
1798      else if (item->GetLayout())
1799      match = item->GetLayout()->GetAllText();
1800      else*/
1801     match = item->GetLabel(); // Filter label only for now
1802     
1803     if (numericMatch)
1804       StringUtils::WordToDigits(match);
1805     
1806     size_t pos = StringUtils::FindWords(match.c_str(), trimmedFilter.c_str());
1807     if (pos != CStdString::npos)
1808       filteredItems.Add(item);
1809   }
1810
1811   items.ClearItems();
1812   items.Append(filteredItems);
1813
1814   return items.GetObjectCount() > 0;
1815 }
1816
1817 bool CGUIMediaWindow::GetAdvanceFilteredItems(CFileItemList &items)
1818 {
1819   // don't run the advanced filter if the filter is empty
1820   // and there hasn't been a filter applied before which
1821   // would have to be removed
1822   CURL url(m_strFilterPath);
1823   if (m_filter.IsEmpty() && !url.HasOption("filter"))
1824     return false;
1825
1826   CFileItemList resultItems;
1827   XFILE::CSmartPlaylistDirectory::GetDirectory(m_filter, resultItems, m_strFilterPath, true);
1828
1829   // put together a lookup map for faster path comparison
1830   map<CStdString, CFileItemPtr> lookup;
1831   for (int j = 0; j < resultItems.Size(); j++)
1832   {
1833     CStdString itemPath = RemoveParameterFromPath(resultItems[j]->GetPath(), "filter");
1834     itemPath.ToLower();
1835
1836     lookup[itemPath] = resultItems[j];
1837   }
1838
1839   // loop through all the original items and find
1840   // those which are still part of the filter
1841   CFileItemList filteredItems;
1842   for (int i = 0; i < items.Size(); i++)
1843   {
1844     CFileItemPtr item = items.Get(i);
1845     if (item->IsParentFolder())
1846     {
1847       filteredItems.Add(item);
1848       continue;
1849     }
1850
1851     // check if the item is part of the resultItems list
1852     // by comparing their paths (but ignoring any special
1853     // options because they differ from filter to filter)
1854     CStdString path = RemoveParameterFromPath(item->GetPath(), "filter");
1855     path.ToLower();
1856
1857     map<CStdString, CFileItemPtr>::iterator itItem = lookup.find(path);
1858     if (itItem != lookup.end())
1859     {
1860       // add the item to the list of filtered items
1861       filteredItems.Add(item);
1862
1863       // remove the item from the lists
1864       resultItems.Remove(itItem->second.get());
1865       lookup.erase(itItem);
1866     }
1867   }
1868
1869   if (resultItems.Size() > 0)
1870     CLog::Log(LOGWARNING, "CGUIMediaWindow::GetAdvanceFilteredItems(): %d unknown items", resultItems.Size());
1871
1872   items.ClearItems();
1873   items.Append(filteredItems);
1874   items.SetPath(resultItems.GetPath());
1875   if (resultItems.HasProperty(PROPERTY_PATH_DB))
1876     items.SetProperty(PROPERTY_PATH_DB, resultItems.GetProperty(PROPERTY_PATH_DB));
1877   return true;
1878 }
1879
1880 bool CGUIMediaWindow::IsFiltered()
1881 {
1882   return (!m_canFilterAdvanced && !GetProperty("filter").empty()) ||
1883          (m_canFilterAdvanced && !m_filter.IsEmpty());
1884 }
1885
1886 bool CGUIMediaWindow::Filter(bool advanced /* = true */)
1887 {
1888   // basic filtering
1889   if (!m_canFilterAdvanced || !advanced)
1890   {
1891     const CGUIControl *btnFilter = GetControl(CONTROL_BTN_FILTER);
1892     if (btnFilter != NULL && btnFilter->GetControlType() == CGUIControl::GUICONTROL_EDIT)
1893     { // filter updated
1894       CGUIMessage selected(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTN_FILTER);
1895       OnMessage(selected);
1896       OnFilterItems(selected.GetLabel());
1897       return true;
1898     }
1899     if (GetProperty("filter").empty())
1900     {
1901       CStdString filter = GetProperty("filter").asString();
1902       CGUIKeyboardFactory::ShowAndGetFilter(filter, false);
1903       SetProperty("filter", filter);
1904     }
1905     else
1906       OnFilterItems("");
1907   }
1908   // advanced filtering
1909   else
1910     CGUIDialogMediaFilter::ShowAndEditMediaFilter(m_strFilterPath, m_filter);
1911
1912   return true;
1913 }
1914
1915 CStdString CGUIMediaWindow::GetStartFolder(const CStdString &dir)
1916 {
1917   if (dir.Equals("$ROOT") || dir.Equals("Root"))
1918     return "";
1919   return dir;
1920 }
1921
1922 CStdString CGUIMediaWindow::RemoveParameterFromPath(const CStdString &strDirectory, const CStdString &strParameter)
1923 {
1924   CURL url(strDirectory);
1925   if (url.HasOption(strParameter))
1926   {
1927     url.RemoveOption(strParameter);
1928     return url.Get();
1929   }
1930
1931   return strDirectory;
1932 }