fixed: auto play next item didn't work for music add-ons
[xbmc:xbmc-antiquated.git] / xbmc / GUIMediaWindow.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
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/ZipManager.h"
29 #include "FileSystem/PluginDirectory.h"
30 #include "FileSystem/MultiPathDirectory.h"
31 #include "GUIPassword.h"
32 #include "Application.h"
33 #include "utils/Network.h"
34 #include "utils/RegExp.h"
35 #include "PartyModeManager.h"
36 #include "GUIDialogMediaSource.h"
37 #include "GUIWindowFileManager.h"
38 #include "Favourites.h"
39 #include "utils/LabelFormatter.h"
40 #include "GUIDialogProgress.h"
41 #include "AdvancedSettings.h"
42 #include "GUISettings.h"
43
44 #include "GUIDialogSmartPlaylistEditor.h"
45 #include "GUIDialogAddonSettings.h"
46 #include "GUIDialogYesNo.h"
47 #include "GUIWindowManager.h"
48 #include "GUIDialogOK.h"
49 #include "PlayList.h"
50 #include "MediaManager.h"
51 #include "Settings.h"
52 #include "StringUtils.h"
53 #include "LocalizeStrings.h"
54 #include "utils/TimeUtils.h"
55 #include "FactoryFileDirectory.h"
56 #include "utils/log.h"
57 #include "utils/FileUtils.h"
58 #include "GUIEditControl.h"
59 #include "GUIDialogKeyboard.h"
60 #ifdef HAS_PYTHON
61 #include "lib/libPython/XBPython.h"
62 #endif
63 #include "utils/Builtins.h"
64
65 #define CONTROL_BTNVIEWASICONS     2
66 #define CONTROL_BTNSORTBY          3
67 #define CONTROL_BTNSORTASC         4
68 #define CONTROL_BTN_FILTER        19
69
70 #define CONTROL_LABELFILES        12
71
72 using namespace std;
73 using namespace ADDON;
74
75 CGUIMediaWindow::CGUIMediaWindow(int id, const char *xmlFile)
76     : CGUIWindow(id, xmlFile)
77 {
78   m_vecItems = new CFileItemList;
79   m_unfilteredItems = new CFileItemList;
80   m_vecItems->m_strPath = "?";
81   m_iLastControl = -1;
82   m_iSelectedItem = -1;
83
84   m_guiState.reset(CGUIViewState::GetViewState(GetID(), *m_vecItems));
85 }
86
87 CGUIMediaWindow::~CGUIMediaWindow()
88 {
89   delete m_vecItems;
90   delete m_unfilteredItems;
91 }
92
93 #define CONTROL_VIEW_START        50
94 #define CONTROL_VIEW_END          59
95
96 void CGUIMediaWindow::LoadAdditionalTags(TiXmlElement *root)
97 {
98   CGUIWindow::LoadAdditionalTags(root);
99   // configure our view control
100   m_viewControl.Reset();
101   m_viewControl.SetParentWindow(GetID());
102   TiXmlElement *element = root->FirstChildElement("views");
103   if (element && element->FirstChild())
104   { // format is <views>50,29,51,95</views>
105     CStdString allViews = element->FirstChild()->Value();
106     CStdStringArray views;
107     StringUtils::SplitString(allViews, ",", views);
108     for (unsigned int i = 0; i < views.size(); i++)
109     {
110       int controlID = atol(views[i].c_str());
111       CGUIControl *control = (CGUIControl *)GetControl(controlID);
112       if (control && control->IsContainer())
113         m_viewControl.AddView(control);
114     }
115   }
116   else
117   { // backward compatibility
118     vector<CGUIControl *> controls;
119     GetContainers(controls);
120     for (ciControls it = controls.begin(); it != controls.end(); it++)
121     {
122       CGUIControl *control = *it;
123       if (control->GetID() >= CONTROL_VIEW_START && control->GetID() <= CONTROL_VIEW_END)
124         m_viewControl.AddView(control);
125     }
126   }
127   m_viewControl.SetViewControlID(CONTROL_BTNVIEWASICONS);
128 }
129
130 void CGUIMediaWindow::OnWindowLoaded()
131 {
132   SendMessage(GUI_MSG_SET_TYPE, CONTROL_BTN_FILTER, CGUIEditControl::INPUT_TYPE_FILTER);
133   CGUIWindow::OnWindowLoaded();
134   SetupShares();
135 }
136
137 void CGUIMediaWindow::OnWindowUnload()
138 {
139   CGUIWindow::OnWindowUnload();
140   m_viewControl.Reset();
141 }
142
143 CFileItemPtr CGUIMediaWindow::GetCurrentListItem(int offset)
144 {
145   int item = m_viewControl.GetSelectedItem();
146   if (!m_vecItems->Size() || item < 0)
147     return CFileItemPtr();
148   item = (item + offset) % m_vecItems->Size();
149   if (item < 0) item += m_vecItems->Size();
150   return m_vecItems->Get(item);
151 }
152
153 bool CGUIMediaWindow::OnAction(const CAction &action)
154 {
155   if (action.GetID() == ACTION_PARENT_DIR)
156   {
157     if ((m_vecItems->IsVirtualDirectoryRoot() || m_vecItems->m_strPath == m_startDirectory) && g_advancedSettings.m_bUseEvilB)
158       g_windowManager.PreviousWindow();
159     else
160       GoParentFolder();
161     return true;
162   }
163
164   if (action.GetID() == ACTION_PREVIOUS_MENU)
165   {
166     g_windowManager.PreviousWindow();
167     return true;
168   }
169
170   // the non-contextual menu can be called at any time
171   if (action.GetID() == ACTION_CONTEXT_MENU && !m_viewControl.HasControl(GetFocusedControlID()))
172   {
173     OnPopupMenu(-1);
174     return true;
175   }
176
177   if (CGUIWindow::OnAction(action))
178     return true;
179
180   // live filtering
181   if (action.GetID() == ACTION_FILTER_CLEAR)
182   {
183     CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS);
184     message.SetStringParam("");
185     OnMessage(message);
186     return true;
187   }
188
189   if (action.GetID() == ACTION_BACKSPACE)
190   {
191     CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS, 2); // 2 for delete
192     OnMessage(message);
193     return true;
194   }
195
196   if (action.GetID() >= ACTION_FILTER_SMS2 && action.GetID() <= ACTION_FILTER_SMS9)
197   {
198     CStdString filter;
199     filter.Format("%i", (int)(action.GetID() - ACTION_FILTER_SMS2 + 2));
200     CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS, 1); // 1 for append
201     message.SetStringParam(filter);
202     OnMessage(message);
203     return true;
204   }
205
206   return false;
207 }
208
209 bool CGUIMediaWindow::OnMessage(CGUIMessage& message)
210 {
211   switch ( message.GetMessage() )
212   {
213   case GUI_MSG_WINDOW_DEINIT:
214     {
215       m_iSelectedItem = m_viewControl.GetSelectedItem();
216       m_iLastControl = GetFocusedControlID();
217       CGUIWindow::OnMessage(message);
218       // Call ClearFileItems() after our window has finished doing any WindowClose
219       // animations
220       ClearFileItems();
221       return true;
222     }
223     break;
224
225   case GUI_MSG_CLICKED:
226     {
227       int iControl = message.GetSenderId();
228       if (iControl == CONTROL_BTNVIEWASICONS)
229       {
230         // view as control could be a select button
231         int viewMode = 0;
232         const CGUIControl *control = GetControl(CONTROL_BTNVIEWASICONS);
233         if (control && control->GetControlType() != CGUIControl::GUICONTROL_BUTTON)
234         {
235           CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTNVIEWASICONS);
236           OnMessage(msg);
237           viewMode = m_viewControl.GetViewModeNumber(msg.GetParam1());
238         }
239         else
240           viewMode = m_viewControl.GetNextViewMode();
241
242         if (m_guiState.get())
243           m_guiState->SaveViewAsControl(viewMode);
244
245         UpdateButtons();
246         return true;
247       }
248       else if (iControl == CONTROL_BTNSORTASC) // sort asc
249       {
250         if (m_guiState.get())
251           m_guiState->SetNextSortOrder();
252         UpdateFileList();
253         return true;
254       }
255       else if (iControl == CONTROL_BTNSORTBY) // sort by
256       {
257         if (m_guiState.get())
258           m_guiState->SetNextSortMethod();
259         UpdateFileList();
260         return true;
261       }
262       else if (iControl == CONTROL_BTN_FILTER)
263       {
264         if (GetControl(iControl)->GetControlType() == CGUIControl::GUICONTROL_EDIT)
265         { // filter updated
266           CGUIMessage selected(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTN_FILTER);
267           OnMessage(selected);
268           OnFilterItems(selected.GetLabel());
269           return true;
270         }
271         if (GetProperty("filter").IsEmpty())
272         {
273           CStdString filter = GetProperty("filter");
274           CGUIDialogKeyboard::ShowAndGetFilter(filter, false);
275           SetProperty("filter", filter);
276         }
277         else
278           OnFilterItems("");
279         return true;
280       }
281       else if (m_viewControl.HasControl(iControl))  // list/thumb control
282       {
283         int iItem = m_viewControl.GetSelectedItem();
284         int iAction = message.GetParam1();
285         if (iItem < 0) break;
286         if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
287         {
288           OnSelect(iItem);
289         }
290         else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
291         {
292           OnPopupMenu(iItem);
293           return true;
294         }
295       }
296     }
297     break;
298
299   case GUI_MSG_SETFOCUS:
300     {
301       if (m_viewControl.HasControl(message.GetControlId()) && m_viewControl.GetCurrentControl() != message.GetControlId())
302       {
303         m_viewControl.SetFocused();
304         return true;
305       }
306     }
307     break;
308
309   case GUI_MSG_NOTIFY_ALL:
310     { // Message is received even if this window is inactive
311       if (message.GetParam1() == GUI_MSG_WINDOW_RESET)
312       {
313         m_vecItems->m_strPath = "?";
314         return true;
315       }
316       else if ( message.GetParam1() == GUI_MSG_REFRESH_THUMBS )
317       {
318         for (int i = 0; i < m_vecItems->Size(); i++)
319           m_vecItems->Get(i)->FreeMemory(true);
320         break;  // the window will take care of any info images
321       }
322       else if (message.GetParam1() == GUI_MSG_REMOVED_MEDIA)
323       {
324         if (m_vecItems->IsVirtualDirectoryRoot() && IsActive())
325         {
326           int iItem = m_viewControl.GetSelectedItem();
327           Update(m_vecItems->m_strPath);
328           m_viewControl.SetSelectedItem(iItem);
329         }
330         else if (m_vecItems->IsRemovable())
331         { // check that we have this removable share still
332           if (!m_rootDir.IsInSource(m_vecItems->m_strPath))
333           { // don't have this share any more
334             if (IsActive()) Update("");
335             else
336             {
337               m_history.ClearPathHistory();
338               m_vecItems->m_strPath="";
339             }
340           }
341         }
342
343         return true;
344       }
345       else if (message.GetParam1()==GUI_MSG_UPDATE_SOURCES)
346       { // State of the sources changed, so update our view
347         if (m_vecItems->IsVirtualDirectoryRoot() && IsActive())
348         {
349           int iItem = m_viewControl.GetSelectedItem();
350           Update(m_vecItems->m_strPath);
351           m_viewControl.SetSelectedItem(iItem);
352         }
353         return true;
354       }
355       else if (message.GetParam1()==GUI_MSG_UPDATE && IsActive())
356       {
357         if (message.GetNumStringParams())
358         {
359           m_vecItems->m_strPath = message.GetStringParam();
360           if (message.GetParam2()) // param2 is used for resetting the history
361             SetHistoryForPath(m_vecItems->m_strPath);
362         }
363         // clear any cached listing
364         m_vecItems->RemoveDiscCache(GetID());
365         Update(m_vecItems->m_strPath);
366       }
367       else if (message.GetParam1()==GUI_MSG_UPDATE_ITEM && message.GetItem())
368       {
369         CFileItemPtr newItem = boost::static_pointer_cast<CFileItem>(message.GetItem());
370         if (IsActive())
371         {
372           if (m_vecItems->UpdateItem(newItem.get()) && message.GetParam2() == 1)
373           { // need the list updated as well
374             UpdateFileList();
375           }
376         }
377         else if (newItem)
378         { // need to remove the disc cache
379           CFileItemList items;
380           CUtil::GetDirectory(newItem->m_strPath, items.m_strPath);
381           items.RemoveDiscCache(GetID());
382         }
383       }
384       else if (message.GetParam1()==GUI_MSG_UPDATE_PATH)
385       {
386         if (IsActive())
387         {
388           if((message.GetStringParam() == m_vecItems->m_strPath) ||
389              (m_vecItems->IsMultiPath() && XFILE::CMultiPathDirectory::HasPath(m_vecItems->m_strPath, message.GetStringParam())))
390           {
391             Update(m_vecItems->m_strPath);
392           }
393         }
394       }
395       else if (message.GetParam1() == GUI_MSG_FILTER_ITEMS && IsActive())
396       {
397         CStdString filter(GetProperty("filter"));
398         if (message.GetParam2() == 1) // append
399           filter += message.GetStringParam();
400         else if (message.GetParam2() == 2)
401         { // delete
402           if (filter.size())
403             filter = filter.Left(filter.size() - 1);
404         }
405         else
406           filter = message.GetStringParam();
407         OnFilterItems(filter);
408         return true;
409       }
410       else
411         return CGUIWindow::OnMessage(message);
412
413       return true;
414     }
415     break;
416   case GUI_MSG_PLAYBACK_STARTED:
417   case GUI_MSG_PLAYBACK_ENDED:
418   case GUI_MSG_PLAYBACK_STOPPED:
419   case GUI_MSG_PLAYLIST_CHANGED:
420   case GUI_MSG_PLAYLISTPLAYER_STOPPED:
421   case GUI_MSG_PLAYLISTPLAYER_STARTED:
422   case GUI_MSG_PLAYLISTPLAYER_CHANGED:
423     { // send a notify all to all controls on this window
424       CGUIMessage msg(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_REFRESH_LIST);
425       OnMessage(msg);
426       break;
427     }
428   case GUI_MSG_CHANGE_VIEW_MODE:
429     {
430       int viewMode = 0;
431       if (message.GetParam1())  // we have an id
432         viewMode = m_viewControl.GetViewModeByID(message.GetParam1());
433       else if (message.GetParam2())
434         viewMode = m_viewControl.GetNextViewMode((int)message.GetParam2());
435
436       if (m_guiState.get())
437         m_guiState->SaveViewAsControl(viewMode);
438       UpdateButtons();
439       return true;
440     }
441     break;
442   case GUI_MSG_CHANGE_SORT_METHOD:
443     {
444       if (m_guiState.get())
445       {
446         if (message.GetParam1())
447           m_guiState->SetCurrentSortMethod((int)message.GetParam1());
448         else if (message.GetParam2())
449           m_guiState->SetNextSortMethod((int)message.GetParam2());
450       }
451       UpdateFileList();
452       return true;
453     }
454     break;
455   case GUI_MSG_CHANGE_SORT_DIRECTION:
456     {
457       if (m_guiState.get())
458         m_guiState->SetNextSortOrder();
459       UpdateFileList();
460       return true;
461     }
462     break;
463   case GUI_MSG_WINDOW_INIT:
464     {
465       if (m_vecItems->m_strPath == "?")
466         m_vecItems->m_strPath.Empty();
467       CStdString dir = message.GetStringParam(0);
468       const CStdString &ret = message.GetStringParam(1);
469       bool returning = ret.CompareNoCase("return") == 0;
470       if (!dir.IsEmpty())
471       {
472         m_history.ClearPathHistory();
473         // ensure our directory is valid
474         dir = GetStartFolder(dir);
475         if (!returning || m_vecItems->m_strPath.Left(dir.GetLength()) != dir)
476         { // we're not returning to the same path, so set our directory to the requested path
477           m_vecItems->m_strPath = dir;
478         }
479         // check for network up
480         if (CUtil::IsRemote(m_vecItems->m_strPath) && !WaitForNetwork())
481           m_vecItems->m_strPath.Empty();
482         SetHistoryForPath(m_vecItems->m_strPath);
483       }
484       if (message.GetParam1() != WINDOW_INVALID)
485       { // first time to this window - make sure we set the root path
486         m_startDirectory = returning ? dir : "";
487       }
488     }
489     break;
490   }
491
492   return CGUIWindow::OnMessage(message);
493 }
494
495 // \brief Updates the states (enable, disable, visible...)
496 // of the controls defined by this window
497 // Override this function in a derived class to add new controls
498 void CGUIMediaWindow::UpdateButtons()
499 {
500   if (m_guiState.get())
501   {
502     // Update sorting controls
503     if (m_guiState->GetDisplaySortOrder()==SORT_ORDER_NONE)
504     {
505       CONTROL_DISABLE(CONTROL_BTNSORTASC);
506     }
507     else
508     {
509       CONTROL_ENABLE(CONTROL_BTNSORTASC);
510       if (m_guiState->GetDisplaySortOrder()==SORT_ORDER_ASC)
511       {
512         CGUIMessage msg(GUI_MSG_DESELECTED, GetID(), CONTROL_BTNSORTASC);
513         g_windowManager.SendMessage(msg);
514       }
515       else
516       {
517         CGUIMessage msg(GUI_MSG_SELECTED, GetID(), CONTROL_BTNSORTASC);
518         g_windowManager.SendMessage(msg);
519       }
520     }
521
522     // Update list/thumb control
523     m_viewControl.SetCurrentView(m_guiState->GetViewAsControl());
524
525     // Update sort by button
526     if (m_guiState->GetSortMethod()==SORT_METHOD_NONE)
527     {
528       CONTROL_DISABLE(CONTROL_BTNSORTBY);
529     }
530     else
531     {
532       CONTROL_ENABLE(CONTROL_BTNSORTBY);
533     }
534     CStdString sortLabel;
535     sortLabel.Format(g_localizeStrings.Get(550).c_str(), g_localizeStrings.Get(m_guiState->GetSortMethodLabel()).c_str());
536     SET_CONTROL_LABEL(CONTROL_BTNSORTBY, sortLabel);
537   }
538
539   CStdString items;
540   items.Format("%i %s", m_vecItems->GetObjectCount(), g_localizeStrings.Get(127).c_str());
541   SET_CONTROL_LABEL(CONTROL_LABELFILES, items);
542
543   //#ifdef PRE_SKIN_VERSION_3
544   SET_CONTROL_SELECTED(GetID(),CONTROL_BTN_FILTER, !GetProperty("filter").IsEmpty());
545   SET_CONTROL_LABEL2(CONTROL_BTN_FILTER, GetProperty("filter"));
546   //#endif
547 }
548
549 void CGUIMediaWindow::ClearFileItems()
550 {
551   m_viewControl.Clear();
552   m_vecItems->Clear(); // will clean up everything
553   m_unfilteredItems->Clear();
554 }
555
556 // \brief Sorts Fileitems based on the sort method and sort oder provided by guiViewState
557 void CGUIMediaWindow::SortItems(CFileItemList &items)
558 {
559   auto_ptr<CGUIViewState> guiState(CGUIViewState::GetViewState(GetID(), items));
560
561   if (guiState.get())
562   {
563     items.Sort(guiState->GetSortMethod(), guiState->GetDisplaySortOrder());
564
565     // Should these items be saved to the hdd
566     if (items.CacheToDiscAlways())
567       items.Save(GetID());
568   }
569 }
570
571 // \brief Formats item labels based on the formatting provided by guiViewState
572 void CGUIMediaWindow::FormatItemLabels(CFileItemList &items, const LABEL_MASKS &labelMasks)
573 {
574   CLabelFormatter fileFormatter(labelMasks.m_strLabelFile, labelMasks.m_strLabel2File);
575   CLabelFormatter folderFormatter(labelMasks.m_strLabelFolder, labelMasks.m_strLabel2Folder);
576   for (int i=0; i<items.Size(); ++i)
577   {
578     CFileItemPtr pItem=items[i];
579
580     if (pItem->IsLabelPreformated())
581       continue;
582
583     if (pItem->m_bIsFolder)
584       folderFormatter.FormatLabels(pItem.get());
585     else
586       fileFormatter.FormatLabels(pItem.get());
587   }
588
589   if(items.GetSortMethod() == SORT_METHOD_LABEL_IGNORE_THE
590   || items.GetSortMethod() == SORT_METHOD_LABEL)
591     items.ClearSortState();
592 }
593
594 // \brief Prepares and adds the fileitems list/thumb panel
595 void CGUIMediaWindow::FormatAndSort(CFileItemList &items)
596 {
597   auto_ptr<CGUIViewState> viewState(CGUIViewState::GetViewState(GetID(), items));
598
599   if (viewState.get())
600   {
601     LABEL_MASKS labelMasks;
602     viewState->GetSortMethodLabelMasks(labelMasks);
603     FormatItemLabels(items, labelMasks);
604   }
605   SortItems(items);
606 }
607
608 /*!
609   \brief Overwrite to fill fileitems from a source
610   \param strDirectory Path to read
611   \param items Fill with items specified in \e strDirectory
612   */
613 bool CGUIMediaWindow::GetDirectory(const CStdString &strDirectory, CFileItemList &items)
614 {
615   // cleanup items
616   if (items.Size())
617     items.Clear();
618
619   CStdString strParentPath=m_history.GetParentPath();
620
621   CLog::Log(LOGDEBUG,"CGUIMediaWindow::GetDirectory (%s)", strDirectory.c_str());
622   CLog::Log(LOGDEBUG,"  ParentPath = [%s]", strParentPath.c_str());
623
624   // see if we can load a previously cached folder
625   CFileItemList cachedItems(strDirectory);
626   if (!strDirectory.IsEmpty() && cachedItems.Load(GetID()))
627   {
628     items.Assign(cachedItems);
629   }
630   else
631   {
632     unsigned int time = CTimeUtils::GetTimeMS();
633
634     if (strDirectory.IsEmpty())
635       SetupShares();
636
637     if (!m_rootDir.GetDirectory(strDirectory, items))
638       return false;
639
640     // took over a second, and not normally cached, so cache it
641     if (time + 1000 < CTimeUtils::GetTimeMS() && items.CacheToDiscIfSlow())
642       items.Save(GetID());
643
644     // if these items should replace the current listing, then pop it off the top
645     if (items.GetReplaceListing())
646       m_history.RemoveParentPath();
647   }
648
649   if (m_guiState.get() && !m_guiState->HideParentDirItems() && !items.m_strPath.IsEmpty())
650   {
651     CFileItemPtr pItem(new CFileItem(".."));
652     pItem->m_strPath = strParentPath;
653     pItem->m_bIsFolder = true;
654     pItem->m_bIsShareOrDrive = false;
655     items.AddFront(pItem, 0);
656   }
657
658   int iWindow = GetID();
659   CStdStringArray regexps;
660
661   if (iWindow == WINDOW_VIDEO_FILES)
662     regexps = g_advancedSettings.m_videoExcludeFromListingRegExps;
663   if (iWindow == WINDOW_MUSIC_FILES)
664     regexps = g_advancedSettings.m_audioExcludeFromListingRegExps;
665   if (iWindow == WINDOW_PICTURES)
666     regexps = g_advancedSettings.m_pictureExcludeFromListingRegExps;
667
668   if (regexps.size())
669   {
670     for (int i=0; i < items.Size();)
671     {
672       if (CUtil::ExcludeFileOrFolder(items[i]->m_strPath, regexps))
673         items.Remove(i);
674       else
675         i++;
676     }
677   }
678
679   // clear the filter
680   SetProperty("filter", "");
681   return true;
682 }
683
684 // \brief Set window to a specific directory
685 // \param strDirectory The directory to be displayed in list/thumb control
686 // This function calls OnPrepareFileItems() and OnFinalizeFileItems()
687 bool CGUIMediaWindow::Update(const CStdString &strDirectory)
688 {
689   // TODO: OnInitWindow calls Update() before window path has been set properly.
690   if (strDirectory == "?")
691     return false;
692
693   // get selected item
694   int iItem = m_viewControl.GetSelectedItem();
695   CStdString strSelectedItem = "";
696   if (iItem >= 0 && iItem < m_vecItems->Size())
697   {
698     CFileItemPtr pItem = m_vecItems->Get(iItem);
699     if (!pItem->IsParentFolder())
700     {
701       GetDirectoryHistoryString(pItem.get(), strSelectedItem);
702     }
703   }
704
705   CStdString strOldDirectory = m_vecItems->m_strPath;
706
707   m_history.SetSelectedItem(strSelectedItem, strOldDirectory);
708
709   CFileItemList items;
710   if (!GetDirectory(strDirectory, items))
711   {
712     CLog::Log(LOGERROR,"CGUIMediaWindow::GetDirectory(%s) failed", strDirectory.c_str());
713     // if the directory is the same as the old directory, then we'll return
714     // false.  Else, we assume we can get the previous directory
715     if (strDirectory.Equals(strOldDirectory))
716       return false;
717
718     // We assume, we can get the parent
719     // directory again, but we have to
720     // return false to be able to eg. show
721     // an error message.
722     CStdString strParentPath = m_history.GetParentPath();
723     m_history.RemoveParentPath();
724     Update(strParentPath);
725     return false;
726   }
727
728   ClearFileItems();
729   m_vecItems->Copy(items);
730
731   // if we're getting the root source listing
732   // make sure the path history is clean
733   if (strDirectory.IsEmpty())
734     m_history.ClearPathHistory();
735
736   int iWindow = GetID();
737   bool bOkay = (iWindow == WINDOW_MUSIC_FILES || iWindow == WINDOW_VIDEO_FILES || iWindow == WINDOW_FILES || iWindow == WINDOW_PICTURES || iWindow == WINDOW_PROGRAMS);
738   if (strDirectory.IsEmpty() && bOkay && (m_vecItems->Size() == 0 || !m_guiState->DisableAddSourceButtons())) // add 'add source button'
739   {
740     CStdString strLabel = g_localizeStrings.Get(1026);
741     CFileItemPtr pItem(new CFileItem(strLabel));
742     pItem->m_strPath = "add";
743     pItem->SetIconImage("DefaultAddSource.png");
744     pItem->SetLabel(strLabel);
745     pItem->SetLabelPreformated(true);
746     m_vecItems->Add(pItem);
747   }
748   m_iLastControl = GetFocusedControlID();
749
750   //  Ask the derived class if it wants to load additional info
751   //  for the fileitems like media info or additional
752   //  filtering on the items, setting thumbs.
753   OnPrepareFileItems(*m_vecItems);
754
755   m_vecItems->FillInDefaultIcons();
756
757   m_guiState.reset(CGUIViewState::GetViewState(GetID(), *m_vecItems));
758
759   FormatAndSort(*m_vecItems);
760
761   // Ask the devived class if it wants to do custom list operations,
762   // eg. changing the label
763   OnFinalizeFileItems(*m_vecItems);
764   UpdateButtons();
765
766   m_viewControl.SetItems(*m_vecItems);
767
768   strSelectedItem = m_history.GetSelectedItem(m_vecItems->m_strPath);
769
770   bool bSelectedFound = false;
771   //int iSongInDirectory = -1;
772   for (int i = 0; i < m_vecItems->Size(); ++i)
773   {
774     CFileItemPtr pItem = m_vecItems->Get(i);
775
776     // Update selected item
777     if (!bSelectedFound)
778     {
779       CStdString strHistory;
780       GetDirectoryHistoryString(pItem.get(), strHistory);
781       if (strHistory == strSelectedItem)
782       {
783         m_viewControl.SetSelectedItem(i);
784         bSelectedFound = true;
785       }
786     }
787   }
788
789   // if we haven't found the selected item, select the first item
790   if (!bSelectedFound)
791     m_viewControl.SetSelectedItem(0);
792
793   m_history.AddPath(m_vecItems->m_strPath);
794
795   //m_history.DumpPathHistory();
796
797   return true;
798 }
799
800 // \brief This function will be called by Update() before the
801 // labels of the fileitems are formatted. Override this function
802 // to set custom thumbs or load additional media info.
803 // It's used to load tag info for music.
804 void CGUIMediaWindow::OnPrepareFileItems(CFileItemList &items)
805 {
806
807 }
808
809 // \brief This function will be called by Update() after the
810 // labels of the fileitems are formatted. Override this function
811 // to modify the fileitems. Eg. to modify the item label
812 void CGUIMediaWindow::OnFinalizeFileItems(CFileItemList &items)
813 {
814   m_unfilteredItems->Append(items);
815   
816   CStdString filter(GetProperty("filter"));
817   if (!filter.IsEmpty())
818   {
819     items.ClearItems();
820     GetFilteredItems(filter, items);
821   }
822
823   // The idea here is to ensure we have something to focus if our file list
824   // is empty.  As such, this check MUST be last and ignore the hide parent
825   // fileitems settings.
826   if (items.IsEmpty())
827   {
828     CFileItemPtr pItem(new CFileItem(".."));
829     pItem->m_strPath=m_history.GetParentPath();
830     pItem->m_bIsFolder = true;
831     pItem->m_bIsShareOrDrive = false;
832     items.AddFront(pItem, 0);
833   }
834 }
835
836 // \brief With this function you can react on a users click in the list/thumb panel.
837 // It returns true, if the click is handled.
838 // This function calls OnPlayMedia()
839 bool CGUIMediaWindow::OnClick(int iItem)
840 {
841   if ( iItem < 0 || iItem >= (int)m_vecItems->Size() ) return true;
842   CFileItemPtr pItem = m_vecItems->Get(iItem);
843
844   if (pItem->IsParentFolder())
845   {
846     GoParentFolder();
847     return true;
848   }
849   if (pItem->m_strPath == "add" && pItem->GetLabel() == g_localizeStrings.Get(1026)) // 'add source button' in empty root
850   {
851     OnContextButton(0, CONTEXT_BUTTON_ADD_SOURCE);
852     return true;
853   }
854
855   if (!pItem->m_bIsFolder && pItem->IsFileFolder())
856   {
857     XFILE::IFileDirectory *pFileDirectory = NULL;
858     pFileDirectory = XFILE::CFactoryFileDirectory::Create(pItem->m_strPath, pItem.get(), "");
859     if(pFileDirectory)
860       pItem->m_bIsFolder = true;
861     else if(pItem->m_bIsFolder)
862       pItem->m_bIsFolder = false;
863     delete pFileDirectory;
864   }
865
866   CURL url(pItem->m_strPath);
867   if (url.GetProtocol() == "script")
868   {
869     // execute the script
870     AddonPtr addon;
871     if (CAddonMgr::Get().GetAddon(url.GetHostName(), addon))
872     {
873 #ifdef HAS_PYTHON
874       if (!g_pythonParser.StopScript(addon->LibPath()))
875         g_pythonParser.evalFile(addon->LibPath());
876 #endif
877       return true;
878     }
879   }
880
881   if (pItem->m_bIsFolder)
882   {
883     if ( pItem->m_bIsShareOrDrive )
884     {
885       const CStdString& strLockType=m_guiState->GetLockType();
886       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE)
887         if (!strLockType.IsEmpty() && !g_passwordManager.IsItemUnlocked(pItem.get(), strLockType))
888             return true;
889
890       if (!HaveDiscOrConnection(pItem->m_strPath, pItem->m_iDriveType))
891         return true;
892     }
893
894     // check for the partymode playlist items - they may not exist yet
895     if ((pItem->m_strPath == g_settings.GetUserDataItem("PartyMode.xsp")) ||
896         (pItem->m_strPath == g_settings.GetUserDataItem("PartyMode-Video.xsp")))
897     {
898       // party mode playlist item - if it doesn't exist, prompt for user to define it
899       if (!XFILE::CFile::Exists(pItem->m_strPath))
900       {
901         m_vecItems->RemoveDiscCache(GetID());
902         if (CGUIDialogSmartPlaylistEditor::EditPlaylist(pItem->m_strPath))
903           Update(m_vecItems->m_strPath);
904         return true;
905       }
906     }
907
908     // remove the directory cache if the folder is not normally cached
909     CFileItemList items(pItem->m_strPath);
910     if (!items.AlwaysCache())
911       items.RemoveDiscCache(GetID());
912
913     CFileItem directory(*pItem);
914     if (!Update(directory.m_strPath))
915       ShowShareErrorMessage(&directory);
916
917     return true;
918   }
919   else if (pItem->IsPlugin() && pItem->GetProperty("isplayable") != "true")
920   {
921     return XFILE::CPluginDirectory::RunScriptWithParams(pItem->m_strPath);
922   }
923   else
924   {
925     m_iSelectedItem = m_viewControl.GetSelectedItem();
926
927     if (pItem->m_strPath == "newplaylist://")
928     {
929       m_vecItems->RemoveDiscCache(GetID());
930       g_windowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST_EDITOR,"newplaylist://");
931       return true;
932     }
933     else if (pItem->m_strPath.Left(19).Equals("newsmartplaylist://"))
934     {
935       m_vecItems->RemoveDiscCache(GetID());
936       if (CGUIDialogSmartPlaylistEditor::NewPlaylist(pItem->m_strPath.Mid(19)))
937         Update(m_vecItems->m_strPath);
938       return true;
939     }
940     else if (pItem->m_strPath.Left(14).Equals("addons://more/"))
941     {
942       CBuiltins::Execute("ActivateWindow(AddonBrowser,addons://all/xbmc.addon." + pItem->m_strPath.Mid(14) + ",return)");
943       return true;
944     }
945
946     // If karaoke song is being played AND popup autoselector is enabled, the playlist should not be added
947     bool do_not_add_karaoke = g_guiSettings.GetBool("karaoke.enabled") &&
948       g_guiSettings.GetBool("karaoke.autopopupselector") && pItem->IsKaraoke();
949     bool autoplay = m_guiState.get() && m_guiState->AutoPlayNextItem();
950     int iPlaylist = m_guiState.get()?m_guiState->GetPlaylist():PLAYLIST_MUSIC;
951
952     if (pItem->IsPlugin())
953     {
954       CURL url(pItem->m_strPath);
955       AddonPtr addon;
956       if (CAddonMgr::Get().GetAddon(url.GetHostName(),addon))
957       {
958         PluginPtr plugin = boost::dynamic_pointer_cast<CPluginSource>(addon);
959         if (plugin && plugin->Provides(CPluginSource::AUDIO))
960         {
961           iPlaylist = PLAYLIST_MUSIC;
962           autoplay = g_guiSettings.GetBool("musicplayer.autoplaynextitem");
963         }
964       }
965     }
966
967     if (autoplay && !g_partyModeManager.IsEnabled() && 
968         !pItem->IsPlayList() && !do_not_add_karaoke)
969     {
970       g_playlistPlayer.ClearPlaylist(iPlaylist);
971       g_playlistPlayer.Reset();
972       int songToPlay = 0;
973       CFileItemList queueItems;
974       for ( int i = 0; i < m_vecItems->Size(); i++ )
975       {
976         CFileItemPtr item = m_vecItems->Get(i);
977
978         if (item->m_bIsFolder)
979           continue;
980
981         if (!item->IsPlayList() && !item->IsZIP() && !item->IsRAR())
982           queueItems.Add(item);
983
984         if (item == pItem)
985         { // item that was clicked
986           songToPlay = queueItems.Size() - 1;
987         }
988       }
989       g_playlistPlayer.Add(iPlaylist, queueItems);
990
991       // Save current window and directory to know where the selected item was
992       if (m_guiState.get())
993         m_guiState->SetPlaylistDirectory(m_vecItems->m_strPath);
994
995       // figure out where we start playback
996       if (g_playlistPlayer.IsShuffled(iPlaylist))
997       {
998         int iIndex = g_playlistPlayer.GetPlaylist(iPlaylist).FindOrder(songToPlay);
999         g_playlistPlayer.GetPlaylist(iPlaylist).Swap(0, iIndex);
1000         songToPlay = 0;
1001       }
1002
1003       // play
1004       g_playlistPlayer.SetCurrentPlaylist(iPlaylist);
1005       g_playlistPlayer.Play(songToPlay);
1006       return true;
1007     }
1008     else
1009     {
1010       return OnPlayMedia(iItem);
1011     }
1012   }
1013
1014   return false;
1015 }
1016
1017 bool CGUIMediaWindow::OnSelect(int item)
1018 {
1019   return OnClick(item);
1020 }
1021
1022 // \brief Checks if there is a disc in the dvd drive and whether the
1023 // network is connected or not.
1024 bool CGUIMediaWindow::HaveDiscOrConnection(CStdString& strPath, int iDriveType)
1025 {
1026   if (iDriveType==CMediaSource::SOURCE_TYPE_DVD)
1027   {
1028     if (!g_mediaManager.IsDiscInDrive())
1029     {
1030       CGUIDialogOK::ShowAndGetInput(218, 219, 0, 0);
1031       return false;
1032     }
1033   }
1034   else if (iDriveType==CMediaSource::SOURCE_TYPE_REMOTE)
1035   {
1036     // TODO: Handle not connected to a remote share
1037     if ( !g_application.getNetwork().IsConnected() )
1038     {
1039       CGUIDialogOK::ShowAndGetInput(220, 221, 0, 0);
1040       return false;
1041     }
1042   }
1043
1044   return true;
1045 }
1046
1047 // \brief Shows a standard errormessage for a given pItem.
1048 void CGUIMediaWindow::ShowShareErrorMessage(CFileItem* pItem)
1049 {
1050   if (pItem->m_bIsShareOrDrive)
1051   {
1052     int idMessageText=0;
1053     const CURL& url=pItem->GetAsUrl();
1054     const CStdString& strHostName=url.GetHostName();
1055
1056     if (pItem->m_iDriveType != CMediaSource::SOURCE_TYPE_REMOTE) //  Local shares incl. dvd drive
1057       idMessageText=15300;
1058     else if (url.GetProtocol() == "xbms" && strHostName.IsEmpty()) //  xbms server discover
1059       idMessageText=15302;
1060     else if (url.GetProtocol() == "smb" && strHostName.IsEmpty()) //  smb workgroup
1061       idMessageText=15303;
1062     else  //  All other remote shares
1063       idMessageText=15301;
1064
1065     CGUIDialogOK::ShowAndGetInput(220, idMessageText, 0, 0);
1066   }
1067 }
1068
1069 // \brief The functon goes up one level in the directory tree
1070 void CGUIMediaWindow::GoParentFolder()
1071 {
1072   //m_history.DumpPathHistory();
1073
1074   // remove current directory if its on the stack
1075   // there were some issues due some folders having a trailing slash and some not
1076   // so just add a trailing slash to all of them for comparison.
1077   CStdString strPath = m_vecItems->m_strPath;
1078   CUtil::AddSlashAtEnd(strPath);
1079   CStdString strParent = m_history.GetParentPath();
1080   // in case the path history is messed up and the current folder is on
1081   // the stack more than once, keep going until there's nothing left or they
1082   // dont match anymore.
1083   while (!strParent.IsEmpty())
1084   {
1085     CUtil::AddSlashAtEnd(strParent);
1086     if (strParent.Equals(strPath))
1087       m_history.RemoveParentPath();
1088     else
1089       break;
1090     strParent = m_history.GetParentPath();
1091   }
1092
1093   // if vector is not empty, pop parent
1094   // if vector is empty, parent is root source listing
1095   CStdString strOldPath(m_vecItems->m_strPath);
1096   strParent = m_history.RemoveParentPath();
1097   Update(strParent);
1098 }
1099
1100 // \brief Override the function to change the default behavior on how
1101 // a selected item history should look like
1102 void CGUIMediaWindow::GetDirectoryHistoryString(const CFileItem* pItem, CStdString& strHistoryString)
1103 {
1104   if (pItem->m_bIsShareOrDrive)
1105   {
1106     // We are in the virual directory
1107
1108     // History string of the DVD drive
1109     // must be handel separately
1110     if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_DVD)
1111     {
1112       // Remove disc label from item label
1113       // and use as history string, m_strPath
1114       // can change for new discs
1115       CStdString strLabel = pItem->GetLabel();
1116       int nPosOpen = strLabel.Find('(');
1117       int nPosClose = strLabel.ReverseFind(')');
1118       if (nPosOpen > -1 && nPosClose > -1 && nPosClose > nPosOpen)
1119       {
1120         strLabel.Delete(nPosOpen + 1, (nPosClose) - (nPosOpen + 1));
1121         strHistoryString = strLabel;
1122       }
1123       else
1124         strHistoryString = strLabel;
1125     }
1126     else
1127     {
1128       // Other items in virual directory
1129       CStdString strPath = pItem->m_strPath;
1130       CUtil::RemoveSlashAtEnd(strPath);
1131
1132       strHistoryString = pItem->GetLabel() + strPath;
1133     }
1134   }
1135   else if (pItem->m_lEndOffset>pItem->m_lStartOffset && pItem->m_lStartOffset != -1)
1136   {
1137     // Could be a cue item, all items of a cue share the same filename
1138     // so add the offsets to build the history string
1139     strHistoryString.Format("%ld%ld", pItem->m_lStartOffset, pItem->m_lEndOffset);
1140     strHistoryString += pItem->m_strPath;
1141   }
1142   else
1143   {
1144     // Normal directory items
1145     strHistoryString = pItem->m_strPath;
1146   }
1147   CUtil::RemoveSlashAtEnd(strHistoryString);
1148   strHistoryString.ToLower();
1149 }
1150
1151 // \brief Call this function to create a directory history for the
1152 // path given by strDirectory.
1153 void CGUIMediaWindow::SetHistoryForPath(const CStdString& strDirectory)
1154 {
1155   // Make sure our shares are configured
1156   SetupShares();
1157   if (!strDirectory.IsEmpty())
1158   {
1159     // Build the directory history for default path
1160     CStdString strPath, strParentPath;
1161     strPath = strDirectory;
1162     CUtil::RemoveSlashAtEnd(strPath);
1163
1164     CFileItemList items;
1165     m_rootDir.GetDirectory("", items);
1166
1167     m_history.ClearPathHistory();
1168
1169     while (CUtil::GetParentPath(strPath, strParentPath))
1170     {
1171       for (int i = 0; i < (int)items.Size(); ++i)
1172       {
1173         CFileItemPtr pItem = items[i];
1174         CUtil::RemoveSlashAtEnd(pItem->m_strPath);
1175         if (pItem->m_strPath == strPath)
1176         {
1177           CStdString strHistory;
1178           GetDirectoryHistoryString(pItem.get(), strHistory);
1179           m_history.SetSelectedItem(strHistory, "");
1180           CUtil::AddSlashAtEnd(strPath);
1181           m_history.AddPathFront(strPath);
1182           m_history.AddPathFront("");
1183
1184           //m_history.DumpPathHistory();
1185           return ;
1186         }
1187       }
1188
1189       CUtil::AddSlashAtEnd(strPath);
1190       m_history.AddPathFront(strPath);
1191       m_history.SetSelectedItem(strPath, strParentPath);
1192       strPath = strParentPath;
1193       CUtil::RemoveSlashAtEnd(strPath);
1194     }
1195   }
1196   else
1197     m_history.ClearPathHistory();
1198
1199   //m_history.DumpPathHistory();
1200 }
1201
1202 // \brief Override if you want to change the default behavior, what is done
1203 // when the user clicks on a file.
1204 // This function is called by OnClick()
1205 bool CGUIMediaWindow::OnPlayMedia(int iItem)
1206 {
1207   // Reset Playlistplayer, playback started now does
1208   // not use the playlistplayer.
1209   g_playlistPlayer.Reset();
1210   g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_NONE);
1211   CFileItemPtr pItem=m_vecItems->Get(iItem);
1212
1213   bool bResult = false;
1214   if (pItem->IsInternetStream() || pItem->IsPlayList())
1215     bResult = g_application.PlayMedia(*pItem, m_guiState->GetPlaylist());
1216   else
1217     bResult = g_application.PlayFile(*pItem);
1218
1219   if (pItem->m_lStartOffset == STARTOFFSET_RESUME)
1220     pItem->m_lStartOffset = 0;
1221
1222   return bResult;
1223 }
1224
1225 // \brief Synchonize the fileitems with the playlistplayer
1226 // It recreated the playlist of the playlistplayer based
1227 // on the fileitems of the window
1228 void CGUIMediaWindow::UpdateFileList()
1229 {
1230   int nItem = m_viewControl.GetSelectedItem();
1231   CStdString strSelected;
1232   if (nItem >= 0)
1233     strSelected = m_vecItems->Get(nItem)->m_strPath;
1234
1235   FormatAndSort(*m_vecItems);
1236   UpdateButtons();
1237
1238   m_viewControl.SetItems(*m_vecItems);
1239   m_viewControl.SetSelectedItem(strSelected);
1240
1241   //  set the currently playing item as selected, if its in this directory
1242   if (m_guiState.get() && m_guiState->IsCurrentPlaylistDirectory(m_vecItems->m_strPath))
1243   {
1244     int iPlaylist=m_guiState->GetPlaylist();
1245     int nSong = g_playlistPlayer.GetCurrentSong();
1246     CFileItem playlistItem;
1247     if (nSong > -1 && iPlaylist > -1)
1248       playlistItem=*g_playlistPlayer.GetPlaylist(iPlaylist)[nSong];
1249
1250     g_playlistPlayer.ClearPlaylist(iPlaylist);
1251     g_playlistPlayer.Reset();
1252
1253     for (int i = 0; i < m_vecItems->Size(); i++)
1254     {
1255       CFileItemPtr pItem = m_vecItems->Get(i);
1256       if (pItem->m_bIsFolder)
1257         continue;
1258
1259       if (!pItem->IsPlayList() && !pItem->IsZIP() && !pItem->IsRAR())
1260         g_playlistPlayer.Add(iPlaylist, pItem);
1261
1262       if (pItem->m_strPath == playlistItem.m_strPath &&
1263           pItem->m_lStartOffset == playlistItem.m_lStartOffset)
1264         g_playlistPlayer.SetCurrentSong(g_playlistPlayer.GetPlaylist(iPlaylist).size() - 1);
1265     }
1266   }
1267 }
1268
1269 void CGUIMediaWindow::OnDeleteItem(int iItem)
1270 {
1271   if ( iItem < 0 || iItem >= m_vecItems->Size()) return;
1272   CFileItemPtr item = m_vecItems->Get(iItem);
1273
1274   if (item->IsPlayList())
1275     item->m_bIsFolder = false;
1276
1277   if (g_settings.GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE && g_settings.GetCurrentProfile().filesLocked())
1278     if (!g_passwordManager.IsMasterLockUnlocked(true))
1279       return;
1280
1281   if (!CFileUtils::DeleteItem(item))
1282     return;
1283   m_vecItems->RemoveDiscCache(GetID());
1284   Update(m_vecItems->m_strPath);
1285   m_viewControl.SetSelectedItem(iItem);
1286 }
1287
1288 void CGUIMediaWindow::OnRenameItem(int iItem)
1289 {
1290   if ( iItem < 0 || iItem >= m_vecItems->Size()) return;
1291
1292   if (g_settings.GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE && g_settings.GetCurrentProfile().filesLocked())
1293     if (!g_passwordManager.IsMasterLockUnlocked(true))
1294       return;
1295
1296   if (!CFileUtils::RenameFile(m_vecItems->Get(iItem)->m_strPath))
1297     return;
1298   m_vecItems->RemoveDiscCache(GetID());
1299   Update(m_vecItems->m_strPath);
1300   m_viewControl.SetSelectedItem(iItem);
1301 }
1302
1303 void CGUIMediaWindow::OnInitWindow()
1304 {
1305   // initial fetch is done unthreaded to ensure the items are setup prior to skin animations kicking off
1306   m_rootDir.SetAllowThreads(false);
1307   Update(m_vecItems->m_strPath);
1308   m_rootDir.SetAllowThreads(true);
1309
1310   if (m_iSelectedItem > -1)
1311     m_viewControl.SetSelectedItem(m_iSelectedItem);
1312
1313   CGUIWindow::OnInitWindow();
1314 }
1315
1316 CGUIControl *CGUIMediaWindow::GetFirstFocusableControl(int id)
1317 {
1318   if (m_viewControl.HasControl(id))
1319     id = m_viewControl.GetCurrentControl();
1320   return CGUIWindow::GetFirstFocusableControl(id);
1321 }
1322
1323 void CGUIMediaWindow::SetupShares()
1324 {
1325   // Setup shares and filemasks for this window
1326   CFileItemList items;
1327   CGUIViewState* viewState=CGUIViewState::GetViewState(GetID(), items);
1328   if (viewState)
1329   {
1330     m_rootDir.SetMask(viewState->GetExtensions());
1331     m_rootDir.SetSources(viewState->GetSources());
1332     delete viewState;
1333   }
1334 }
1335
1336 bool CGUIMediaWindow::OnPopupMenu(int iItem)
1337 {
1338   // popup the context menu
1339   // grab our context menu
1340   CContextButtons buttons;
1341   GetContextButtons(iItem, buttons);
1342
1343   if (buttons.size())
1344   {
1345     // mark the item
1346     if (iItem >= 0 && iItem < m_vecItems->Size())
1347       m_vecItems->Get(iItem)->Select(true);
1348
1349     int choice = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
1350
1351     // deselect our item
1352     if (iItem >= 0 && iItem < m_vecItems->Size())
1353       m_vecItems->Get(iItem)->Select(false);
1354
1355     if (choice >= 0)
1356       return OnContextButton(iItem, (CONTEXT_BUTTON)choice);
1357   }
1358   return false;
1359 }
1360
1361 void CGUIMediaWindow::GetContextButtons(int itemNumber, CContextButtons &buttons)
1362 {
1363   CFileItemPtr item = (itemNumber >= 0 && itemNumber < m_vecItems->Size()) ? m_vecItems->Get(itemNumber) : CFileItemPtr();
1364
1365   if (!item)
1366     return;
1367
1368   // user added buttons
1369   CStdString label;
1370   CStdString action;
1371   for (int i = CONTEXT_BUTTON_USER1; i <= CONTEXT_BUTTON_USER10; i++)
1372   {
1373     label.Format("contextmenulabel(%i)", i - CONTEXT_BUTTON_USER1);
1374     if (item->GetProperty(label).IsEmpty())
1375       break;
1376
1377     action.Format("contextmenuaction(%i)", i - CONTEXT_BUTTON_USER1);
1378     if (item->GetProperty(action).IsEmpty())
1379       break;
1380
1381     buttons.Add((CONTEXT_BUTTON)i, item->GetProperty(label));
1382   }
1383
1384   if (item->GetPropertyBOOL("pluginreplacecontextitems"))
1385     return;
1386
1387   // TODO: FAVOURITES Conditions on masterlock and localisation
1388   if (!item->IsParentFolder() && !item->m_strPath.Equals("add") && !item->m_strPath.Equals("newplaylist://") && !item->m_strPath.Left(19).Equals("newsmartplaylist://"))
1389   {
1390     if (CFavourites::IsFavourite(item.get(), GetID()))
1391       buttons.Add(CONTEXT_BUTTON_ADD_FAVOURITE, 14077);     // Remove Favourite
1392     else
1393       buttons.Add(CONTEXT_BUTTON_ADD_FAVOURITE, 14076);     // Add To Favourites;
1394   }
1395 }
1396
1397 bool CGUIMediaWindow::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
1398 {
1399   switch (button)
1400   {
1401   case CONTEXT_BUTTON_ADD_FAVOURITE:
1402     {
1403       CFileItemPtr item = m_vecItems->Get(itemNumber);
1404       CFavourites::AddOrRemove(item.get(), GetID());
1405       return true;
1406     }
1407   case CONTEXT_BUTTON_PLUGIN_SETTINGS:
1408     {
1409       CURL plugin(m_vecItems->Get(itemNumber)->m_strPath);
1410       ADDON::AddonPtr addon;
1411       if (CAddonMgr::Get().GetAddon(plugin.GetHostName(), addon, ADDON_PLUGIN))
1412         if (CGUIDialogAddonSettings::ShowAndGetInput(addon))
1413           Update(m_vecItems->m_strPath);
1414       return true;
1415     }
1416   case CONTEXT_BUTTON_USER1:
1417   case CONTEXT_BUTTON_USER2:
1418   case CONTEXT_BUTTON_USER3:
1419   case CONTEXT_BUTTON_USER4:
1420   case CONTEXT_BUTTON_USER5:
1421   case CONTEXT_BUTTON_USER6:
1422   case CONTEXT_BUTTON_USER7:
1423   case CONTEXT_BUTTON_USER8:
1424   case CONTEXT_BUTTON_USER9:
1425   case CONTEXT_BUTTON_USER10:
1426     {
1427       CStdString action;
1428       action.Format("contextmenuaction(%i)", button - CONTEXT_BUTTON_USER1);
1429       g_application.getApplicationMessenger().ExecBuiltIn(m_vecItems->Get(itemNumber)->GetProperty(action));
1430       return true;
1431     }
1432   default:
1433     break;
1434   }
1435   return false;
1436 }
1437
1438 const CGUIViewState *CGUIMediaWindow::GetViewState() const
1439 {
1440   return m_guiState.get();
1441 }
1442
1443 const CFileItemList& CGUIMediaWindow::CurrentDirectory() const
1444 {
1445   return *m_vecItems;
1446 }
1447
1448 bool CGUIMediaWindow::WaitForNetwork() const
1449 {
1450   if (g_application.getNetwork().IsAvailable())
1451     return true;
1452
1453   CGUIDialogProgress *progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
1454   if (!progress)
1455     return true;
1456
1457   CURL url(m_vecItems->m_strPath);
1458   progress->SetHeading(1040); // Loading Directory
1459   progress->SetLine(1, url.GetWithoutUserDetails());
1460   progress->ShowProgressBar(false);
1461   progress->StartModal();
1462   while (!g_application.getNetwork().IsAvailable())
1463   {
1464     progress->Progress();
1465     if (progress->IsCanceled())
1466     {
1467       progress->Close();
1468       return false;
1469     }
1470   }
1471   progress->Close();
1472   return true;
1473 }
1474
1475 void CGUIMediaWindow::OnFilterItems(const CStdString &filter)
1476 {
1477   CStdString currentItem;
1478   int item = m_viewControl.GetSelectedItem();
1479   if (item >= 0)
1480     currentItem = m_vecItems->Get(item)->m_strPath;
1481   
1482   m_viewControl.Clear();
1483   
1484   CFileItemList items;
1485   GetFilteredItems(filter, items);
1486   if (filter.IsEmpty() || items.GetObjectCount() > 0)
1487   {
1488     m_vecItems->ClearItems();
1489     m_vecItems->Append(items);
1490     SetProperty("filter", filter);
1491   }
1492   
1493   // and update our view control + buttons
1494   m_viewControl.SetItems(*m_vecItems);
1495   m_viewControl.SetSelectedItem(currentItem);
1496   UpdateButtons();
1497 }
1498
1499 void CGUIMediaWindow::GetFilteredItems(const CStdString &filter, CFileItemList &items)
1500 {
1501   CStdString trimmedFilter(filter);
1502   trimmedFilter.TrimLeft().ToLower();
1503   
1504   if (trimmedFilter.IsEmpty())
1505   {
1506     items.Append(*m_unfilteredItems);
1507     return;
1508   }
1509   
1510   bool numericMatch = StringUtils::IsNaturalNumber(trimmedFilter);
1511   for (int i = 0; i < m_unfilteredItems->Size(); i++)
1512   {
1513     CFileItemPtr item = m_unfilteredItems->Get(i);
1514     if (item->IsParentFolder())
1515     {
1516       items.Add(item);
1517       continue;
1518     }
1519     // TODO: Need to update this to get all labels, ideally out of the displayed info (ie from m_layout and m_focusedLayout)
1520     // though that isn't practical.  Perhaps a better idea would be to just grab the info that we should filter on based on
1521     // where we are in the library tree.
1522     // Another idea is tying the filter string to the current level of the tree, so that going deeper disables the filter,
1523     // but it's re-enabled on the way back out.
1524     CStdString match;
1525     /*    if (item->GetFocusedLayout())
1526      match = item->GetFocusedLayout()->GetAllText();
1527      else if (item->GetLayout())
1528      match = item->GetLayout()->GetAllText();
1529      else*/
1530     match = item->GetLabel(); // Filter label only for now
1531     
1532     if (numericMatch)
1533       StringUtils::WordToDigits(match);
1534     
1535     size_t pos = StringUtils::FindWords(match.c_str(), trimmedFilter.c_str());
1536     if (pos != CStdString::npos)
1537       items.Add(item);
1538   }
1539 }
1540
1541 CStdString CGUIMediaWindow::GetStartFolder(const CStdString &dir)
1542 {
1543   if (dir.Equals("$ROOT") || dir.Equals("Root"))
1544     return "";
1545   return dir;
1546 }