[filesystem][SpecialProtocol] Removed assert from GetPath
[xbmc.git] / xbmc / windows / GUIMediaWindow.cpp
blobbabb9d9cf1eb8c039bfc9885e5bc92820193c86e
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 "FileItemListModification.h"
14 #include "GUIPassword.h"
15 #include "GUIUserMessages.h"
16 #include "PartyModeManager.h"
17 #include "PlayListPlayer.h"
18 #include "ServiceBroker.h"
19 #include "URL.h"
20 #include "Util.h"
21 #include "addons/AddonManager.h"
22 #include "addons/PluginSource.h"
23 #include "addons/addoninfo/AddonType.h"
24 #include "application/Application.h"
25 #include "messaging/ApplicationMessenger.h"
26 #if defined(TARGET_ANDROID)
27 #include "platform/android/activity/XBMCApp.h"
28 #endif
29 #include "dialogs/GUIDialogBusy.h"
30 #include "dialogs/GUIDialogKaiToast.h"
31 #include "dialogs/GUIDialogMediaFilter.h"
32 #include "dialogs/GUIDialogProgress.h"
33 #include "dialogs/GUIDialogSmartPlaylistEditor.h"
34 #include "filesystem/FileDirectoryFactory.h"
35 #include "filesystem/MultiPathDirectory.h"
36 #include "filesystem/PluginDirectory.h"
37 #include "filesystem/SmartPlaylistDirectory.h"
38 #include "guilib/GUIComponent.h"
39 #include "guilib/GUIEditControl.h"
40 #include "guilib/GUIKeyboardFactory.h"
41 #include "guilib/GUIWindowManager.h"
42 #include "guilib/LocalizeStrings.h"
43 #include "input/actions/Action.h"
44 #include "input/actions/ActionIDs.h"
45 #include "interfaces/generic/ScriptInvocationManager.h"
46 #include "messaging/helpers/DialogOKHelper.h"
47 #include "network/Network.h"
48 #include "playlists/PlayList.h"
49 #include "profiles/ProfileManager.h"
50 #include "settings/AdvancedSettings.h"
51 #include "settings/Settings.h"
52 #include "settings/SettingsComponent.h"
53 #include "storage/MediaManager.h"
54 #include "threads/IRunnable.h"
55 #include "utils/FileUtils.h"
56 #include "utils/LabelFormatter.h"
57 #include "utils/SortUtils.h"
58 #include "utils/StringUtils.h"
59 #include "utils/URIUtils.h"
60 #include "utils/Variant.h"
61 #include "utils/log.h"
62 #include "view/GUIViewState.h"
64 #include <inttypes.h>
66 #define CONTROL_BTNVIEWASICONS 2
67 #define CONTROL_BTNSORTBY 3
68 #define CONTROL_BTNSORTASC 4
69 #define CONTROL_BTN_FILTER 19
71 #define CONTROL_LABELFILES 12
73 #define PROPERTY_PATH_DB "path.db"
74 #define PROPERTY_SORT_ORDER "sort.order"
75 #define PROPERTY_SORT_ASCENDING "sort.ascending"
77 #define PLUGIN_REFRESH_DELAY 200
79 using namespace ADDON;
80 using namespace KODI::MESSAGING;
81 using namespace std::chrono_literals;
83 namespace
85 class CGetDirectoryItems : public IRunnable
87 public:
88 CGetDirectoryItems(XFILE::CVirtualDirectory &dir, CURL &url, CFileItemList &items, bool useDir)
89 : m_dir(dir), m_url(url), m_items(items), m_useDir(useDir)
93 void Run() override
95 m_result = m_dir.GetDirectory(m_url, m_items, m_useDir, true);
98 void Cancel() override
100 m_dir.CancelDirectory();
103 bool m_result = false;
105 protected:
106 XFILE::CVirtualDirectory &m_dir;
107 CURL m_url;
108 CFileItemList &m_items;
109 bool m_useDir;
113 CGUIMediaWindow::CGUIMediaWindow(int id, const char *xmlFile)
114 : CGUIWindow(id, xmlFile)
116 m_loadType = KEEP_IN_MEMORY;
117 m_vecItems = new CFileItemList;
118 m_unfilteredItems = new CFileItemList;
119 m_vecItems->SetPath("?");
120 m_iLastControl = -1;
121 m_canFilterAdvanced = false;
123 m_guiState.reset(CGUIViewState::GetViewState(GetID(), *m_vecItems));
126 CGUIMediaWindow::~CGUIMediaWindow()
128 delete m_vecItems;
129 delete m_unfilteredItems;
132 bool CGUIMediaWindow::Load(TiXmlElement *pRootElement)
134 bool retVal = CGUIWindow::Load(pRootElement);
136 if (!retVal)
137 return false;
139 // configure our view control
140 m_viewControl.Reset();
141 m_viewControl.SetParentWindow(GetID());
142 TiXmlElement *element = pRootElement->FirstChildElement("views");
143 if (element && element->FirstChild())
144 { // format is <views>50,29,51,95</views>
145 const std::string &allViews = element->FirstChild()->ValueStr();
146 std::vector<std::string> views = StringUtils::Split(allViews, ",");
147 for (std::vector<std::string>::const_iterator i = views.begin(); i != views.end(); ++i)
149 int controlID = atol(i->c_str());
150 CGUIControl *control = GetControl(controlID);
151 if (control && control->IsContainer())
152 m_viewControl.AddView(control);
155 m_viewControl.SetViewControlID(CONTROL_BTNVIEWASICONS);
157 return true;
160 void CGUIMediaWindow::OnWindowLoaded()
162 SendMessage(GUI_MSG_SET_TYPE, CONTROL_BTN_FILTER, CGUIEditControl::INPUT_TYPE_FILTER);
163 CGUIWindow::OnWindowLoaded();
164 SetupShares();
167 void CGUIMediaWindow::OnWindowUnload()
169 CGUIWindow::OnWindowUnload();
170 m_viewControl.Reset();
173 CFileItemPtr CGUIMediaWindow::GetCurrentListItem(int offset)
175 int item = m_viewControl.GetSelectedItem();
176 if (!m_vecItems->Size() || item < 0)
177 return CFileItemPtr();
178 item = (item + offset) % m_vecItems->Size();
179 if (item < 0) item += m_vecItems->Size();
180 return m_vecItems->Get(item);
183 bool CGUIMediaWindow::OnAction(const CAction &action)
185 if (action.GetID() == ACTION_PARENT_DIR)
187 GoParentFolder();
188 return true;
191 if (CGUIWindow::OnAction(action))
192 return true;
194 if (action.GetID() == ACTION_FILTER)
195 return Filter();
197 // live filtering
198 if (action.GetID() == ACTION_FILTER_CLEAR)
200 CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS);
201 message.SetStringParam("");
202 OnMessage(message);
203 return true;
206 if (action.GetID() == ACTION_BACKSPACE)
208 CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS, 2); // 2 for delete
209 OnMessage(message);
210 return true;
213 if (action.GetID() >= ACTION_FILTER_SMS2 && action.GetID() <= ACTION_FILTER_SMS9)
215 std::string filter = std::to_string(action.GetID() - ACTION_FILTER_SMS2 + 2);
216 CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS, 1); // 1 for append
217 message.SetStringParam(filter);
218 OnMessage(message);
219 return true;
222 return false;
225 bool CGUIMediaWindow::OnBack(int actionID)
227 CancelUpdateItems();
229 CURL filterUrl(m_strFilterPath);
230 if (actionID == ACTION_NAV_BACK &&
231 !m_vecItems->IsVirtualDirectoryRoot() &&
232 !URIUtils::PathEquals(m_vecItems->GetPath(), GetRootPath(), true) &&
233 (!URIUtils::PathEquals(m_vecItems->GetPath(), m_startDirectory, true) || (m_canFilterAdvanced && filterUrl.HasOption("filter"))))
235 if (GoParentFolder())
236 return true;
238 return CGUIWindow::OnBack(actionID);
241 bool CGUIMediaWindow::OnMessage(CGUIMessage& message)
243 switch ( message.GetMessage() )
245 case GUI_MSG_WINDOW_DEINIT:
247 CancelUpdateItems();
249 m_iLastControl = GetFocusedControlID();
250 CGUIWindow::OnMessage(message);
252 // get rid of any active filtering
253 if (m_canFilterAdvanced)
255 m_canFilterAdvanced = false;
256 m_filter.Reset();
258 m_strFilterPath.clear();
260 // Call ClearFileItems() after our window has finished doing any WindowClose
261 // animations
262 ClearFileItems();
263 return true;
265 break;
267 case GUI_MSG_CLICKED:
269 int iControl = message.GetSenderId();
270 if (iControl == CONTROL_BTNVIEWASICONS)
272 // view as control could be a select button
273 int viewMode = 0;
274 const CGUIControl *control = GetControl(CONTROL_BTNVIEWASICONS);
275 if (control && control->GetControlType() != CGUIControl::GUICONTROL_BUTTON)
277 CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTNVIEWASICONS);
278 OnMessage(msg);
279 viewMode = m_viewControl.GetViewModeNumber(msg.GetParam1());
281 else
282 viewMode = m_viewControl.GetNextViewMode();
284 if (m_guiState)
285 m_guiState->SaveViewAsControl(viewMode);
287 UpdateButtons();
288 return true;
290 else if (iControl == CONTROL_BTNSORTASC) // sort asc
292 if (m_guiState)
293 m_guiState->SetNextSortOrder();
294 UpdateFileList();
295 return true;
297 else if (iControl == CONTROL_BTNSORTBY) // sort by
299 if (m_guiState.get() && m_guiState->ChooseSortMethod())
300 UpdateFileList();
301 return true;
303 else if (iControl == CONTROL_BTN_FILTER)
304 return Filter(false);
305 else if (m_viewControl.HasControl(iControl)) // list/thumb control
307 int iItem = m_viewControl.GetSelectedItem();
308 int iAction = message.GetParam1();
309 if (iItem < 0) break;
310 if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
312 OnSelect(iItem);
314 else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
316 OnPopupMenu(iItem);
317 return true;
321 break;
323 case GUI_MSG_SETFOCUS:
325 if (m_viewControl.HasControl(message.GetControlId()) && m_viewControl.GetCurrentControl() != message.GetControlId())
327 m_viewControl.SetFocused();
328 return true;
331 break;
333 case GUI_MSG_NOTIFY_ALL:
334 { // Message is received even if this window is inactive
335 if (message.GetParam1() == GUI_MSG_WINDOW_RESET)
337 m_vecItems->SetPath("?");
338 return true;
340 else if ( message.GetParam1() == GUI_MSG_REFRESH_THUMBS )
342 for (int i = 0; i < m_vecItems->Size(); i++)
343 m_vecItems->Get(i)->FreeMemory(true);
344 break; // the window will take care of any info images
346 else if (message.GetParam1() == GUI_MSG_REMOVED_MEDIA)
348 if ((m_vecItems->IsVirtualDirectoryRoot() ||
349 m_vecItems->IsSourcesPath()) && IsActive())
351 int iItem = m_viewControl.GetSelectedItem();
352 Refresh();
353 m_viewControl.SetSelectedItem(iItem);
355 else if (m_vecItems->IsRemovable())
356 { // check that we have this removable share still
357 if (!m_rootDir.IsInSource(m_vecItems->GetPath()))
358 { // don't have this share any more
359 if (IsActive()) Update("");
360 else
362 m_history.ClearPathHistory();
363 m_vecItems->SetPath("");
368 return true;
370 else if (message.GetParam1()==GUI_MSG_UPDATE_SOURCES)
371 { // State of the sources changed, so update our view
372 if ((m_vecItems->IsVirtualDirectoryRoot() ||
373 m_vecItems->IsSourcesPath()) && IsActive())
375 if (m_vecItemsUpdating)
377 CLog::Log(LOGWARNING, "CGUIMediaWindow::OnMessage - updating in progress");
378 return true;
380 CUpdateGuard ug(m_vecItemsUpdating);
381 int iItem = m_viewControl.GetSelectedItem();
382 Refresh(true);
383 m_viewControl.SetSelectedItem(iItem);
385 return true;
387 else if (message.GetParam1()==GUI_MSG_UPDATE && IsActive())
389 if (m_vecItemsUpdating)
391 CLog::Log(LOGWARNING, "CGUIMediaWindow::OnMessage - updating in progress");
392 return true;
394 CUpdateGuard ug(m_vecItemsUpdating);
395 if (message.GetNumStringParams())
397 if (message.GetParam2()) // param2 is used for resetting the history
398 SetHistoryForPath(message.GetStringParam());
400 CFileItemList list(message.GetStringParam());
401 list.RemoveDiscCache(GetID());
402 Update(message.GetStringParam());
404 else
405 Refresh(true); // refresh the listing
407 else if (message.GetParam1()==GUI_MSG_UPDATE_ITEM && message.GetItem())
409 int flag = message.GetParam2();
410 CFileItemPtr newItem = std::static_pointer_cast<CFileItem>(message.GetItem());
412 if (IsActive() || (flag & GUI_MSG_FLAG_FORCE_UPDATE))
414 m_vecItems->UpdateItem(newItem.get());
416 if (flag & GUI_MSG_FLAG_UPDATE_LIST)
417 { // need the list updated as well
418 UpdateFileList();
421 else if (newItem)
422 { // need to remove the disc cache
423 CFileItemList items;
424 items.SetPath(URIUtils::GetDirectory(newItem->GetPath()));
425 if (newItem->HasProperty("cachefilename"))
427 // Use stored cache file name
428 std::string crcfile = newItem->GetProperty("cachefilename").asString();
429 items.RemoveDiscCacheCRC(crcfile);
431 else
432 // No stored cache file name, attempt using truncated item path as list path
433 items.RemoveDiscCache(GetID());
436 else if (message.GetParam1()==GUI_MSG_UPDATE_PATH)
438 if (IsActive())
440 if((message.GetStringParam() == m_vecItems->GetPath()) ||
441 (m_vecItems->IsMultiPath() && XFILE::CMultiPathDirectory::HasPath(m_vecItems->GetPath(), message.GetStringParam())))
442 Refresh();
445 else if (message.GetParam1() == GUI_MSG_FILTER_ITEMS && IsActive())
447 std::string filter = GetProperty("filter").asString();
448 // check if this is meant for advanced filtering
449 if (message.GetParam2() != 10)
451 if (message.GetParam2() == 1) // append
452 filter += message.GetStringParam();
453 else if (message.GetParam2() == 2)
454 { // delete
455 if (filter.size())
456 filter.erase(filter.size() - 1);
458 else
459 filter = message.GetStringParam();
461 OnFilterItems(filter);
462 UpdateButtons();
463 return true;
465 else
466 return CGUIWindow::OnMessage(message);
468 return true;
470 break;
471 case GUI_MSG_PLAYBACK_STARTED:
472 case GUI_MSG_PLAYBACK_ENDED:
473 case GUI_MSG_PLAYBACK_STOPPED:
474 case GUI_MSG_PLAYLIST_CHANGED:
475 case GUI_MSG_PLAYLISTPLAYER_STOPPED:
476 case GUI_MSG_PLAYLISTPLAYER_STARTED:
477 case GUI_MSG_PLAYLISTPLAYER_CHANGED:
478 { // send a notify all to all controls on this window
479 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_REFRESH_LIST);
480 OnMessage(msg);
481 break;
483 case GUI_MSG_CHANGE_VIEW_MODE:
485 int viewMode = 0;
486 if (message.GetParam1()) // we have an id
487 viewMode = m_viewControl.GetViewModeByID(message.GetParam1());
488 else if (message.GetParam2())
489 viewMode = m_viewControl.GetNextViewMode(message.GetParam2());
491 if (m_guiState)
492 m_guiState->SaveViewAsControl(viewMode);
493 UpdateButtons();
494 return true;
496 break;
497 case GUI_MSG_CHANGE_SORT_METHOD:
499 if (m_guiState)
501 if (message.GetParam1())
502 m_guiState->SetCurrentSortMethod(message.GetParam1());
503 else if (message.GetParam2())
504 m_guiState->SetNextSortMethod(message.GetParam2());
506 UpdateFileList();
507 return true;
509 break;
510 case GUI_MSG_CHANGE_SORT_DIRECTION:
512 if (m_guiState)
513 m_guiState->SetNextSortOrder();
514 UpdateFileList();
515 return true;
517 break;
518 case GUI_MSG_WINDOW_INIT:
520 if (m_vecItems->GetPath() == "?")
521 m_vecItems->SetPath("");
523 std::string dir = message.GetStringParam(0);
524 const std::string& ret = message.GetStringParam(1);
525 const std::string& swap = message.GetStringParam(message.GetNumStringParams() - 1);
526 const bool returning = StringUtils::EqualsNoCase(ret, "return");
527 const bool replacing = StringUtils::EqualsNoCase(swap, "replace");
529 if (!dir.empty())
531 // ensure our directory is valid
532 dir = GetStartFolder(dir);
533 bool resetHistory = false;
534 if (!returning || !URIUtils::PathEquals(dir, m_startDirectory, true))
535 { // we're not returning to the same path, so set our directory to the requested path
536 m_vecItems->SetPath(dir);
537 resetHistory = true;
539 else if (m_vecItems->GetPath().empty() && URIUtils::PathEquals(dir, m_startDirectory, true))
540 m_vecItems->SetPath(dir);
542 // check for network up
543 if (URIUtils::IsRemote(m_vecItems->GetPath()) && !WaitForNetwork())
545 m_vecItems->SetPath("");
546 resetHistory = true;
548 if (resetHistory)
550 m_vecItems->RemoveDiscCache(GetID());
551 // only compute the history for the provided path if "return" is not defined
552 // (otherwise the root level for the path will be added by default to the path history
553 // and we won't be able to move back to the path we came from)
554 if (!returning)
555 SetHistoryForPath(m_vecItems->GetPath());
558 if (message.GetParam1() != WINDOW_INVALID)
560 // if this is the first time to this window - make sure we set the root path
561 // if "return" is defined make sure we set the startDirectory to the directory we are
562 // moving to (so that we can move back to where we were onBack). If we are activating
563 // the same window but with a different path, do nothing - we are simply adding to the
564 // window history. Note that if the window is just being replaced, the start directory
565 // also needs to be set as the manager has just popped the previous window.
566 if (message.GetParam1() != message.GetParam2() || replacing)
567 m_startDirectory = returning ? dir : GetRootPath();
569 if (message.GetParam2() == PLUGIN_REFRESH_DELAY)
571 Refresh();
572 SetInitialVisibility();
573 RestoreControlStates();
574 SetInitialVisibility();
575 return true;
578 break;
581 return CGUIWindow::OnMessage(message);
585 * \brief Updates the states
587 * This updates the states (enable, disable, visible...) of the controls defined
588 * by this window.
590 * \note Override this function in a derived class to add new controls
592 void CGUIMediaWindow::UpdateButtons()
594 if (m_guiState)
596 // Update sorting controls
597 if (m_guiState->GetSortOrder() == SortOrderNone)
599 CONTROL_DISABLE(CONTROL_BTNSORTASC);
601 else
603 CONTROL_ENABLE(CONTROL_BTNSORTASC);
604 SET_CONTROL_SELECTED(GetID(), CONTROL_BTNSORTASC, m_guiState->GetSortOrder() != SortOrderAscending);
607 // Update list/thumb control
608 m_viewControl.SetCurrentView(m_guiState->GetViewAsControl());
610 // Update sort by button
611 if (!m_guiState->HasMultipleSortMethods())
612 CONTROL_DISABLE(CONTROL_BTNSORTBY);
613 else
614 CONTROL_ENABLE(CONTROL_BTNSORTBY);
616 std::string sortLabel = StringUtils::Format(
617 g_localizeStrings.Get(550), g_localizeStrings.Get(m_guiState->GetSortMethodLabel()));
618 SET_CONTROL_LABEL(CONTROL_BTNSORTBY, sortLabel);
621 std::string items =
622 StringUtils::Format("{} {}", m_vecItems->GetObjectCount(), g_localizeStrings.Get(127));
623 SET_CONTROL_LABEL(CONTROL_LABELFILES, items);
625 SET_CONTROL_LABEL2(CONTROL_BTN_FILTER, GetProperty("filter").asString());
628 void CGUIMediaWindow::ClearFileItems()
630 m_viewControl.Clear();
631 m_vecItems->Clear();
632 m_unfilteredItems->Clear();
636 * \brief Sort file items
638 * This sorts file items based on the sort method and sort order provided by
639 * guiViewState.
641 void CGUIMediaWindow::SortItems(CFileItemList &items)
643 std::unique_ptr<CGUIViewState> guiState(CGUIViewState::GetViewState(GetID(), items));
645 if (guiState)
647 SortDescription sorting = guiState->GetSortMethod();
648 sorting.sortOrder = guiState->GetSortOrder();
649 // If the sort method is "sort by playlist" and we have a specific
650 // sort order available we can use the specified sort order to do the sorting
651 // We do this as the new SortBy methods are a superset of the SORT_METHOD methods, thus
652 // not all are available. This may be removed once SORT_METHOD_* have been replaced by
653 // SortBy.
654 if ((sorting.sortBy == SortByPlaylistOrder) && items.HasProperty(PROPERTY_SORT_ORDER))
656 SortBy sortBy = (SortBy)items.GetProperty(PROPERTY_SORT_ORDER).asInteger();
657 if (sortBy != SortByNone && sortBy != SortByPlaylistOrder && sortBy != SortByProgramCount)
659 sorting.sortBy = sortBy;
660 sorting.sortOrder = items.GetProperty(PROPERTY_SORT_ASCENDING).asBoolean() ? SortOrderAscending : SortOrderDescending;
661 sorting.sortAttributes = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING) ? SortAttributeIgnoreArticle : SortAttributeNone;
663 // if the sort order is descending, we need to switch the original sort order, as we assume
664 // in CGUIViewState::AddPlaylistOrder that SortByPlaylistOrder is ascending.
665 if (guiState->GetSortOrder() == SortOrderDescending)
666 sorting.sortOrder = sorting.sortOrder == SortOrderDescending ? SortOrderAscending : SortOrderDescending;
670 items.Sort(sorting);
675 * \brief Formats item labels
677 * This is based on the formatting provided by guiViewState.
679 void CGUIMediaWindow::FormatItemLabels(CFileItemList &items, const LABEL_MASKS &labelMasks)
681 CLabelFormatter fileFormatter(labelMasks.m_strLabelFile, labelMasks.m_strLabel2File);
682 CLabelFormatter folderFormatter(labelMasks.m_strLabelFolder, labelMasks.m_strLabel2Folder);
683 for (int i=0; i<items.Size(); ++i)
685 CFileItemPtr pItem=items[i];
687 if (pItem->IsLabelPreformatted())
688 continue;
690 if (pItem->m_bIsFolder)
691 folderFormatter.FormatLabels(pItem.get());
692 else
693 fileFormatter.FormatLabels(pItem.get());
696 if (items.GetSortMethod() == SortByLabel)
697 items.ClearSortState();
701 * \brief Format and sort file items
703 * Prepares and adds the fileitems to list/thumb panel
705 void CGUIMediaWindow::FormatAndSort(CFileItemList &items)
707 std::unique_ptr<CGUIViewState> viewState(CGUIViewState::GetViewState(GetID(), items));
709 if (viewState)
711 LABEL_MASKS labelMasks;
712 viewState->GetSortMethodLabelMasks(labelMasks);
713 FormatItemLabels(items, labelMasks);
715 items.Sort(viewState->GetSortMethod().sortBy, viewState->GetSortOrder(), viewState->GetSortMethod().sortAttributes);
720 * \brief Overwrite to fill fileitems from a source
722 * \param[in] strDirectory Path to read
723 * \param[out] items Fill with items specified in \e strDirectory
724 * \return false if given directory not present
726 bool CGUIMediaWindow::GetDirectory(const std::string &strDirectory, CFileItemList &items)
728 CURL pathToUrl(strDirectory);
730 std::string strParentPath = m_history.GetParentPath();
732 CLog::Log(LOGDEBUG, "CGUIMediaWindow::GetDirectory ({})", CURL::GetRedacted(strDirectory));
733 CLog::Log(LOGDEBUG, " ParentPath = [{}]", CURL::GetRedacted(strParentPath));
735 if (pathToUrl.IsProtocol("plugin") && !pathToUrl.GetHostName().empty())
736 CServiceBroker::GetAddonMgr().UpdateLastUsed(pathToUrl.GetHostName());
738 // see if we can load a previously cached folder
739 CFileItemList cachedItems(strDirectory);
740 if (!strDirectory.empty() && cachedItems.Load(GetID()))
742 items.Assign(cachedItems);
744 else
746 auto start = std::chrono::steady_clock::now();
748 if (strDirectory.empty())
749 SetupShares();
751 CFileItemList dirItems;
752 if (!GetDirectoryItems(pathToUrl, dirItems, UseFileDirectories()))
753 return false;
755 // assign fetched directory items
756 items.Assign(dirItems);
758 // took over a second, and not normally cached, so cache it
759 auto end = std::chrono::steady_clock::now();
760 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
762 if (duration.count() > 1000 && items.CacheToDiscIfSlow())
763 items.Save(GetID());
765 // if these items should replace the current listing, then pop it off the top
766 if (items.GetReplaceListing())
767 m_history.RemoveParentPath();
770 // update the view state's reference to the current items
771 m_guiState.reset(CGUIViewState::GetViewState(GetID(), items));
773 bool bHideParent = false;
775 if (m_guiState && m_guiState->HideParentDirItems())
776 bHideParent = true;
777 if (items.GetPath() == GetRootPath())
778 bHideParent = true;
780 if (!bHideParent)
782 CFileItemPtr pItem(new CFileItem(".."));
783 pItem->SetPath(strParentPath);
784 pItem->m_bIsFolder = true;
785 pItem->m_bIsShareOrDrive = false;
786 items.AddFront(pItem, 0);
789 int iWindow = GetID();
790 std::vector<std::string> regexps;
792 //! @todo Do we want to limit the directories we apply the video ones to?
793 if (iWindow == WINDOW_VIDEO_NAV)
794 regexps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoExcludeFromListingRegExps;
795 if (iWindow == WINDOW_MUSIC_NAV)
796 regexps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_audioExcludeFromListingRegExps;
797 if (iWindow == WINDOW_PICTURES)
798 regexps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_pictureExcludeFromListingRegExps;
800 if (regexps.size())
802 for (int i=0; i < items.Size();)
804 if (CUtil::ExcludeFileOrFolder(items[i]->GetPath(), regexps))
805 items.Remove(i);
806 else
807 i++;
811 // clear the filter
812 SetProperty("filter", "");
813 m_canFilterAdvanced = false;
814 m_filter.Reset();
815 return true;
818 bool CGUIMediaWindow::Update(const std::string &strDirectory, bool updateFilterPath /* = true */)
820 //! @todo OnInitWindow calls Update() before window path has been set properly.
821 if (strDirectory == "?")
822 return false;
824 // The path to load. Empty string is used in various places to denote root, so translate to the
825 // real root path first
826 const std::string path = strDirectory.empty() ? GetRootPath() : strDirectory;
828 // stores the selected item in history
829 SaveSelectedItemInHistory();
831 const std::string previousPath = m_vecItems->GetPath();
833 // check if the path contains a filter and temporarily remove it
834 // so that the retrieved list of items is unfiltered
835 std::string pathNoFilter = path;
836 if (CanContainFilter(pathNoFilter) && CURL(pathNoFilter).HasOption("filter"))
837 pathNoFilter = RemoveParameterFromPath(pathNoFilter, "filter");
839 if (!GetDirectory(pathNoFilter, *m_vecItems))
841 CLog::Log(LOGERROR, "CGUIMediaWindow::GetDirectory({}) failed", CURL(path).GetRedacted());
843 if (URIUtils::PathEquals(path, GetRootPath()))
844 return false; // Nothing to fallback to
846 // Try to return to the previous directory, if not the same
847 // else fallback to root
848 if (URIUtils::PathEquals(path, previousPath) || !Update(m_history.RemoveParentPath()))
849 Update(""); // Fallback to root
851 // Return false to be able to eg. show
852 // an error message.
853 return false;
856 if (m_vecItems->GetLabel().empty())
858 // Removable sources
859 VECSOURCES removables;
860 CServiceBroker::GetMediaManager().GetRemovableDrives(removables);
861 for (const auto& s : removables)
863 if (URIUtils::CompareWithoutSlashAtEnd(s.strPath, m_vecItems->GetPath()))
865 m_vecItems->SetLabel(s.strName);
866 break;
871 if (m_vecItems->GetLabel().empty())
872 m_vecItems->SetLabel(CUtil::GetTitleFromPath(m_vecItems->GetPath(), true));
874 // check the given path for filter data
875 UpdateFilterPath(path, *m_vecItems, updateFilterPath);
877 // if we're getting the root source listing
878 // make sure the path history is clean
879 if (URIUtils::PathEquals(path, GetRootPath()))
880 m_history.ClearPathHistory();
882 int iWindow = GetID();
883 int showLabel = 0;
884 if (URIUtils::PathEquals(path, GetRootPath()))
886 if (iWindow == WINDOW_PICTURES)
887 showLabel = 997;
888 else if (iWindow == WINDOW_FILES)
889 showLabel = 1026;
890 else if (iWindow == WINDOW_GAMES)
891 showLabel = 35250; // "Add games..."
893 if (m_vecItems->IsPath("sources://video/"))
894 showLabel = 999;
895 else if (m_vecItems->IsPath("sources://music/"))
896 showLabel = 998;
897 else if (m_vecItems->IsPath("sources://pictures/"))
898 showLabel = 997;
899 else if (m_vecItems->IsPath("sources://files/"))
900 showLabel = 1026;
901 else if (m_vecItems->IsPath("sources://games/"))
902 showLabel = 35250; // "Add games..."
903 // Add 'Add source ' item
904 if (showLabel && (m_vecItems->Size() == 0 || !m_guiState->DisableAddSourceButtons()) &&
905 iWindow != WINDOW_MUSIC_PLAYLIST_EDITOR)
907 const std::string& strLabel = g_localizeStrings.Get(showLabel);
908 CFileItemPtr pItem(new CFileItem(strLabel));
909 pItem->SetPath("add");
910 pItem->SetArt("icon", "DefaultAddSource.png");
911 pItem->SetLabel(strLabel);
912 pItem->SetLabelPreformatted(true);
913 pItem->m_bIsFolder = true;
914 pItem->SetSpecialSort(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_addSourceOnTop ?
915 SortSpecialOnTop : SortSpecialOnBottom);
916 m_vecItems->Add(pItem);
918 m_iLastControl = GetFocusedControlID();
920 // Check whether to enabled advanced filtering based on the content type
921 m_canFilterAdvanced = CheckFilterAdvanced(*m_vecItems);
922 if (m_canFilterAdvanced)
923 m_filter.SetType(m_vecItems->GetContent());
925 // Ask the derived class if it wants to load additional info
926 // for the fileitems like media info or additional
927 // filtering on the items, setting thumbs.
928 OnPrepareFileItems(*m_vecItems);
930 m_vecItems->FillInDefaultIcons();
932 // remember the original (untouched) list of items (for filtering etc)
933 m_unfilteredItems->Assign(*m_vecItems);
935 // Cache the list of items if possible
936 OnCacheFileItems(*m_vecItems);
938 // Filter and group the items if necessary
939 OnFilterItems(GetProperty("filter").asString());
940 UpdateButtons();
942 // Restore selected item from history
943 RestoreSelectedItemFromHistory();
945 m_history.AddPath(m_vecItems->GetPath(), m_strFilterPath);
947 //m_history.DumpPathHistory();
949 return true;
952 bool CGUIMediaWindow::Refresh(bool clearCache /* = false */)
954 std::string strCurrentDirectory = m_vecItems->GetPath();
955 if (strCurrentDirectory == "?")
956 return false;
958 if (clearCache)
959 m_vecItems->RemoveDiscCache(GetID());
961 bool ret = true;
963 // get the original number of items
964 if (!Update(strCurrentDirectory, false))
966 ret = false;
969 return ret;
973 * \brief On prepare file items
975 * This function will be called by Update() before the labels of the fileitems
976 * are formatted.
978 * \note Override this function to set custom thumbs or load additional media
979 * info.
981 * It's used to load tag info for music.
983 void CGUIMediaWindow::OnPrepareFileItems(CFileItemList &items)
985 CFileItemListModification::GetInstance().Modify(items);
989 * \brief On cache file items
991 * This function will be called by Update() before
992 * any additional formatting, filtering or sorting is applied.
994 * \note Override this function to define a custom caching behaviour.
996 void CGUIMediaWindow::OnCacheFileItems(CFileItemList &items)
998 // Should these items be saved to the hdd
999 if (items.CacheToDiscAlways() && !IsFiltered())
1000 items.Save(GetID());
1004 * \brief On click
1006 * With this function you can react on a users click in the list/thumb panel.
1007 * It returns true, if the click is handled.
1008 * This function calls OnPlayMedia()
1010 bool CGUIMediaWindow::OnClick(int iItem, const std::string &player)
1012 if (iItem < 0 || iItem >= m_vecItems->Size())
1013 return true;
1015 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
1017 CFileItemPtr pItem = m_vecItems->Get(iItem);
1019 if (pItem->IsParentFolder())
1021 GoParentFolder();
1022 return true;
1025 if (pItem->GetPath() == "add" || pItem->GetPath() == "sources://add/") // 'add source button' in empty root
1027 if (profileManager->IsMasterProfile())
1029 if (!g_passwordManager.IsMasterLockUnlocked(true))
1030 return false;
1032 else if (!profileManager->GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked())
1033 return false;
1035 if (OnAddMediaSource())
1036 Refresh(true);
1038 return true;
1041 if (!pItem->m_bIsFolder && pItem->IsFileFolder(EFILEFOLDER_MASK_ONCLICK))
1043 XFILE::IFileDirectory *pFileDirectory = nullptr;
1044 pFileDirectory = XFILE::CFileDirectoryFactory::Create(pItem->GetURL(), pItem.get(), "");
1045 if(pFileDirectory)
1046 pItem->m_bIsFolder = true;
1047 else if(pItem->m_bIsFolder)
1048 pItem->m_bIsFolder = false;
1049 delete pFileDirectory;
1052 if (pItem->IsScript())
1054 // execute the script
1055 CURL url(pItem->GetPath());
1056 AddonPtr addon;
1057 if (CServiceBroker::GetAddonMgr().GetAddon(url.GetHostName(), addon, AddonType::SCRIPT,
1058 OnlyEnabled::CHOICE_YES))
1060 if (!CScriptInvocationManager::GetInstance().Stop(addon->LibPath()))
1062 CServiceBroker::GetAddonMgr().UpdateLastUsed(addon->ID());
1063 CScriptInvocationManager::GetInstance().ExecuteAsync(addon->LibPath(), addon);
1065 return true;
1069 if (pItem->m_bIsFolder)
1071 if ( pItem->m_bIsShareOrDrive )
1073 const std::string& strLockType=m_guiState->GetLockType();
1074 if (profileManager->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE)
1075 if (!strLockType.empty() && !g_passwordManager.IsItemUnlocked(pItem.get(), strLockType))
1076 return true;
1078 if (!HaveDiscOrConnection(pItem->GetPath(), pItem->m_iDriveType))
1079 return true;
1082 // check for the partymode playlist items - they may not exist yet
1083 if ((pItem->GetPath() == profileManager->GetUserDataItem("PartyMode.xsp")) ||
1084 (pItem->GetPath() == profileManager->GetUserDataItem("PartyMode-Video.xsp")))
1086 // party mode playlist item - if it doesn't exist, prompt for user to define it
1087 if (!CFileUtils::Exists(pItem->GetPath()))
1089 m_vecItems->RemoveDiscCache(GetID());
1090 if (CGUIDialogSmartPlaylistEditor::EditPlaylist(pItem->GetPath()))
1091 Refresh();
1092 return true;
1096 // remove the directory cache if the folder is not normally cached
1097 CFileItemList items(pItem->GetPath());
1098 if (!items.AlwaysCache())
1099 items.RemoveDiscCache(GetID());
1101 // if we have a filtered list, we need to add the filtered
1102 // path to be able to come back to the filtered view
1103 std::string strCurrentDirectory = m_vecItems->GetPath();
1104 if (m_canFilterAdvanced && !m_filter.IsEmpty() &&
1105 !URIUtils::PathEquals(m_strFilterPath, strCurrentDirectory))
1107 m_history.RemoveParentPath();
1108 m_history.AddPath(strCurrentDirectory, m_strFilterPath);
1111 if (m_vecItemsUpdating)
1113 CLog::Log(LOGWARNING, "CGUIMediaWindow::OnClick - updating in progress");
1114 return true;
1116 CUpdateGuard ug(m_vecItemsUpdating);
1118 CFileItem directory(*pItem);
1119 if (!Update(directory.GetPath()))
1120 ShowShareErrorMessage(&directory);
1122 return true;
1124 else if (pItem->IsPlugin() && !pItem->GetProperty("isplayable").asBoolean())
1126 bool resume = pItem->GetStartOffset() == STARTOFFSET_RESUME;
1127 return XFILE::CPluginDirectory::RunScriptWithParams(pItem->GetPath(), resume);
1129 #if defined(TARGET_ANDROID)
1130 else if (pItem->IsAndroidApp())
1132 std::string appName = URIUtils::GetFileName(pItem->GetPath());
1133 CLog::Log(LOGDEBUG, "CGUIMediaWindow::OnClick Trying to run: {}", appName);
1134 return CXBMCApp::StartActivity(appName);
1136 #endif
1137 else
1139 SaveSelectedItemInHistory();
1141 if (pItem->GetPath() == "newplaylist://")
1143 m_vecItems->RemoveDiscCache(GetID());
1144 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_MUSIC_PLAYLIST_EDITOR,"newplaylist://");
1145 return true;
1147 else if (StringUtils::StartsWithNoCase(pItem->GetPath(), "newsmartplaylist://"))
1149 m_vecItems->RemoveDiscCache(GetID());
1150 if (CGUIDialogSmartPlaylistEditor::NewPlaylist(pItem->GetPath().substr(19)))
1151 Refresh();
1152 return true;
1155 bool autoplay = m_guiState.get() && m_guiState->AutoPlayNextItem();
1157 if (m_vecItems->IsPlugin())
1159 CURL url(m_vecItems->GetPath());
1160 AddonPtr addon;
1161 if (CServiceBroker::GetAddonMgr().GetAddon(url.GetHostName(), addon, OnlyEnabled::CHOICE_YES))
1163 const auto plugin = std::dynamic_pointer_cast<CPluginSource>(addon);
1164 if (plugin && plugin->Provides(CPluginSource::AUDIO))
1166 CFileItemList items;
1167 std::unique_ptr<CGUIViewState> state(CGUIViewState::GetViewState(GetID(), items));
1168 autoplay = state.get() && state->AutoPlayNextItem();
1173 if (autoplay && !g_partyModeManager.IsEnabled())
1175 return OnPlayAndQueueMedia(pItem, player);
1177 else
1179 return OnPlayMedia(iItem, player);
1183 return false;
1186 bool CGUIMediaWindow::OnSelect(int item)
1188 return OnClick(item);
1192 * \brief Check disc or connection present
1194 * Checks if there is a disc in the dvd drive and whether the
1195 * network is connected or not.
1197 bool CGUIMediaWindow::HaveDiscOrConnection(const std::string& strPath, int iDriveType)
1199 if (iDriveType==CMediaSource::SOURCE_TYPE_DVD)
1201 if (!CServiceBroker::GetMediaManager().IsDiscInDrive(strPath))
1203 HELPERS::ShowOKDialogText(CVariant{218}, CVariant{219});
1204 return false;
1207 else if (iDriveType==CMediaSource::SOURCE_TYPE_REMOTE)
1209 //! @todo Handle not connected to a remote share
1210 if (!CServiceBroker::GetNetwork().IsConnected())
1212 HELPERS::ShowOKDialogText(CVariant{220}, CVariant{221});
1213 return false;
1217 return true;
1221 * \brief Shows a standard error message for a given pItem.
1223 void CGUIMediaWindow::ShowShareErrorMessage(CFileItem* pItem) const
1225 if (!pItem->m_bIsShareOrDrive)
1226 return;
1228 int idMessageText = 0;
1229 CURL url(pItem->GetPath());
1231 if (url.IsProtocol("smb") && url.GetHostName().empty()) // smb workgroup
1232 idMessageText = 15303; // Workgroup not found
1233 else if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_REMOTE || URIUtils::IsRemote(pItem->GetPath()))
1234 idMessageText = 15301; // Could not connect to network server
1235 else
1236 idMessageText = 15300; // Path not found or invalid
1238 HELPERS::ShowOKDialogText(CVariant{220}, CVariant{idMessageText});
1242 * \brief Go one directory up on list items
1244 * The function goes up one level in the directory tree
1246 bool CGUIMediaWindow::GoParentFolder()
1248 if (m_vecItems->IsVirtualDirectoryRoot())
1249 return false;
1251 if (URIUtils::PathEquals(m_vecItems->GetPath(), GetRootPath()))
1252 return false;
1254 //m_history.DumpPathHistory();
1256 const std::string currentPath = m_vecItems->GetPath();
1257 std::string parentPath = m_history.GetParentPath();
1258 // Check if a) the current folder is on the stack more than once, (parent is
1259 // often same as current), OR
1260 // b) the parent is an xml file (happens when ActivateWindow() called with
1261 // a node file) and so current path is the result of expanding the xml.
1262 // Keep going until there's nothing left or they dont match anymore.
1263 while (!parentPath.empty() &&
1264 (URIUtils::PathEquals(parentPath, currentPath, true) ||
1265 StringUtils::EndsWith(parentPath, ".xml/") || StringUtils::EndsWith(parentPath, ".xml")))
1267 m_history.RemoveParentPath();
1268 parentPath = m_history.GetParentPath();
1271 // remove the current filter but only if the parent
1272 // item doesn't have a filter as well
1273 CURL filterUrl(m_strFilterPath);
1274 if (filterUrl.HasOption("filter"))
1276 CURL parentUrl(m_history.GetParentPath(true));
1277 if (!parentUrl.HasOption("filter"))
1279 // we need to overwrite m_strFilterPath because
1280 // Refresh() will set updateFilterPath to false
1281 m_strFilterPath.clear();
1282 Refresh();
1283 return true;
1287 // pop directory path from the stack
1288 m_strFilterPath = m_history.GetParentPath(true);
1289 m_history.RemoveParentPath();
1291 if (!Update(parentPath, false))
1292 return false;
1294 // No items to show so go another level up
1295 if (!m_vecItems->GetPath().empty() && (m_filter.IsEmpty() ? m_vecItems->Size() : m_unfilteredItems->Size()) <= 0)
1297 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(2080), g_localizeStrings.Get(2081));
1298 return GoParentFolder();
1300 return true;
1303 void CGUIMediaWindow::SaveSelectedItemInHistory()
1305 int iItem = m_viewControl.GetSelectedItem();
1306 std::string strSelectedItem;
1307 if (iItem >= 0 && iItem < m_vecItems->Size())
1309 CFileItemPtr pItem = m_vecItems->Get(iItem);
1310 GetDirectoryHistoryString(pItem.get(), strSelectedItem);
1313 m_history.SetSelectedItem(strSelectedItem, m_vecItems->GetPath());
1316 void CGUIMediaWindow::RestoreSelectedItemFromHistory()
1318 std::string strSelectedItem = m_history.GetSelectedItem(m_vecItems->GetPath());
1320 if (!strSelectedItem.empty())
1322 for (int i = 0; i < m_vecItems->Size(); ++i)
1324 CFileItemPtr pItem = m_vecItems->Get(i);
1325 std::string strHistory;
1326 GetDirectoryHistoryString(pItem.get(), strHistory);
1327 // set selected item if equals with history
1328 if (strHistory == strSelectedItem)
1330 m_viewControl.SetSelectedItem(i);
1331 return;
1336 // if we haven't found the selected item, select the first item
1337 m_viewControl.SetSelectedItem(0);
1341 * \brief Get history string for given file item
1343 * \note Override the function to change the default behavior on how
1344 * a selected item history should look like
1346 void CGUIMediaWindow::GetDirectoryHistoryString(const CFileItem* pItem, std::string& strHistoryString) const
1348 if (pItem->m_bIsShareOrDrive)
1350 // We are in the virtual directory
1352 // History string of the DVD drive
1353 // must be handled separately
1354 if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_DVD)
1356 // Remove disc label from item label
1357 // and use as history string, m_strPath
1358 // can change for new discs
1359 std::string strLabel = pItem->GetLabel();
1360 size_t nPosOpen = strLabel.find('(');
1361 size_t nPosClose = strLabel.rfind(')');
1362 if (nPosOpen != std::string::npos &&
1363 nPosClose != std::string::npos &&
1364 nPosClose > nPosOpen)
1366 strLabel.erase(nPosOpen + 1, (nPosClose) - (nPosOpen + 1));
1367 strHistoryString = strLabel;
1369 else
1370 strHistoryString = strLabel;
1372 else
1374 // Other items in virtual directory
1375 std::string strPath = pItem->GetPath();
1376 URIUtils::RemoveSlashAtEnd(strPath);
1378 strHistoryString = pItem->GetLabel() + strPath;
1381 else if (pItem->GetEndOffset() > pItem->GetStartOffset() &&
1382 pItem->GetStartOffset() != STARTOFFSET_RESUME)
1384 // Could be a cue item, all items of a cue share the same filename
1385 // so add the offsets to build the history string
1386 strHistoryString = StringUtils::Format("{}{}", pItem->GetStartOffset(), pItem->GetEndOffset());
1387 strHistoryString += pItem->GetPath();
1389 else
1391 // Normal directory items
1392 strHistoryString = pItem->GetPath();
1395 // remove any filter
1396 if (CanContainFilter(strHistoryString))
1397 strHistoryString = RemoveParameterFromPath(strHistoryString, "filter");
1399 URIUtils::RemoveSlashAtEnd(strHistoryString);
1400 StringUtils::ToLower(strHistoryString);
1404 * \brief Set history for path
1406 * Call this function to create a directory history for the
1407 * path given by strDirectory.
1409 void CGUIMediaWindow::SetHistoryForPath(const std::string& strDirectory)
1411 // Make sure our shares are configured
1412 SetupShares();
1413 if (!strDirectory.empty())
1415 // Build the directory history for default path
1416 std::string strPath, strParentPath;
1417 strPath = strDirectory;
1418 URIUtils::RemoveSlashAtEnd(strPath);
1420 CFileItemList items;
1421 CURL url;
1422 GetDirectoryItems(url, items, UseFileDirectories());
1424 m_history.ClearPathHistory();
1426 bool originalPath = true;
1427 while (URIUtils::GetParentPath(strPath, strParentPath))
1429 for (int i = 0; i < items.Size(); ++i)
1431 CFileItemPtr pItem = items[i];
1432 std::string path(pItem->GetPath());
1433 URIUtils::RemoveSlashAtEnd(path);
1434 if (URIUtils::PathEquals(path, strPath))
1436 std::string strHistory;
1437 GetDirectoryHistoryString(pItem.get(), strHistory);
1438 m_history.SetSelectedItem(strHistory, "");
1439 URIUtils::AddSlashAtEnd(strPath);
1440 m_history.AddPathFront(strPath);
1441 m_history.AddPathFront("");
1443 //m_history.DumpPathHistory();
1444 return ;
1448 if (URIUtils::IsVideoDb(strPath))
1450 CURL url(strParentPath);
1451 url.SetOptions(""); // clear any URL options from recreated parent path
1452 strParentPath = url.Get();
1455 // set the original path exactly as it was passed in
1456 if (URIUtils::PathEquals(strPath, strDirectory, true))
1457 strPath = strDirectory;
1458 else
1459 URIUtils::AddSlashAtEnd(strPath);
1461 m_history.AddPathFront(strPath, originalPath ? m_strFilterPath : "");
1462 m_history.SetSelectedItem(strPath, strParentPath);
1463 originalPath = false;
1464 strPath = strParentPath;
1465 URIUtils::RemoveSlashAtEnd(strPath);
1468 else
1469 m_history.ClearPathHistory();
1471 //m_history.DumpPathHistory();
1475 * \brief On media play
1477 * \note Override if you want to change the default behavior, what is done
1478 * when the user clicks on a file.
1480 * This function is called by OnClick()
1482 bool CGUIMediaWindow::OnPlayMedia(int iItem, const std::string &player)
1484 // Reset Playlistplayer, playback started now does
1485 // not use the playlistplayer.
1486 CServiceBroker::GetPlaylistPlayer().Reset();
1487 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_NONE);
1488 CFileItemPtr pItem=m_vecItems->Get(iItem);
1490 CLog::Log(LOGDEBUG, "{} {}", __FUNCTION__, CURL::GetRedacted(pItem->GetPath()));
1492 bool bResult = false;
1493 if (pItem->IsInternetStream() || pItem->IsPlayList())
1494 bResult = g_application.PlayMedia(*pItem, player, m_guiState->GetPlaylist());
1495 else
1496 bResult = g_application.PlayFile(*pItem, player);
1498 if (pItem->GetStartOffset() == STARTOFFSET_RESUME)
1499 pItem->SetStartOffset(0);
1501 return bResult;
1505 * \brief On play and media queue
1507 * \note Override if you want to change the default behavior of what is done
1508 * when the user clicks on a file in a "folder" with similar files.
1510 * This function is called by OnClick()
1512 bool CGUIMediaWindow::OnPlayAndQueueMedia(const CFileItemPtr& item, const std::string& player)
1514 //play and add current directory to temporary playlist
1515 PLAYLIST::Id playlistId = m_guiState->GetPlaylist();
1516 if (playlistId != PLAYLIST::TYPE_NONE)
1518 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(playlistId);
1519 CServiceBroker::GetPlaylistPlayer().Reset();
1520 int mediaToPlay = 0;
1522 // first try to find mainDVD file (VIDEO_TS.IFO).
1523 // If we find this we should not allow to queue VOB files
1524 std::string mainDVD;
1525 for (int i = 0; i < m_vecItems->Size(); i++)
1527 std::string path = URIUtils::GetFileName(m_vecItems->Get(i)->GetDynPath());
1528 if (StringUtils::EqualsNoCase(path, "VIDEO_TS.IFO"))
1530 mainDVD = path;
1531 break;
1535 // now queue...
1536 for ( int i = 0; i < m_vecItems->Size(); i++ )
1538 CFileItemPtr nItem = m_vecItems->Get(i);
1540 if (nItem->m_bIsFolder)
1541 continue;
1543 if (!nItem->IsZIP() && !nItem->IsRAR() && (!nItem->IsDVDFile() || (URIUtils::GetFileName(nItem->GetDynPath()) == mainDVD)))
1544 CServiceBroker::GetPlaylistPlayer().Add(playlistId, nItem);
1546 if (item->IsSamePath(nItem.get()))
1547 { // item that was clicked
1548 mediaToPlay = CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId).size() - 1;
1552 // Save current window and directory to know where the selected item was
1553 if (m_guiState)
1554 m_guiState->SetPlaylistDirectory(m_vecItems->GetPath());
1556 // figure out where we start playback
1557 if (CServiceBroker::GetPlaylistPlayer().IsShuffled(playlistId))
1559 int iIndex =
1560 CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId).FindOrder(mediaToPlay);
1561 CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId).Swap(0, iIndex);
1562 mediaToPlay = 0;
1565 // play
1566 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(playlistId);
1567 CServiceBroker::GetPlaylistPlayer().Play(mediaToPlay, player);
1569 return true;
1573 * \brief Update file list
1575 * Synchronize the fileitems with the playlistplayer
1576 * also recreates the playlist of the playlistplayer based
1577 * on the fileitems of the window
1579 void CGUIMediaWindow::UpdateFileList()
1581 int nItem = m_viewControl.GetSelectedItem();
1582 std::string strSelected;
1583 if (nItem >= 0)
1584 strSelected = m_vecItems->Get(nItem)->GetPath();
1586 FormatAndSort(*m_vecItems);
1587 UpdateButtons();
1589 m_viewControl.SetItems(*m_vecItems);
1590 m_viewControl.SetSelectedItem(strSelected);
1592 // set the currently playing item as selected, if its in this directory
1593 if (m_guiState.get() && m_guiState->IsCurrentPlaylistDirectory(m_vecItems->GetPath()))
1595 PLAYLIST::Id playlistId = m_guiState->GetPlaylist();
1596 int nSong = CServiceBroker::GetPlaylistPlayer().GetCurrentSong();
1597 CFileItem playlistItem;
1598 if (nSong > -1 && playlistId != PLAYLIST::TYPE_NONE)
1599 playlistItem = *CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId)[nSong];
1601 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(playlistId);
1602 CServiceBroker::GetPlaylistPlayer().Reset();
1604 for (int i = 0; i < m_vecItems->Size(); i++)
1606 CFileItemPtr pItem = m_vecItems->Get(i);
1607 if (pItem->m_bIsFolder)
1608 continue;
1610 if (!pItem->IsPlayList() && !pItem->IsZIP() && !pItem->IsRAR())
1611 CServiceBroker::GetPlaylistPlayer().Add(playlistId, pItem);
1613 if (pItem->GetPath() == playlistItem.GetPath() &&
1614 pItem->GetStartOffset() == playlistItem.GetStartOffset())
1615 CServiceBroker::GetPlaylistPlayer().SetCurrentSong(
1616 CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId).size() - 1);
1621 void CGUIMediaWindow::OnDeleteItem(int iItem)
1623 if ( iItem < 0 || iItem >= m_vecItems->Size()) return;
1624 CFileItemPtr item = m_vecItems->Get(iItem);
1626 if (item->IsPlayList())
1627 item->m_bIsFolder = false;
1629 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
1631 if (profileManager->GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE && profileManager->GetCurrentProfile().filesLocked())
1633 if (!g_passwordManager.IsMasterLockUnlocked(true))
1634 return;
1637 CGUIComponent *gui = CServiceBroker::GetGUI();
1638 if (gui && gui->ConfirmDelete(item->GetPath()))
1640 if (!CFileUtils::DeleteItem(item))
1641 return;
1643 else
1644 return;
1646 Refresh(true);
1647 m_viewControl.SetSelectedItem(iItem);
1650 void CGUIMediaWindow::OnRenameItem(int iItem)
1652 if (iItem < 0 || iItem >= m_vecItems->Size())
1653 return;
1655 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
1657 if (profileManager->GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE && profileManager->GetCurrentProfile().filesLocked())
1659 if (!g_passwordManager.IsMasterLockUnlocked(true))
1660 return;
1663 if (!CFileUtils::RenameFile(m_vecItems->Get(iItem)->GetPath()))
1664 return;
1666 Refresh(true);
1667 m_viewControl.SetSelectedItem(iItem);
1670 void CGUIMediaWindow::OnInitWindow()
1672 // initial fetch is done unthreaded to ensure the items are setup prior to skin animations kicking off
1673 m_backgroundLoad = false;
1675 // the start directory may change during Refresh
1676 bool updateStartDirectory = URIUtils::PathEquals(m_vecItems->GetPath(), m_startDirectory, true);
1678 // we have python scripts hooked in everywhere :(
1679 // those scripts may open windows and we can't open a window
1680 // while opening this one.
1681 // for plugin sources delay call to Refresh
1682 if (!URIUtils::IsPlugin(m_vecItems->GetPath()))
1684 Refresh();
1686 else
1688 CGUIMessage msg(GUI_MSG_WINDOW_INIT, 0, 0, WINDOW_INVALID, PLUGIN_REFRESH_DELAY);
1689 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, GetID());
1692 if (updateStartDirectory)
1694 // reset the start directory to the path of the items
1695 m_startDirectory = m_vecItems->GetPath();
1697 // reset the history based on the path of the items
1698 SetHistoryForPath(m_startDirectory);
1701 m_backgroundLoad = true;
1703 CGUIWindow::OnInitWindow();
1706 void CGUIMediaWindow::SaveControlStates()
1708 CGUIWindow::SaveControlStates();
1709 SaveSelectedItemInHistory();
1712 void CGUIMediaWindow::RestoreControlStates()
1714 CGUIWindow::RestoreControlStates();
1715 RestoreSelectedItemFromHistory();
1718 CGUIControl *CGUIMediaWindow::GetFirstFocusableControl(int id)
1720 if (m_viewControl.HasControl(id))
1721 id = m_viewControl.GetCurrentControl();
1722 return CGUIWindow::GetFirstFocusableControl(id);
1725 void CGUIMediaWindow::SetupShares()
1727 // Setup shares and filemasks for this window
1728 CFileItemList items;
1729 CGUIViewState* viewState=CGUIViewState::GetViewState(GetID(), items);
1730 if (viewState)
1732 m_rootDir.SetMask(viewState->GetExtensions());
1733 m_rootDir.SetSources(viewState->GetSources());
1734 delete viewState;
1738 bool CGUIMediaWindow::OnPopupMenu(int itemIdx)
1740 auto InRange = [](size_t i, std::pair<size_t, size_t> range){ return i >= range.first && i < range.second; };
1742 if (itemIdx < 0 || itemIdx >= m_vecItems->Size())
1743 return false;
1745 auto item = m_vecItems->Get(itemIdx);
1746 if (!item)
1747 return false;
1749 item->SetProperty("ParentPath", m_vecItems->GetPath());
1751 CContextButtons buttons;
1753 //Add items from plugin
1755 int i = 0;
1756 while (item->HasProperty(StringUtils::Format("contextmenulabel({})", i)))
1758 buttons.emplace_back(
1759 ~buttons.size(),
1760 item->GetProperty(StringUtils::Format("contextmenulabel({})", i)).asString());
1761 ++i;
1764 auto pluginMenuRange = std::make_pair(static_cast<size_t>(0), buttons.size());
1766 //Add the global menu
1767 auto globalMenu = CServiceBroker::GetContextMenuManager().GetItems(*item, CContextMenuManager::MAIN);
1768 auto globalMenuRange = std::make_pair(buttons.size(), buttons.size() + globalMenu.size());
1769 for (const auto& menu : globalMenu)
1770 buttons.emplace_back(~buttons.size(), menu->GetLabel(*item));
1772 //Add legacy items from windows
1773 auto buttonsSize = buttons.size();
1774 GetContextButtons(itemIdx, buttons);
1775 auto windowMenuRange = std::make_pair(buttonsSize, buttons.size());
1777 //Add addon menus
1778 auto addonMenu = CServiceBroker::GetContextMenuManager().GetAddonItems(*item, CContextMenuManager::MAIN);
1779 auto addonMenuRange = std::make_pair(buttons.size(), buttons.size() + addonMenu.size());
1780 for (const auto& menu : addonMenu)
1781 buttons.emplace_back(~buttons.size(), menu->GetLabel(*item));
1783 if (buttons.empty())
1784 return true;
1786 int idx = CGUIDialogContextMenu::Show(buttons);
1787 if (idx < 0 || idx >= static_cast<int>(buttons.size()))
1788 return false;
1790 if (InRange(static_cast<size_t>(idx), pluginMenuRange))
1792 bool saveVal = m_backgroundLoad;
1793 m_backgroundLoad = false;
1794 CServiceBroker::GetAppMessenger()->SendMsg(
1795 TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr,
1796 item->GetProperty(StringUtils::Format("contextmenuaction({})", idx - pluginMenuRange.first))
1797 .asString());
1798 m_backgroundLoad = saveVal;
1799 return true;
1802 if (InRange(idx, windowMenuRange))
1803 return OnContextButton(itemIdx, static_cast<CONTEXT_BUTTON>(buttons[idx].first));
1805 if (InRange(idx, globalMenuRange))
1806 return CONTEXTMENU::LoopFrom(*globalMenu[idx - globalMenuRange.first], item);
1808 return CONTEXTMENU::LoopFrom(*addonMenu[idx - addonMenuRange.first], item);
1811 void CGUIMediaWindow::GetContextButtons(int itemNumber, CContextButtons &buttons)
1813 CFileItemPtr item = (itemNumber >= 0 && itemNumber < m_vecItems->Size()) ? m_vecItems->Get(itemNumber) : CFileItemPtr();
1815 if (!item || item->IsParentFolder())
1816 return;
1818 if (item->IsFileFolder(EFILEFOLDER_MASK_ONBROWSE))
1819 buttons.Add(CONTEXT_BUTTON_BROWSE_INTO, 37015);
1823 bool CGUIMediaWindow::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
1825 switch (button)
1827 case CONTEXT_BUTTON_BROWSE_INTO:
1829 CFileItemPtr item = m_vecItems->Get(itemNumber);
1830 Update(item->GetPath());
1831 return true;
1833 default:
1834 break;
1836 return false;
1839 const CGUIViewState *CGUIMediaWindow::GetViewState() const
1841 return m_guiState.get();
1844 const CFileItemList& CGUIMediaWindow::CurrentDirectory() const
1846 return *m_vecItems;
1849 bool CGUIMediaWindow::WaitForNetwork() const
1851 if (CServiceBroker::GetNetwork().IsAvailable())
1852 return true;
1854 CGUIDialogProgress *progress = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS);
1855 if (!progress)
1856 return true;
1858 CURL url(m_vecItems->GetPath());
1859 progress->SetHeading(CVariant{1040}); // Loading Directory
1860 progress->SetLine(1, CVariant{url.GetWithoutUserDetails()});
1861 progress->ShowProgressBar(false);
1862 progress->Open();
1863 while (!CServiceBroker::GetNetwork().IsAvailable())
1865 progress->Progress();
1866 if (progress->IsCanceled())
1868 progress->Close();
1869 return false;
1872 progress->Close();
1873 return true;
1876 void CGUIMediaWindow::UpdateFilterPath(const std::string &strDirectory, const CFileItemList &items, bool updateFilterPath)
1878 bool canfilter = CanContainFilter(strDirectory);
1880 std::string filter;
1881 CURL url(strDirectory);
1882 if (canfilter && url.HasOption("filter"))
1883 filter = url.GetOption("filter");
1885 // only set the filter path if it hasn't been marked
1886 // as preset or if it's empty
1887 if (updateFilterPath || m_strFilterPath.empty())
1889 if (items.HasProperty(PROPERTY_PATH_DB))
1890 m_strFilterPath = items.GetProperty(PROPERTY_PATH_DB).asString();
1891 else
1892 m_strFilterPath = items.GetPath();
1895 // maybe the filter path can contain a filter
1896 if (!canfilter && CanContainFilter(m_strFilterPath))
1897 canfilter = true;
1899 // check if the filter path contains a filter
1900 CURL filterPathUrl(m_strFilterPath);
1901 if (canfilter && filter.empty())
1903 if (filterPathUrl.HasOption("filter"))
1904 filter = filterPathUrl.GetOption("filter");
1907 // check if there is a filter and re-apply it
1908 if (canfilter && !filter.empty())
1910 if (!m_filter.LoadFromJson(filter))
1912 CLog::Log(LOGWARNING,
1913 "CGUIMediaWindow::UpdateFilterPath(): unable to load existing filter ({})", filter);
1914 m_filter.Reset();
1915 m_strFilterPath = m_vecItems->GetPath();
1917 else
1919 // add the filter to the filter path
1920 filterPathUrl.SetOption("filter", filter);
1921 m_strFilterPath = filterPathUrl.Get();
1926 void CGUIMediaWindow::OnFilterItems(const std::string &filter)
1928 m_viewControl.Clear();
1930 CFileItemList items;
1931 items.Copy(*m_vecItems, false); // use the original path - it'll likely be relied on for other things later.
1932 items.Append(*m_unfilteredItems);
1933 bool filtered = GetFilteredItems(filter, items);
1935 m_vecItems->ClearItems();
1936 // we need to clear the sort state and re-sort the items
1937 m_vecItems->ClearSortState();
1938 m_vecItems->Append(items);
1940 // if the filter has changed, get the new filter path
1941 if (filtered && m_canFilterAdvanced)
1943 if (items.HasProperty(PROPERTY_PATH_DB))
1944 m_strFilterPath = items.GetProperty(PROPERTY_PATH_DB).asString();
1945 // only set m_strFilterPath if it hasn't been set before
1946 // otherwise we might overwrite it with a non-filter path
1947 // in case GetFilteredItems() returns true even though no
1948 // db-based filter (e.g. watched filter) has been applied
1949 else if (m_strFilterPath.empty())
1950 m_strFilterPath = items.GetPath();
1953 GetGroupedItems(*m_vecItems);
1954 FormatAndSort(*m_vecItems);
1956 CFileItemPtr currentItem;
1957 std::string currentItemPath;
1958 int item = m_viewControl.GetSelectedItem();
1959 if (item >= 0 && item < m_vecItems->Size())
1961 currentItem = m_vecItems->Get(item);
1962 currentItemPath = currentItem->GetPath();
1965 // get the "filter" option
1966 std::string filterOption;
1967 CURL filterUrl(m_strFilterPath);
1968 if (filterUrl.HasOption("filter"))
1969 filterOption = filterUrl.GetOption("filter");
1971 // apply the "filter" option to any folder item so that
1972 // the filter can be passed down to the sub-directory
1973 for (int index = 0; index < m_vecItems->Size(); index++)
1975 CFileItemPtr pItem = m_vecItems->Get(index);
1976 // if the item is a folder we need to copy the path of
1977 // the filtered item to be able to keep the applied filters
1978 if (pItem->m_bIsFolder)
1980 CURL itemUrl(pItem->GetPath());
1981 if (!filterOption.empty())
1982 itemUrl.SetOption("filter", filterOption);
1983 else
1984 itemUrl.RemoveOption("filter");
1985 pItem->SetPath(itemUrl.Get());
1989 SetProperty("filter", filter);
1990 if (filtered && m_canFilterAdvanced)
1992 // to be able to select the same item as before we need to adjust
1993 // the path of the item i.e. add or remove the "filter=" URL option
1994 // but that's only necessary for folder items
1995 if (currentItem.get() && currentItem->m_bIsFolder)
1997 CURL curUrl(currentItemPath), newUrl(m_strFilterPath);
1998 if (newUrl.HasOption("filter"))
1999 curUrl.SetOption("filter", newUrl.GetOption("filter"));
2000 else if (curUrl.HasOption("filter"))
2001 curUrl.RemoveOption("filter");
2003 currentItemPath = curUrl.Get();
2007 // The idea here is to ensure we have something to focus if our file list
2008 // is empty. As such, this check MUST be last and ignore the hide parent
2009 // fileitems settings.
2010 if (m_vecItems->IsEmpty())
2012 CFileItemPtr pItem(new CFileItem(".."));
2013 pItem->SetPath(m_history.GetParentPath());
2014 pItem->m_bIsFolder = true;
2015 pItem->m_bIsShareOrDrive = false;
2016 m_vecItems->AddFront(pItem, 0);
2019 // and update our view control + buttons
2020 m_viewControl.SetItems(*m_vecItems);
2021 m_viewControl.SetSelectedItem(currentItemPath);
2024 bool CGUIMediaWindow::GetFilteredItems(const std::string &filter, CFileItemList &items)
2026 bool result = false;
2027 if (m_canFilterAdvanced)
2028 result = GetAdvanceFilteredItems(items);
2030 std::string trimmedFilter(filter);
2031 StringUtils::TrimLeft(trimmedFilter);
2032 StringUtils::ToLower(trimmedFilter);
2034 if (trimmedFilter.empty())
2035 return result;
2037 CFileItemList filteredItems(items.GetPath()); // use the original path - it'll likely be relied on for other things later.
2038 bool numericMatch = StringUtils::IsNaturalNumber(trimmedFilter);
2039 for (int i = 0; i < items.Size(); i++)
2041 CFileItemPtr item = items.Get(i);
2042 if (item->IsParentFolder())
2044 filteredItems.Add(item);
2045 continue;
2047 //! @todo Need to update this to get all labels, ideally out of the displayed info (ie from m_layout and m_focusedLayout)
2048 //! though that isn't practical. Perhaps a better idea would be to just grab the info that we should filter on based on
2049 //! where we are in the library tree.
2050 //! Another idea is tying the filter string to the current level of the tree, so that going deeper disables the filter,
2051 //! but it's re-enabled on the way back out.
2052 std::string match;
2053 /* if (item->GetFocusedLayout())
2054 match = item->GetFocusedLayout()->GetAllText();
2055 else if (item->GetLayout())
2056 match = item->GetLayout()->GetAllText();
2057 else*/
2058 match = item->GetLabel(); // Filter label only for now
2060 if (numericMatch)
2061 StringUtils::WordToDigits(match);
2063 size_t pos = StringUtils::FindWords(match.c_str(), trimmedFilter.c_str());
2064 if (pos != std::string::npos)
2065 filteredItems.Add(item);
2068 items.ClearItems();
2069 items.Append(filteredItems);
2071 return items.GetObjectCount() > 0;
2074 bool CGUIMediaWindow::GetAdvanceFilteredItems(CFileItemList &items)
2076 // don't run the advanced filter if the filter is empty
2077 // and there hasn't been a filter applied before which
2078 // would have to be removed
2079 CURL url(m_strFilterPath);
2080 if (m_filter.IsEmpty() && !url.HasOption("filter"))
2081 return false;
2083 CFileItemList resultItems;
2084 XFILE::CSmartPlaylistDirectory::GetDirectory(m_filter, resultItems, m_strFilterPath, true);
2086 // put together a lookup map for faster path comparison
2087 std::map<std::string, CFileItemPtr> lookup;
2088 for (int j = 0; j < resultItems.Size(); j++)
2090 std::string itemPath = CURL(resultItems[j]->GetPath()).GetWithoutOptions();
2091 StringUtils::ToLower(itemPath);
2093 lookup[itemPath] = resultItems[j];
2096 // loop through all the original items and find
2097 // those which are still part of the filter
2098 CFileItemList filteredItems;
2099 for (int i = 0; i < items.Size(); i++)
2101 CFileItemPtr item = items.Get(i);
2102 if (item->IsParentFolder())
2104 filteredItems.Add(item);
2105 continue;
2108 // check if the item is part of the resultItems list
2109 // by comparing their paths (but ignoring any special
2110 // options because they differ from filter to filter)
2111 std::string path = CURL(item->GetPath()).GetWithoutOptions();
2112 StringUtils::ToLower(path);
2114 std::map<std::string, CFileItemPtr>::iterator itItem = lookup.find(path);
2115 if (itItem != lookup.end())
2117 // add the item to the list of filtered items
2118 filteredItems.Add(item);
2120 // remove the item from the lists
2121 resultItems.Remove(itItem->second.get());
2122 lookup.erase(itItem);
2126 if (resultItems.Size() > 0)
2127 CLog::Log(LOGWARNING, "CGUIMediaWindow::GetAdvanceFilteredItems(): {} unknown items",
2128 resultItems.Size());
2130 items.ClearItems();
2131 items.Append(filteredItems);
2132 items.SetPath(resultItems.GetPath());
2133 if (resultItems.HasProperty(PROPERTY_PATH_DB))
2134 items.SetProperty(PROPERTY_PATH_DB, resultItems.GetProperty(PROPERTY_PATH_DB));
2135 return true;
2138 bool CGUIMediaWindow::IsFiltered()
2140 return (!m_canFilterAdvanced && !GetProperty("filter").empty()) ||
2141 (m_canFilterAdvanced && !m_filter.IsEmpty());
2144 bool CGUIMediaWindow::IsSameStartFolder(const std::string &dir)
2146 const std::string startFolder = GetStartFolder(dir);
2147 return URIUtils::PathHasParent(m_vecItems->GetPath(), startFolder);
2150 bool CGUIMediaWindow::Filter(bool advanced /* = true */)
2152 // basic filtering
2153 if (!m_canFilterAdvanced || !advanced)
2155 const CGUIControl *btnFilter = GetControl(CONTROL_BTN_FILTER);
2156 if (btnFilter && btnFilter->GetControlType() == CGUIControl::GUICONTROL_EDIT)
2157 { // filter updated
2158 CGUIMessage selected(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTN_FILTER);
2159 OnMessage(selected);
2160 OnFilterItems(selected.GetLabel());
2161 UpdateButtons();
2162 return true;
2164 if (GetProperty("filter").empty())
2166 std::string filter = GetProperty("filter").asString();
2167 CGUIKeyboardFactory::ShowAndGetFilter(filter, false);
2168 SetProperty("filter", filter);
2170 else
2172 OnFilterItems("");
2173 UpdateButtons();
2176 // advanced filtering
2177 else
2178 CGUIDialogMediaFilter::ShowAndEditMediaFilter(m_strFilterPath, m_filter);
2180 return true;
2183 std::string CGUIMediaWindow::GetStartFolder(const std::string &dir)
2185 if (StringUtils::EqualsNoCase(dir, "$root") ||
2186 StringUtils::EqualsNoCase(dir, "root"))
2187 return "";
2189 // Let plugins handle their own urls themselves
2190 if (StringUtils::StartsWith(dir, "plugin://"))
2191 return dir;
2193 //! @todo This ifdef block probably belongs somewhere else. Move it to a better place!
2194 #if defined(TARGET_ANDROID)
2195 // Hack for Android items (numbered id's) on the leanback screen
2196 std::string path;
2197 std::string fileName;
2198 URIUtils::Split(dir, path, fileName);
2199 URIUtils::RemoveExtension(fileName);
2200 if (StringUtils::IsInteger(fileName))
2201 return path;
2202 #endif
2204 return dir;
2207 std::string CGUIMediaWindow::RemoveParameterFromPath(const std::string &strDirectory, const std::string &strParameter)
2209 CURL url(strDirectory);
2210 if (url.HasOption(strParameter))
2212 url.RemoveOption(strParameter);
2213 return url.Get();
2216 return strDirectory;
2219 bool CGUIMediaWindow::ProcessRenderLoop(bool renderOnly)
2221 return CServiceBroker::GetGUI()->GetWindowManager().ProcessRenderLoop(renderOnly);
2224 bool CGUIMediaWindow::GetDirectoryItems(CURL &url, CFileItemList &items, bool useDir)
2226 if (m_backgroundLoad)
2228 bool ret = true;
2229 CGetDirectoryItems getItems(m_rootDir, url, items, useDir);
2231 if (!WaitGetDirectoryItems(getItems))
2233 // cancelled
2234 ret = false;
2236 else if (!getItems.m_result)
2238 if (CServiceBroker::GetAppMessenger()->IsProcessThread() && m_rootDir.GetDirImpl() &&
2239 !m_rootDir.GetDirImpl()->ProcessRequirements())
2241 ret = false;
2243 else if (!WaitGetDirectoryItems(getItems) || !getItems.m_result)
2245 ret = false;
2249 m_updateJobActive = false;
2250 m_rootDir.ReleaseDirImpl();
2251 return ret;
2253 else
2255 return m_rootDir.GetDirectory(url, items, useDir, false);
2259 bool CGUIMediaWindow::WaitGetDirectoryItems(CGetDirectoryItems &items)
2261 bool ret = true;
2262 CGUIDialogBusy* dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogBusy>(WINDOW_DIALOG_BUSY);
2263 if (dialog && !dialog->IsDialogRunning())
2265 if (!CGUIDialogBusy::Wait(&items, 100, true))
2267 // cancelled
2268 ret = false;
2271 else
2273 m_updateJobActive = true;
2274 m_updateEvent.Reset();
2275 CServiceBroker::GetJobManager()->Submit(
2276 [&]() {
2277 items.Run();
2278 m_updateEvent.Set();
2280 nullptr, CJob::PRIORITY_NORMAL);
2282 while (!m_updateEvent.Wait(1ms))
2284 if (!ProcessRenderLoop(false))
2285 break;
2288 if (m_updateAborted || !items.m_result)
2290 ret = false;
2293 return ret;
2296 void CGUIMediaWindow::CancelUpdateItems()
2298 if (m_updateJobActive)
2300 m_rootDir.CancelDirectory();
2301 m_updateAborted = true;
2302 if (!m_updateEvent.Wait(5000ms))
2304 CLog::Log(LOGERROR, "CGUIMediaWindow::CancelUpdateItems - error cancel update");
2306 m_updateJobActive = false;