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