Merge pull request #4539 from Matricom/amcodec
[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.erase(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 = StringUtils::EqualsNoCase(ret, "return");
491       if (!dir.empty())
492       {
493         m_history.ClearPathHistory();
494         // ensure our directory is valid
495         dir = GetStartFolder(dir);
496         if (!returning || !StringUtils::StartsWith(m_vecItems->GetPath(), 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.empty() && cachedItems.Load(GetID()))
661   {
662     items.Assign(cachedItems);
663   }
664   else
665   {
666     unsigned int time = XbmcThreads::SystemClockMillis();
667
668     if (strDirectory.empty())
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().empty())
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().empty())
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.empty())
780     m_history.ClearPathHistory();
781
782   int iWindow = GetID();
783   int showLabel = 0;
784   if (strDirectory.empty())
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 ||
869       (iWindow == WINDOW_PVR && StringUtils::StartsWith(m_vecItems->GetPath(), "pvr://recordings/")))
870     m_history.AddPath(m_vecItems->GetPath(), m_strFilterPath);
871
872   //m_history.DumpPathHistory();
873
874   return true;
875 }
876
877 bool CGUIMediaWindow::Refresh(bool clearCache /* = false */)
878 {
879   CStdString strCurrentDirectory = m_vecItems->GetPath();
880   if (strCurrentDirectory.Equals("?"))
881     return false;
882
883   if (clearCache)
884     m_vecItems->RemoveDiscCache(GetID());
885
886   // get the original number of items
887   if (!Update(strCurrentDirectory, false))
888     return false;
889
890   return true;
891 }
892
893 // \brief This function will be called by Update() before the
894 // labels of the fileitems are formatted. Override this function
895 // to set custom thumbs or load additional media info.
896 // It's used to load tag info for music.
897 void CGUIMediaWindow::OnPrepareFileItems(CFileItemList &items)
898 {
899   CFileItemListModification::Get().Modify(items);
900 }
901
902 // \brief This function will be called by Update() before
903 // any additional formatting, filtering or sorting is applied.
904 // Override this function to define a custom caching behaviour.
905 void CGUIMediaWindow::OnCacheFileItems(CFileItemList &items)
906 {
907   // Should these items be saved to the hdd
908   if (items.CacheToDiscAlways() && !IsFiltered())
909     items.Save(GetID());
910 }
911
912 // \brief This function will be called by Update() after the
913 // labels of the fileitems are formatted. Override this function
914 // to modify the fileitems. Eg. to modify the item label
915 void CGUIMediaWindow::OnFinalizeFileItems(CFileItemList &items)
916 {
917
918 }
919
920 // \brief With this function you can react on a users click in the list/thumb panel.
921 // It returns true, if the click is handled.
922 // This function calls OnPlayMedia()
923 bool CGUIMediaWindow::OnClick(int iItem)
924 {
925   if ( iItem < 0 || iItem >= (int)m_vecItems->Size() ) return true;
926   CFileItemPtr pItem = m_vecItems->Get(iItem);
927
928   if (pItem->IsParentFolder())
929   {
930     GoParentFolder();
931     return true;
932   }
933   if (pItem->GetPath() == "add" || pItem->GetPath() == "sources://add/") // 'add source button' in empty root
934   {
935     OnContextButton(iItem, CONTEXT_BUTTON_ADD_SOURCE);
936     return true;
937   }
938
939   if (!pItem->m_bIsFolder && pItem->IsFileFolder(EFILEFOLDER_MASK_ONCLICK))
940   {
941     XFILE::IFileDirectory *pFileDirectory = NULL;
942     pFileDirectory = XFILE::CFileDirectoryFactory::Create(pItem->GetPath(), pItem.get(), "");
943     if(pFileDirectory)
944       pItem->m_bIsFolder = true;
945     else if(pItem->m_bIsFolder)
946       pItem->m_bIsFolder = false;
947     delete pFileDirectory;
948   }
949
950   if (pItem->IsScript())
951   {
952     // execute the script
953     CURL url(pItem->GetPath());
954     AddonPtr addon;
955     if (CAddonMgr::Get().GetAddon(url.GetHostName(), addon, ADDON_SCRIPT))
956     {
957       if (!CScriptInvocationManager::Get().Stop(addon->LibPath()))
958         CScriptInvocationManager::Get().Execute(addon->LibPath(), addon);
959       return true;
960     }
961   }
962
963   if (pItem->m_bIsFolder)
964   {
965     if ( pItem->m_bIsShareOrDrive )
966     {
967       const CStdString& strLockType=m_guiState->GetLockType();
968       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE)
969         if (!strLockType.empty() && !g_passwordManager.IsItemUnlocked(pItem.get(), strLockType))
970             return true;
971
972       if (!HaveDiscOrConnection(pItem->GetPath(), pItem->m_iDriveType))
973         return true;
974     }
975
976     // check for the partymode playlist items - they may not exist yet
977     if ((pItem->GetPath() == CProfilesManager::Get().GetUserDataItem("PartyMode.xsp")) ||
978         (pItem->GetPath() == CProfilesManager::Get().GetUserDataItem("PartyMode-Video.xsp")))
979     {
980       // party mode playlist item - if it doesn't exist, prompt for user to define it
981       if (!XFILE::CFile::Exists(pItem->GetPath()))
982       {
983         m_vecItems->RemoveDiscCache(GetID());
984         if (CGUIDialogSmartPlaylistEditor::EditPlaylist(pItem->GetPath()))
985           Refresh();
986         return true;
987       }
988     }
989
990     // remove the directory cache if the folder is not normally cached
991     CFileItemList items(pItem->GetPath());
992     if (!items.AlwaysCache())
993       items.RemoveDiscCache(GetID());
994
995     // if we have a filtered list, we need to add the filtered
996     // path to be able to come back to the filtered view
997     CStdString strCurrentDirectory = m_vecItems->GetPath();
998     if (m_canFilterAdvanced && !m_filter.IsEmpty() &&
999       !m_strFilterPath.Equals(strCurrentDirectory))
1000     {
1001       m_history.RemoveParentPath();
1002       m_history.AddPath(strCurrentDirectory, m_strFilterPath);
1003     }
1004
1005     CFileItem directory(*pItem);
1006     if (!Update(directory.GetPath()))
1007       ShowShareErrorMessage(&directory);
1008
1009     return true;
1010   }
1011   else if (pItem->IsPlugin() && !pItem->GetProperty("isplayable").asBoolean())
1012   {
1013     return XFILE::CPluginDirectory::RunScriptWithParams(pItem->GetPath());
1014   }
1015 #if defined(TARGET_ANDROID)
1016   else if (pItem->IsAndroidApp())
1017   {
1018     CStdString appName = URIUtils::GetFileName(pItem->GetPath());
1019     CLog::Log(LOGDEBUG, "CGUIMediaWindow::OnClick Trying to run: %s",appName.c_str());
1020     return CXBMCApp::StartActivity(appName);
1021   }
1022 #endif
1023   else
1024   {
1025     m_iSelectedItem = m_viewControl.GetSelectedItem();
1026
1027     if (pItem->GetPath() == "newplaylist://")
1028     {
1029       m_vecItems->RemoveDiscCache(GetID());
1030       g_windowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST_EDITOR,"newplaylist://");
1031       return true;
1032     }
1033     else if (StringUtils::StartsWithNoCase(pItem->GetPath(), "newsmartplaylist://"))
1034     {
1035       m_vecItems->RemoveDiscCache(GetID());
1036       if (CGUIDialogSmartPlaylistEditor::NewPlaylist(pItem->GetPath().substr(19)))
1037         Refresh();
1038       return true;
1039     }
1040     else if (StringUtils::StartsWithNoCase(pItem->GetPath(), "addons://more/"))
1041     {
1042       CBuiltins::Execute("ActivateWindow(AddonBrowser,addons://all/xbmc.addon." + pItem->GetPath().substr(14) + ",return)");
1043       return true;
1044     }
1045
1046     // If karaoke song is being played AND popup autoselector is enabled, the playlist should not be added
1047     bool do_not_add_karaoke = CSettings::Get().GetBool("karaoke.enabled") &&
1048       CSettings::Get().GetBool("karaoke.autopopupselector") && pItem->IsKaraoke();
1049     bool autoplay = m_guiState.get() && m_guiState->AutoPlayNextItem();
1050
1051     if (m_vecItems->IsPlugin())
1052     {
1053       CURL url(m_vecItems->GetPath());
1054       AddonPtr addon;
1055       if (CAddonMgr::Get().GetAddon(url.GetHostName(),addon))
1056       {
1057         PluginPtr plugin = boost::dynamic_pointer_cast<CPluginSource>(addon);
1058         if (plugin && plugin->Provides(CPluginSource::AUDIO))
1059         {
1060           CFileItemList items;
1061           auto_ptr<CGUIViewState> state(CGUIViewState::GetViewState(GetID(), items));
1062           autoplay = state.get() && state->AutoPlayNextItem();
1063         }
1064       }
1065     }
1066
1067     if (autoplay && !g_partyModeManager.IsEnabled() && 
1068         !pItem->IsPlayList() && !do_not_add_karaoke)
1069     {
1070       return OnPlayAndQueueMedia(pItem);
1071     }
1072     else
1073     {
1074       return OnPlayMedia(iItem);
1075     }
1076   }
1077
1078   return false;
1079 }
1080
1081 bool CGUIMediaWindow::OnSelect(int item)
1082 {
1083   return OnClick(item);
1084 }
1085
1086 // \brief Checks if there is a disc in the dvd drive and whether the
1087 // network is connected or not.
1088 bool CGUIMediaWindow::HaveDiscOrConnection(const CStdString& strPath, int iDriveType)
1089 {
1090   if (iDriveType==CMediaSource::SOURCE_TYPE_DVD)
1091   {
1092     if (!g_mediaManager.IsDiscInDrive(strPath))
1093     {
1094       CGUIDialogOK::ShowAndGetInput(218, 219, 0, 0);
1095       return false;
1096     }
1097   }
1098   else if (iDriveType==CMediaSource::SOURCE_TYPE_REMOTE)
1099   {
1100     // TODO: Handle not connected to a remote share
1101     if ( !g_application.getNetwork().IsConnected() )
1102     {
1103       CGUIDialogOK::ShowAndGetInput(220, 221, 0, 0);
1104       return false;
1105     }
1106   }
1107
1108   return true;
1109 }
1110
1111 // \brief Shows a standard errormessage for a given pItem.
1112 void CGUIMediaWindow::ShowShareErrorMessage(CFileItem* pItem)
1113 {
1114   if (!pItem->m_bIsShareOrDrive)
1115     return;
1116
1117   int idMessageText = 0;
1118   CURL url(pItem->GetPath());
1119   const CStdString& strHostName = url.GetHostName();
1120
1121   if (url.GetProtocol() == "smb" && strHostName.empty()) //  smb workgroup
1122     idMessageText = 15303; // Workgroup not found
1123   else if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_REMOTE || URIUtils::IsRemote(pItem->GetPath()))
1124     idMessageText = 15301; // Could not connect to network server
1125   else
1126     idMessageText = 15300; // Path not found or invalid
1127
1128   CGUIDialogOK::ShowAndGetInput(220, idMessageText, 0, 0);
1129 }
1130
1131 // \brief The functon goes up one level in the directory tree
1132 void CGUIMediaWindow::GoParentFolder()
1133 {
1134   //m_history.DumpPathHistory();
1135
1136   // remove current directory if its on the stack
1137   // there were some issues due some folders having a trailing slash and some not
1138   // so just add a trailing slash to all of them for comparison.
1139   CStdString strPath = m_vecItems->GetPath();
1140   URIUtils::AddSlashAtEnd(strPath);
1141   CStdString strParent = m_history.GetParentPath();
1142   // in case the path history is messed up and the current folder is on
1143   // the stack more than once, keep going until there's nothing left or they
1144   // dont match anymore.
1145   while (!strParent.empty())
1146   {
1147     URIUtils::AddSlashAtEnd(strParent);
1148     if (strParent.Equals(strPath))
1149       m_history.RemoveParentPath();
1150     else
1151       break;
1152     strParent = m_history.GetParentPath();
1153   }
1154
1155   // remove the current filter but only if the parent
1156   // item doesn't have a filter as well
1157   CURL filterUrl(m_strFilterPath);
1158   if (filterUrl.HasOption("filter"))
1159   {
1160     CURL parentUrl(m_history.GetParentPath(true));
1161     if (!parentUrl.HasOption("filter"))
1162     {
1163       // we need to overwrite m_strFilterPath because
1164       // Refresh() will set updateFilterPath to false
1165       m_strFilterPath.clear();
1166       Refresh();
1167       return;
1168     }
1169   }
1170
1171   // if vector is not empty, pop parent
1172   // if vector is empty, parent is root source listing
1173   m_strFilterPath = m_history.GetParentPath(true);
1174   strParent = m_history.RemoveParentPath();
1175   if (!Update(strParent, false))
1176     return;
1177
1178   // No items to show so go another level up
1179   if (!m_vecItems->GetPath().empty() && (m_filter.IsEmpty() ? m_vecItems->Size() : m_unfilteredItems->Size()) <= 0)
1180   {
1181     CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(2080), g_localizeStrings.Get(2081));
1182     GoParentFolder();
1183   }
1184 }
1185
1186 // \brief Override the function to change the default behavior on how
1187 // a selected item history should look like
1188 void CGUIMediaWindow::GetDirectoryHistoryString(const CFileItem* pItem, CStdString& strHistoryString)
1189 {
1190   if (pItem->m_bIsShareOrDrive)
1191   {
1192     // We are in the virual directory
1193
1194     // History string of the DVD drive
1195     // must be handel separately
1196     if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_DVD)
1197     {
1198       // Remove disc label from item label
1199       // and use as history string, m_strPath
1200       // can change for new discs
1201       CStdString strLabel = pItem->GetLabel();
1202       size_t nPosOpen = strLabel.find('(');
1203       size_t nPosClose = strLabel.rfind(')');
1204       if (nPosOpen != std::string::npos &&
1205           nPosClose != std::string::npos &&
1206           nPosClose > nPosOpen)
1207       {
1208         strLabel.erase(nPosOpen + 1, (nPosClose) - (nPosOpen + 1));
1209         strHistoryString = strLabel;
1210       }
1211       else
1212         strHistoryString = strLabel;
1213     }
1214     else
1215     {
1216       // Other items in virual directory
1217       CStdString strPath = pItem->GetPath();
1218       URIUtils::RemoveSlashAtEnd(strPath);
1219
1220       strHistoryString = pItem->GetLabel() + strPath;
1221     }
1222   }
1223   else if (pItem->m_lEndOffset>pItem->m_lStartOffset && pItem->m_lStartOffset != -1)
1224   {
1225     // Could be a cue item, all items of a cue share the same filename
1226     // so add the offsets to build the history string
1227     strHistoryString = StringUtils::Format("%ld%ld",
1228                                            pItem->m_lStartOffset,
1229                                            pItem->m_lEndOffset);
1230     strHistoryString += pItem->GetPath();
1231   }
1232   else
1233   {
1234     // Normal directory items
1235     strHistoryString = pItem->GetPath();
1236   }
1237
1238   // remove any filter
1239   if (CanContainFilter(strHistoryString))
1240     strHistoryString = RemoveParameterFromPath(strHistoryString, "filter");
1241
1242   URIUtils::RemoveSlashAtEnd(strHistoryString);
1243   StringUtils::ToLower(strHistoryString);
1244 }
1245
1246 // \brief Call this function to create a directory history for the
1247 // path given by strDirectory.
1248 void CGUIMediaWindow::SetHistoryForPath(const CStdString& strDirectory)
1249 {
1250   // Make sure our shares are configured
1251   SetupShares();
1252   if (!strDirectory.empty())
1253   {
1254     // Build the directory history for default path
1255     CStdString strPath, strParentPath;
1256     strPath = strDirectory;
1257     URIUtils::RemoveSlashAtEnd(strPath);
1258
1259     CFileItemList items;
1260     m_rootDir.GetDirectory("", items);
1261
1262     m_history.ClearPathHistory();
1263
1264     while (URIUtils::GetParentPath(strPath, strParentPath))
1265     {
1266       for (int i = 0; i < (int)items.Size(); ++i)
1267       {
1268         CFileItemPtr pItem = items[i];
1269         CStdString path(pItem->GetPath());
1270         URIUtils::RemoveSlashAtEnd(path);
1271         if (path == strPath)
1272         {
1273           CStdString strHistory;
1274           GetDirectoryHistoryString(pItem.get(), strHistory);
1275           m_history.SetSelectedItem(strHistory, "");
1276           URIUtils::AddSlashAtEnd(strPath);
1277           m_history.AddPathFront(strPath);
1278           m_history.AddPathFront("");
1279
1280           //m_history.DumpPathHistory();
1281           return ;
1282         }
1283       }
1284
1285       if (URIUtils::IsVideoDb(strPath))
1286       {
1287         CURL url(strParentPath);
1288         url.SetOptions(""); // clear any URL options from recreated parent path
1289         strParentPath = url.Get();
1290       }
1291
1292       URIUtils::AddSlashAtEnd(strPath);
1293       m_history.AddPathFront(strPath);
1294       m_history.SetSelectedItem(strPath, strParentPath);
1295       strPath = strParentPath;
1296       URIUtils::RemoveSlashAtEnd(strPath);
1297     }
1298   }
1299   else
1300     m_history.ClearPathHistory();
1301
1302   //m_history.DumpPathHistory();
1303 }
1304
1305 // \brief Override if you want to change the default behavior, what is done
1306 // when the user clicks on a file.
1307 // This function is called by OnClick()
1308 bool CGUIMediaWindow::OnPlayMedia(int iItem)
1309 {
1310   // Reset Playlistplayer, playback started now does
1311   // not use the playlistplayer.
1312   g_playlistPlayer.Reset();
1313   g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_NONE);
1314   CFileItemPtr pItem=m_vecItems->Get(iItem);
1315
1316   CLog::Log(LOGDEBUG, "%s %s", __FUNCTION__, CURL::GetRedacted(pItem->GetPath()).c_str());
1317
1318   bool bResult = false;
1319   if (pItem->IsInternetStream() || pItem->IsPlayList())
1320     bResult = g_application.PlayMedia(*pItem, m_guiState->GetPlaylist());
1321   else
1322     bResult = g_application.PlayFile(*pItem) == PLAYBACK_OK;
1323
1324   if (pItem->m_lStartOffset == STARTOFFSET_RESUME)
1325     pItem->m_lStartOffset = 0;
1326
1327   return bResult;
1328 }
1329
1330 // \brief Override if you want to change the default behavior of what is done
1331 // when the user clicks on a file in a "folder" with similar files.
1332 // This function is called by OnClick()
1333 bool CGUIMediaWindow::OnPlayAndQueueMedia(const CFileItemPtr &item)
1334 {
1335   //play and add current directory to temporary playlist
1336   int iPlaylist = m_guiState->GetPlaylist();
1337   if (iPlaylist != PLAYLIST_NONE)
1338   {
1339     g_playlistPlayer.ClearPlaylist(iPlaylist);
1340     g_playlistPlayer.Reset();
1341     int mediaToPlay = 0;
1342     
1343     // first try to find mainDVD file (VIDEO_TS.IFO). 
1344     // If we find this we should not allow to queue VOB files
1345     std::string mainDVD; 
1346     for (int i = 0; i < m_vecItems->Size(); i++) 
1347     { 
1348       std::string path = URIUtils::GetFileName(m_vecItems->Get(i)->GetPath()); 
1349       if (StringUtils::EqualsNoCase(path, "VIDEO_TS.IFO")) 
1350       { 
1351         mainDVD = path; 
1352         break; 
1353       } 
1354     }
1355
1356     // now queue...
1357     for ( int i = 0; i < m_vecItems->Size(); i++ )
1358     {
1359       CFileItemPtr nItem = m_vecItems->Get(i);
1360
1361       if (nItem->m_bIsFolder)
1362         continue;
1363
1364       if (!nItem->IsPlayList() && !nItem->IsZIP() && !nItem->IsRAR() && (!nItem->IsDVDFile() || (URIUtils::GetFileName(nItem->GetPath()) == mainDVD)))
1365         g_playlistPlayer.Add(iPlaylist, nItem);
1366
1367       if (item->IsSamePath(nItem.get()))
1368       { // item that was clicked
1369         mediaToPlay = g_playlistPlayer.GetPlaylist(iPlaylist).size() - 1;
1370       }
1371     }
1372
1373     // Save current window and directory to know where the selected item was
1374     if (m_guiState.get())
1375       m_guiState->SetPlaylistDirectory(m_vecItems->GetPath());
1376
1377     // figure out where we start playback
1378     if (g_playlistPlayer.IsShuffled(iPlaylist))
1379     {
1380       int iIndex = g_playlistPlayer.GetPlaylist(iPlaylist).FindOrder(mediaToPlay);
1381       g_playlistPlayer.GetPlaylist(iPlaylist).Swap(0, iIndex);
1382       mediaToPlay = 0;
1383     }
1384
1385     // play
1386     g_playlistPlayer.SetCurrentPlaylist(iPlaylist);
1387     g_playlistPlayer.Play(mediaToPlay);
1388   }
1389   return true;
1390 }
1391
1392 // \brief Synchonize the fileitems with the playlistplayer
1393 // It recreated the playlist of the playlistplayer based
1394 // on the fileitems of the window
1395 void CGUIMediaWindow::UpdateFileList()
1396 {
1397   int nItem = m_viewControl.GetSelectedItem();
1398   CStdString strSelected;
1399   if (nItem >= 0)
1400     strSelected = m_vecItems->Get(nItem)->GetPath();
1401
1402   FormatAndSort(*m_vecItems);
1403   UpdateButtons();
1404
1405   m_viewControl.SetItems(*m_vecItems);
1406   m_viewControl.SetSelectedItem(strSelected);
1407
1408   //  set the currently playing item as selected, if its in this directory
1409   if (m_guiState.get() && m_guiState->IsCurrentPlaylistDirectory(m_vecItems->GetPath()))
1410   {
1411     int iPlaylist=m_guiState->GetPlaylist();
1412     int nSong = g_playlistPlayer.GetCurrentSong();
1413     CFileItem playlistItem;
1414     if (nSong > -1 && iPlaylist > -1)
1415       playlistItem=*g_playlistPlayer.GetPlaylist(iPlaylist)[nSong];
1416
1417     g_playlistPlayer.ClearPlaylist(iPlaylist);
1418     g_playlistPlayer.Reset();
1419
1420     for (int i = 0; i < m_vecItems->Size(); i++)
1421     {
1422       CFileItemPtr pItem = m_vecItems->Get(i);
1423       if (pItem->m_bIsFolder)
1424         continue;
1425
1426       if (!pItem->IsPlayList() && !pItem->IsZIP() && !pItem->IsRAR())
1427         g_playlistPlayer.Add(iPlaylist, pItem);
1428
1429       if (pItem->GetPath() == playlistItem.GetPath() &&
1430           pItem->m_lStartOffset == playlistItem.m_lStartOffset)
1431         g_playlistPlayer.SetCurrentSong(g_playlistPlayer.GetPlaylist(iPlaylist).size() - 1);
1432     }
1433   }
1434 }
1435
1436 void CGUIMediaWindow::OnDeleteItem(int iItem)
1437 {
1438   if ( iItem < 0 || iItem >= m_vecItems->Size()) return;
1439   CFileItemPtr item = m_vecItems->Get(iItem);
1440
1441   if (item->IsPlayList())
1442     item->m_bIsFolder = false;
1443
1444   if (CProfilesManager::Get().GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE && CProfilesManager::Get().GetCurrentProfile().filesLocked())
1445     if (!g_passwordManager.IsMasterLockUnlocked(true))
1446       return;
1447
1448   if (!CFileUtils::DeleteItem(item))
1449     return;
1450   Refresh(true);
1451   m_viewControl.SetSelectedItem(iItem);
1452 }
1453
1454 void CGUIMediaWindow::OnRenameItem(int iItem)
1455 {
1456   if ( iItem < 0 || iItem >= m_vecItems->Size()) return;
1457
1458   if (CProfilesManager::Get().GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE && CProfilesManager::Get().GetCurrentProfile().filesLocked())
1459     if (!g_passwordManager.IsMasterLockUnlocked(true))
1460       return;
1461
1462   if (!CFileUtils::RenameFile(m_vecItems->Get(iItem)->GetPath()))
1463     return;
1464   Refresh(true);
1465   m_viewControl.SetSelectedItem(iItem);
1466 }
1467
1468 void CGUIMediaWindow::OnInitWindow()
1469 {
1470   // initial fetch is done unthreaded to ensure the items are setup prior to skin animations kicking off
1471   m_rootDir.SetAllowThreads(false);
1472
1473   // the start directory may change during Refresh
1474   bool updateStartDirectory = (m_startDirectory == m_vecItems->GetPath());
1475   Refresh();
1476   if (updateStartDirectory)
1477     m_startDirectory = m_vecItems->GetPath();
1478
1479   m_rootDir.SetAllowThreads(true);
1480
1481   if (m_iSelectedItem > -1)
1482     m_viewControl.SetSelectedItem(m_iSelectedItem);
1483
1484   CGUIWindow::OnInitWindow();
1485 }
1486
1487 CGUIControl *CGUIMediaWindow::GetFirstFocusableControl(int id)
1488 {
1489   if (m_viewControl.HasControl(id))
1490     id = m_viewControl.GetCurrentControl();
1491   return CGUIWindow::GetFirstFocusableControl(id);
1492 }
1493
1494 void CGUIMediaWindow::SetupShares()
1495 {
1496   // Setup shares and filemasks for this window
1497   CFileItemList items;
1498   CGUIViewState* viewState=CGUIViewState::GetViewState(GetID(), items);
1499   if (viewState)
1500   {
1501     m_rootDir.SetMask(viewState->GetExtensions());
1502     m_rootDir.SetSources(viewState->GetSources());
1503     delete viewState;
1504   }
1505 }
1506
1507 bool CGUIMediaWindow::OnPopupMenu(int iItem)
1508 {
1509   // popup the context menu
1510   // grab our context menu
1511   CContextButtons buttons;
1512   GetContextButtons(iItem, buttons);
1513
1514   if (buttons.size())
1515   {
1516     // mark the item
1517     if (iItem >= 0 && iItem < m_vecItems->Size())
1518       m_vecItems->Get(iItem)->Select(true);
1519
1520     int choice = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
1521
1522     // deselect our item
1523     if (iItem >= 0 && iItem < m_vecItems->Size())
1524       m_vecItems->Get(iItem)->Select(false);
1525
1526     if (choice >= 0)
1527       return OnContextButton(iItem, (CONTEXT_BUTTON)choice);
1528   }
1529   return false;
1530 }
1531
1532 void CGUIMediaWindow::GetContextButtons(int itemNumber, CContextButtons &buttons)
1533 {
1534   CFileItemPtr item = (itemNumber >= 0 && itemNumber < m_vecItems->Size()) ? m_vecItems->Get(itemNumber) : CFileItemPtr();
1535
1536   if (!item)
1537     return;
1538
1539   // user added buttons
1540   CStdString label;
1541   CStdString action;
1542   for (int i = CONTEXT_BUTTON_USER1; i <= CONTEXT_BUTTON_USER10; i++)
1543   {
1544     label = StringUtils::Format("contextmenulabel(%i)", i - CONTEXT_BUTTON_USER1);
1545     if (item->GetProperty(label).empty())
1546       break;
1547
1548     action = StringUtils::Format("contextmenuaction(%i)", i - CONTEXT_BUTTON_USER1);
1549     if (item->GetProperty(action).empty())
1550       break;
1551
1552     buttons.Add((CONTEXT_BUTTON)i, item->GetProperty(label).asString());
1553   }
1554
1555   if (item->GetProperty("pluginreplacecontextitems").asBoolean())
1556     return;
1557
1558   // TODO: FAVOURITES Conditions on masterlock and localisation
1559   if (!item->IsParentFolder() && !item->GetPath().Equals("add") && !item->GetPath().Equals("newplaylist://") &&
1560       !StringUtils::StartsWithNoCase(item->GetPath(), "newsmartplaylist://") && !StringUtils::StartsWithNoCase(item->GetPath(), "newtag://") &&
1561       !StringUtils::StartsWithNoCase(item->GetPath(), "addons://more/") && !StringUtils::StartsWithNoCase(item->GetPath(), "musicsearch://"))
1562   {
1563     if (XFILE::CFavouritesDirectory::IsFavourite(item.get(), GetID()))
1564       buttons.Add(CONTEXT_BUTTON_ADD_FAVOURITE, 14077);     // Remove Favourite
1565     else
1566       buttons.Add(CONTEXT_BUTTON_ADD_FAVOURITE, 14076);     // Add To Favourites;
1567   }
1568
1569   if (item->IsFileFolder(EFILEFOLDER_MASK_ONBROWSE))
1570     buttons.Add(CONTEXT_BUTTON_BROWSE_INTO, 37015);
1571
1572 }
1573
1574 bool CGUIMediaWindow::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
1575 {
1576   switch (button)
1577   {
1578   case CONTEXT_BUTTON_ADD_FAVOURITE:
1579     {
1580       CFileItemPtr item = m_vecItems->Get(itemNumber);
1581       XFILE::CFavouritesDirectory::AddOrRemove(item.get(), GetID());
1582       return true;
1583     }
1584   case CONTEXT_BUTTON_PLUGIN_SETTINGS:
1585     {
1586       CFileItemPtr item = m_vecItems->Get(itemNumber);
1587       // CONTEXT_BUTTON_PLUGIN_SETTINGS can be called for plugin item
1588       // or script item; or for the plugin directory current listing.
1589       bool isPluginOrScriptItem = (item && (item->IsPlugin() || item->IsScript()));
1590       CURL plugin(isPluginOrScriptItem ? item->GetPath() : m_vecItems->GetPath());
1591       ADDON::AddonPtr addon;
1592       if (CAddonMgr::Get().GetAddon(plugin.GetHostName(), addon))
1593         if (CGUIDialogAddonSettings::ShowAndGetInput(addon))
1594           Refresh();
1595       return true;
1596     }
1597   case CONTEXT_BUTTON_BROWSE_INTO:
1598     {
1599       CFileItemPtr item = m_vecItems->Get(itemNumber);
1600       if(Update(item->GetPath()))
1601         return true;
1602       return true;
1603     }
1604   case CONTEXT_BUTTON_USER1:
1605   case CONTEXT_BUTTON_USER2:
1606   case CONTEXT_BUTTON_USER3:
1607   case CONTEXT_BUTTON_USER4:
1608   case CONTEXT_BUTTON_USER5:
1609   case CONTEXT_BUTTON_USER6:
1610   case CONTEXT_BUTTON_USER7:
1611   case CONTEXT_BUTTON_USER8:
1612   case CONTEXT_BUTTON_USER9:
1613   case CONTEXT_BUTTON_USER10:
1614     {
1615       CStdString action = StringUtils::Format("contextmenuaction(%i)", button - CONTEXT_BUTTON_USER1);
1616       CApplicationMessenger::Get().ExecBuiltIn(m_vecItems->Get(itemNumber)->GetProperty(action).asString());
1617       return true;
1618     }
1619   default:
1620     break;
1621   }
1622   return false;
1623 }
1624
1625 const CGUIViewState *CGUIMediaWindow::GetViewState() const
1626 {
1627   return m_guiState.get();
1628 }
1629
1630 const CFileItemList& CGUIMediaWindow::CurrentDirectory() const
1631 {
1632   return *m_vecItems;
1633 }
1634
1635 bool CGUIMediaWindow::WaitForNetwork() const
1636 {
1637   if (g_application.getNetwork().IsAvailable())
1638     return true;
1639
1640   CGUIDialogProgress *progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
1641   if (!progress)
1642     return true;
1643
1644   CURL url(m_vecItems->GetPath());
1645   progress->SetHeading(1040); // Loading Directory
1646   progress->SetLine(1, url.GetWithoutUserDetails());
1647   progress->ShowProgressBar(false);
1648   progress->StartModal();
1649   while (!g_application.getNetwork().IsAvailable())
1650   {
1651     progress->Progress();
1652     if (progress->IsCanceled())
1653     {
1654       progress->Close();
1655       return false;
1656     }
1657   }
1658   progress->Close();
1659   return true;
1660 }
1661
1662 void CGUIMediaWindow::UpdateFilterPath(const CStdString &strDirectory, const CFileItemList &items, bool updateFilterPath)
1663 {
1664   bool canfilter = CanContainFilter(strDirectory);
1665
1666   CStdString filter;
1667   CURL url(strDirectory);
1668   if (canfilter && url.HasOption("filter"))
1669     filter = url.GetOption("filter");
1670
1671   // only set the filter path if it hasn't been marked
1672   // as preset or if it's empty
1673   if (updateFilterPath || m_strFilterPath.empty())
1674   {
1675     if (items.HasProperty(PROPERTY_PATH_DB))
1676       m_strFilterPath = items.GetProperty(PROPERTY_PATH_DB).asString();
1677     else
1678       m_strFilterPath = items.GetPath();
1679   }
1680   
1681   // maybe the filter path can contain a filter
1682   if (!canfilter && CanContainFilter(m_strFilterPath))
1683     canfilter = true;
1684
1685   // check if the filter path contains a filter
1686   CURL filterPathUrl(m_strFilterPath);
1687   if (canfilter && filter.empty())
1688   {
1689     if (filterPathUrl.HasOption("filter"))
1690       filter = filterPathUrl.GetOption("filter");
1691   }
1692
1693   // check if there is a filter and re-apply it
1694   if (canfilter && !filter.empty())
1695   {
1696     if (!m_filter.LoadFromJson(filter))
1697     {
1698       CLog::Log(LOGWARNING, "CGUIMediaWindow::UpdateFilterPath(): unable to load existing filter (%s)", filter.c_str());
1699       m_filter.Reset();
1700       m_strFilterPath = m_vecItems->GetPath();
1701     }
1702     else
1703     {
1704       // add the filter to the filter path
1705       filterPathUrl.SetOption("filter", filter);
1706       m_strFilterPath = filterPathUrl.Get();
1707     }
1708   }
1709 }
1710
1711 void CGUIMediaWindow::OnFilterItems(const CStdString &filter)
1712 {
1713   CFileItemPtr currentItem;
1714   CStdString currentItemPath;
1715   int item = m_viewControl.GetSelectedItem();
1716   if (item >= 0 && item < m_vecItems->Size())
1717   {
1718     currentItem = m_vecItems->Get(item);
1719     currentItemPath = currentItem->GetPath();
1720   }
1721   
1722   m_viewControl.Clear();
1723   
1724   CFileItemList items;
1725   items.Copy(*m_vecItems, false); // use the original path - it'll likely be relied on for other things later.
1726   items.Append(*m_unfilteredItems);
1727   bool filtered = GetFilteredItems(filter, items);
1728
1729   m_vecItems->ClearItems();
1730   // we need to clear the sort state and re-sort the items
1731   m_vecItems->ClearSortState();
1732   m_vecItems->Append(items);
1733   
1734   // if the filter has changed, get the new filter path
1735   if (filtered && m_canFilterAdvanced)
1736   {
1737     if (items.HasProperty(PROPERTY_PATH_DB))
1738       m_strFilterPath = items.GetProperty(PROPERTY_PATH_DB).asString();
1739     // only set m_strFilterPath if it hasn't been set before
1740     // otherwise we might overwrite it with a non-filter path
1741     // in case GetFilteredItems() returns true even though no
1742     // db-based filter (e.g. watched filter) has been applied
1743     else if (m_strFilterPath.empty())
1744       m_strFilterPath = items.GetPath();
1745   }
1746   
1747   GetGroupedItems(*m_vecItems);
1748   FormatAndSort(*m_vecItems);
1749
1750   // get the "filter" option
1751   CStdString filterOption;
1752   CURL filterUrl(m_strFilterPath);
1753   if (filterUrl.HasOption("filter"))
1754     filterOption = filterUrl.GetOption("filter");
1755
1756   // apply the "filter" option to any folder item so that
1757   // the filter can be passed down to the sub-directory
1758   for (int index = 0; index < m_vecItems->Size(); index++)
1759   {
1760     CFileItemPtr pItem = m_vecItems->Get(index);
1761     // if the item is a folder we need to copy the path of
1762     // the filtered item to be able to keep the applied filters
1763     if (pItem->m_bIsFolder)
1764     {
1765       CURL itemUrl(pItem->GetPath());
1766       if (!filterOption.empty())
1767         itemUrl.SetOption("filter", filterOption);
1768       else
1769         itemUrl.RemoveOption("filter");
1770       pItem->SetPath(itemUrl.Get());
1771     }
1772   }
1773
1774   SetProperty("filter", filter);
1775   if (filtered && m_canFilterAdvanced)
1776   {
1777     // to be able to select the same item as before we need to adjust
1778     // the path of the item i.e. add or remove the "filter=" URL option
1779     // but that's only necessary for folder items
1780     if (currentItem.get() != NULL && currentItem->m_bIsFolder)
1781     {
1782       CURL curUrl(currentItemPath), newUrl(m_strFilterPath);
1783       if (newUrl.HasOption("filter"))
1784         curUrl.SetOption("filter", newUrl.GetOption("filter"));
1785       else if (curUrl.HasOption("filter"))
1786         curUrl.RemoveOption("filter");
1787
1788       currentItemPath = curUrl.Get();
1789     }
1790   }
1791
1792   // The idea here is to ensure we have something to focus if our file list
1793   // is empty.  As such, this check MUST be last and ignore the hide parent
1794   // fileitems settings.
1795   if (m_vecItems->IsEmpty())
1796   {
1797     CFileItemPtr pItem(new CFileItem(".."));
1798     pItem->SetPath(m_history.GetParentPath());
1799     pItem->m_bIsFolder = true;
1800     pItem->m_bIsShareOrDrive = false;
1801     m_vecItems->AddFront(pItem, 0);
1802   }
1803
1804   // and update our view control + buttons
1805   m_viewControl.SetItems(*m_vecItems);
1806   m_viewControl.SetSelectedItem(currentItemPath);
1807   UpdateButtons();
1808 }
1809
1810 bool CGUIMediaWindow::GetFilteredItems(const CStdString &filter, CFileItemList &items)
1811 {
1812   bool result = false;
1813   if (m_canFilterAdvanced)
1814     result = GetAdvanceFilteredItems(items);
1815
1816   CStdString trimmedFilter(filter);
1817   StringUtils::TrimLeft(trimmedFilter);
1818   StringUtils::ToLower(trimmedFilter);
1819   
1820   if (trimmedFilter.empty())
1821     return result;
1822
1823   CFileItemList filteredItems(items.GetPath()); // use the original path - it'll likely be relied on for other things later.
1824   bool numericMatch = StringUtils::IsNaturalNumber(trimmedFilter);
1825   for (int i = 0; i < items.Size(); i++)
1826   {
1827     CFileItemPtr item = items.Get(i);
1828     if (item->IsParentFolder())
1829     {
1830       filteredItems.Add(item);
1831       continue;
1832     }
1833     // TODO: Need to update this to get all labels, ideally out of the displayed info (ie from m_layout and m_focusedLayout)
1834     // though that isn't practical.  Perhaps a better idea would be to just grab the info that we should filter on based on
1835     // where we are in the library tree.
1836     // Another idea is tying the filter string to the current level of the tree, so that going deeper disables the filter,
1837     // but it's re-enabled on the way back out.
1838     CStdString match;
1839     /*    if (item->GetFocusedLayout())
1840      match = item->GetFocusedLayout()->GetAllText();
1841      else if (item->GetLayout())
1842      match = item->GetLayout()->GetAllText();
1843      else*/
1844     match = item->GetLabel(); // Filter label only for now
1845     
1846     if (numericMatch)
1847       StringUtils::WordToDigits(match);
1848     
1849     size_t pos = StringUtils::FindWords(match.c_str(), trimmedFilter.c_str());
1850     if (pos != CStdString::npos)
1851       filteredItems.Add(item);
1852   }
1853
1854   items.ClearItems();
1855   items.Append(filteredItems);
1856
1857   return items.GetObjectCount() > 0;
1858 }
1859
1860 bool CGUIMediaWindow::GetAdvanceFilteredItems(CFileItemList &items)
1861 {
1862   // don't run the advanced filter if the filter is empty
1863   // and there hasn't been a filter applied before which
1864   // would have to be removed
1865   CURL url(m_strFilterPath);
1866   if (m_filter.IsEmpty() && !url.HasOption("filter"))
1867     return false;
1868
1869   CFileItemList resultItems;
1870   XFILE::CSmartPlaylistDirectory::GetDirectory(m_filter, resultItems, m_strFilterPath, true);
1871
1872   // put together a lookup map for faster path comparison
1873   map<CStdString, CFileItemPtr> lookup;
1874   for (int j = 0; j < resultItems.Size(); j++)
1875   {
1876     CStdString itemPath = RemoveParameterFromPath(resultItems[j]->GetPath(), "filter");
1877     StringUtils::ToLower(itemPath);
1878
1879     lookup[itemPath] = resultItems[j];
1880   }
1881
1882   // loop through all the original items and find
1883   // those which are still part of the filter
1884   CFileItemList filteredItems;
1885   for (int i = 0; i < items.Size(); i++)
1886   {
1887     CFileItemPtr item = items.Get(i);
1888     if (item->IsParentFolder())
1889     {
1890       filteredItems.Add(item);
1891       continue;
1892     }
1893
1894     // check if the item is part of the resultItems list
1895     // by comparing their paths (but ignoring any special
1896     // options because they differ from filter to filter)
1897     CStdString path = RemoveParameterFromPath(item->GetPath(), "filter");
1898     StringUtils::ToLower(path);
1899
1900     map<CStdString, CFileItemPtr>::iterator itItem = lookup.find(path);
1901     if (itItem != lookup.end())
1902     {
1903       // add the item to the list of filtered items
1904       filteredItems.Add(item);
1905
1906       // remove the item from the lists
1907       resultItems.Remove(itItem->second.get());
1908       lookup.erase(itItem);
1909     }
1910   }
1911
1912   if (resultItems.Size() > 0)
1913     CLog::Log(LOGWARNING, "CGUIMediaWindow::GetAdvanceFilteredItems(): %d unknown items", resultItems.Size());
1914
1915   items.ClearItems();
1916   items.Append(filteredItems);
1917   items.SetPath(resultItems.GetPath());
1918   if (resultItems.HasProperty(PROPERTY_PATH_DB))
1919     items.SetProperty(PROPERTY_PATH_DB, resultItems.GetProperty(PROPERTY_PATH_DB));
1920   return true;
1921 }
1922
1923 bool CGUIMediaWindow::IsFiltered()
1924 {
1925   return (!m_canFilterAdvanced && !GetProperty("filter").empty()) ||
1926          (m_canFilterAdvanced && !m_filter.IsEmpty());
1927 }
1928
1929 bool CGUIMediaWindow::Filter(bool advanced /* = true */)
1930 {
1931   // basic filtering
1932   if (!m_canFilterAdvanced || !advanced)
1933   {
1934     const CGUIControl *btnFilter = GetControl(CONTROL_BTN_FILTER);
1935     if (btnFilter != NULL && btnFilter->GetControlType() == CGUIControl::GUICONTROL_EDIT)
1936     { // filter updated
1937       CGUIMessage selected(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTN_FILTER);
1938       OnMessage(selected);
1939       OnFilterItems(selected.GetLabel());
1940       return true;
1941     }
1942     if (GetProperty("filter").empty())
1943     {
1944       CStdString filter = GetProperty("filter").asString();
1945       CGUIKeyboardFactory::ShowAndGetFilter(filter, false);
1946       SetProperty("filter", filter);
1947     }
1948     else
1949       OnFilterItems("");
1950   }
1951   // advanced filtering
1952   else
1953     CGUIDialogMediaFilter::ShowAndEditMediaFilter(m_strFilterPath, m_filter);
1954
1955   return true;
1956 }
1957
1958 CStdString CGUIMediaWindow::GetStartFolder(const CStdString &dir)
1959 {
1960   if (dir.Equals("$ROOT") || dir.Equals("Root"))
1961     return "";
1962   return dir;
1963 }
1964
1965 CStdString CGUIMediaWindow::RemoveParameterFromPath(const CStdString &strDirectory, const CStdString &strParameter)
1966 {
1967   CURL url(strDirectory);
1968   if (url.HasOption(strParameter))
1969   {
1970     url.RemoveOption(strParameter);
1971     return url.Get();
1972   }
1973
1974   return strDirectory;
1975 }