2 * Copyright (C) 2005-2013 Team XBMC
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)
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.
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/>.
21 #include "threads/SystemClock.h"
22 #include "GUIMediaWindow.h"
23 #include "GUIUserMessages.h"
25 #include "PlayListPlayer.h"
26 #include "addons/AddonManager.h"
27 #include "addons/PluginSource.h"
28 #include "filesystem/PluginDirectory.h"
29 #include "filesystem/MultiPathDirectory.h"
30 #include "GUIPassword.h"
31 #include "Application.h"
32 #include "ApplicationMessenger.h"
33 #include "network/Network.h"
34 #include "utils/RegExp.h"
35 #include "PartyModeManager.h"
36 #include "dialogs/GUIDialogMediaSource.h"
37 #include "GUIWindowFileManager.h"
38 #include "Favourites.h"
39 #include "utils/LabelFormatter.h"
40 #include "dialogs/GUIDialogProgress.h"
41 #include "settings/AdvancedSettings.h"
42 #include "settings/GUISettings.h"
45 #include "dialogs/GUIDialogSmartPlaylistEditor.h"
46 #include "addons/GUIDialogAddonSettings.h"
47 #include "dialogs/GUIDialogYesNo.h"
48 #include "guilib/GUIWindowManager.h"
49 #include "dialogs/GUIDialogOK.h"
50 #include "playlists/PlayList.h"
51 #include "storage/MediaManager.h"
52 #include "settings/Settings.h"
53 #include "utils/StringUtils.h"
54 #include "utils/URIUtils.h"
55 #include "guilib/LocalizeStrings.h"
56 #include "utils/TimeUtils.h"
57 #include "filesystem/File.h"
58 #include "filesystem/FileDirectoryFactory.h"
59 #include "utils/log.h"
60 #include "utils/FileUtils.h"
61 #include "guilib/GUIEditControl.h"
62 #include "guilib/GUIKeyboardFactory.h"
64 #include "interfaces/python/XBPython.h"
66 #include "interfaces/Builtins.h"
67 #include "dialogs/GUIDialogKaiToast.h"
68 #include "dialogs/GUIDialogMediaFilter.h"
69 #include "filesystem/SmartPlaylistDirectory.h"
70 #if defined(TARGET_ANDROID)
71 #include "xbmc/android/activity/XBMCApp.h"
74 #define CONTROL_BTNVIEWASICONS 2
75 #define CONTROL_BTNSORTBY 3
76 #define CONTROL_BTNSORTASC 4
77 #define CONTROL_BTN_FILTER 19
79 #define CONTROL_LABELFILES 12
81 #define PROPERTY_PATH_DB "path.db"
82 #define PROPERTY_SORT_ORDER "sort.order"
83 #define PROPERTY_SORT_ASCENDING "sort.ascending"
86 using namespace ADDON;
88 CGUIMediaWindow::CGUIMediaWindow(int id, const char *xmlFile)
89 : CGUIWindow(id, xmlFile)
91 m_loadType = KEEP_IN_MEMORY;
92 m_vecItems = new CFileItemList;
93 m_unfilteredItems = new CFileItemList;
94 m_vecItems->SetPath("?");
97 m_canFilterAdvanced = false;
99 m_guiState.reset(CGUIViewState::GetViewState(GetID(), *m_vecItems));
102 CGUIMediaWindow::~CGUIMediaWindow()
105 delete m_unfilteredItems;
108 #define CONTROL_VIEW_START 50
109 #define CONTROL_VIEW_END 59
111 void CGUIMediaWindow::LoadAdditionalTags(TiXmlElement *root)
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++)
125 int controlID = atol(views[i].c_str());
126 CGUIControl *control = (CGUIControl *)GetControl(controlID);
127 if (control && control->IsContainer())
128 m_viewControl.AddView(control);
132 { // backward compatibility
133 vector<CGUIControl *> controls;
134 GetContainers(controls);
135 for (ciControls it = controls.begin(); it != controls.end(); it++)
137 CGUIControl *control = *it;
138 if (control->GetID() >= CONTROL_VIEW_START && control->GetID() <= CONTROL_VIEW_END)
139 m_viewControl.AddView(control);
142 m_viewControl.SetViewControlID(CONTROL_BTNVIEWASICONS);
145 void CGUIMediaWindow::OnWindowLoaded()
147 SendMessage(GUI_MSG_SET_TYPE, CONTROL_BTN_FILTER, CGUIEditControl::INPUT_TYPE_FILTER);
148 CGUIWindow::OnWindowLoaded();
152 void CGUIMediaWindow::OnWindowUnload()
154 CGUIWindow::OnWindowUnload();
155 m_viewControl.Reset();
158 CFileItemPtr CGUIMediaWindow::GetCurrentListItem(int offset)
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);
168 bool CGUIMediaWindow::OnAction(const CAction &action)
170 if (action.GetID() == ACTION_PARENT_DIR)
176 // the non-contextual menu can be called at any time
177 if (action.GetID() == ACTION_CONTEXT_MENU && !m_viewControl.HasControl(GetFocusedControlID()))
183 if (CGUIWindow::OnAction(action))
186 if (action.GetID() == ACTION_FILTER)
190 if (action.GetID() == ACTION_FILTER_CLEAR)
192 CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS);
193 message.SetStringParam("");
198 if (action.GetID() == ACTION_BACKSPACE)
200 CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS, 2); // 2 for delete
205 if (action.GetID() >= ACTION_FILTER_SMS2 && action.GetID() <= ACTION_FILTER_SMS9)
208 filter.Format("%i", (int)(action.GetID() - ACTION_FILTER_SMS2 + 2));
209 CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS, 1); // 1 for append
210 message.SetStringParam(filter);
218 bool CGUIMediaWindow::OnBack(int actionID)
220 CURL filterUrl(m_strFilterPath);
221 if (actionID == ACTION_NAV_BACK && !m_vecItems->IsVirtualDirectoryRoot() &&
222 (m_vecItems->GetPath() != m_startDirectory || (m_canFilterAdvanced && filterUrl.HasOption("filter"))))
227 return CGUIWindow::OnBack(actionID);
230 bool CGUIMediaWindow::OnMessage(CGUIMessage& message)
232 switch ( message.GetMessage() )
234 case GUI_MSG_WINDOW_DEINIT:
236 m_iSelectedItem = m_viewControl.GetSelectedItem();
237 m_iLastControl = GetFocusedControlID();
238 CGUIWindow::OnMessage(message);
239 CGUIDialogContextMenu* pDlg = (CGUIDialogContextMenu*)g_windowManager.GetWindow(WINDOW_DIALOG_CONTEXT_MENU);
240 if (pDlg && pDlg->IsActive())
243 // get rid of any active filtering
244 if (m_canFilterAdvanced)
246 m_canFilterAdvanced = false;
249 m_strFilterPath.clear();
251 // Call ClearFileItems() after our window has finished doing any WindowClose
258 case GUI_MSG_CLICKED:
260 int iControl = message.GetSenderId();
261 if (iControl == CONTROL_BTNVIEWASICONS)
263 // view as control could be a select button
265 const CGUIControl *control = GetControl(CONTROL_BTNVIEWASICONS);
266 if (control && control->GetControlType() != CGUIControl::GUICONTROL_BUTTON)
268 CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTNVIEWASICONS);
270 viewMode = m_viewControl.GetViewModeNumber(msg.GetParam1());
273 viewMode = m_viewControl.GetNextViewMode();
275 if (m_guiState.get())
276 m_guiState->SaveViewAsControl(viewMode);
281 else if (iControl == CONTROL_BTNSORTASC) // sort asc
283 if (m_guiState.get())
284 m_guiState->SetNextSortOrder();
288 else if (iControl == CONTROL_BTNSORTBY) // sort by
290 if (m_guiState.get())
291 m_guiState->SetNextSortMethod();
295 else if (iControl == CONTROL_BTN_FILTER)
297 if (m_canFilterAdvanced)
302 else if (m_viewControl.HasControl(iControl)) // list/thumb control
304 int iItem = m_viewControl.GetSelectedItem();
305 int iAction = message.GetParam1();
306 if (iItem < 0) break;
307 if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
311 else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
320 case GUI_MSG_SETFOCUS:
322 if (m_viewControl.HasControl(message.GetControlId()) && m_viewControl.GetCurrentControl() != message.GetControlId())
324 m_viewControl.SetFocused();
330 case GUI_MSG_NOTIFY_ALL:
331 { // Message is received even if this window is inactive
332 if (message.GetParam1() == GUI_MSG_WINDOW_RESET)
334 m_vecItems->SetPath("?");
337 else if ( message.GetParam1() == GUI_MSG_REFRESH_THUMBS )
339 for (int i = 0; i < m_vecItems->Size(); i++)
340 m_vecItems->Get(i)->FreeMemory(true);
341 break; // the window will take care of any info images
343 else if (message.GetParam1() == GUI_MSG_REMOVED_MEDIA)
345 if ((m_vecItems->IsVirtualDirectoryRoot() ||
346 m_vecItems->IsSourcesPath()) && IsActive())
348 int iItem = m_viewControl.GetSelectedItem();
350 m_viewControl.SetSelectedItem(iItem);
352 else if (m_vecItems->IsRemovable())
353 { // check that we have this removable share still
354 if (!m_rootDir.IsInSource(m_vecItems->GetPath()))
355 { // don't have this share any more
356 if (IsActive()) Update("");
359 m_history.ClearPathHistory();
360 m_vecItems->SetPath("");
367 else if (message.GetParam1()==GUI_MSG_UPDATE_SOURCES)
368 { // State of the sources changed, so update our view
369 if ((m_vecItems->IsVirtualDirectoryRoot() ||
370 m_vecItems->IsSourcesPath()) && IsActive())
372 int iItem = m_viewControl.GetSelectedItem();
374 m_viewControl.SetSelectedItem(iItem);
378 else if (message.GetParam1()==GUI_MSG_UPDATE && IsActive())
380 if (message.GetNumStringParams())
382 if (message.GetParam2()) // param2 is used for resetting the history
383 SetHistoryForPath(message.GetStringParam());
385 CFileItemList list(message.GetStringParam());
386 list.RemoveDiscCache(GetID());
387 Update(message.GetStringParam());
390 Refresh(true); // refresh the listing
392 else if (message.GetParam1()==GUI_MSG_UPDATE_ITEM && message.GetItem())
394 CFileItemPtr newItem = boost::static_pointer_cast<CFileItem>(message.GetItem());
397 if (m_vecItems->UpdateItem(newItem.get()) && message.GetParam2() == 1)
398 { // need the list updated as well
403 { // need to remove the disc cache
406 URIUtils::GetDirectory(newItem->GetPath(), path);
408 items.RemoveDiscCache(GetID());
411 else if (message.GetParam1()==GUI_MSG_UPDATE_PATH)
415 if((message.GetStringParam() == m_vecItems->GetPath()) ||
416 (m_vecItems->IsMultiPath() && XFILE::CMultiPathDirectory::HasPath(m_vecItems->GetPath(), message.GetStringParam())))
420 else if (message.GetParam1() == GUI_MSG_FILTER_ITEMS && IsActive())
423 // check if this is meant for advanced filtering
424 if (message.GetParam2() != 10)
426 filter = GetProperty("filter").asString();
427 if (message.GetParam2() == 1) // append
428 filter += message.GetStringParam();
429 else if (message.GetParam2() == 2)
432 filter = filter.Left(filter.size() - 1);
435 filter = message.GetStringParam();
437 OnFilterItems(filter);
441 return CGUIWindow::OnMessage(message);
446 case GUI_MSG_PLAYBACK_STARTED:
447 case GUI_MSG_PLAYBACK_ENDED:
448 case GUI_MSG_PLAYBACK_STOPPED:
449 case GUI_MSG_PLAYLIST_CHANGED:
450 case GUI_MSG_PLAYLISTPLAYER_STOPPED:
451 case GUI_MSG_PLAYLISTPLAYER_STARTED:
452 case GUI_MSG_PLAYLISTPLAYER_CHANGED:
453 { // send a notify all to all controls on this window
454 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_REFRESH_LIST);
458 case GUI_MSG_CHANGE_VIEW_MODE:
461 if (message.GetParam1()) // we have an id
462 viewMode = m_viewControl.GetViewModeByID(message.GetParam1());
463 else if (message.GetParam2())
464 viewMode = m_viewControl.GetNextViewMode((int)message.GetParam2());
466 if (m_guiState.get())
467 m_guiState->SaveViewAsControl(viewMode);
472 case GUI_MSG_CHANGE_SORT_METHOD:
474 if (m_guiState.get())
476 if (message.GetParam1())
477 m_guiState->SetCurrentSortMethod((int)message.GetParam1());
478 else if (message.GetParam2())
479 m_guiState->SetNextSortMethod((int)message.GetParam2());
485 case GUI_MSG_CHANGE_SORT_DIRECTION:
487 if (m_guiState.get())
488 m_guiState->SetNextSortOrder();
493 case GUI_MSG_WINDOW_INIT:
495 if (m_vecItems->GetPath() == "?")
496 m_vecItems->SetPath("");
497 CStdString dir = message.GetStringParam(0);
498 const CStdString &ret = message.GetStringParam(1);
499 bool returning = ret.CompareNoCase("return") == 0;
502 m_history.ClearPathHistory();
503 // ensure our directory is valid
504 dir = GetStartFolder(dir);
505 if (!returning || m_vecItems->GetPath().Left(dir.GetLength()) != dir)
506 { // we're not returning to the same path, so set our directory to the requested path
507 m_vecItems->SetPath(dir);
509 // check for network up
510 if (URIUtils::IsRemote(m_vecItems->GetPath()) && !WaitForNetwork())
511 m_vecItems->SetPath("");
512 SetHistoryForPath(m_vecItems->GetPath());
514 if (message.GetParam1() != WINDOW_INVALID)
515 { // first time to this window - make sure we set the root path
516 m_startDirectory = returning ? dir : "";
522 return CGUIWindow::OnMessage(message);
525 // \brief Updates the states (enable, disable, visible...)
526 // of the controls defined by this window
527 // Override this function in a derived class to add new controls
528 void CGUIMediaWindow::UpdateButtons()
530 if (m_guiState.get())
532 // Update sorting controls
533 if (m_guiState->GetDisplaySortOrder() == SortOrderNone)
535 CONTROL_DISABLE(CONTROL_BTNSORTASC);
539 CONTROL_ENABLE(CONTROL_BTNSORTASC);
540 if (m_guiState->GetDisplaySortOrder() == SortOrderAscending)
542 CGUIMessage msg(GUI_MSG_DESELECTED, GetID(), CONTROL_BTNSORTASC);
543 g_windowManager.SendMessage(msg);
547 CGUIMessage msg(GUI_MSG_SELECTED, GetID(), CONTROL_BTNSORTASC);
548 g_windowManager.SendMessage(msg);
552 // Update list/thumb control
553 m_viewControl.SetCurrentView(m_guiState->GetViewAsControl());
555 // Update sort by button
556 if (m_guiState->GetSortMethod()==SORT_METHOD_NONE)
558 CONTROL_DISABLE(CONTROL_BTNSORTBY);
562 CONTROL_ENABLE(CONTROL_BTNSORTBY);
564 CStdString sortLabel;
565 sortLabel.Format(g_localizeStrings.Get(550).c_str(), g_localizeStrings.Get(m_guiState->GetSortMethodLabel()).c_str());
566 SET_CONTROL_LABEL(CONTROL_BTNSORTBY, sortLabel);
570 items.Format("%i %s", m_vecItems->GetObjectCount(), g_localizeStrings.Get(127).c_str());
571 SET_CONTROL_LABEL(CONTROL_LABELFILES, items);
573 if (!m_canFilterAdvanced)
574 SET_CONTROL_LABEL2(CONTROL_BTN_FILTER, GetProperty("filter").asString());
577 void CGUIMediaWindow::ClearFileItems()
579 m_viewControl.Clear();
581 m_unfilteredItems->Clear();
584 // \brief Sorts Fileitems based on the sort method and sort oder provided by guiViewState
585 void CGUIMediaWindow::SortItems(CFileItemList &items)
587 auto_ptr<CGUIViewState> guiState(CGUIViewState::GetViewState(GetID(), items));
592 SORT_METHOD sortMethod = guiState->GetSortMethod();
593 // If the sort method is "sort by playlist" and we have a specific
594 // sort order available we can use the specified sort order to do the sorting
595 // We do this as the new SortBy methods are a superset of the SORT_METHOD methods, thus
596 // not all are available. This may be removed once SORT_METHOD_* have been replaced by
598 if ((sortMethod == SORT_METHOD_PLAYLIST_ORDER) && items.HasProperty(PROPERTY_SORT_ORDER))
600 SortBy sortBy = (SortBy)items.GetProperty(PROPERTY_SORT_ORDER).asInteger();
601 if (sortBy != SortByNone && sortBy != SortByPlaylistOrder && sortBy != SortByProgramCount)
603 SortDescription sorting;
604 sorting.sortBy = sortBy;
605 sorting.sortOrder = items.GetProperty(PROPERTY_SORT_ASCENDING).asBoolean() ? SortOrderAscending : SortOrderDescending;
606 sorting.sortAttributes = g_guiSettings.GetBool("filelists.ignorethewhensorting") ? SortAttributeIgnoreArticle : SortAttributeNone;
608 // if the sort order is descending, we need to switch the original sort order, as we assume
609 // in CGUIViewState::AddPlaylistOrder that SORT_METHOD_PLAYLIST_ORDER is ascending.
610 if (guiState->GetDisplaySortOrder() == SortOrderDescending)
611 sorting.sortOrder = sorting.sortOrder == SortOrderDescending ? SortOrderAscending : SortOrderDescending;
619 items.Sort(sortMethod, guiState->GetDisplaySortOrder());
623 // \brief Formats item labels based on the formatting provided by guiViewState
624 void CGUIMediaWindow::FormatItemLabels(CFileItemList &items, const LABEL_MASKS &labelMasks)
626 CLabelFormatter fileFormatter(labelMasks.m_strLabelFile, labelMasks.m_strLabel2File);
627 CLabelFormatter folderFormatter(labelMasks.m_strLabelFolder, labelMasks.m_strLabel2Folder);
628 for (int i=0; i<items.Size(); ++i)
630 CFileItemPtr pItem=items[i];
632 if (pItem->IsLabelPreformated())
635 if (pItem->m_bIsFolder)
636 folderFormatter.FormatLabels(pItem.get());
638 fileFormatter.FormatLabels(pItem.get());
641 if(items.GetSortMethod() == SORT_METHOD_LABEL_IGNORE_THE
642 || items.GetSortMethod() == SORT_METHOD_LABEL)
643 items.ClearSortState();
646 // \brief Prepares and adds the fileitems list/thumb panel
647 void CGUIMediaWindow::FormatAndSort(CFileItemList &items)
649 auto_ptr<CGUIViewState> viewState(CGUIViewState::GetViewState(GetID(), items));
653 LABEL_MASKS labelMasks;
654 viewState->GetSortMethodLabelMasks(labelMasks);
655 FormatItemLabels(items, labelMasks);
657 items.Sort(viewState->GetSortMethod(), viewState->GetDisplaySortOrder());
662 \brief Overwrite to fill fileitems from a source
663 \param strDirectory Path to read
664 \param items Fill with items specified in \e strDirectory
666 bool CGUIMediaWindow::GetDirectory(const CStdString &strDirectory, CFileItemList &items)
672 CStdString strParentPath = m_history.GetParentPath();
674 CLog::Log(LOGDEBUG,"CGUIMediaWindow::GetDirectory (%s)", strDirectory.c_str());
675 CLog::Log(LOGDEBUG," ParentPath = [%s]", strParentPath.c_str());
677 // see if we can load a previously cached folder
678 CFileItemList cachedItems(strDirectory);
679 if (!strDirectory.IsEmpty() && cachedItems.Load(GetID()))
681 items.Assign(cachedItems);
685 unsigned int time = XbmcThreads::SystemClockMillis();
687 if (strDirectory.IsEmpty())
690 if (!m_rootDir.GetDirectory(strDirectory, items))
693 // took over a second, and not normally cached, so cache it
694 if ((XbmcThreads::SystemClockMillis() - time) > 1000 && items.CacheToDiscIfSlow())
697 // if these items should replace the current listing, then pop it off the top
698 if (items.GetReplaceListing())
699 m_history.RemoveParentPath();
702 if (m_guiState.get() && !m_guiState->HideParentDirItems() && !items.GetPath().IsEmpty())
704 CFileItemPtr pItem(new CFileItem(".."));
705 pItem->SetPath(strParentPath);
706 pItem->m_bIsFolder = true;
707 pItem->m_bIsShareOrDrive = false;
708 items.AddFront(pItem, 0);
711 int iWindow = GetID();
712 CStdStringArray regexps;
714 // TODO: Do we want to limit the directories we apply the video ones to?
715 if (iWindow == WINDOW_VIDEO_NAV)
716 regexps = g_advancedSettings.m_videoExcludeFromListingRegExps;
717 if (iWindow == WINDOW_MUSIC_FILES)
718 regexps = g_advancedSettings.m_audioExcludeFromListingRegExps;
719 if (iWindow == WINDOW_PICTURES)
720 regexps = g_advancedSettings.m_pictureExcludeFromListingRegExps;
724 for (int i=0; i < items.Size();)
726 if (CUtil::ExcludeFileOrFolder(items[i]->GetPath(), regexps))
734 SetProperty("filter", "");
735 m_canFilterAdvanced = false;
740 // \brief Set window to a specific directory
741 // \param strDirectory The directory to be displayed in list/thumb control
742 // This function calls OnPrepareFileItems() and OnFinalizeFileItems()
743 bool CGUIMediaWindow::Update(const CStdString &strDirectory, bool updateFilterPath /* = true */)
745 // TODO: OnInitWindow calls Update() before window path has been set properly.
746 if (strDirectory == "?")
750 int iItem = m_viewControl.GetSelectedItem();
751 CStdString strSelectedItem = "";
752 if (iItem >= 0 && iItem < m_vecItems->Size())
754 CFileItemPtr pItem = m_vecItems->Get(iItem);
755 if (!pItem->IsParentFolder())
757 GetDirectoryHistoryString(pItem.get(), strSelectedItem);
761 CStdString strCurrentDirectory = m_vecItems->GetPath();
762 m_history.SetSelectedItem(strSelectedItem, strCurrentDirectory);
764 CStdString directory = strDirectory;
765 // check if the path contains a filter and temporarily remove it
766 // so that the retrieved list of items is unfiltered
767 bool canfilter = CanContainFilter(directory);
770 if (canfilter && url.HasOption("filter"))
772 filter = url.GetOption("filter");
773 directory = RemoveParameterFromPath(directory, "filter");
777 if (!GetDirectory(directory, items))
779 CLog::Log(LOGERROR,"CGUIMediaWindow::GetDirectory(%s) failed", strDirectory.c_str());
780 // if the directory is the same as the old directory, then we'll return
781 // false. Else, we assume we can get the previous directory
782 if (strDirectory.Equals(strCurrentDirectory))
785 // We assume, we can get the parent
786 // directory again, but we have to
787 // return false to be able to eg. show
789 Update(m_history.RemoveParentPath());
793 if (items.GetLabel().IsEmpty())
794 items.SetLabel(CUtil::GetTitleFromPath(items.GetPath(), true));
797 m_vecItems->Copy(items);
799 // only set the filter path if it hasn't been marked
800 // as preset or if it's empty
801 if (updateFilterPath || m_strFilterPath.empty())
803 if (items.HasProperty(PROPERTY_PATH_DB))
804 m_strFilterPath = items.GetProperty(PROPERTY_PATH_DB).asString();
806 m_strFilterPath = items.GetPath();
809 // maybe the filter path can contain a filter
810 if (!canfilter && CanContainFilter(m_strFilterPath))
813 // check if the filter path contains a filter
814 CURL filterPathUrl(m_strFilterPath);
815 if (canfilter && filter.empty())
817 if (filterPathUrl.HasOption("filter"))
818 filter = filterPathUrl.GetOption("filter");
821 // check if there is a filter and re-apply it
822 if (canfilter && !filter.empty())
824 if (!m_filter.LoadFromJson(filter))
826 CLog::Log(LOGWARNING, "CGUIMediaWindow::Update: unable to load existing filter (%s)", filter.c_str());
828 m_strFilterPath = m_vecItems->GetPath();
832 // add the filter to the filter path
833 filterPathUrl.SetOption("filter", filter);
834 m_strFilterPath = filterPathUrl.Get();
838 // if we're getting the root source listing
839 // make sure the path history is clean
840 if (strDirectory.IsEmpty())
841 m_history.ClearPathHistory();
843 int iWindow = GetID();
845 if (strDirectory.IsEmpty())
847 if (iWindow == WINDOW_PICTURES)
849 else if (iWindow == WINDOW_MUSIC_FILES)
851 else if (iWindow == WINDOW_FILES || iWindow == WINDOW_PROGRAMS)
854 if (strDirectory.Equals("sources://video/"))
856 if (showLabel && (m_vecItems->Size() == 0 || !m_guiState->DisableAddSourceButtons())) // add 'add source button'
858 CStdString strLabel = g_localizeStrings.Get(showLabel);
859 CFileItemPtr pItem(new CFileItem(strLabel));
860 pItem->SetPath("add");
861 pItem->SetIconImage("DefaultAddSource.png");
862 pItem->SetLabel(strLabel);
863 pItem->SetLabelPreformated(true);
864 pItem->m_bIsFolder = true;
865 pItem->SetSpecialSort(SortSpecialOnBottom);
866 m_vecItems->Add(pItem);
868 m_iLastControl = GetFocusedControlID();
870 // Check whether to enabled advanced filtering based on the content type
871 m_canFilterAdvanced = CheckFilterAdvanced(*m_vecItems);
872 if (m_canFilterAdvanced)
873 m_filter.SetType(m_vecItems->GetContent());
875 // Ask the derived class if it wants to load additional info
876 // for the fileitems like media info or additional
877 // filtering on the items, setting thumbs.
878 OnPrepareFileItems(*m_vecItems);
880 m_vecItems->FillInDefaultIcons();
882 m_guiState.reset(CGUIViewState::GetViewState(GetID(), *m_vecItems));
884 // remember the original (untouched) list of items (for filtering etc)
885 m_unfilteredItems->SetPath(m_vecItems->GetPath()); // use the original path - it'll likely be relied on for other things later.
886 m_unfilteredItems->Append(*m_vecItems);
888 // Cache the list of items if possible
889 OnCacheFileItems(*m_vecItems);
891 // Filter and group the items if necessary
892 CStdString titleFilter = GetProperty("filter").asString();
893 OnFilterItems(titleFilter);
895 // Ask the devived class if it wants to do custom list operations,
896 // eg. changing the label
897 OnFinalizeFileItems(*m_vecItems);
900 strSelectedItem = m_history.GetSelectedItem(m_vecItems->GetPath());
902 bool bSelectedFound = false;
903 //int iSongInDirectory = -1;
904 for (int i = 0; i < m_vecItems->Size(); ++i)
906 CFileItemPtr pItem = m_vecItems->Get(i);
908 // Update selected item
909 CStdString strHistory;
910 GetDirectoryHistoryString(pItem.get(), strHistory);
911 if (strHistory == strSelectedItem)
913 m_viewControl.SetSelectedItem(i);
914 bSelectedFound = true;
919 // if we haven't found the selected item, select the first item
921 m_viewControl.SetSelectedItem(0);
923 if (iWindow != WINDOW_PVR || (iWindow == WINDOW_PVR && m_vecItems->GetPath().Left(17) == "pvr://recordings/"))
924 m_history.AddPath(m_vecItems->GetPath(), m_strFilterPath);
926 //m_history.DumpPathHistory();
931 bool CGUIMediaWindow::Refresh(bool clearCache /* = false */)
933 CStdString strCurrentDirectory = m_vecItems->GetPath();
934 if (strCurrentDirectory.Equals("?"))
938 m_vecItems->RemoveDiscCache(GetID());
940 // get the original number of items
941 if (!Update(strCurrentDirectory, false))
947 // \brief This function will be called by Update() before the
948 // labels of the fileitems are formatted. Override this function
949 // to set custom thumbs or load additional media info.
950 // It's used to load tag info for music.
951 void CGUIMediaWindow::OnPrepareFileItems(CFileItemList &items)
956 // \brief This function will be called by Update() before
957 // any additional formatting, filtering or sorting is applied.
958 // Override this function to define a custom caching behaviour.
959 void CGUIMediaWindow::OnCacheFileItems(CFileItemList &items)
961 // Should these items be saved to the hdd
962 if (items.CacheToDiscAlways() && !IsFiltered())
966 // \brief This function will be called by Update() after the
967 // labels of the fileitems are formatted. Override this function
968 // to modify the fileitems. Eg. to modify the item label
969 void CGUIMediaWindow::OnFinalizeFileItems(CFileItemList &items)
974 // \brief With this function you can react on a users click in the list/thumb panel.
975 // It returns true, if the click is handled.
976 // This function calls OnPlayMedia()
977 bool CGUIMediaWindow::OnClick(int iItem)
979 if ( iItem < 0 || iItem >= (int)m_vecItems->Size() ) return true;
980 CFileItemPtr pItem = m_vecItems->Get(iItem);
982 if (pItem->IsParentFolder())
987 if (pItem->GetPath() == "add" || pItem->GetPath() == "sources://add/") // 'add source button' in empty root
989 OnContextButton(iItem, CONTEXT_BUTTON_ADD_SOURCE);
993 if (!pItem->m_bIsFolder && pItem->IsFileFolder())
995 XFILE::IFileDirectory *pFileDirectory = NULL;
996 pFileDirectory = XFILE::CFileDirectoryFactory::Create(pItem->GetPath(), pItem.get(), "");
998 pItem->m_bIsFolder = true;
999 else if(pItem->m_bIsFolder)
1000 pItem->m_bIsFolder = false;
1001 delete pFileDirectory;
1004 if (pItem->IsScript())
1006 // execute the script
1007 CURL url(pItem->GetPath());
1009 if (CAddonMgr::Get().GetAddon(url.GetHostName(), addon, ADDON_SCRIPT))
1012 if (!g_pythonParser.StopScript(addon->LibPath()))
1013 g_pythonParser.evalFile(addon->LibPath(),addon);
1019 if (pItem->m_bIsFolder)
1021 if ( pItem->m_bIsShareOrDrive )
1023 const CStdString& strLockType=m_guiState->GetLockType();
1024 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE)
1025 if (!strLockType.IsEmpty() && !g_passwordManager.IsItemUnlocked(pItem.get(), strLockType))
1028 if (!HaveDiscOrConnection(pItem->GetPath(), pItem->m_iDriveType))
1032 // check for the partymode playlist items - they may not exist yet
1033 if ((pItem->GetPath() == g_settings.GetUserDataItem("PartyMode.xsp")) ||
1034 (pItem->GetPath() == g_settings.GetUserDataItem("PartyMode-Video.xsp")))
1036 // party mode playlist item - if it doesn't exist, prompt for user to define it
1037 if (!XFILE::CFile::Exists(pItem->GetPath()))
1039 m_vecItems->RemoveDiscCache(GetID());
1040 if (CGUIDialogSmartPlaylistEditor::EditPlaylist(pItem->GetPath()))
1046 // remove the directory cache if the folder is not normally cached
1047 CFileItemList items(pItem->GetPath());
1048 if (!items.AlwaysCache())
1049 items.RemoveDiscCache(GetID());
1051 // if we have a filtered list, we need to add the filtered
1052 // path to be able to come back to the filtered view
1053 CStdString strCurrentDirectory = m_vecItems->GetPath();
1054 if (m_canFilterAdvanced && !m_filter.IsEmpty() &&
1055 !m_strFilterPath.Equals(strCurrentDirectory))
1057 m_history.RemoveParentPath();
1058 m_history.AddPath(strCurrentDirectory, m_strFilterPath);
1061 CFileItem directory(*pItem);
1062 if (!Update(directory.GetPath()))
1063 ShowShareErrorMessage(&directory);
1067 else if (pItem->IsPlugin() && !pItem->GetProperty("isplayable").asBoolean())
1069 return XFILE::CPluginDirectory::RunScriptWithParams(pItem->GetPath());
1071 #if defined(TARGET_ANDROID)
1072 else if (pItem->IsAndroidApp())
1074 CStdString appName = URIUtils::GetFileName(pItem->GetPath());
1075 CLog::Log(LOGDEBUG, "CGUIMediaWindow::OnClick Trying to run: %s",appName.c_str());
1076 return CXBMCApp::StartActivity(appName);
1081 m_iSelectedItem = m_viewControl.GetSelectedItem();
1083 if (pItem->GetPath() == "newplaylist://")
1085 m_vecItems->RemoveDiscCache(GetID());
1086 g_windowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST_EDITOR,"newplaylist://");
1089 else if (pItem->GetPath().Left(19).Equals("newsmartplaylist://"))
1091 m_vecItems->RemoveDiscCache(GetID());
1092 if (CGUIDialogSmartPlaylistEditor::NewPlaylist(pItem->GetPath().Mid(19)))
1096 else if (pItem->GetPath().Left(14).Equals("addons://more/"))
1098 CBuiltins::Execute("ActivateWindow(AddonBrowser,addons://all/xbmc.addon." + pItem->GetPath().Mid(14) + ",return)");
1102 // If karaoke song is being played AND popup autoselector is enabled, the playlist should not be added
1103 bool do_not_add_karaoke = g_guiSettings.GetBool("karaoke.enabled") &&
1104 g_guiSettings.GetBool("karaoke.autopopupselector") && pItem->IsKaraoke();
1105 bool autoplay = m_guiState.get() && m_guiState->AutoPlayNextItem();
1107 if (m_vecItems->IsPlugin())
1109 CURL url(m_vecItems->GetPath());
1111 if (CAddonMgr::Get().GetAddon(url.GetHostName(),addon))
1113 PluginPtr plugin = boost::dynamic_pointer_cast<CPluginSource>(addon);
1114 if (plugin && plugin->Provides(CPluginSource::AUDIO))
1116 CFileItemList items;
1117 auto_ptr<CGUIViewState> state(CGUIViewState::GetViewState(GetID(), items));
1118 autoplay = state.get() && state->AutoPlayNextItem();
1123 if (autoplay && !g_partyModeManager.IsEnabled() &&
1124 !pItem->IsPlayList() && !do_not_add_karaoke)
1126 return OnPlayAndQueueMedia(pItem);
1130 return OnPlayMedia(iItem);
1137 bool CGUIMediaWindow::OnSelect(int item)
1139 return OnClick(item);
1142 // \brief Checks if there is a disc in the dvd drive and whether the
1143 // network is connected or not.
1144 bool CGUIMediaWindow::HaveDiscOrConnection(const CStdString& strPath, int iDriveType)
1146 if (iDriveType==CMediaSource::SOURCE_TYPE_DVD)
1148 if (!g_mediaManager.IsDiscInDrive(strPath))
1150 CGUIDialogOK::ShowAndGetInput(218, 219, 0, 0);
1154 else if (iDriveType==CMediaSource::SOURCE_TYPE_REMOTE)
1156 // TODO: Handle not connected to a remote share
1157 if ( !g_application.getNetwork().IsConnected() )
1159 CGUIDialogOK::ShowAndGetInput(220, 221, 0, 0);
1167 // \brief Shows a standard errormessage for a given pItem.
1168 void CGUIMediaWindow::ShowShareErrorMessage(CFileItem* pItem)
1170 if (pItem->m_bIsShareOrDrive)
1172 int idMessageText=0;
1173 const CURL& url=pItem->GetAsUrl();
1174 const CStdString& strHostName=url.GetHostName();
1176 if (pItem->m_iDriveType != CMediaSource::SOURCE_TYPE_REMOTE) // Local shares incl. dvd drive
1177 idMessageText=15300;
1178 else if (url.GetProtocol() == "smb" && strHostName.IsEmpty()) // smb workgroup
1179 idMessageText=15303;
1180 else // All other remote shares
1181 idMessageText=15301;
1183 CGUIDialogOK::ShowAndGetInput(220, idMessageText, 0, 0);
1187 // \brief The functon goes up one level in the directory tree
1188 void CGUIMediaWindow::GoParentFolder()
1190 //m_history.DumpPathHistory();
1192 // remove current directory if its on the stack
1193 // there were some issues due some folders having a trailing slash and some not
1194 // so just add a trailing slash to all of them for comparison.
1195 CStdString strPath = m_vecItems->GetPath();
1196 URIUtils::AddSlashAtEnd(strPath);
1197 CStdString strParent = m_history.GetParentPath();
1198 // in case the path history is messed up and the current folder is on
1199 // the stack more than once, keep going until there's nothing left or they
1200 // dont match anymore.
1201 while (!strParent.IsEmpty())
1203 URIUtils::AddSlashAtEnd(strParent);
1204 if (strParent.Equals(strPath))
1205 m_history.RemoveParentPath();
1208 strParent = m_history.GetParentPath();
1211 // remove the current filter but only if the parent
1212 // item doesn't have a filter as well
1213 CURL filterUrl(m_strFilterPath);
1214 if (filterUrl.HasOption("filter"))
1216 CURL parentUrl(m_history.GetParentPath(true));
1217 if (!parentUrl.HasOption("filter"))
1219 // we need to overwrite m_strFilterPath because
1220 // Refresh() will set updateFilterPath to false
1221 m_strFilterPath.clear();
1227 // if vector is not empty, pop parent
1228 // if vector is empty, parent is root source listing
1229 m_strFilterPath = m_history.GetParentPath(true);
1230 strParent = m_history.RemoveParentPath();
1231 if (!Update(strParent, false))
1234 // No items to show so go another level up
1235 if (!m_vecItems->GetPath().empty() && (m_filter.IsEmpty() ? m_vecItems->Size() : m_unfilteredItems->Size()) <= 0)
1237 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(2080), g_localizeStrings.Get(2081));
1242 // \brief Override the function to change the default behavior on how
1243 // a selected item history should look like
1244 void CGUIMediaWindow::GetDirectoryHistoryString(const CFileItem* pItem, CStdString& strHistoryString)
1246 if (pItem->m_bIsShareOrDrive)
1248 // We are in the virual directory
1250 // History string of the DVD drive
1251 // must be handel separately
1252 if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_DVD)
1254 // Remove disc label from item label
1255 // and use as history string, m_strPath
1256 // can change for new discs
1257 CStdString strLabel = pItem->GetLabel();
1258 int nPosOpen = strLabel.Find('(');
1259 int nPosClose = strLabel.ReverseFind(')');
1260 if (nPosOpen > -1 && nPosClose > -1 && nPosClose > nPosOpen)
1262 strLabel.Delete(nPosOpen + 1, (nPosClose) - (nPosOpen + 1));
1263 strHistoryString = strLabel;
1266 strHistoryString = strLabel;
1270 // Other items in virual directory
1271 CStdString strPath = pItem->GetPath();
1272 URIUtils::RemoveSlashAtEnd(strPath);
1274 strHistoryString = pItem->GetLabel() + strPath;
1277 else if (pItem->m_lEndOffset>pItem->m_lStartOffset && pItem->m_lStartOffset != -1)
1279 // Could be a cue item, all items of a cue share the same filename
1280 // so add the offsets to build the history string
1281 strHistoryString.Format("%ld%ld", pItem->m_lStartOffset, pItem->m_lEndOffset);
1282 strHistoryString += pItem->GetPath();
1286 // Normal directory items
1287 strHistoryString = pItem->GetPath();
1290 // remove any filter
1291 if (CanContainFilter(strHistoryString))
1292 strHistoryString = RemoveParameterFromPath(strHistoryString, "filter");
1294 URIUtils::RemoveSlashAtEnd(strHistoryString);
1295 strHistoryString.ToLower();
1298 // \brief Call this function to create a directory history for the
1299 // path given by strDirectory.
1300 void CGUIMediaWindow::SetHistoryForPath(const CStdString& strDirectory)
1302 // Make sure our shares are configured
1304 if (!strDirectory.IsEmpty())
1306 // Build the directory history for default path
1307 CStdString strPath, strParentPath;
1308 strPath = strDirectory;
1309 URIUtils::RemoveSlashAtEnd(strPath);
1311 CFileItemList items;
1312 m_rootDir.GetDirectory("", items);
1314 m_history.ClearPathHistory();
1316 while (URIUtils::GetParentPath(strPath, strParentPath))
1318 for (int i = 0; i < (int)items.Size(); ++i)
1320 CFileItemPtr pItem = items[i];
1321 CStdString path(pItem->GetPath());
1322 URIUtils::RemoveSlashAtEnd(path);
1323 if (path == strPath)
1325 CStdString strHistory;
1326 GetDirectoryHistoryString(pItem.get(), strHistory);
1327 m_history.SetSelectedItem(strHistory, "");
1328 URIUtils::AddSlashAtEnd(strPath);
1329 m_history.AddPathFront(strPath);
1330 m_history.AddPathFront("");
1332 //m_history.DumpPathHistory();
1337 if (URIUtils::IsVideoDb(strPath))
1339 CURL url(strParentPath);
1340 url.SetOptions(""); // clear any URL options from recreated parent path
1341 strParentPath = url.Get();
1344 URIUtils::AddSlashAtEnd(strPath);
1345 m_history.AddPathFront(strPath);
1346 m_history.SetSelectedItem(strPath, strParentPath);
1347 strPath = strParentPath;
1348 URIUtils::RemoveSlashAtEnd(strPath);
1352 m_history.ClearPathHistory();
1354 //m_history.DumpPathHistory();
1357 // \brief Override if you want to change the default behavior, what is done
1358 // when the user clicks on a file.
1359 // This function is called by OnClick()
1360 bool CGUIMediaWindow::OnPlayMedia(int iItem)
1362 // Reset Playlistplayer, playback started now does
1363 // not use the playlistplayer.
1364 g_playlistPlayer.Reset();
1365 g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_NONE);
1366 CFileItemPtr pItem=m_vecItems->Get(iItem);
1368 CLog::Log(LOGDEBUG, "%s %s", __FUNCTION__, pItem->GetPath().c_str());
1370 bool bResult = false;
1371 if (pItem->IsInternetStream() || pItem->IsPlayList())
1372 bResult = g_application.PlayMedia(*pItem, m_guiState->GetPlaylist());
1374 bResult = g_application.PlayFile(*pItem);
1376 if (pItem->m_lStartOffset == STARTOFFSET_RESUME)
1377 pItem->m_lStartOffset = 0;
1382 // \brief Override if you want to change the default behavior of what is done
1383 // when the user clicks on a file in a "folder" with similar files.
1384 // This function is called by OnClick()
1385 bool CGUIMediaWindow::OnPlayAndQueueMedia(const CFileItemPtr &item)
1387 //play and add current directory to temporary playlist
1388 int iPlaylist = m_guiState->GetPlaylist();
1389 if (iPlaylist != PLAYLIST_NONE)
1391 g_playlistPlayer.ClearPlaylist(iPlaylist);
1392 g_playlistPlayer.Reset();
1393 int mediaToPlay = 0;
1394 for ( int i = 0; i < m_vecItems->Size(); i++ )
1396 CFileItemPtr nItem = m_vecItems->Get(i);
1398 if (nItem->m_bIsFolder)
1401 if (!nItem->IsPlayList() && !nItem->IsZIP() && !nItem->IsRAR())
1402 g_playlistPlayer.Add(iPlaylist, nItem);
1404 if (item->IsSamePath(nItem.get()))
1405 { // item that was clicked
1406 mediaToPlay = g_playlistPlayer.GetPlaylist(iPlaylist).size() - 1;
1410 // Save current window and directory to know where the selected item was
1411 if (m_guiState.get())
1412 m_guiState->SetPlaylistDirectory(m_vecItems->GetPath());
1414 // figure out where we start playback
1415 if (g_playlistPlayer.IsShuffled(iPlaylist))
1417 int iIndex = g_playlistPlayer.GetPlaylist(iPlaylist).FindOrder(mediaToPlay);
1418 g_playlistPlayer.GetPlaylist(iPlaylist).Swap(0, iIndex);
1423 g_playlistPlayer.SetCurrentPlaylist(iPlaylist);
1424 g_playlistPlayer.Play(mediaToPlay);
1429 // \brief Synchonize the fileitems with the playlistplayer
1430 // It recreated the playlist of the playlistplayer based
1431 // on the fileitems of the window
1432 void CGUIMediaWindow::UpdateFileList()
1434 int nItem = m_viewControl.GetSelectedItem();
1435 CStdString strSelected;
1437 strSelected = m_vecItems->Get(nItem)->GetPath();
1439 FormatAndSort(*m_vecItems);
1442 m_viewControl.SetItems(*m_vecItems);
1443 m_viewControl.SetSelectedItem(strSelected);
1445 // set the currently playing item as selected, if its in this directory
1446 if (m_guiState.get() && m_guiState->IsCurrentPlaylistDirectory(m_vecItems->GetPath()))
1448 int iPlaylist=m_guiState->GetPlaylist();
1449 int nSong = g_playlistPlayer.GetCurrentSong();
1450 CFileItem playlistItem;
1451 if (nSong > -1 && iPlaylist > -1)
1452 playlistItem=*g_playlistPlayer.GetPlaylist(iPlaylist)[nSong];
1454 g_playlistPlayer.ClearPlaylist(iPlaylist);
1455 g_playlistPlayer.Reset();
1457 for (int i = 0; i < m_vecItems->Size(); i++)
1459 CFileItemPtr pItem = m_vecItems->Get(i);
1460 if (pItem->m_bIsFolder)
1463 if (!pItem->IsPlayList() && !pItem->IsZIP() && !pItem->IsRAR())
1464 g_playlistPlayer.Add(iPlaylist, pItem);
1466 if (pItem->GetPath() == playlistItem.GetPath() &&
1467 pItem->m_lStartOffset == playlistItem.m_lStartOffset)
1468 g_playlistPlayer.SetCurrentSong(g_playlistPlayer.GetPlaylist(iPlaylist).size() - 1);
1473 void CGUIMediaWindow::OnDeleteItem(int iItem)
1475 if ( iItem < 0 || iItem >= m_vecItems->Size()) return;
1476 CFileItemPtr item = m_vecItems->Get(iItem);
1478 if (item->IsPlayList())
1479 item->m_bIsFolder = false;
1481 if (g_settings.GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE && g_settings.GetCurrentProfile().filesLocked())
1482 if (!g_passwordManager.IsMasterLockUnlocked(true))
1485 if (!CFileUtils::DeleteItem(item))
1488 m_viewControl.SetSelectedItem(iItem);
1491 void CGUIMediaWindow::OnRenameItem(int iItem)
1493 if ( iItem < 0 || iItem >= m_vecItems->Size()) return;
1495 if (g_settings.GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE && g_settings.GetCurrentProfile().filesLocked())
1496 if (!g_passwordManager.IsMasterLockUnlocked(true))
1499 if (!CFileUtils::RenameFile(m_vecItems->Get(iItem)->GetPath()))
1502 m_viewControl.SetSelectedItem(iItem);
1505 void CGUIMediaWindow::OnInitWindow()
1507 // initial fetch is done unthreaded to ensure the items are setup prior to skin animations kicking off
1508 m_rootDir.SetAllowThreads(false);
1510 m_rootDir.SetAllowThreads(true);
1512 if (m_iSelectedItem > -1)
1513 m_viewControl.SetSelectedItem(m_iSelectedItem);
1515 CGUIWindow::OnInitWindow();
1518 CGUIControl *CGUIMediaWindow::GetFirstFocusableControl(int id)
1520 if (m_viewControl.HasControl(id))
1521 id = m_viewControl.GetCurrentControl();
1522 return CGUIWindow::GetFirstFocusableControl(id);
1525 void CGUIMediaWindow::SetupShares()
1527 // Setup shares and filemasks for this window
1528 CFileItemList items;
1529 CGUIViewState* viewState=CGUIViewState::GetViewState(GetID(), items);
1532 m_rootDir.SetMask(viewState->GetExtensions());
1533 m_rootDir.SetSources(viewState->GetSources());
1538 bool CGUIMediaWindow::OnPopupMenu(int iItem)
1540 // popup the context menu
1541 // grab our context menu
1542 CContextButtons buttons;
1543 GetContextButtons(iItem, buttons);
1548 if (iItem >= 0 && iItem < m_vecItems->Size())
1549 m_vecItems->Get(iItem)->Select(true);
1551 int choice = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
1553 // deselect our item
1554 if (iItem >= 0 && iItem < m_vecItems->Size())
1555 m_vecItems->Get(iItem)->Select(false);
1558 return OnContextButton(iItem, (CONTEXT_BUTTON)choice);
1563 void CGUIMediaWindow::GetContextButtons(int itemNumber, CContextButtons &buttons)
1565 CFileItemPtr item = (itemNumber >= 0 && itemNumber < m_vecItems->Size()) ? m_vecItems->Get(itemNumber) : CFileItemPtr();
1570 // user added buttons
1573 for (int i = CONTEXT_BUTTON_USER1; i <= CONTEXT_BUTTON_USER10; i++)
1575 label.Format("contextmenulabel(%i)", i - CONTEXT_BUTTON_USER1);
1576 if (item->GetProperty(label).empty())
1579 action.Format("contextmenuaction(%i)", i - CONTEXT_BUTTON_USER1);
1580 if (item->GetProperty(action).empty())
1583 buttons.Add((CONTEXT_BUTTON)i, item->GetProperty(label).asString());
1586 if (item->GetProperty("pluginreplacecontextitems").asBoolean())
1589 // TODO: FAVOURITES Conditions on masterlock and localisation
1590 if (!item->IsParentFolder() && !item->GetPath().Equals("add") && !item->GetPath().Equals("newplaylist://") &&
1591 !item->GetPath().Left(19).Equals("newsmartplaylist://") && !item->GetPath().Left(9).Equals("newtag://"))
1593 if (CFavourites::IsFavourite(item.get(), GetID()))
1594 buttons.Add(CONTEXT_BUTTON_ADD_FAVOURITE, 14077); // Remove Favourite
1596 buttons.Add(CONTEXT_BUTTON_ADD_FAVOURITE, 14076); // Add To Favourites;
1600 bool CGUIMediaWindow::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
1604 case CONTEXT_BUTTON_ADD_FAVOURITE:
1606 CFileItemPtr item = m_vecItems->Get(itemNumber);
1607 CFavourites::AddOrRemove(item.get(), GetID());
1610 case CONTEXT_BUTTON_PLUGIN_SETTINGS:
1612 CURL plugin(m_vecItems->Get(itemNumber)->GetPath());
1613 ADDON::AddonPtr addon;
1614 if (CAddonMgr::Get().GetAddon(plugin.GetHostName(), addon))
1615 if (CGUIDialogAddonSettings::ShowAndGetInput(addon))
1619 case CONTEXT_BUTTON_USER1:
1620 case CONTEXT_BUTTON_USER2:
1621 case CONTEXT_BUTTON_USER3:
1622 case CONTEXT_BUTTON_USER4:
1623 case CONTEXT_BUTTON_USER5:
1624 case CONTEXT_BUTTON_USER6:
1625 case CONTEXT_BUTTON_USER7:
1626 case CONTEXT_BUTTON_USER8:
1627 case CONTEXT_BUTTON_USER9:
1628 case CONTEXT_BUTTON_USER10:
1631 action.Format("contextmenuaction(%i)", button - CONTEXT_BUTTON_USER1);
1632 CApplicationMessenger::Get().ExecBuiltIn(m_vecItems->Get(itemNumber)->GetProperty(action).asString());
1641 const CGUIViewState *CGUIMediaWindow::GetViewState() const
1643 return m_guiState.get();
1646 const CFileItemList& CGUIMediaWindow::CurrentDirectory() const
1651 bool CGUIMediaWindow::WaitForNetwork() const
1653 if (g_application.getNetwork().IsAvailable())
1656 CGUIDialogProgress *progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
1660 CURL url(m_vecItems->GetPath());
1661 progress->SetHeading(1040); // Loading Directory
1662 progress->SetLine(1, url.GetWithoutUserDetails());
1663 progress->ShowProgressBar(false);
1664 progress->StartModal();
1665 while (!g_application.getNetwork().IsAvailable())
1667 progress->Progress();
1668 if (progress->IsCanceled())
1678 void CGUIMediaWindow::OnFilterItems(const CStdString &filter)
1680 CFileItemPtr currentItem;
1681 CStdString currentItemPath;
1682 int item = m_viewControl.GetSelectedItem();
1683 if (item >= 0 && item < m_vecItems->Size())
1685 currentItem = m_vecItems->Get(item);
1686 currentItemPath = currentItem->GetPath();
1689 m_viewControl.Clear();
1691 CFileItemList items(m_vecItems->GetPath()); // use the original path - it'll likely be relied on for other things later.
1692 items.Append(*m_unfilteredItems);
1693 bool filtered = GetFilteredItems(filter, items);
1695 m_vecItems->ClearItems();
1696 // we need to clear the sort state and re-sort the items
1697 m_vecItems->ClearSortState();
1698 m_vecItems->Append(items);
1700 // if the filter has changed, get the new filter path
1701 if (filtered && m_canFilterAdvanced)
1703 if (items.HasProperty(PROPERTY_PATH_DB))
1704 m_strFilterPath = items.GetProperty(PROPERTY_PATH_DB).asString();
1705 // only set m_strFilterPath if it hasn't been set before
1706 // otherwise we might overwrite it with a non-filter path
1707 // in case GetFilteredItems() returns true even though no
1708 // db-based filter (e.g. watched filter) has been applied
1709 else if (m_strFilterPath.empty())
1710 m_strFilterPath = items.GetPath();
1713 GetGroupedItems(*m_vecItems);
1714 FormatAndSort(*m_vecItems);
1716 // get the "filter" option
1717 CStdString filterOption;
1718 CURL filterUrl(m_strFilterPath);
1719 if (filterUrl.HasOption("filter"))
1720 filterOption = filterUrl.GetOption("filter");
1722 // apply the "filter" option to any folder item so that
1723 // the filter can be passed down to the sub-directory
1724 for (int index = 0; index < m_vecItems->Size(); index++)
1726 CFileItemPtr pItem = m_vecItems->Get(index);
1727 // if the item is a folder we need to copy the path of
1728 // the filtered item to be able to keep the applied filters
1729 if (pItem->m_bIsFolder)
1731 CURL itemUrl(pItem->GetPath());
1732 if (!filterOption.empty())
1733 itemUrl.SetOption("filter", filterOption);
1735 itemUrl.RemoveOption("filter");
1736 pItem->SetPath(itemUrl.Get());
1742 if (!m_canFilterAdvanced)
1743 SetProperty("filter", filter);
1746 // to be able to select the same item as before we need to adjust
1747 // the path of the item i.e. add or remove the "filter=" URL option
1748 // but that's only necessary for folder items
1749 if (currentItem.get() != NULL && currentItem->m_bIsFolder)
1751 CURL curUrl(currentItemPath), newUrl(m_strFilterPath);
1752 if (newUrl.HasOption("filter"))
1753 curUrl.SetOption("filter", newUrl.GetOption("filter"));
1754 else if (curUrl.HasOption("filter"))
1755 curUrl.RemoveOption("filter");
1757 currentItemPath = curUrl.Get();
1762 // The idea here is to ensure we have something to focus if our file list
1763 // is empty. As such, this check MUST be last and ignore the hide parent
1764 // fileitems settings.
1765 if (m_vecItems->IsEmpty())
1767 CFileItemPtr pItem(new CFileItem(".."));
1768 pItem->SetPath(m_history.GetParentPath());
1769 pItem->m_bIsFolder = true;
1770 pItem->m_bIsShareOrDrive = false;
1771 m_vecItems->AddFront(pItem, 0);
1774 // and update our view control + buttons
1775 m_viewControl.SetItems(*m_vecItems);
1776 m_viewControl.SetSelectedItem(currentItemPath);
1780 bool CGUIMediaWindow::GetFilteredItems(const CStdString &filter, CFileItemList &items)
1782 if (m_canFilterAdvanced)
1783 return GetAdvanceFilteredItems(items);
1785 CStdString trimmedFilter(filter);
1786 trimmedFilter.TrimLeft().ToLower();
1788 if (trimmedFilter.IsEmpty())
1791 CFileItemList filteredItems(items.GetPath()); // use the original path - it'll likely be relied on for other things later.
1792 bool numericMatch = StringUtils::IsNaturalNumber(trimmedFilter);
1793 for (int i = 0; i < items.Size(); i++)
1795 CFileItemPtr item = items.Get(i);
1796 if (item->IsParentFolder())
1798 filteredItems.Add(item);
1801 // TODO: Need to update this to get all labels, ideally out of the displayed info (ie from m_layout and m_focusedLayout)
1802 // though that isn't practical. Perhaps a better idea would be to just grab the info that we should filter on based on
1803 // where we are in the library tree.
1804 // Another idea is tying the filter string to the current level of the tree, so that going deeper disables the filter,
1805 // but it's re-enabled on the way back out.
1807 /* if (item->GetFocusedLayout())
1808 match = item->GetFocusedLayout()->GetAllText();
1809 else if (item->GetLayout())
1810 match = item->GetLayout()->GetAllText();
1812 match = item->GetLabel(); // Filter label only for now
1815 StringUtils::WordToDigits(match);
1817 size_t pos = StringUtils::FindWords(match.c_str(), trimmedFilter.c_str());
1818 if (pos != CStdString::npos)
1819 filteredItems.Add(item);
1823 items.Append(filteredItems);
1825 return items.GetObjectCount() > 0;
1828 bool CGUIMediaWindow::GetAdvanceFilteredItems(CFileItemList &items)
1830 // don't run the advanced filter if the filter is empty
1831 // and there hasn't been a filter applied before which
1832 // would have to be removed
1833 CURL url(m_strFilterPath);
1834 if (m_filter.IsEmpty() && !url.HasOption("filter"))
1837 CFileItemList resultItems;
1838 XFILE::CSmartPlaylistDirectory::GetDirectory(m_filter, resultItems, m_strFilterPath, true);
1840 // put together a lookup map for faster path comparison
1841 map<CStdString, CFileItemPtr> lookup;
1842 for (int j = 0; j < resultItems.Size(); j++)
1844 CStdString itemPath = RemoveParameterFromPath(resultItems[j]->GetPath(), "filter");
1847 lookup[itemPath] = resultItems[j];
1850 // loop through all the original items and find
1851 // those which are still part of the filter
1852 CFileItemList filteredItems;
1853 for (int i = 0; i < items.Size(); i++)
1855 CFileItemPtr item = items.Get(i);
1856 if (item->IsParentFolder())
1858 filteredItems.Add(item);
1862 // check if the item is part of the resultItems list
1863 // by comparing their paths (but ignoring any special
1864 // options because they differ from filter to filter)
1865 CStdString path = RemoveParameterFromPath(item->GetPath(), "filter");
1868 map<CStdString, CFileItemPtr>::iterator itItem = lookup.find(path);
1869 if (itItem != lookup.end())
1871 // add the item to the list of filtered items
1872 filteredItems.Add(item);
1874 // remove the item from the lists
1875 resultItems.Remove(itItem->second.get());
1876 lookup.erase(itItem);
1880 if (resultItems.Size() > 0)
1881 CLog::Log(LOGWARNING, "CGUIMediaWindow::GetAdvanceFilteredItems(): %d unknown items", resultItems.Size());
1884 items.Append(filteredItems);
1885 items.SetPath(resultItems.GetPath());
1886 if (resultItems.HasProperty(PROPERTY_PATH_DB))
1887 items.SetProperty(PROPERTY_PATH_DB, resultItems.GetProperty(PROPERTY_PATH_DB));
1891 bool CGUIMediaWindow::IsFiltered()
1893 return (!m_canFilterAdvanced && !GetProperty("filter").empty()) ||
1894 (m_canFilterAdvanced && !m_filter.IsEmpty());
1897 bool CGUIMediaWindow::Filter()
1900 if (!m_canFilterAdvanced)
1902 const CGUIControl *btnFilter = GetControl(CONTROL_BTN_FILTER);
1903 if (btnFilter != NULL && btnFilter->GetControlType() == CGUIControl::GUICONTROL_EDIT)
1905 CGUIMessage selected(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTN_FILTER);
1906 OnMessage(selected);
1907 OnFilterItems(selected.GetLabel());
1910 if (GetProperty("filter").empty())
1912 CStdString filter = GetProperty("filter").asString();
1913 CGUIKeyboardFactory::ShowAndGetFilter(filter, false);
1914 SetProperty("filter", filter);
1919 // advanced filtering
1921 CGUIDialogMediaFilter::ShowAndEditMediaFilter(m_strFilterPath, m_filter);
1926 CStdString CGUIMediaWindow::GetStartFolder(const CStdString &dir)
1928 if (dir.Equals("$ROOT") || dir.Equals("Root"))
1933 CStdString CGUIMediaWindow::RemoveParameterFromPath(const CStdString &strDirectory, const CStdString &strParameter)
1935 CURL url(strDirectory);
1936 if (url.HasOption(strParameter))
1938 url.RemoveOption(strParameter);
1942 return strDirectory;