[videodb] remove unused seasons table from episode_view
[xbmc.git] / xbmc / windows / GUIMediaWindow.cpp
blobafb236c2050a589a7197300d4a2e70b3c8137e6a
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "GUIMediaWindow.h"
11 #include "ContextMenuManager.h"
12 #include "FileItem.h"
13 #include "FileItemList.h"
14 #include "FileItemListModification.h"
15 #include "GUIPassword.h"
16 #include "GUIUserMessages.h"
17 #include "PartyModeManager.h"
18 #include "PlayListPlayer.h"
19 #include "ServiceBroker.h"
20 #include "URL.h"
21 #include "Util.h"
22 #include "addons/AddonManager.h"
23 #include "addons/PluginSource.h"
24 #include "addons/addoninfo/AddonType.h"
25 #include "application/Application.h"
26 #include "messaging/ApplicationMessenger.h"
27 #include "network/NetworkFileItemClassify.h"
28 #include "playlists/PlayListFileItemClassify.h"
29 #if defined(TARGET_ANDROID)
30 #include "platform/android/activity/XBMCApp.h"
31 #endif
32 #include "dialogs/GUIDialogBusy.h"
33 #include "dialogs/GUIDialogKaiToast.h"
34 #include "dialogs/GUIDialogMediaFilter.h"
35 #include "dialogs/GUIDialogProgress.h"
36 #include "dialogs/GUIDialogSmartPlaylistEditor.h"
37 #include "filesystem/FileDirectoryFactory.h"
38 #include "filesystem/MultiPathDirectory.h"
39 #include "filesystem/PluginDirectory.h"
40 #include "filesystem/SmartPlaylistDirectory.h"
41 #include "guilib/GUIComponent.h"
42 #include "guilib/GUIEditControl.h"
43 #include "guilib/GUIKeyboardFactory.h"
44 #include "guilib/GUIWindowManager.h"
45 #include "guilib/LocalizeStrings.h"
46 #include "input/actions/Action.h"
47 #include "input/actions/ActionIDs.h"
48 #include "interfaces/generic/ScriptInvocationManager.h"
49 #include "messaging/helpers/DialogOKHelper.h"
50 #include "network/Network.h"
51 #include "playlists/PlayList.h"
52 #include "profiles/ProfileManager.h"
53 #include "settings/AdvancedSettings.h"
54 #include "settings/Settings.h"
55 #include "settings/SettingsComponent.h"
56 #include "storage/MediaManager.h"
57 #include "threads/IRunnable.h"
58 #include "utils/FileUtils.h"
59 #include "utils/LabelFormatter.h"
60 #include "utils/SortUtils.h"
61 #include "utils/StringUtils.h"
62 #include "utils/URIUtils.h"
63 #include "utils/Variant.h"
64 #include "utils/log.h"
65 #include "view/GUIViewState.h"
67 #define CONTROL_BTNVIEWASICONS 2
68 #define CONTROL_BTNSORTBY 3
69 #define CONTROL_BTNSORTASC 4
70 #define CONTROL_BTN_FILTER 19
72 #define CONTROL_LABELFILES 12
74 #define PROPERTY_PATH_DB "path.db"
75 #define PROPERTY_SORT_ORDER "sort.order"
76 #define PROPERTY_SORT_ASCENDING "sort.ascending"
78 #define PLUGIN_REFRESH_DELAY 200
80 using namespace ADDON;
81 using namespace KODI;
82 using namespace KODI::MESSAGING;
83 using namespace std::chrono_literals;
85 namespace
87 class CGetDirectoryItems : public IRunnable
89 public:
90 CGetDirectoryItems(XFILE::CVirtualDirectory &dir, CURL &url, CFileItemList &items, bool useDir)
91 : m_dir(dir), m_url(url), m_items(items), m_useDir(useDir)
95 void Run() override
97 m_result = m_dir.GetDirectory(m_url, m_items, m_useDir, true);
100 void Cancel() override
102 m_dir.CancelDirectory();
105 bool m_result = false;
107 protected:
108 XFILE::CVirtualDirectory &m_dir;
109 CURL m_url;
110 CFileItemList &m_items;
111 bool m_useDir;
115 CGUIMediaWindow::CGUIMediaWindow(int id, const char *xmlFile)
116 : CGUIWindow(id, xmlFile)
118 m_loadType = KEEP_IN_MEMORY;
119 m_vecItems = new CFileItemList;
120 m_unfilteredItems = new CFileItemList;
121 m_vecItems->SetPath("?");
122 m_iLastControl = -1;
123 m_canFilterAdvanced = false;
125 m_guiState.reset(CGUIViewState::GetViewState(GetID(), *m_vecItems));
128 CGUIMediaWindow::~CGUIMediaWindow()
130 delete m_vecItems;
131 delete m_unfilteredItems;
134 bool CGUIMediaWindow::Load(TiXmlElement *pRootElement)
136 bool retVal = CGUIWindow::Load(pRootElement);
138 if (!retVal)
139 return false;
141 // configure our view control
142 m_viewControl.Reset();
143 m_viewControl.SetParentWindow(GetID());
144 TiXmlElement *element = pRootElement->FirstChildElement("views");
145 if (element && element->FirstChild())
146 { // format is <views>50,29,51,95</views>
147 const std::string &allViews = element->FirstChild()->ValueStr();
148 std::vector<std::string> views = StringUtils::Split(allViews, ",");
149 for (std::vector<std::string>::const_iterator i = views.begin(); i != views.end(); ++i)
151 int controlID = atol(i->c_str());
152 CGUIControl *control = GetControl(controlID);
153 if (control && control->IsContainer())
154 m_viewControl.AddView(control);
157 m_viewControl.SetViewControlID(CONTROL_BTNVIEWASICONS);
159 return true;
162 void CGUIMediaWindow::OnWindowLoaded()
164 SendMessage(GUI_MSG_SET_TYPE, CONTROL_BTN_FILTER, CGUIEditControl::INPUT_TYPE_FILTER);
165 CGUIWindow::OnWindowLoaded();
166 SetupShares();
169 void CGUIMediaWindow::OnWindowUnload()
171 CGUIWindow::OnWindowUnload();
172 m_viewControl.Reset();
175 CFileItemPtr CGUIMediaWindow::GetCurrentListItem(int offset)
177 int item = m_viewControl.GetSelectedItem();
178 if (!m_vecItems->Size() || item < 0)
179 return CFileItemPtr();
180 item = (item + offset) % m_vecItems->Size();
181 if (item < 0) item += m_vecItems->Size();
182 return m_vecItems->Get(item);
185 bool CGUIMediaWindow::OnAction(const CAction &action)
187 if (action.GetID() == ACTION_PARENT_DIR)
189 GoParentFolder();
190 return true;
193 if (CGUIWindow::OnAction(action))
194 return true;
196 if (action.GetID() == ACTION_FILTER)
197 return Filter();
199 // live filtering
200 if (action.GetID() == ACTION_FILTER_CLEAR)
202 CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS);
203 message.SetStringParam("");
204 OnMessage(message);
205 return true;
208 if (action.GetID() == ACTION_BACKSPACE)
210 CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS, 2); // 2 for delete
211 OnMessage(message);
212 return true;
215 if (action.GetID() >= ACTION_FILTER_SMS2 && action.GetID() <= ACTION_FILTER_SMS9)
217 std::string filter = std::to_string(action.GetID() - ACTION_FILTER_SMS2 + 2);
218 CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS, 1); // 1 for append
219 message.SetStringParam(filter);
220 OnMessage(message);
221 return true;
224 return false;
227 bool CGUIMediaWindow::OnBack(int actionID)
229 CancelUpdateItems();
231 CURL filterUrl(m_strFilterPath);
232 if (actionID == ACTION_NAV_BACK &&
233 !m_vecItems->IsVirtualDirectoryRoot() &&
234 !URIUtils::PathEquals(m_vecItems->GetPath(), GetRootPath(), true) &&
235 (!URIUtils::PathEquals(m_vecItems->GetPath(), m_startDirectory, true) || (m_canFilterAdvanced && filterUrl.HasOption("filter"))))
237 if (GoParentFolder())
238 return true;
240 return CGUIWindow::OnBack(actionID);
243 bool CGUIMediaWindow::OnMessage(CGUIMessage& message)
245 switch ( message.GetMessage() )
247 case GUI_MSG_WINDOW_DEINIT:
249 CancelUpdateItems();
251 m_iLastControl = GetFocusedControlID();
252 CGUIWindow::OnMessage(message);
254 // get rid of any active filtering
255 if (m_canFilterAdvanced)
257 m_canFilterAdvanced = false;
258 m_filter.Reset();
260 m_strFilterPath.clear();
262 // Call ClearFileItems() after our window has finished doing any WindowClose
263 // animations
264 ClearFileItems();
265 return true;
267 break;
269 case GUI_MSG_CLICKED:
271 int iControl = message.GetSenderId();
272 if (iControl == CONTROL_BTNVIEWASICONS)
274 // view as control could be a select button
275 int viewMode = 0;
276 const CGUIControl *control = GetControl(CONTROL_BTNVIEWASICONS);
277 if (control && control->GetControlType() != CGUIControl::GUICONTROL_BUTTON)
279 CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTNVIEWASICONS);
280 OnMessage(msg);
281 viewMode = m_viewControl.GetViewModeNumber(msg.GetParam1());
283 else
284 viewMode = m_viewControl.GetNextViewMode();
286 if (m_guiState)
287 m_guiState->SaveViewAsControl(viewMode);
289 UpdateButtons();
290 return true;
292 else if (iControl == CONTROL_BTNSORTASC) // sort asc
294 if (m_guiState)
295 m_guiState->SetNextSortOrder();
296 UpdateFileList();
297 return true;
299 else if (iControl == CONTROL_BTNSORTBY) // sort by
301 if (m_guiState.get() && m_guiState->ChooseSortMethod())
302 UpdateFileList();
303 return true;
305 else if (iControl == CONTROL_BTN_FILTER)
306 return Filter(false);
307 else if (m_viewControl.HasControl(iControl)) // list/thumb control
309 int iItem = m_viewControl.GetSelectedItem();
310 int iAction = message.GetParam1();
311 if (iItem < 0) break;
312 if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
314 OnSelect(iItem);
316 else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
318 OnPopupMenu(iItem);
319 return true;
323 break;
325 case GUI_MSG_SETFOCUS:
327 if (m_viewControl.HasControl(message.GetControlId()) && m_viewControl.GetCurrentControl() != message.GetControlId())
329 m_viewControl.SetFocused();
330 return true;
333 break;
335 case GUI_MSG_NOTIFY_ALL:
336 { // Message is received even if this window is inactive
337 if (message.GetParam1() == GUI_MSG_WINDOW_RESET)
339 m_vecItems->SetPath("?");
340 return true;
342 else if ( message.GetParam1() == GUI_MSG_REFRESH_THUMBS )
344 for (int i = 0; i < m_vecItems->Size(); i++)
345 m_vecItems->Get(i)->FreeMemory(true);
346 break; // the window will take care of any info images
348 else if (message.GetParam1() == GUI_MSG_REMOVED_MEDIA)
350 if ((m_vecItems->IsVirtualDirectoryRoot() ||
351 m_vecItems->IsSourcesPath()) && IsActive())
353 int iItem = m_viewControl.GetSelectedItem();
354 Refresh();
355 m_viewControl.SetSelectedItem(iItem);
357 else if (m_vecItems->IsRemovable())
358 { // check that we have this removable share still
359 if (!m_rootDir.IsInSource(m_vecItems->GetPath()))
360 { // don't have this share any more
361 if (IsActive()) Update("");
362 else
364 m_history.ClearPathHistory();
365 m_vecItems->SetPath("");
370 return true;
372 else if (message.GetParam1()==GUI_MSG_UPDATE_SOURCES)
373 { // State of the sources changed, so update our view
374 if ((m_vecItems->IsVirtualDirectoryRoot() ||
375 m_vecItems->IsSourcesPath()) && IsActive())
377 if (m_vecItemsUpdating)
379 CLog::Log(LOGWARNING, "CGUIMediaWindow::OnMessage - updating in progress");
380 return true;
382 CUpdateGuard ug(m_vecItemsUpdating);
383 int iItem = m_viewControl.GetSelectedItem();
384 Refresh(true);
385 m_viewControl.SetSelectedItem(iItem);
387 return true;
389 else if (message.GetParam1()==GUI_MSG_UPDATE && IsActive())
391 if (m_vecItemsUpdating)
393 CLog::Log(LOGWARNING, "CGUIMediaWindow::OnMessage - updating in progress");
394 return true;
396 CUpdateGuard ug(m_vecItemsUpdating);
397 if (message.GetNumStringParams())
399 if (message.GetParam2()) // param2 is used for resetting the history
400 SetHistoryForPath(message.GetStringParam());
402 CFileItemList list(message.GetStringParam());
403 list.RemoveDiscCache(GetID());
404 Update(message.GetStringParam());
406 else
407 Refresh(true); // refresh the listing
409 else if (message.GetParam1()==GUI_MSG_UPDATE_ITEM && message.GetItem())
411 int flag = message.GetParam2();
412 CFileItemPtr newItem = std::static_pointer_cast<CFileItem>(message.GetItem());
414 if (IsActive() || (flag & GUI_MSG_FLAG_FORCE_UPDATE))
416 m_vecItems->UpdateItem(newItem.get());
418 if (flag & GUI_MSG_FLAG_UPDATE_LIST)
419 { // need the list updated as well
420 UpdateFileList();
423 else if (newItem)
424 { // need to remove the disc cache
425 CFileItemList items;
426 items.SetPath(URIUtils::GetDirectory(newItem->GetPath()));
427 if (newItem->HasProperty("cachefilename"))
429 // Use stored cache file name
430 std::string crcfile = newItem->GetProperty("cachefilename").asString();
431 items.RemoveDiscCacheCRC(crcfile);
433 else
434 // No stored cache file name, attempt using truncated item path as list path
435 items.RemoveDiscCache(GetID());
438 else if (message.GetParam1()==GUI_MSG_UPDATE_PATH)
440 if (IsActive())
442 if((message.GetStringParam() == m_vecItems->GetPath()) ||
443 (m_vecItems->IsMultiPath() && XFILE::CMultiPathDirectory::HasPath(m_vecItems->GetPath(), message.GetStringParam())))
444 Refresh();
447 else if (message.GetParam1() == GUI_MSG_FILTER_ITEMS && IsActive())
449 std::string filter = GetProperty("filter").asString();
450 // check if this is meant for advanced filtering
451 if (message.GetParam2() != 10)
453 if (message.GetParam2() == 1) // append
454 filter += message.GetStringParam();
455 else if (message.GetParam2() == 2)
456 { // delete
457 if (filter.size())
458 filter.erase(filter.size() - 1);
460 else
461 filter = message.GetStringParam();
463 OnFilterItems(filter);
464 UpdateButtons();
465 return true;
467 else
468 return CGUIWindow::OnMessage(message);
470 return true;
472 break;
473 case GUI_MSG_PLAYBACK_STARTED:
474 case GUI_MSG_PLAYBACK_ENDED:
475 case GUI_MSG_PLAYBACK_STOPPED:
476 case GUI_MSG_PLAYLIST_CHANGED:
477 case GUI_MSG_PLAYLISTPLAYER_STOPPED:
478 case GUI_MSG_PLAYLISTPLAYER_STARTED:
479 case GUI_MSG_PLAYLISTPLAYER_CHANGED:
480 { // send a notify all to all controls on this window
481 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_REFRESH_LIST);
482 OnMessage(msg);
483 break;
485 case GUI_MSG_CHANGE_VIEW_MODE:
487 int viewMode = 0;
488 if (message.GetParam1()) // we have an id
489 viewMode = m_viewControl.GetViewModeByID(message.GetParam1());
490 else if (message.GetParam2())
491 viewMode = m_viewControl.GetNextViewMode(message.GetParam2());
493 if (m_guiState)
494 m_guiState->SaveViewAsControl(viewMode);
495 UpdateButtons();
496 return true;
498 break;
499 case GUI_MSG_CHANGE_SORT_METHOD:
501 if (m_guiState)
503 if (message.GetParam1())
504 m_guiState->SetCurrentSortMethod(message.GetParam1());
505 else if (message.GetParam2())
506 m_guiState->SetNextSortMethod(message.GetParam2());
508 UpdateFileList();
509 return true;
511 break;
512 case GUI_MSG_CHANGE_SORT_DIRECTION:
514 if (m_guiState)
515 m_guiState->SetNextSortOrder();
516 UpdateFileList();
517 return true;
519 break;
520 case GUI_MSG_WINDOW_INIT:
522 if (m_vecItems->GetPath() == "?")
523 m_vecItems->SetPath("");
525 std::string dir = message.GetStringParam(0);
526 const std::string& ret = message.GetStringParam(1);
527 const std::string& swap = message.GetStringParam(message.GetNumStringParams() - 1);
528 const bool returning = StringUtils::EqualsNoCase(ret, "return");
529 const bool replacing = StringUtils::EqualsNoCase(swap, "replace");
531 if (!dir.empty())
533 // ensure our directory is valid
534 dir = GetStartFolder(dir);
535 bool resetHistory = false;
536 if (!returning || !URIUtils::PathEquals(dir, m_startDirectory, true))
537 { // we're not returning to the same path, so set our directory to the requested path
538 m_vecItems->SetPath(dir);
539 resetHistory = true;
541 else if (m_vecItems->GetPath().empty() && URIUtils::PathEquals(dir, m_startDirectory, true))
542 m_vecItems->SetPath(dir);
544 // check for network up
545 if (URIUtils::IsRemote(m_vecItems->GetPath()) && !WaitForNetwork())
547 m_vecItems->SetPath("");
548 resetHistory = true;
550 if (resetHistory)
552 m_vecItems->RemoveDiscCache(GetID());
553 // only compute the history for the provided path if "return" is not defined
554 // (otherwise the root level for the path will be added by default to the path history
555 // and we won't be able to move back to the path we came from)
556 if (!returning)
557 SetHistoryForPath(m_vecItems->GetPath());
560 if (message.GetParam1() != WINDOW_INVALID)
562 // if this is the first time to this window - make sure we set the root path
563 // if "return" is defined make sure we set the startDirectory to the directory we are
564 // moving to (so that we can move back to where we were onBack). If we are activating
565 // the same window but with a different path, do nothing - we are simply adding to the
566 // window history. Note that if the window is just being replaced, the start directory
567 // also needs to be set as the manager has just popped the previous window.
568 if (message.GetParam1() != message.GetParam2() || replacing)
569 m_startDirectory = returning ? dir : GetRootPath();
571 if (message.GetParam2() == PLUGIN_REFRESH_DELAY)
573 Refresh();
574 SetInitialVisibility();
575 RestoreControlStates();
576 SetInitialVisibility();
577 return true;
580 break;
583 return CGUIWindow::OnMessage(message);
587 * \brief Updates the states
589 * This updates the states (enable, disable, visible...) of the controls defined
590 * by this window.
592 * \note Override this function in a derived class to add new controls
594 void CGUIMediaWindow::UpdateButtons()
596 if (m_guiState)
598 // Update sorting controls
599 if (m_guiState->GetSortOrder() == SortOrderNone)
601 CONTROL_DISABLE(CONTROL_BTNSORTASC);
603 else
605 CONTROL_ENABLE(CONTROL_BTNSORTASC);
606 SET_CONTROL_SELECTED(GetID(), CONTROL_BTNSORTASC, m_guiState->GetSortOrder() != SortOrderAscending);
609 // Update list/thumb control
610 m_viewControl.SetCurrentView(m_guiState->GetViewAsControl());
612 // Update sort by button
613 if (!m_guiState->HasMultipleSortMethods())
614 CONTROL_DISABLE(CONTROL_BTNSORTBY);
615 else
616 CONTROL_ENABLE(CONTROL_BTNSORTBY);
618 std::string sortLabel = StringUtils::Format(
619 g_localizeStrings.Get(550), g_localizeStrings.Get(m_guiState->GetSortMethodLabel()));
620 SET_CONTROL_LABEL(CONTROL_BTNSORTBY, sortLabel);
623 std::string items =
624 StringUtils::Format("{} {}", m_vecItems->GetObjectCount(), g_localizeStrings.Get(127));
625 SET_CONTROL_LABEL(CONTROL_LABELFILES, items);
627 SET_CONTROL_LABEL2(CONTROL_BTN_FILTER, GetProperty("filter").asString());
630 void CGUIMediaWindow::ClearFileItems()
632 m_viewControl.Clear();
633 m_vecItems->Clear();
634 m_unfilteredItems->Clear();
638 * \brief Sort file items
640 * This sorts file items based on the sort method and sort order provided by
641 * guiViewState.
643 void CGUIMediaWindow::SortItems(CFileItemList &items)
645 std::unique_ptr<CGUIViewState> guiState(CGUIViewState::GetViewState(GetID(), items));
647 if (guiState)
649 SortDescription sorting = guiState->GetSortMethod();
650 sorting.sortOrder = guiState->GetSortOrder();
651 // If the sort method is "sort by playlist" and we have a specific
652 // sort order available we can use the specified sort order to do the sorting
653 // We do this as the new SortBy methods are a superset of the SORT_METHOD methods, thus
654 // not all are available. This may be removed once SORT_METHOD_* have been replaced by
655 // SortBy.
656 if ((sorting.sortBy == SortByPlaylistOrder) && items.HasProperty(PROPERTY_SORT_ORDER))
658 SortBy sortBy = (SortBy)items.GetProperty(PROPERTY_SORT_ORDER).asInteger();
659 if (sortBy != SortByNone && sortBy != SortByPlaylistOrder && sortBy != SortByProgramCount)
661 sorting.sortBy = sortBy;
662 sorting.sortOrder = items.GetProperty(PROPERTY_SORT_ASCENDING).asBoolean() ? SortOrderAscending : SortOrderDescending;
663 sorting.sortAttributes = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING) ? SortAttributeIgnoreArticle : SortAttributeNone;
665 // if the sort order is descending, we need to switch the original sort order, as we assume
666 // in CGUIViewState::AddPlaylistOrder that SortByPlaylistOrder is ascending.
667 if (guiState->GetSortOrder() == SortOrderDescending)
668 sorting.sortOrder = sorting.sortOrder == SortOrderDescending ? SortOrderAscending : SortOrderDescending;
672 items.Sort(sorting);
677 * \brief Formats item labels
679 * This is based on the formatting provided by guiViewState.
681 void CGUIMediaWindow::FormatItemLabels(CFileItemList &items, const LABEL_MASKS &labelMasks)
683 CLabelFormatter fileFormatter(labelMasks.m_strLabelFile, labelMasks.m_strLabel2File);
684 CLabelFormatter folderFormatter(labelMasks.m_strLabelFolder, labelMasks.m_strLabel2Folder);
685 for (int i=0; i<items.Size(); ++i)
687 CFileItemPtr pItem=items[i];
689 if (pItem->IsLabelPreformatted())
690 continue;
692 if (pItem->m_bIsFolder)
693 folderFormatter.FormatLabels(pItem.get());
694 else
695 fileFormatter.FormatLabels(pItem.get());
698 if (items.GetSortMethod() == SortByLabel)
699 items.ClearSortState();
703 * \brief Format and sort file items
705 * Prepares and adds the fileitems to list/thumb panel
707 void CGUIMediaWindow::FormatAndSort(CFileItemList &items)
709 std::unique_ptr<CGUIViewState> viewState(CGUIViewState::GetViewState(GetID(), items));
711 if (viewState)
713 LABEL_MASKS labelMasks;
714 viewState->GetSortMethodLabelMasks(labelMasks);
715 FormatItemLabels(items, labelMasks);
717 items.Sort(viewState->GetSortMethod().sortBy, viewState->GetSortOrder(), viewState->GetSortMethod().sortAttributes);
722 * \brief Overwrite to fill fileitems from a source
724 * \param[in] strDirectory Path to read
725 * \param[out] items Fill with items specified in \e strDirectory
726 * \return false if given directory not present
728 bool CGUIMediaWindow::GetDirectory(const std::string &strDirectory, CFileItemList &items)
730 CURL pathToUrl(strDirectory);
732 std::string strParentPath = m_history.GetParentPath();
734 CLog::Log(LOGDEBUG, "CGUIMediaWindow::GetDirectory ({})", CURL::GetRedacted(strDirectory));
735 CLog::Log(LOGDEBUG, " ParentPath = [{}]", CURL::GetRedacted(strParentPath));
737 if (pathToUrl.IsProtocol("plugin") && !pathToUrl.GetHostName().empty())
738 CServiceBroker::GetAddonMgr().UpdateLastUsed(pathToUrl.GetHostName());
740 // see if we can load a previously cached folder
741 CFileItemList cachedItems(strDirectory);
742 if (!strDirectory.empty() && cachedItems.Load(GetID()))
744 items.Assign(cachedItems);
746 else
748 auto start = std::chrono::steady_clock::now();
750 if (strDirectory.empty())
751 SetupShares();
753 CFileItemList dirItems;
754 if (!GetDirectoryItems(pathToUrl, dirItems, UseFileDirectories()))
755 return false;
757 // assign fetched directory items
758 items.Assign(dirItems);
760 // took over a second, and not normally cached, so cache it
761 auto end = std::chrono::steady_clock::now();
762 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
764 if (duration.count() > 1000 && items.CacheToDiscIfSlow())
765 items.Save(GetID());
767 // if these items should replace the current listing, then pop it off the top
768 if (items.GetReplaceListing())
769 m_history.RemoveParentPath();
772 // update the view state's reference to the current items
773 m_guiState.reset(CGUIViewState::GetViewState(GetID(), items));
775 bool bHideParent = false;
777 if (m_guiState && m_guiState->HideParentDirItems())
778 bHideParent = true;
779 if (items.GetPath() == GetRootPath())
780 bHideParent = true;
782 if (!bHideParent)
784 CFileItemPtr pItem(new CFileItem(".."));
785 pItem->SetPath(strParentPath);
786 pItem->m_bIsFolder = true;
787 pItem->m_bIsShareOrDrive = false;
788 items.AddFront(pItem, 0);
791 int iWindow = GetID();
792 std::vector<std::string> regexps;
794 //! @todo Do we want to limit the directories we apply the video ones to?
795 if (iWindow == WINDOW_VIDEO_NAV)
796 regexps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoExcludeFromListingRegExps;
797 if (iWindow == WINDOW_MUSIC_NAV)
798 regexps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_audioExcludeFromListingRegExps;
799 if (iWindow == WINDOW_PICTURES)
800 regexps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_pictureExcludeFromListingRegExps;
802 if (regexps.size())
804 for (int i=0; i < items.Size();)
806 if (CUtil::ExcludeFileOrFolder(items[i]->GetPath(), regexps))
807 items.Remove(i);
808 else
809 i++;
813 // clear the filter
814 SetProperty("filter", "");
815 m_canFilterAdvanced = false;
816 m_filter.Reset();
817 return true;
820 bool CGUIMediaWindow::Update(const std::string &strDirectory, bool updateFilterPath /* = true */)
822 //! @todo OnInitWindow calls Update() before window path has been set properly.
823 if (strDirectory == "?")
824 return false;
826 // The path to load. Empty string is used in various places to denote root, so translate to the
827 // real root path first
828 const std::string path = strDirectory.empty() ? GetRootPath() : strDirectory;
830 // stores the selected item in history
831 SaveSelectedItemInHistory();
833 const std::string previousPath = m_vecItems->GetPath();
835 // check if the path contains a filter and temporarily remove it
836 // so that the retrieved list of items is unfiltered
837 std::string pathNoFilter = path;
838 if (CanContainFilter(pathNoFilter) && CURL(pathNoFilter).HasOption("filter"))
839 pathNoFilter = RemoveParameterFromPath(pathNoFilter, "filter");
841 if (!GetDirectory(pathNoFilter, *m_vecItems))
843 CLog::Log(LOGERROR, "CGUIMediaWindow::GetDirectory({}) failed", CURL(path).GetRedacted());
845 if (URIUtils::PathEquals(path, GetRootPath()))
846 return false; // Nothing to fallback to
848 // Try to return to the previous directory, if not the same
849 // else fallback to root
850 if (URIUtils::PathEquals(path, previousPath) || !Update(m_history.RemoveParentPath()))
851 Update(""); // Fallback to root
853 // Return false to be able to eg. show
854 // an error message.
855 return false;
858 if (m_vecItems->GetLabel().empty())
860 // Removable sources
861 VECSOURCES removables;
862 CServiceBroker::GetMediaManager().GetRemovableDrives(removables);
863 for (const auto& s : removables)
865 if (URIUtils::CompareWithoutSlashAtEnd(s.strPath, m_vecItems->GetPath()))
867 m_vecItems->SetLabel(s.strName);
868 break;
873 if (m_vecItems->GetLabel().empty())
874 m_vecItems->SetLabel(CUtil::GetTitleFromPath(m_vecItems->GetPath(), true));
876 // check the given path for filter data
877 UpdateFilterPath(path, *m_vecItems, updateFilterPath);
879 // if we're getting the root source listing
880 // make sure the path history is clean
881 if (URIUtils::PathEquals(path, GetRootPath()))
882 m_history.ClearPathHistory();
884 int iWindow = GetID();
885 int showLabel = 0;
886 if (URIUtils::PathEquals(path, GetRootPath()))
888 if (iWindow == WINDOW_PICTURES)
889 showLabel = 997;
890 else if (iWindow == WINDOW_FILES)
891 showLabel = 1026;
892 else if (iWindow == WINDOW_GAMES)
893 showLabel = 35250; // "Add games..."
895 if (m_vecItems->IsPath("sources://video/"))
896 showLabel = 999;
897 else if (m_vecItems->IsPath("sources://music/"))
898 showLabel = 998;
899 else if (m_vecItems->IsPath("sources://pictures/"))
900 showLabel = 997;
901 else if (m_vecItems->IsPath("sources://files/"))
902 showLabel = 1026;
903 else if (m_vecItems->IsPath("sources://games/"))
904 showLabel = 35250; // "Add games..."
905 // Add 'Add source ' item
906 if (showLabel && (m_vecItems->Size() == 0 || !m_guiState->DisableAddSourceButtons()) &&
907 iWindow != WINDOW_MUSIC_PLAYLIST_EDITOR)
909 const std::string& strLabel = g_localizeStrings.Get(showLabel);
910 CFileItemPtr pItem(new CFileItem(strLabel));
911 pItem->SetPath("add");
912 pItem->SetArt("icon", "DefaultAddSource.png");
913 pItem->SetLabel(strLabel);
914 pItem->SetLabelPreformatted(true);
915 pItem->m_bIsFolder = true;
916 pItem->SetSpecialSort(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_addSourceOnTop ?
917 SortSpecialOnTop : SortSpecialOnBottom);
918 m_vecItems->Add(pItem);
920 m_iLastControl = GetFocusedControlID();
922 // Check whether to enabled advanced filtering based on the content type
923 m_canFilterAdvanced = CheckFilterAdvanced(*m_vecItems);
924 if (m_canFilterAdvanced)
925 m_filter.SetType(m_vecItems->GetContent());
927 // Ask the derived class if it wants to load additional info
928 // for the fileitems like media info or additional
929 // filtering on the items, setting thumbs.
930 OnPrepareFileItems(*m_vecItems);
932 m_vecItems->FillInDefaultIcons();
934 // remember the original (untouched) list of items (for filtering etc)
935 m_unfilteredItems->Assign(*m_vecItems);
937 // Cache the list of items if possible
938 OnCacheFileItems(*m_vecItems);
940 // Filter and group the items if necessary
941 OnFilterItems(GetProperty("filter").asString());
942 UpdateButtons();
944 // Restore selected item from history
945 RestoreSelectedItemFromHistory();
947 m_history.AddPath(m_vecItems->GetPath(), m_strFilterPath);
949 //m_history.DumpPathHistory();
951 return true;
954 bool CGUIMediaWindow::Refresh(bool clearCache /* = false */)
956 std::string strCurrentDirectory = m_vecItems->GetPath();
957 if (strCurrentDirectory == "?")
958 return false;
960 if (clearCache)
961 m_vecItems->RemoveDiscCache(GetID());
963 bool ret = true;
965 // get the original number of items
966 if (!Update(strCurrentDirectory, false))
968 ret = false;
971 return ret;
975 * \brief On prepare file items
977 * This function will be called by Update() before the labels of the fileitems
978 * are formatted.
980 * \note Override this function to set custom thumbs or load additional media
981 * info.
983 * It's used to load tag info for music.
985 void CGUIMediaWindow::OnPrepareFileItems(CFileItemList &items)
987 CFileItemListModification::GetInstance().Modify(items);
991 * \brief On cache file items
993 * This function will be called by Update() before
994 * any additional formatting, filtering or sorting is applied.
996 * \note Override this function to define a custom caching behaviour.
998 void CGUIMediaWindow::OnCacheFileItems(CFileItemList &items)
1000 // Should these items be saved to the hdd
1001 if (items.CacheToDiscAlways() && !IsFiltered())
1002 items.Save(GetID());
1006 * \brief On click
1008 * With this function you can react on a users click in the list/thumb panel.
1009 * It returns true, if the click is handled.
1010 * This function calls OnPlayMedia()
1012 bool CGUIMediaWindow::OnClick(int iItem, const std::string &player)
1014 if (iItem < 0 || iItem >= m_vecItems->Size())
1015 return true;
1017 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
1019 CFileItemPtr pItem = m_vecItems->Get(iItem);
1021 if (pItem->IsParentFolder())
1023 GoParentFolder();
1024 return true;
1027 if (pItem->GetPath() == "add" || pItem->GetPath() == "sources://add/") // 'add source button' in empty root
1029 if (profileManager->IsMasterProfile())
1031 if (!g_passwordManager.IsMasterLockUnlocked(true))
1032 return false;
1034 else if (!profileManager->GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked())
1035 return false;
1037 if (OnAddMediaSource())
1038 Refresh(true);
1040 return true;
1043 if (!pItem->m_bIsFolder && pItem->IsFileFolder(EFILEFOLDER_MASK_ONCLICK))
1045 XFILE::IFileDirectory *pFileDirectory = nullptr;
1046 pFileDirectory = XFILE::CFileDirectoryFactory::Create(pItem->GetURL(), pItem.get(), "");
1047 if(pFileDirectory)
1048 pItem->m_bIsFolder = true;
1049 else if(pItem->m_bIsFolder)
1050 pItem->m_bIsFolder = false;
1051 delete pFileDirectory;
1054 if (pItem->IsScript())
1056 // execute the script
1057 CURL url(pItem->GetPath());
1058 AddonPtr addon;
1059 if (CServiceBroker::GetAddonMgr().GetAddon(url.GetHostName(), addon, AddonType::SCRIPT,
1060 OnlyEnabled::CHOICE_YES))
1062 if (!CScriptInvocationManager::GetInstance().Stop(addon->LibPath()))
1064 CServiceBroker::GetAddonMgr().UpdateLastUsed(addon->ID());
1065 CScriptInvocationManager::GetInstance().ExecuteAsync(addon->LibPath(), addon);
1067 return true;
1071 if (pItem->m_bIsFolder)
1073 if ( pItem->m_bIsShareOrDrive )
1075 const std::string& strLockType=m_guiState->GetLockType();
1076 if (profileManager->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE)
1077 if (!strLockType.empty() && !g_passwordManager.IsItemUnlocked(pItem.get(), strLockType))
1078 return true;
1080 if (!HaveDiscOrConnection(pItem->GetPath(), pItem->m_iDriveType))
1081 return true;
1084 // check for the partymode playlist items - they may not exist yet
1085 if ((pItem->GetPath() == profileManager->GetUserDataItem("PartyMode.xsp")) ||
1086 (pItem->GetPath() == profileManager->GetUserDataItem("PartyMode-Video.xsp")))
1088 // party mode playlist item - if it doesn't exist, prompt for user to define it
1089 if (!CFileUtils::Exists(pItem->GetPath()))
1091 m_vecItems->RemoveDiscCache(GetID());
1092 if (CGUIDialogSmartPlaylistEditor::EditPlaylist(pItem->GetPath()))
1093 Refresh();
1094 return true;
1098 // remove the directory cache if the folder is not normally cached
1099 CFileItemList items(pItem->GetPath());
1100 if (!items.AlwaysCache())
1101 items.RemoveDiscCache(GetID());
1103 // if we have a filtered list, we need to add the filtered
1104 // path to be able to come back to the filtered view
1105 std::string strCurrentDirectory = m_vecItems->GetPath();
1106 if (m_canFilterAdvanced && !m_filter.IsEmpty() &&
1107 !URIUtils::PathEquals(m_strFilterPath, strCurrentDirectory))
1109 m_history.RemoveParentPath();
1110 m_history.AddPath(strCurrentDirectory, m_strFilterPath);
1113 if (m_vecItemsUpdating)
1115 CLog::Log(LOGWARNING, "CGUIMediaWindow::OnClick - updating in progress");
1116 return true;
1118 CUpdateGuard ug(m_vecItemsUpdating);
1120 CFileItem directory(*pItem);
1121 if (!Update(directory.GetPath()))
1122 ShowShareErrorMessage(&directory);
1124 return true;
1126 else if (pItem->IsPlugin() && !pItem->GetProperty("isplayable").asBoolean())
1128 bool resume = pItem->GetStartOffset() == STARTOFFSET_RESUME;
1129 return XFILE::CPluginDirectory::RunScriptWithParams(pItem->GetPath(), resume);
1131 #if defined(TARGET_ANDROID)
1132 else if (pItem->IsAndroidApp())
1134 std::string appName = URIUtils::GetFileName(pItem->GetPath());
1135 CLog::Log(LOGDEBUG, "CGUIMediaWindow::OnClick Trying to run: {}", appName);
1136 return CXBMCApp::StartActivity(appName);
1138 #endif
1139 else
1141 SaveSelectedItemInHistory();
1143 if (pItem->GetPath() == "newplaylist://")
1145 m_vecItems->RemoveDiscCache(GetID());
1146 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_MUSIC_PLAYLIST_EDITOR,"newplaylist://");
1147 return true;
1149 else if (StringUtils::StartsWithNoCase(pItem->GetPath(), "newsmartplaylist://"))
1151 m_vecItems->RemoveDiscCache(GetID());
1152 if (CGUIDialogSmartPlaylistEditor::NewPlaylist(pItem->GetPath().substr(19)))
1153 Refresh();
1154 return true;
1157 bool autoplay = m_guiState.get() && m_guiState->AutoPlayNextItem();
1159 if (m_vecItems->IsPlugin())
1161 CURL url(m_vecItems->GetPath());
1162 AddonPtr addon;
1163 if (CServiceBroker::GetAddonMgr().GetAddon(url.GetHostName(), addon, OnlyEnabled::CHOICE_YES))
1165 const auto plugin = std::dynamic_pointer_cast<CPluginSource>(addon);
1166 if (plugin && plugin->Provides(CPluginSource::AUDIO))
1168 CFileItemList items;
1169 std::unique_ptr<CGUIViewState> state(CGUIViewState::GetViewState(GetID(), items));
1170 autoplay = state.get() && state->AutoPlayNextItem();
1175 if (autoplay && !g_partyModeManager.IsEnabled())
1177 return OnPlayAndQueueMedia(pItem, player);
1179 else
1181 return OnPlayMedia(iItem, player);
1185 return false;
1188 bool CGUIMediaWindow::OnSelect(int item)
1190 return OnClick(item);
1194 * \brief Check disc or connection present
1196 * Checks if there is a disc in the dvd drive and whether the
1197 * network is connected or not.
1199 bool CGUIMediaWindow::HaveDiscOrConnection(const std::string& strPath, int iDriveType)
1201 if (iDriveType==CMediaSource::SOURCE_TYPE_DVD)
1203 if (!CServiceBroker::GetMediaManager().IsDiscInDrive(strPath))
1205 HELPERS::ShowOKDialogText(CVariant{218}, CVariant{219});
1206 return false;
1209 else if (iDriveType==CMediaSource::SOURCE_TYPE_REMOTE)
1211 //! @todo Handle not connected to a remote share
1212 if (!CServiceBroker::GetNetwork().IsConnected())
1214 HELPERS::ShowOKDialogText(CVariant{220}, CVariant{221});
1215 return false;
1219 return true;
1223 * \brief Shows a standard error message for a given pItem.
1225 void CGUIMediaWindow::ShowShareErrorMessage(CFileItem* pItem) const
1227 if (!pItem->m_bIsShareOrDrive)
1228 return;
1230 int idMessageText = 0;
1231 CURL url(pItem->GetPath());
1233 if (url.IsProtocol("smb") && url.GetHostName().empty()) // smb workgroup
1234 idMessageText = 15303; // Workgroup not found
1235 else if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_REMOTE || URIUtils::IsRemote(pItem->GetPath()))
1236 idMessageText = 15301; // Could not connect to network server
1237 else
1238 idMessageText = 15300; // Path not found or invalid
1240 HELPERS::ShowOKDialogText(CVariant{220}, CVariant{idMessageText});
1244 * \brief Go one directory up on list items
1246 * The function goes up one level in the directory tree
1248 bool CGUIMediaWindow::GoParentFolder()
1250 if (m_vecItems->IsVirtualDirectoryRoot())
1251 return false;
1253 if (URIUtils::PathEquals(m_vecItems->GetPath(), GetRootPath()))
1254 return false;
1256 //m_history.DumpPathHistory();
1258 const std::string currentPath = m_vecItems->GetPath();
1259 std::string parentPath = m_history.GetParentPath();
1260 // Check if a) the current folder is on the stack more than once, (parent is
1261 // often same as current), OR
1262 // b) the parent is an xml file (happens when ActivateWindow() called with
1263 // a node file) and so current path is the result of expanding the xml.
1264 // Keep going until there's nothing left or they dont match anymore.
1265 while (!parentPath.empty() &&
1266 (URIUtils::PathEquals(parentPath, currentPath, true) ||
1267 StringUtils::EndsWith(parentPath, ".xml/") || StringUtils::EndsWith(parentPath, ".xml")))
1269 m_history.RemoveParentPath();
1270 parentPath = m_history.GetParentPath();
1273 // remove the current filter but only if the parent
1274 // item doesn't have a filter as well
1275 CURL filterUrl(m_strFilterPath);
1276 if (filterUrl.HasOption("filter"))
1278 CURL parentUrl(m_history.GetParentPath(true));
1279 if (!parentUrl.HasOption("filter"))
1281 // we need to overwrite m_strFilterPath because
1282 // Refresh() will set updateFilterPath to false
1283 m_strFilterPath.clear();
1284 Refresh();
1285 return true;
1289 // pop directory path from the stack
1290 m_strFilterPath = m_history.GetParentPath(true);
1291 m_history.RemoveParentPath();
1293 if (!Update(parentPath, false))
1294 return false;
1296 // No items to show so go another level up
1297 if (!m_vecItems->GetPath().empty() && (m_filter.IsEmpty() ? m_vecItems->Size() : m_unfilteredItems->Size()) <= 0)
1299 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(2080), g_localizeStrings.Get(2081));
1300 return GoParentFolder();
1302 return true;
1305 void CGUIMediaWindow::SaveSelectedItemInHistory()
1307 int iItem = m_viewControl.GetSelectedItem();
1308 std::string strSelectedItem;
1309 if (iItem >= 0 && iItem < m_vecItems->Size())
1311 CFileItemPtr pItem = m_vecItems->Get(iItem);
1312 GetDirectoryHistoryString(pItem.get(), strSelectedItem);
1315 m_history.SetSelectedItem(strSelectedItem, m_vecItems->GetPath(), iItem);
1318 void CGUIMediaWindow::RestoreSelectedItemFromHistory()
1320 std::string strSelectedItem = m_history.GetSelectedItem(m_vecItems->GetPath());
1322 if (!strSelectedItem.empty())
1324 for (int i = 0; i < m_vecItems->Size(); ++i)
1326 CFileItemPtr pItem = m_vecItems->Get(i);
1327 std::string strHistory;
1328 GetDirectoryHistoryString(pItem.get(), strHistory);
1329 // set selected item if equals with history
1330 if (strHistory == strSelectedItem)
1332 m_viewControl.SetSelectedItem(i);
1333 return;
1338 // Exact item not found - maybe deleted, watched status change, filtered out, ...
1339 // Attempt to restore the position of the selection
1340 int selectedItemIndex = m_history.GetSelectedItemIndex(m_vecItems->GetPath());
1341 if (selectedItemIndex >= 0 && m_vecItems->Size() > 0)
1343 int newIndex = std::min(selectedItemIndex, m_vecItems->Size() - 1);
1344 m_viewControl.SetSelectedItem(newIndex);
1345 return;
1348 // Fallback: select the first item
1349 m_viewControl.SetSelectedItem(0);
1353 * \brief Get history string for given file item
1355 * \note Override the function to change the default behavior on how
1356 * a selected item history should look like
1358 void CGUIMediaWindow::GetDirectoryHistoryString(const CFileItem* pItem, std::string& strHistoryString) const
1360 if (pItem->m_bIsShareOrDrive)
1362 // We are in the virtual directory
1364 // History string of the DVD drive
1365 // must be handled separately
1366 if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_DVD)
1368 // Remove disc label from item label
1369 // and use as history string, m_strPath
1370 // can change for new discs
1371 std::string strLabel = pItem->GetLabel();
1372 size_t nPosOpen = strLabel.find('(');
1373 size_t nPosClose = strLabel.rfind(')');
1374 if (nPosOpen != std::string::npos &&
1375 nPosClose != std::string::npos &&
1376 nPosClose > nPosOpen)
1378 strLabel.erase(nPosOpen + 1, (nPosClose) - (nPosOpen + 1));
1379 strHistoryString = strLabel;
1381 else
1382 strHistoryString = strLabel;
1384 else
1386 // Other items in virtual directory
1387 std::string strPath = pItem->GetPath();
1388 URIUtils::RemoveSlashAtEnd(strPath);
1390 strHistoryString = pItem->GetLabel() + strPath;
1393 else if (pItem->GetEndOffset() > pItem->GetStartOffset() &&
1394 pItem->GetStartOffset() != STARTOFFSET_RESUME)
1396 // Could be a cue item, all items of a cue share the same filename
1397 // so add the offsets to build the history string
1398 strHistoryString = StringUtils::Format("{}{}", pItem->GetStartOffset(), pItem->GetEndOffset());
1399 strHistoryString += pItem->GetPath();
1401 else
1403 // Normal directory items
1404 strHistoryString = pItem->GetPath();
1407 // remove any filter
1408 if (CanContainFilter(strHistoryString))
1409 strHistoryString = RemoveParameterFromPath(strHistoryString, "filter");
1411 URIUtils::RemoveSlashAtEnd(strHistoryString);
1412 StringUtils::ToLower(strHistoryString);
1416 * \brief Set history for path
1418 * Call this function to create a directory history for the
1419 * path given by strDirectory.
1421 void CGUIMediaWindow::SetHistoryForPath(const std::string& strDirectory)
1423 // Make sure our shares are configured
1424 SetupShares();
1425 if (!strDirectory.empty())
1427 // Build the directory history for default path
1428 std::string strPath, strParentPath;
1429 strPath = strDirectory;
1430 URIUtils::RemoveSlashAtEnd(strPath);
1432 CFileItemList items;
1433 CURL url;
1434 GetDirectoryItems(url, items, UseFileDirectories());
1436 m_history.ClearPathHistory();
1438 bool originalPath = true;
1439 while (URIUtils::GetParentPath(strPath, strParentPath))
1441 for (int i = 0; i < items.Size(); ++i)
1443 CFileItemPtr pItem = items[i];
1444 std::string path(pItem->GetPath());
1445 URIUtils::RemoveSlashAtEnd(path);
1446 if (URIUtils::PathEquals(path, strPath))
1448 std::string strHistory;
1449 GetDirectoryHistoryString(pItem.get(), strHistory);
1450 m_history.SetSelectedItem(strHistory, "");
1451 URIUtils::AddSlashAtEnd(strPath);
1452 m_history.AddPathFront(strPath);
1453 m_history.AddPathFront("");
1455 //m_history.DumpPathHistory();
1456 return ;
1460 if (URIUtils::IsVideoDb(strPath))
1462 CURL url(strParentPath);
1463 url.SetOptions(""); // clear any URL options from recreated parent path
1464 strParentPath = url.Get();
1467 // set the original path exactly as it was passed in
1468 if (URIUtils::PathEquals(strPath, strDirectory, true))
1469 strPath = strDirectory;
1470 else
1471 URIUtils::AddSlashAtEnd(strPath);
1473 m_history.AddPathFront(strPath, originalPath ? m_strFilterPath : "");
1474 m_history.SetSelectedItem(strPath, strParentPath);
1475 originalPath = false;
1476 strPath = strParentPath;
1477 URIUtils::RemoveSlashAtEnd(strPath);
1480 else
1481 m_history.ClearPathHistory();
1483 //m_history.DumpPathHistory();
1487 * \brief On media play
1489 * \note Override if you want to change the default behavior, what is done
1490 * when the user clicks on a file.
1492 * This function is called by OnClick()
1494 bool CGUIMediaWindow::OnPlayMedia(int iItem, const std::string &player)
1496 // Reset Playlistplayer, playback started now does
1497 // not use the playlistplayer.
1498 CServiceBroker::GetPlaylistPlayer().Reset();
1499 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::Id::TYPE_NONE);
1500 CFileItemPtr pItem=m_vecItems->Get(iItem);
1502 CLog::Log(LOGDEBUG, "{} {}", __FUNCTION__, CURL::GetRedacted(pItem->GetPath()));
1504 bool bResult = false;
1505 if (NETWORK::IsInternetStream(*pItem) || PLAYLIST::IsPlayList(*pItem))
1506 bResult = g_application.PlayMedia(*pItem, player, m_guiState->GetPlaylist());
1507 else
1508 bResult = g_application.PlayFile(*pItem, player);
1510 if (pItem->GetStartOffset() == STARTOFFSET_RESUME)
1511 pItem->SetStartOffset(0);
1513 return bResult;
1517 * \brief On play and media queue
1519 * \note Override if you want to change the default behavior of what is done
1520 * when the user clicks on a file in a "folder" with similar files.
1522 * This function is called by OnClick()
1524 bool CGUIMediaWindow::OnPlayAndQueueMedia(const CFileItemPtr& item, const std::string& player)
1526 //play and add current directory to temporary playlist
1527 PLAYLIST::Id playlistId = m_guiState->GetPlaylist();
1528 if (playlistId != PLAYLIST::Id::TYPE_NONE)
1530 // Remove ZIP, RAR files and folders
1531 CFileItemList playlist;
1532 playlist.Copy(*m_vecItems, true);
1533 playlist.erase(std::remove_if(playlist.begin(), playlist.end(),
1534 [](const std::shared_ptr<CFileItem>& i)
1535 { return i->IsZIP() || i->IsRAR() || i->m_bIsFolder; }),
1536 playlist.end());
1538 // Chosen item
1539 int mediaToPlay =
1540 std::distance(playlist.begin(), std::find_if(playlist.begin(), playlist.end(),
1541 [&item](const std::shared_ptr<CFileItem>& i)
1542 { return i->GetPath() == item->GetPath(); }));
1544 // Add to playlist
1545 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(playlistId);
1546 CServiceBroker::GetPlaylistPlayer().Reset();
1547 CServiceBroker::GetPlaylistPlayer().Add(playlistId, playlist);
1549 // Save current window and directory to know where the selected item was
1550 if (m_guiState)
1551 m_guiState->SetPlaylistDirectory(m_vecItems->GetPath());
1553 // figure out where we start playback
1554 if (CServiceBroker::GetPlaylistPlayer().IsShuffled(playlistId))
1556 int iIndex =
1557 CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId).FindOrder(mediaToPlay);
1558 CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId).Swap(0, iIndex);
1559 mediaToPlay = 0;
1562 // play
1563 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(playlistId);
1564 CServiceBroker::GetPlaylistPlayer().Play(mediaToPlay, player);
1566 return true;
1570 * \brief Update file list
1572 * Synchronize the fileitems with the playlistplayer
1573 * also recreates the playlist of the playlistplayer based
1574 * on the fileitems of the window
1576 void CGUIMediaWindow::UpdateFileList()
1578 int nItem = m_viewControl.GetSelectedItem();
1579 std::string strSelected;
1580 if (nItem >= 0)
1581 strSelected = m_vecItems->Get(nItem)->GetPath();
1583 FormatAndSort(*m_vecItems);
1584 UpdateButtons();
1586 m_viewControl.SetItems(*m_vecItems);
1587 m_viewControl.SetSelectedItem(strSelected);
1589 // set the currently playing item as selected, if its in this directory
1590 if (m_guiState.get() && m_guiState->IsCurrentPlaylistDirectory(m_vecItems->GetPath()))
1592 PLAYLIST::Id playlistId = m_guiState->GetPlaylist();
1593 int nSong = CServiceBroker::GetPlaylistPlayer().GetCurrentItemIdx();
1594 CFileItem playlistItem;
1595 if (nSong > -1 && playlistId != PLAYLIST::Id::TYPE_NONE)
1596 playlistItem = *CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId)[nSong];
1598 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(playlistId);
1599 CServiceBroker::GetPlaylistPlayer().Reset();
1601 for (int i = 0; i < m_vecItems->Size(); i++)
1603 CFileItemPtr pItem = m_vecItems->Get(i);
1604 if (pItem->m_bIsFolder)
1605 continue;
1607 if (!PLAYLIST::IsPlayList(*pItem) && !pItem->IsZIP() && !pItem->IsRAR())
1608 CServiceBroker::GetPlaylistPlayer().Add(playlistId, pItem);
1610 if (pItem->GetPath() == playlistItem.GetPath() &&
1611 pItem->GetStartOffset() == playlistItem.GetStartOffset())
1612 CServiceBroker::GetPlaylistPlayer().SetCurrentItemIdx(
1613 CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId).size() - 1);
1618 void CGUIMediaWindow::OnDeleteItem(int iItem)
1620 if ( iItem < 0 || iItem >= m_vecItems->Size()) return;
1621 CFileItemPtr item = m_vecItems->Get(iItem);
1623 if (PLAYLIST::IsPlayList(*item))
1624 item->m_bIsFolder = false;
1626 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
1628 if (profileManager->GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE && profileManager->GetCurrentProfile().filesLocked())
1630 if (!g_passwordManager.IsMasterLockUnlocked(true))
1631 return;
1634 CGUIComponent *gui = CServiceBroker::GetGUI();
1635 if (gui && gui->ConfirmDelete(item->GetPath()))
1637 if (!CFileUtils::DeleteItem(item))
1638 return;
1640 else
1641 return;
1643 Refresh(true);
1644 m_viewControl.SetSelectedItem(iItem);
1647 void CGUIMediaWindow::OnRenameItem(int iItem)
1649 if (iItem < 0 || iItem >= m_vecItems->Size())
1650 return;
1652 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
1654 if (profileManager->GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE && profileManager->GetCurrentProfile().filesLocked())
1656 if (!g_passwordManager.IsMasterLockUnlocked(true))
1657 return;
1660 if (!CFileUtils::RenameFile(m_vecItems->Get(iItem)->GetPath()))
1661 return;
1663 Refresh(true);
1664 m_viewControl.SetSelectedItem(iItem);
1667 void CGUIMediaWindow::OnInitWindow()
1669 // initial fetch is done unthreaded to ensure the items are setup prior to skin animations kicking off
1670 m_backgroundLoad = false;
1672 // the start directory may change during Refresh
1673 bool updateStartDirectory = URIUtils::PathEquals(m_vecItems->GetPath(), m_startDirectory, true);
1675 // we have python scripts hooked in everywhere :(
1676 // those scripts may open windows and we can't open a window
1677 // while opening this one.
1678 // for plugin sources delay call to Refresh
1679 if (!URIUtils::IsPlugin(m_vecItems->GetPath()))
1681 Refresh();
1683 else
1685 CGUIMessage msg(GUI_MSG_WINDOW_INIT, 0, 0, WINDOW_INVALID, PLUGIN_REFRESH_DELAY);
1686 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, GetID());
1689 if (updateStartDirectory)
1691 // reset the start directory to the path of the items
1692 m_startDirectory = m_vecItems->GetPath();
1694 // reset the history based on the path of the items
1695 SetHistoryForPath(m_startDirectory);
1698 m_backgroundLoad = true;
1700 CGUIWindow::OnInitWindow();
1703 void CGUIMediaWindow::SaveControlStates()
1705 CGUIWindow::SaveControlStates();
1706 SaveSelectedItemInHistory();
1709 void CGUIMediaWindow::RestoreControlStates()
1711 CGUIWindow::RestoreControlStates();
1712 RestoreSelectedItemFromHistory();
1715 CGUIControl *CGUIMediaWindow::GetFirstFocusableControl(int id)
1717 if (m_viewControl.HasControl(id))
1718 id = m_viewControl.GetCurrentControl();
1719 return CGUIWindow::GetFirstFocusableControl(id);
1722 void CGUIMediaWindow::SetupShares()
1724 // Setup shares and filemasks for this window
1725 CFileItemList items;
1726 CGUIViewState* viewState=CGUIViewState::GetViewState(GetID(), items);
1727 if (viewState)
1729 m_rootDir.SetMask(viewState->GetExtensions());
1730 m_rootDir.SetSources(viewState->GetSources());
1731 delete viewState;
1735 bool CGUIMediaWindow::OnPopupMenu(int itemIdx)
1737 auto InRange = [](size_t i, std::pair<size_t, size_t> range){ return i >= range.first && i < range.second; };
1739 if (itemIdx < 0 || itemIdx >= m_vecItems->Size())
1740 return false;
1742 auto item = m_vecItems->Get(itemIdx);
1743 if (!item)
1744 return false;
1746 item->SetProperty("ParentPath", m_vecItems->GetPath());
1748 CContextButtons buttons;
1750 //Add items from plugin
1752 int i = 0;
1753 while (item->HasProperty(StringUtils::Format("contextmenulabel({})", i)))
1755 buttons.emplace_back(
1756 ~buttons.size(),
1757 item->GetProperty(StringUtils::Format("contextmenulabel({})", i)).asString());
1758 ++i;
1761 auto pluginMenuRange = std::make_pair(static_cast<size_t>(0), buttons.size());
1763 //Add the global menu
1764 auto globalMenu = CServiceBroker::GetContextMenuManager().GetItems(*item, CContextMenuManager::MAIN);
1765 auto globalMenuRange = std::make_pair(buttons.size(), buttons.size() + globalMenu.size());
1766 for (const auto& menu : globalMenu)
1767 buttons.emplace_back(~buttons.size(), menu->GetLabel(*item));
1769 //Add legacy items from windows
1770 auto buttonsSize = buttons.size();
1771 GetContextButtons(itemIdx, buttons);
1772 auto windowMenuRange = std::make_pair(buttonsSize, buttons.size());
1774 //Add addon menus
1775 auto addonMenu = CServiceBroker::GetContextMenuManager().GetAddonItems(*item, CContextMenuManager::MAIN);
1776 auto addonMenuRange = std::make_pair(buttons.size(), buttons.size() + addonMenu.size());
1777 for (const auto& menu : addonMenu)
1778 buttons.emplace_back(~buttons.size(), menu->GetLabel(*item));
1780 if (buttons.empty())
1781 return true;
1783 int idx = CGUIDialogContextMenu::Show(buttons);
1784 if (idx < 0 || idx >= static_cast<int>(buttons.size()))
1785 return false;
1787 if (InRange(static_cast<size_t>(idx), pluginMenuRange))
1789 bool saveVal = m_backgroundLoad;
1790 m_backgroundLoad = false;
1791 CServiceBroker::GetAppMessenger()->SendMsg(
1792 TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr,
1793 item->GetProperty(StringUtils::Format("contextmenuaction({})", idx - pluginMenuRange.first))
1794 .asString());
1795 m_backgroundLoad = saveVal;
1796 return true;
1799 if (InRange(idx, windowMenuRange))
1800 return OnContextButton(itemIdx, static_cast<CONTEXT_BUTTON>(buttons[idx].first));
1802 if (InRange(idx, globalMenuRange))
1803 return CONTEXTMENU::LoopFrom(*globalMenu[idx - globalMenuRange.first], item);
1805 return CONTEXTMENU::LoopFrom(*addonMenu[idx - addonMenuRange.first], item);
1808 const CGUIViewState *CGUIMediaWindow::GetViewState() const
1810 return m_guiState.get();
1813 const CFileItemList& CGUIMediaWindow::CurrentDirectory() const
1815 return *m_vecItems;
1818 bool CGUIMediaWindow::WaitForNetwork() const
1820 if (CServiceBroker::GetNetwork().IsAvailable())
1821 return true;
1823 CGUIDialogProgress *progress = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS);
1824 if (!progress)
1825 return true;
1827 CURL url(m_vecItems->GetPath());
1828 progress->SetHeading(CVariant{1040}); // Loading Directory
1829 progress->SetLine(1, CVariant{url.GetWithoutUserDetails()});
1830 progress->ShowProgressBar(false);
1831 progress->Open();
1832 while (!CServiceBroker::GetNetwork().IsAvailable())
1834 progress->Progress();
1835 if (progress->IsCanceled())
1837 progress->Close();
1838 return false;
1841 progress->Close();
1842 return true;
1845 void CGUIMediaWindow::UpdateFilterPath(const std::string &strDirectory, const CFileItemList &items, bool updateFilterPath)
1847 bool canfilter = CanContainFilter(strDirectory);
1849 std::string filter;
1850 CURL url(strDirectory);
1851 if (canfilter && url.HasOption("filter"))
1852 filter = url.GetOption("filter");
1854 // only set the filter path if it hasn't been marked
1855 // as preset or if it's empty
1856 if (updateFilterPath || m_strFilterPath.empty())
1858 if (items.HasProperty(PROPERTY_PATH_DB))
1859 m_strFilterPath = items.GetProperty(PROPERTY_PATH_DB).asString();
1860 else
1861 m_strFilterPath = items.GetPath();
1864 // maybe the filter path can contain a filter
1865 if (!canfilter && CanContainFilter(m_strFilterPath))
1866 canfilter = true;
1868 // check if the filter path contains a filter
1869 CURL filterPathUrl(m_strFilterPath);
1870 if (canfilter && filter.empty())
1872 if (filterPathUrl.HasOption("filter"))
1873 filter = filterPathUrl.GetOption("filter");
1876 // check if there is a filter and re-apply it
1877 if (canfilter && !filter.empty())
1879 if (!m_filter.LoadFromJson(filter))
1881 CLog::Log(LOGWARNING,
1882 "CGUIMediaWindow::UpdateFilterPath(): unable to load existing filter ({})", filter);
1883 m_filter.Reset();
1884 m_strFilterPath = m_vecItems->GetPath();
1886 else
1888 // add the filter to the filter path
1889 filterPathUrl.SetOption("filter", filter);
1890 m_strFilterPath = filterPathUrl.Get();
1895 void CGUIMediaWindow::OnFilterItems(const std::string &filter)
1897 m_viewControl.Clear();
1899 CFileItemList items;
1900 items.Copy(*m_vecItems, false); // use the original path - it'll likely be relied on for other things later.
1901 items.Append(*m_unfilteredItems);
1902 bool filtered = GetFilteredItems(filter, items);
1904 m_vecItems->ClearItems();
1905 // we need to clear the sort state and re-sort the items
1906 m_vecItems->ClearSortState();
1907 m_vecItems->Append(items);
1909 // if the filter has changed, get the new filter path
1910 if (filtered && m_canFilterAdvanced)
1912 if (items.HasProperty(PROPERTY_PATH_DB))
1913 m_strFilterPath = items.GetProperty(PROPERTY_PATH_DB).asString();
1914 // only set m_strFilterPath if it hasn't been set before
1915 // otherwise we might overwrite it with a non-filter path
1916 // in case GetFilteredItems() returns true even though no
1917 // db-based filter (e.g. watched filter) has been applied
1918 else if (m_strFilterPath.empty())
1919 m_strFilterPath = items.GetPath();
1922 GetGroupedItems(*m_vecItems);
1923 FormatAndSort(*m_vecItems);
1925 CFileItemPtr currentItem;
1926 std::string currentItemPath;
1927 int item = m_viewControl.GetSelectedItem();
1928 if (item >= 0 && item < m_vecItems->Size())
1930 currentItem = m_vecItems->Get(item);
1931 currentItemPath = currentItem->GetPath();
1934 // get the "filter" option
1935 std::string filterOption;
1936 CURL filterUrl(m_strFilterPath);
1937 if (filterUrl.HasOption("filter"))
1938 filterOption = filterUrl.GetOption("filter");
1940 // apply the "filter" option to any folder item so that
1941 // the filter can be passed down to the sub-directory
1942 for (int index = 0; index < m_vecItems->Size(); index++)
1944 CFileItemPtr pItem = m_vecItems->Get(index);
1945 // if the item is a folder we need to copy the path of
1946 // the filtered item to be able to keep the applied filters
1947 if (pItem->m_bIsFolder)
1949 CURL itemUrl(pItem->GetPath());
1950 if (!filterOption.empty())
1951 itemUrl.SetOption("filter", filterOption);
1952 else
1953 itemUrl.RemoveOption("filter");
1954 pItem->SetPath(itemUrl.Get());
1958 SetProperty("filter", filter);
1959 if (filtered && m_canFilterAdvanced)
1961 // to be able to select the same item as before we need to adjust
1962 // the path of the item i.e. add or remove the "filter=" URL option
1963 // but that's only necessary for folder items
1964 if (currentItem.get() && currentItem->m_bIsFolder)
1966 CURL curUrl(currentItemPath), newUrl(m_strFilterPath);
1967 if (newUrl.HasOption("filter"))
1968 curUrl.SetOption("filter", newUrl.GetOption("filter"));
1969 else if (curUrl.HasOption("filter"))
1970 curUrl.RemoveOption("filter");
1972 currentItemPath = curUrl.Get();
1976 // The idea here is to ensure we have something to focus if our file list
1977 // is empty. As such, this check MUST be last and ignore the hide parent
1978 // fileitems settings.
1979 if (m_vecItems->IsEmpty())
1981 CFileItemPtr pItem(new CFileItem(".."));
1982 pItem->SetPath(m_history.GetParentPath());
1983 pItem->m_bIsFolder = true;
1984 pItem->m_bIsShareOrDrive = false;
1985 m_vecItems->AddFront(pItem, 0);
1988 // and update our view control + buttons
1989 m_viewControl.SetItems(*m_vecItems);
1990 m_viewControl.SetSelectedItem(currentItemPath);
1993 bool CGUIMediaWindow::GetFilteredItems(const std::string &filter, CFileItemList &items)
1995 bool result = false;
1996 if (m_canFilterAdvanced)
1997 result = GetAdvanceFilteredItems(items);
1999 std::string trimmedFilter(filter);
2000 StringUtils::TrimLeft(trimmedFilter);
2001 StringUtils::ToLower(trimmedFilter);
2003 if (trimmedFilter.empty())
2004 return result;
2006 CFileItemList filteredItems(items.GetPath()); // use the original path - it'll likely be relied on for other things later.
2007 bool numericMatch = StringUtils::IsNaturalNumber(trimmedFilter);
2008 for (int i = 0; i < items.Size(); i++)
2010 CFileItemPtr item = items.Get(i);
2011 if (item->IsParentFolder())
2013 filteredItems.Add(item);
2014 continue;
2016 //! @todo Need to update this to get all labels, ideally out of the displayed info (ie from m_layout and m_focusedLayout)
2017 //! though that isn't practical. Perhaps a better idea would be to just grab the info that we should filter on based on
2018 //! where we are in the library tree.
2019 //! Another idea is tying the filter string to the current level of the tree, so that going deeper disables the filter,
2020 //! but it's re-enabled on the way back out.
2021 std::string match;
2022 /* if (item->GetFocusedLayout())
2023 match = item->GetFocusedLayout()->GetAllText();
2024 else if (item->GetLayout())
2025 match = item->GetLayout()->GetAllText();
2026 else*/
2027 match = item->GetLabel(); // Filter label only for now
2029 if (numericMatch)
2030 StringUtils::WordToDigits(match);
2032 size_t pos = StringUtils::FindWords(match.c_str(), trimmedFilter.c_str());
2033 if (pos != std::string::npos)
2034 filteredItems.Add(item);
2037 items.ClearItems();
2038 items.Append(filteredItems);
2040 return items.GetObjectCount() > 0;
2043 bool CGUIMediaWindow::GetAdvanceFilteredItems(CFileItemList &items)
2045 // don't run the advanced filter if the filter is empty
2046 // and there hasn't been a filter applied before which
2047 // would have to be removed
2048 CURL url(m_strFilterPath);
2049 if (m_filter.IsEmpty() && !url.HasOption("filter"))
2050 return false;
2052 CFileItemList resultItems;
2053 XFILE::CSmartPlaylistDirectory::GetDirectory(m_filter, resultItems, m_strFilterPath, true);
2055 // put together a lookup map for faster path comparison
2056 std::map<std::string, CFileItemPtr> lookup;
2057 for (int j = 0; j < resultItems.Size(); j++)
2059 std::string itemPath = CURL(resultItems[j]->GetPath()).GetWithoutOptions();
2060 StringUtils::ToLower(itemPath);
2062 lookup[itemPath] = resultItems[j];
2065 // loop through all the original items and find
2066 // those which are still part of the filter
2067 CFileItemList filteredItems;
2068 for (int i = 0; i < items.Size(); i++)
2070 CFileItemPtr item = items.Get(i);
2071 if (item->IsParentFolder())
2073 filteredItems.Add(item);
2074 continue;
2077 // check if the item is part of the resultItems list
2078 // by comparing their paths (but ignoring any special
2079 // options because they differ from filter to filter)
2080 std::string path = CURL(item->GetPath()).GetWithoutOptions();
2081 StringUtils::ToLower(path);
2083 std::map<std::string, CFileItemPtr>::iterator itItem = lookup.find(path);
2084 if (itItem != lookup.end())
2086 // add the item to the list of filtered items
2087 filteredItems.Add(item);
2089 // remove the item from the lists
2090 resultItems.Remove(itItem->second.get());
2091 lookup.erase(itItem);
2095 if (resultItems.Size() > 0)
2096 CLog::Log(LOGWARNING, "CGUIMediaWindow::GetAdvanceFilteredItems(): {} unknown items",
2097 resultItems.Size());
2099 items.ClearItems();
2100 items.Append(filteredItems);
2101 items.SetPath(resultItems.GetPath());
2102 if (resultItems.HasProperty(PROPERTY_PATH_DB))
2103 items.SetProperty(PROPERTY_PATH_DB, resultItems.GetProperty(PROPERTY_PATH_DB));
2104 return true;
2107 bool CGUIMediaWindow::IsFiltered()
2109 return (!m_canFilterAdvanced && !GetProperty("filter").empty()) ||
2110 (m_canFilterAdvanced && !m_filter.IsEmpty());
2113 bool CGUIMediaWindow::IsSameStartFolder(const std::string &dir)
2115 const std::string startFolder = GetStartFolder(dir);
2116 return URIUtils::PathHasParent(m_vecItems->GetPath(), startFolder);
2119 bool CGUIMediaWindow::Filter(bool advanced /* = true */)
2121 // basic filtering
2122 if (!m_canFilterAdvanced || !advanced)
2124 const CGUIControl *btnFilter = GetControl(CONTROL_BTN_FILTER);
2125 if (btnFilter && btnFilter->GetControlType() == CGUIControl::GUICONTROL_EDIT)
2126 { // filter updated
2127 CGUIMessage selected(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTN_FILTER);
2128 OnMessage(selected);
2129 OnFilterItems(selected.GetLabel());
2130 UpdateButtons();
2131 return true;
2133 if (GetProperty("filter").empty())
2135 std::string filter = GetProperty("filter").asString();
2136 CGUIKeyboardFactory::ShowAndGetFilter(filter, false);
2137 SetProperty("filter", filter);
2139 else
2141 OnFilterItems("");
2142 UpdateButtons();
2145 // advanced filtering
2146 else
2147 CGUIDialogMediaFilter::ShowAndEditMediaFilter(m_strFilterPath, m_filter);
2149 return true;
2152 std::string CGUIMediaWindow::GetStartFolder(const std::string &dir)
2154 if (StringUtils::EqualsNoCase(dir, "$root") ||
2155 StringUtils::EqualsNoCase(dir, "root"))
2156 return "";
2158 // Let plugins handle their own urls themselves
2159 if (StringUtils::StartsWith(dir, "plugin://"))
2160 return dir;
2162 //! @todo This ifdef block probably belongs somewhere else. Move it to a better place!
2163 #if defined(TARGET_ANDROID)
2164 // Hack for Android items (numbered id's) on the leanback screen
2165 std::string path;
2166 std::string fileName;
2167 URIUtils::Split(dir, path, fileName);
2168 URIUtils::RemoveExtension(fileName);
2169 if (StringUtils::IsInteger(fileName))
2170 return path;
2171 #endif
2173 return dir;
2176 std::string CGUIMediaWindow::RemoveParameterFromPath(const std::string &strDirectory, const std::string &strParameter)
2178 CURL url(strDirectory);
2179 if (url.HasOption(strParameter))
2181 url.RemoveOption(strParameter);
2182 return url.Get();
2185 return strDirectory;
2188 bool CGUIMediaWindow::ProcessRenderLoop(bool renderOnly)
2190 return CServiceBroker::GetGUI()->GetWindowManager().ProcessRenderLoop(renderOnly);
2193 bool CGUIMediaWindow::GetDirectoryItems(CURL &url, CFileItemList &items, bool useDir)
2195 if (m_backgroundLoad)
2197 bool ret = true;
2198 CGetDirectoryItems getItems(m_rootDir, url, items, useDir);
2200 if (!WaitGetDirectoryItems(getItems))
2202 // cancelled
2203 ret = false;
2205 else if (!getItems.m_result)
2207 if (CServiceBroker::GetAppMessenger()->IsProcessThread() && m_rootDir.GetDirImpl() &&
2208 !m_rootDir.GetDirImpl()->ProcessRequirements())
2210 ret = false;
2212 else if (!WaitGetDirectoryItems(getItems) || !getItems.m_result)
2214 ret = false;
2218 m_updateJobActive = false;
2219 m_rootDir.ReleaseDirImpl();
2220 return ret;
2222 else
2224 return m_rootDir.GetDirectory(url, items, useDir, false);
2228 bool CGUIMediaWindow::WaitGetDirectoryItems(CGetDirectoryItems &items)
2230 bool ret = true;
2231 CGUIDialogBusy* dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogBusy>(WINDOW_DIALOG_BUSY);
2232 if (dialog && !dialog->IsDialogRunning())
2234 if (!CGUIDialogBusy::Wait(&items, 100, true))
2236 // cancelled
2237 ret = false;
2240 else
2242 m_updateJobActive = true;
2243 m_updateAborted = false;
2244 m_updateEvent.Reset();
2245 CServiceBroker::GetJobManager()->Submit(
2246 [&]() {
2247 items.Run();
2248 m_updateEvent.Set();
2250 nullptr, CJob::PRIORITY_NORMAL);
2252 // Loop until either the job ended or update canceled via CGUIMediaWindow::CancelUpdateItems.
2253 while (!m_updateAborted && !m_updateEvent.Wait(1ms))
2255 if (!ProcessRenderLoop(false))
2256 break;
2259 if (m_updateAborted)
2261 CLog::LogF(LOGDEBUG, "Get directory items job was canceled.");
2262 ret = false;
2264 else if (!items.m_result)
2266 CLog::LogF(LOGDEBUG, "Get directory items job was unsuccessful.");
2267 ret = false;
2270 return ret;
2273 void CGUIMediaWindow::CancelUpdateItems()
2275 if (m_updateJobActive)
2277 m_rootDir.CancelDirectory();
2278 m_updateAborted = true;
2279 if (!m_updateEvent.Wait(5000ms))
2281 CLog::Log(LOGERROR, "CGUIMediaWindow::CancelUpdateItems - error cancel update");
2283 m_updateJobActive = false;