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.
9 #include "GUIMediaWindow.h"
11 #include "ContextMenuManager.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"
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"
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 "music/MusicFileItemClassify.h"
51 #include "music/tags/MusicInfoTag.h"
52 #include "network/Network.h"
53 #include "playlists/PlayList.h"
54 #include "profiles/ProfileManager.h"
55 #include "settings/AdvancedSettings.h"
56 #include "settings/Settings.h"
57 #include "settings/SettingsComponent.h"
58 #include "storage/MediaManager.h"
59 #include "threads/IRunnable.h"
60 #include "utils/FileUtils.h"
61 #include "utils/LabelFormatter.h"
62 #include "utils/SortUtils.h"
63 #include "utils/StringUtils.h"
64 #include "utils/URIUtils.h"
65 #include "utils/Variant.h"
66 #include "utils/log.h"
67 #include "view/GUIViewState.h"
69 #define CONTROL_BTNVIEWASICONS 2
70 #define CONTROL_BTNSORTBY 3
71 #define CONTROL_BTNSORTASC 4
72 #define CONTROL_BTN_FILTER 19
74 #define CONTROL_LABELFILES 12
76 #define PROPERTY_PATH_DB "path.db"
77 #define PROPERTY_SORT_ORDER "sort.order"
78 #define PROPERTY_SORT_ASCENDING "sort.ascending"
80 #define PLUGIN_REFRESH_DELAY 200
82 using namespace ADDON
;
84 using namespace KODI::MESSAGING
;
85 using namespace std::chrono_literals
;
89 class CGetDirectoryItems
: public IRunnable
92 CGetDirectoryItems(XFILE::CVirtualDirectory
&dir
, CURL
&url
, CFileItemList
&items
, bool useDir
)
93 : m_dir(dir
), m_url(url
), m_items(items
), m_useDir(useDir
)
99 m_result
= m_dir
.GetDirectory(m_url
, m_items
, m_useDir
, true);
102 void Cancel() override
104 m_dir
.CancelDirectory();
107 bool m_result
= false;
110 XFILE::CVirtualDirectory
&m_dir
;
112 CFileItemList
&m_items
;
117 CGUIMediaWindow::CGUIMediaWindow(int id
, const char *xmlFile
)
118 : CGUIWindow(id
, xmlFile
)
120 m_loadType
= KEEP_IN_MEMORY
;
121 m_vecItems
= new CFileItemList
;
122 m_unfilteredItems
= new CFileItemList
;
123 m_vecItems
->SetPath("?");
125 m_canFilterAdvanced
= false;
127 m_guiState
.reset(CGUIViewState::GetViewState(GetID(), *m_vecItems
));
130 CGUIMediaWindow::~CGUIMediaWindow()
133 delete m_unfilteredItems
;
136 bool CGUIMediaWindow::Load(TiXmlElement
*pRootElement
)
138 bool retVal
= CGUIWindow::Load(pRootElement
);
143 // configure our view control
144 m_viewControl
.Reset();
145 m_viewControl
.SetParentWindow(GetID());
146 TiXmlElement
*element
= pRootElement
->FirstChildElement("views");
147 if (element
&& element
->FirstChild())
148 { // format is <views>50,29,51,95</views>
149 const std::string
&allViews
= element
->FirstChild()->ValueStr();
150 std::vector
<std::string
> views
= StringUtils::Split(allViews
, ",");
151 for (std::vector
<std::string
>::const_iterator i
= views
.begin(); i
!= views
.end(); ++i
)
153 int controlID
= atol(i
->c_str());
154 CGUIControl
*control
= GetControl(controlID
);
155 if (control
&& control
->IsContainer())
156 m_viewControl
.AddView(control
);
159 m_viewControl
.SetViewControlID(CONTROL_BTNVIEWASICONS
);
164 void CGUIMediaWindow::OnWindowLoaded()
166 SendMessage(GUI_MSG_SET_TYPE
, CONTROL_BTN_FILTER
, CGUIEditControl::INPUT_TYPE_FILTER
);
167 CGUIWindow::OnWindowLoaded();
171 void CGUIMediaWindow::OnWindowUnload()
173 CGUIWindow::OnWindowUnload();
174 m_viewControl
.Reset();
177 CFileItemPtr
CGUIMediaWindow::GetCurrentListItem(int offset
)
179 int item
= m_viewControl
.GetSelectedItem();
180 if (!m_vecItems
->Size() || item
< 0)
181 return CFileItemPtr();
182 item
= (item
+ offset
) % m_vecItems
->Size();
183 if (item
< 0) item
+= m_vecItems
->Size();
184 return m_vecItems
->Get(item
);
187 bool CGUIMediaWindow::OnAction(const CAction
&action
)
189 if (action
.GetID() == ACTION_PARENT_DIR
)
195 if (CGUIWindow::OnAction(action
))
198 if (action
.GetID() == ACTION_FILTER
)
202 if (action
.GetID() == ACTION_FILTER_CLEAR
)
204 CGUIMessage
message(GUI_MSG_NOTIFY_ALL
, GetID(), 0, GUI_MSG_FILTER_ITEMS
);
205 message
.SetStringParam("");
210 if (action
.GetID() == ACTION_BACKSPACE
)
212 CGUIMessage
message(GUI_MSG_NOTIFY_ALL
, GetID(), 0, GUI_MSG_FILTER_ITEMS
, 2); // 2 for delete
217 if (action
.GetID() >= ACTION_FILTER_SMS2
&& action
.GetID() <= ACTION_FILTER_SMS9
)
219 std::string filter
= std::to_string(action
.GetID() - ACTION_FILTER_SMS2
+ 2);
220 CGUIMessage
message(GUI_MSG_NOTIFY_ALL
, GetID(), 0, GUI_MSG_FILTER_ITEMS
, 1); // 1 for append
221 message
.SetStringParam(filter
);
229 bool CGUIMediaWindow::OnBack(int actionID
)
233 CURL
filterUrl(m_strFilterPath
);
234 if (actionID
== ACTION_NAV_BACK
&&
235 !m_vecItems
->IsVirtualDirectoryRoot() &&
236 !URIUtils::PathEquals(m_vecItems
->GetPath(), GetRootPath(), true) &&
237 (!URIUtils::PathEquals(m_vecItems
->GetPath(), m_startDirectory
, true) || (m_canFilterAdvanced
&& filterUrl
.HasOption("filter"))))
239 if (GoParentFolder())
242 return CGUIWindow::OnBack(actionID
);
245 bool CGUIMediaWindow::OnMessage(CGUIMessage
& message
)
247 switch ( message
.GetMessage() )
249 case GUI_MSG_WINDOW_DEINIT
:
253 m_iLastControl
= GetFocusedControlID();
254 CGUIWindow::OnMessage(message
);
256 // get rid of any active filtering
257 if (m_canFilterAdvanced
)
259 m_canFilterAdvanced
= false;
262 m_strFilterPath
.clear();
264 // Call ClearFileItems() after our window has finished doing any WindowClose
271 case GUI_MSG_CLICKED
:
273 int iControl
= message
.GetSenderId();
274 if (iControl
== CONTROL_BTNVIEWASICONS
)
276 // view as control could be a select button
278 const CGUIControl
*control
= GetControl(CONTROL_BTNVIEWASICONS
);
279 if (control
&& control
->GetControlType() != CGUIControl::GUICONTROL_BUTTON
)
281 CGUIMessage
msg(GUI_MSG_ITEM_SELECTED
, GetID(), CONTROL_BTNVIEWASICONS
);
283 viewMode
= m_viewControl
.GetViewModeNumber(msg
.GetParam1());
286 viewMode
= m_viewControl
.GetNextViewMode();
289 m_guiState
->SaveViewAsControl(viewMode
);
294 else if (iControl
== CONTROL_BTNSORTASC
) // sort asc
297 m_guiState
->SetNextSortOrder();
301 else if (iControl
== CONTROL_BTNSORTBY
) // sort by
303 if (m_guiState
.get() && m_guiState
->ChooseSortMethod())
307 else if (iControl
== CONTROL_BTN_FILTER
)
308 return Filter(false);
309 else if (m_viewControl
.HasControl(iControl
)) // list/thumb control
311 int iItem
= m_viewControl
.GetSelectedItem();
312 int iAction
= message
.GetParam1();
313 if (iItem
< 0) break;
314 if (iAction
== ACTION_SELECT_ITEM
|| iAction
== ACTION_MOUSE_LEFT_CLICK
)
318 else if (iAction
== ACTION_CONTEXT_MENU
|| iAction
== ACTION_MOUSE_RIGHT_CLICK
)
327 case GUI_MSG_SETFOCUS
:
329 if (m_viewControl
.HasControl(message
.GetControlId()) && m_viewControl
.GetCurrentControl() != message
.GetControlId())
331 m_viewControl
.SetFocused();
337 case GUI_MSG_NOTIFY_ALL
:
338 { // Message is received even if this window is inactive
339 if (message
.GetParam1() == GUI_MSG_WINDOW_RESET
)
341 m_vecItems
->SetPath("?");
344 else if ( message
.GetParam1() == GUI_MSG_REFRESH_THUMBS
)
346 for (int i
= 0; i
< m_vecItems
->Size(); i
++)
347 m_vecItems
->Get(i
)->FreeMemory(true);
348 break; // the window will take care of any info images
350 else if (message
.GetParam1() == GUI_MSG_REMOVED_MEDIA
)
352 if ((m_vecItems
->IsVirtualDirectoryRoot() ||
353 m_vecItems
->IsSourcesPath()) && IsActive())
355 int iItem
= m_viewControl
.GetSelectedItem();
357 m_viewControl
.SetSelectedItem(iItem
);
359 else if (m_vecItems
->IsRemovable())
360 { // check that we have this removable share still
361 if (!m_rootDir
.IsInSource(m_vecItems
->GetPath()))
362 { // don't have this share any more
363 if (IsActive()) Update("");
366 m_history
.ClearPathHistory();
367 m_vecItems
->SetPath("");
374 else if (message
.GetParam1()==GUI_MSG_UPDATE_SOURCES
)
375 { // State of the sources changed, so update our view
376 if ((m_vecItems
->IsVirtualDirectoryRoot() ||
377 m_vecItems
->IsSourcesPath()) && IsActive())
379 if (m_vecItemsUpdating
)
381 CLog::Log(LOGWARNING
, "CGUIMediaWindow::OnMessage - updating in progress");
384 CUpdateGuard
ug(m_vecItemsUpdating
);
385 int iItem
= m_viewControl
.GetSelectedItem();
387 m_viewControl
.SetSelectedItem(iItem
);
391 else if (message
.GetParam1()==GUI_MSG_UPDATE
&& IsActive())
393 if (m_vecItemsUpdating
)
395 CLog::Log(LOGWARNING
, "CGUIMediaWindow::OnMessage - updating in progress");
398 CUpdateGuard
ug(m_vecItemsUpdating
);
399 if (message
.GetNumStringParams())
401 if (message
.GetParam2()) // param2 is used for resetting the history
402 SetHistoryForPath(message
.GetStringParam());
404 CFileItemList
list(message
.GetStringParam());
405 list
.RemoveDiscCache(GetID());
406 Update(message
.GetStringParam());
409 Refresh(true); // refresh the listing
411 else if (message
.GetParam1()==GUI_MSG_UPDATE_ITEM
&& message
.GetItem())
413 int flag
= message
.GetParam2();
414 CFileItemPtr newItem
= std::static_pointer_cast
<CFileItem
>(message
.GetItem());
416 if (IsActive() || (flag
& GUI_MSG_FLAG_FORCE_UPDATE
))
418 m_vecItems
->UpdateItem(newItem
.get());
420 if (flag
& GUI_MSG_FLAG_UPDATE_LIST
)
421 { // need the list updated as well
426 { // need to remove the disc cache
428 items
.SetPath(URIUtils::GetDirectory(newItem
->GetPath()));
429 if (newItem
->HasProperty("cachefilename"))
431 // Use stored cache file name
432 std::string crcfile
= newItem
->GetProperty("cachefilename").asString();
433 items
.RemoveDiscCacheCRC(crcfile
);
436 // No stored cache file name, attempt using truncated item path as list path
437 items
.RemoveDiscCache(GetID());
440 else if (message
.GetParam1()==GUI_MSG_UPDATE_PATH
)
444 if((message
.GetStringParam() == m_vecItems
->GetPath()) ||
445 (m_vecItems
->IsMultiPath() && XFILE::CMultiPathDirectory::HasPath(m_vecItems
->GetPath(), message
.GetStringParam())))
449 else if (message
.GetParam1() == GUI_MSG_FILTER_ITEMS
&& IsActive())
451 std::string filter
= GetProperty("filter").asString();
452 // check if this is meant for advanced filtering
453 if (message
.GetParam2() != 10)
455 if (message
.GetParam2() == 1) // append
456 filter
+= message
.GetStringParam();
457 else if (message
.GetParam2() == 2)
460 filter
.erase(filter
.size() - 1);
463 filter
= message
.GetStringParam();
465 OnFilterItems(filter
);
470 return CGUIWindow::OnMessage(message
);
475 case GUI_MSG_PLAYBACK_STARTED
:
476 case GUI_MSG_PLAYBACK_ENDED
:
477 case GUI_MSG_PLAYBACK_STOPPED
:
478 case GUI_MSG_PLAYLIST_CHANGED
:
479 case GUI_MSG_PLAYLISTPLAYER_STOPPED
:
480 case GUI_MSG_PLAYLISTPLAYER_STARTED
:
481 case GUI_MSG_PLAYLISTPLAYER_CHANGED
:
482 { // send a notify all to all controls on this window
483 CGUIMessage
msg(GUI_MSG_NOTIFY_ALL
, GetID(), 0, GUI_MSG_REFRESH_LIST
);
487 case GUI_MSG_CHANGE_VIEW_MODE
:
490 if (message
.GetParam1()) // we have an id
491 viewMode
= m_viewControl
.GetViewModeByID(message
.GetParam1());
492 else if (message
.GetParam2())
493 viewMode
= m_viewControl
.GetNextViewMode(message
.GetParam2());
496 m_guiState
->SaveViewAsControl(viewMode
);
501 case GUI_MSG_CHANGE_SORT_METHOD
:
505 if (message
.GetParam1())
506 m_guiState
->SetCurrentSortMethod(message
.GetParam1());
507 else if (message
.GetParam2())
508 m_guiState
->SetNextSortMethod(message
.GetParam2());
514 case GUI_MSG_CHANGE_SORT_DIRECTION
:
517 m_guiState
->SetNextSortOrder();
522 case GUI_MSG_WINDOW_INIT
:
524 if (m_vecItems
->GetPath() == "?")
525 m_vecItems
->SetPath("");
527 std::string dir
= message
.GetStringParam(0);
528 const std::string
& ret
= message
.GetStringParam(1);
529 const std::string
& swap
= message
.GetStringParam(message
.GetNumStringParams() - 1);
530 const bool returning
= StringUtils::EqualsNoCase(ret
, "return");
531 const bool replacing
= StringUtils::EqualsNoCase(swap
, "replace");
535 // ensure our directory is valid
536 dir
= GetStartFolder(dir
);
537 bool resetHistory
= false;
538 if (!returning
|| !URIUtils::PathEquals(dir
, m_startDirectory
, true))
539 { // we're not returning to the same path, so set our directory to the requested path
540 m_vecItems
->SetPath(dir
);
543 else if (m_vecItems
->GetPath().empty() && URIUtils::PathEquals(dir
, m_startDirectory
, true))
544 m_vecItems
->SetPath(dir
);
546 // check for network up
547 if (URIUtils::IsRemote(m_vecItems
->GetPath()) && !WaitForNetwork())
549 m_vecItems
->SetPath("");
554 m_vecItems
->RemoveDiscCache(GetID());
555 // only compute the history for the provided path if "return" is not defined
556 // (otherwise the root level for the path will be added by default to the path history
557 // and we won't be able to move back to the path we came from)
559 SetHistoryForPath(m_vecItems
->GetPath());
562 if (message
.GetParam1() != WINDOW_INVALID
)
564 // if this is the first time to this window - make sure we set the root path
565 // if "return" is defined make sure we set the startDirectory to the directory we are
566 // moving to (so that we can move back to where we were onBack). If we are activating
567 // the same window but with a different path, do nothing - we are simply adding to the
568 // window history. Note that if the window is just being replaced, the start directory
569 // also needs to be set as the manager has just popped the previous window.
570 if (message
.GetParam1() != message
.GetParam2() || replacing
)
571 m_startDirectory
= returning
? dir
: GetRootPath();
573 if (message
.GetParam2() == PLUGIN_REFRESH_DELAY
)
576 SetInitialVisibility();
577 RestoreControlStates();
578 SetInitialVisibility();
585 return CGUIWindow::OnMessage(message
);
589 * \brief Updates the states
591 * This updates the states (enable, disable, visible...) of the controls defined
594 * \note Override this function in a derived class to add new controls
596 void CGUIMediaWindow::UpdateButtons()
600 // Update sorting controls
601 if (m_guiState
->GetSortOrder() == SortOrderNone
)
603 CONTROL_DISABLE(CONTROL_BTNSORTASC
);
607 CONTROL_ENABLE(CONTROL_BTNSORTASC
);
608 SET_CONTROL_SELECTED(GetID(), CONTROL_BTNSORTASC
, m_guiState
->GetSortOrder() != SortOrderAscending
);
611 // Update list/thumb control
612 m_viewControl
.SetCurrentView(m_guiState
->GetViewAsControl());
614 // Update sort by button
615 if (!m_guiState
->HasMultipleSortMethods())
616 CONTROL_DISABLE(CONTROL_BTNSORTBY
);
618 CONTROL_ENABLE(CONTROL_BTNSORTBY
);
620 std::string sortLabel
= StringUtils::Format(
621 g_localizeStrings
.Get(550), g_localizeStrings
.Get(m_guiState
->GetSortMethodLabel()));
622 SET_CONTROL_LABEL(CONTROL_BTNSORTBY
, sortLabel
);
626 StringUtils::Format("{} {}", m_vecItems
->GetObjectCount(), g_localizeStrings
.Get(127));
627 SET_CONTROL_LABEL(CONTROL_LABELFILES
, items
);
629 SET_CONTROL_LABEL2(CONTROL_BTN_FILTER
, GetProperty("filter").asString());
632 void CGUIMediaWindow::ClearFileItems()
634 m_viewControl
.Clear();
636 m_unfilteredItems
->Clear();
640 * \brief Sort file items
642 * This sorts file items based on the sort method and sort order provided by
645 void CGUIMediaWindow::SortItems(CFileItemList
&items
)
647 std::unique_ptr
<CGUIViewState
> guiState(CGUIViewState::GetViewState(GetID(), items
));
651 SortDescription sorting
= guiState
->GetSortMethod();
652 sorting
.sortOrder
= guiState
->GetSortOrder();
653 // If the sort method is "sort by playlist" and we have a specific
654 // sort order available we can use the specified sort order to do the sorting
655 // We do this as the new SortBy methods are a superset of the SORT_METHOD methods, thus
656 // not all are available. This may be removed once SORT_METHOD_* have been replaced by
658 if ((sorting
.sortBy
== SortByPlaylistOrder
) && items
.HasProperty(PROPERTY_SORT_ORDER
))
660 SortBy sortBy
= (SortBy
)items
.GetProperty(PROPERTY_SORT_ORDER
).asInteger();
661 if (sortBy
!= SortByNone
&& sortBy
!= SortByPlaylistOrder
&& sortBy
!= SortByProgramCount
)
663 sorting
.sortBy
= sortBy
;
664 sorting
.sortOrder
= items
.GetProperty(PROPERTY_SORT_ASCENDING
).asBoolean() ? SortOrderAscending
: SortOrderDescending
;
665 sorting
.sortAttributes
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING
) ? SortAttributeIgnoreArticle
: SortAttributeNone
;
667 // if the sort order is descending, we need to switch the original sort order, as we assume
668 // in CGUIViewState::AddPlaylistOrder that SortByPlaylistOrder is ascending.
669 if (guiState
->GetSortOrder() == SortOrderDescending
)
670 sorting
.sortOrder
= sorting
.sortOrder
== SortOrderDescending
? SortOrderAscending
: SortOrderDescending
;
679 * \brief Formats item labels
681 * This is based on the formatting provided by guiViewState.
683 void CGUIMediaWindow::FormatItemLabels(CFileItemList
&items
, const LABEL_MASKS
&labelMasks
)
685 CLabelFormatter
fileFormatter(labelMasks
.m_strLabelFile
, labelMasks
.m_strLabel2File
);
686 CLabelFormatter
folderFormatter(labelMasks
.m_strLabelFolder
, labelMasks
.m_strLabel2Folder
);
687 for (int i
=0; i
<items
.Size(); ++i
)
689 CFileItemPtr pItem
=items
[i
];
691 if (pItem
->IsLabelPreformatted())
694 if (pItem
->m_bIsFolder
)
695 folderFormatter
.FormatLabels(pItem
.get());
697 fileFormatter
.FormatLabels(pItem
.get());
700 if (items
.GetSortMethod() == SortByLabel
)
701 items
.ClearSortState();
705 * \brief Format and sort file items
707 * Prepares and adds the fileitems to list/thumb panel
709 void CGUIMediaWindow::FormatAndSort(CFileItemList
&items
)
711 std::unique_ptr
<CGUIViewState
> viewState(CGUIViewState::GetViewState(GetID(), items
));
715 LABEL_MASKS labelMasks
;
716 viewState
->GetSortMethodLabelMasks(labelMasks
);
717 FormatItemLabels(items
, labelMasks
);
719 items
.Sort(viewState
->GetSortMethod().sortBy
, viewState
->GetSortOrder(), viewState
->GetSortMethod().sortAttributes
);
724 * \brief Overwrite to fill fileitems from a source
726 * \param[in] strDirectory Path to read
727 * \param[out] items Fill with items specified in \e strDirectory
728 * \return false if given directory not present
730 bool CGUIMediaWindow::GetDirectory(const std::string
&strDirectory
, CFileItemList
&items
)
732 CURL
pathToUrl(strDirectory
);
734 std::string strParentPath
= m_history
.GetParentPath();
736 CLog::Log(LOGDEBUG
, "CGUIMediaWindow::GetDirectory ({})", CURL::GetRedacted(strDirectory
));
737 CLog::Log(LOGDEBUG
, " ParentPath = [{}]", CURL::GetRedacted(strParentPath
));
739 if (pathToUrl
.IsProtocol("plugin") && !pathToUrl
.GetHostName().empty())
740 CServiceBroker::GetAddonMgr().UpdateLastUsed(pathToUrl
.GetHostName());
742 // see if we can load a previously cached folder
743 CFileItemList
cachedItems(strDirectory
);
744 if (!strDirectory
.empty() && cachedItems
.Load(GetID()))
746 items
.Assign(cachedItems
);
750 auto start
= std::chrono::steady_clock::now();
752 if (strDirectory
.empty())
755 CFileItemList dirItems
;
756 if (!GetDirectoryItems(pathToUrl
, dirItems
, UseFileDirectories()))
759 // assign fetched directory items
760 items
.Assign(dirItems
);
762 // took over a second, and not normally cached, so cache it
763 auto end
= std::chrono::steady_clock::now();
764 auto duration
= std::chrono::duration_cast
<std::chrono::milliseconds
>(end
- start
);
766 if (duration
.count() > 1000 && items
.CacheToDiscIfSlow())
769 // if these items should replace the current listing, then pop it off the top
770 if (items
.GetReplaceListing())
771 m_history
.RemoveParentPath();
774 // Store parent path along with item as parent path cannot safely be calculated from item's path.
775 for (const auto& item
: items
)
777 item
->SetProperty("ParentPath", m_vecItems
->GetPath());
780 // update the view state's reference to the current items
781 m_guiState
.reset(CGUIViewState::GetViewState(GetID(), items
));
783 bool bHideParent
= false;
785 if (m_guiState
&& m_guiState
->HideParentDirItems())
787 if (items
.GetPath() == GetRootPath())
792 CFileItemPtr
pItem(new CFileItem(".."));
793 pItem
->SetPath(strParentPath
);
794 pItem
->m_bIsFolder
= true;
795 pItem
->m_bIsShareOrDrive
= false;
796 items
.AddFront(pItem
, 0);
799 int iWindow
= GetID();
800 std::vector
<std::string
> regexps
;
802 //! @todo Do we want to limit the directories we apply the video ones to?
803 if (iWindow
== WINDOW_VIDEO_NAV
)
804 regexps
= CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoExcludeFromListingRegExps
;
805 if (iWindow
== WINDOW_MUSIC_NAV
)
806 regexps
= CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_audioExcludeFromListingRegExps
;
807 if (iWindow
== WINDOW_PICTURES
)
808 regexps
= CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_pictureExcludeFromListingRegExps
;
812 for (int i
=0; i
< items
.Size();)
814 if (CUtil::ExcludeFileOrFolder(items
[i
]->GetPath(), regexps
))
822 SetProperty("filter", "");
823 m_canFilterAdvanced
= false;
828 bool CGUIMediaWindow::Update(const std::string
&strDirectory
, bool updateFilterPath
/* = true */)
830 //! @todo OnInitWindow calls Update() before window path has been set properly.
831 if (strDirectory
== "?")
834 // The path to load. Empty string is used in various places to denote root, so translate to the
835 // real root path first
836 const std::string path
= strDirectory
.empty() ? GetRootPath() : strDirectory
;
838 // stores the selected item in history
839 SaveSelectedItemInHistory();
841 const std::string previousPath
= m_vecItems
->GetPath();
843 // check if the path contains a filter and temporarily remove it
844 // so that the retrieved list of items is unfiltered
845 std::string pathNoFilter
= path
;
846 if (CanContainFilter(pathNoFilter
) && CURL(pathNoFilter
).HasOption("filter"))
847 pathNoFilter
= RemoveParameterFromPath(pathNoFilter
, "filter");
849 if (!GetDirectory(pathNoFilter
, *m_vecItems
))
851 CLog::Log(LOGERROR
, "CGUIMediaWindow::GetDirectory({}) failed", CURL(path
).GetRedacted());
853 if (URIUtils::PathEquals(path
, GetRootPath()))
854 return false; // Nothing to fallback to
856 // Try to return to the previous directory, if not the same
857 // else fallback to root
858 if (URIUtils::PathEquals(path
, previousPath
) || !Update(m_history
.RemoveParentPath()))
859 Update(""); // Fallback to root
861 // Return false to be able to eg. show
866 if (m_vecItems
->GetLabel().empty())
869 VECSOURCES removables
;
870 CServiceBroker::GetMediaManager().GetRemovableDrives(removables
);
871 for (const auto& s
: removables
)
873 if (URIUtils::CompareWithoutSlashAtEnd(s
.strPath
, m_vecItems
->GetPath()))
875 m_vecItems
->SetLabel(s
.strName
);
881 if (m_vecItems
->GetLabel().empty())
882 m_vecItems
->SetLabel(CUtil::GetTitleFromPath(m_vecItems
->GetPath(), true));
884 // check the given path for filter data
885 UpdateFilterPath(path
, *m_vecItems
, updateFilterPath
);
887 // if we're getting the root source listing
888 // make sure the path history is clean
889 if (URIUtils::PathEquals(path
, GetRootPath()))
890 m_history
.ClearPathHistory();
892 int iWindow
= GetID();
894 if (URIUtils::PathEquals(path
, GetRootPath()))
896 if (iWindow
== WINDOW_PICTURES
)
898 else if (iWindow
== WINDOW_FILES
)
900 else if (iWindow
== WINDOW_GAMES
)
901 showLabel
= 35250; // "Add games..."
903 if (m_vecItems
->IsPath("sources://video/"))
905 else if (m_vecItems
->IsPath("sources://music/"))
907 else if (m_vecItems
->IsPath("sources://pictures/"))
909 else if (m_vecItems
->IsPath("sources://files/"))
911 else if (m_vecItems
->IsPath("sources://games/"))
912 showLabel
= 35250; // "Add games..."
913 // Add 'Add source ' item
914 if (showLabel
&& (m_vecItems
->Size() == 0 || !m_guiState
->DisableAddSourceButtons()) &&
915 iWindow
!= WINDOW_MUSIC_PLAYLIST_EDITOR
)
917 const std::string
& strLabel
= g_localizeStrings
.Get(showLabel
);
918 CFileItemPtr
pItem(new CFileItem(strLabel
));
919 pItem
->SetPath("add");
920 pItem
->SetArt("icon", "DefaultAddSource.png");
921 pItem
->SetLabel(strLabel
);
922 pItem
->SetLabelPreformatted(true);
923 pItem
->m_bIsFolder
= true;
924 pItem
->SetSpecialSort(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_addSourceOnTop
?
925 SortSpecialOnTop
: SortSpecialOnBottom
);
926 m_vecItems
->Add(pItem
);
928 m_iLastControl
= GetFocusedControlID();
930 // Check whether to enabled advanced filtering based on the content type
931 m_canFilterAdvanced
= CheckFilterAdvanced(*m_vecItems
);
932 if (m_canFilterAdvanced
)
933 m_filter
.SetType(m_vecItems
->GetContent());
935 // Ask the derived class if it wants to load additional info
936 // for the fileitems like media info or additional
937 // filtering on the items, setting thumbs.
938 OnPrepareFileItems(*m_vecItems
);
940 m_vecItems
->FillInDefaultIcons();
942 // remember the original (untouched) list of items (for filtering etc)
943 m_unfilteredItems
->Assign(*m_vecItems
);
945 // Cache the list of items if possible
946 OnCacheFileItems(*m_vecItems
);
948 // Filter and group the items if necessary
949 OnFilterItems(GetProperty("filter").asString());
952 // Restore selected item from history
953 RestoreSelectedItemFromHistory();
955 m_history
.AddPath(m_vecItems
->GetPath(), m_strFilterPath
);
957 //m_history.DumpPathHistory();
962 bool CGUIMediaWindow::Refresh(bool clearCache
/* = false */)
964 std::string strCurrentDirectory
= m_vecItems
->GetPath();
965 if (strCurrentDirectory
== "?")
969 m_vecItems
->RemoveDiscCache(GetID());
973 // get the original number of items
974 if (!Update(strCurrentDirectory
, false))
983 * \brief On prepare file items
985 * This function will be called by Update() before the labels of the fileitems
988 * \note Override this function to set custom thumbs or load additional media
991 * It's used to load tag info for music.
993 void CGUIMediaWindow::OnPrepareFileItems(CFileItemList
&items
)
995 CFileItemListModification::GetInstance().Modify(items
);
999 * \brief On cache file items
1001 * This function will be called by Update() before
1002 * any additional formatting, filtering or sorting is applied.
1004 * \note Override this function to define a custom caching behaviour.
1006 void CGUIMediaWindow::OnCacheFileItems(CFileItemList
&items
)
1008 // Should these items be saved to the hdd
1009 if (items
.CacheToDiscAlways() && !IsFiltered())
1010 items
.Save(GetID());
1016 * With this function you can react on a users click in the list/thumb panel.
1017 * It returns true, if the click is handled.
1018 * This function calls OnPlayMedia()
1020 bool CGUIMediaWindow::OnClick(int iItem
, const std::string
&player
)
1022 if (iItem
< 0 || iItem
>= m_vecItems
->Size())
1025 const std::shared_ptr
<CProfileManager
> profileManager
= CServiceBroker::GetSettingsComponent()->GetProfileManager();
1027 CFileItemPtr pItem
= m_vecItems
->Get(iItem
);
1029 if (pItem
->IsParentFolder())
1035 if (pItem
->GetPath() == "add" || pItem
->GetPath() == "sources://add/") // 'add source button' in empty root
1037 if (profileManager
->IsMasterProfile())
1039 if (!g_passwordManager
.IsMasterLockUnlocked(true))
1042 else if (!profileManager
->GetCurrentProfile().canWriteSources() && !g_passwordManager
.IsProfileLockUnlocked())
1045 if (OnAddMediaSource())
1051 if (!pItem
->m_bIsFolder
&& pItem
->IsFileFolder(EFILEFOLDER_MASK_ONCLICK
))
1053 XFILE::IFileDirectory
*pFileDirectory
= nullptr;
1054 pFileDirectory
= XFILE::CFileDirectoryFactory::Create(pItem
->GetURL(), pItem
.get(), "");
1056 pItem
->m_bIsFolder
= true;
1057 else if(pItem
->m_bIsFolder
)
1058 pItem
->m_bIsFolder
= false;
1059 delete pFileDirectory
;
1062 if (pItem
->IsScript())
1064 // execute the script
1065 CURL
url(pItem
->GetPath());
1067 if (CServiceBroker::GetAddonMgr().GetAddon(url
.GetHostName(), addon
, AddonType::SCRIPT
,
1068 OnlyEnabled::CHOICE_YES
))
1070 if (!CScriptInvocationManager::GetInstance().Stop(addon
->LibPath()))
1072 CServiceBroker::GetAddonMgr().UpdateLastUsed(addon
->ID());
1073 CScriptInvocationManager::GetInstance().ExecuteAsync(addon
->LibPath(), addon
);
1079 if (pItem
->m_bIsFolder
)
1081 if ( pItem
->m_bIsShareOrDrive
)
1083 const std::string
& strLockType
=m_guiState
->GetLockType();
1084 if (profileManager
->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE
)
1085 if (!strLockType
.empty() && !g_passwordManager
.IsItemUnlocked(pItem
.get(), strLockType
))
1088 if (!HaveDiscOrConnection(pItem
->GetPath(), pItem
->m_iDriveType
))
1092 // check for the partymode playlist items - they may not exist yet
1093 if ((pItem
->GetPath() == profileManager
->GetUserDataItem("PartyMode.xsp")) ||
1094 (pItem
->GetPath() == profileManager
->GetUserDataItem("PartyMode-Video.xsp")))
1096 // party mode playlist item - if it doesn't exist, prompt for user to define it
1097 if (!CFileUtils::Exists(pItem
->GetPath()))
1099 m_vecItems
->RemoveDiscCache(GetID());
1100 if (CGUIDialogSmartPlaylistEditor::EditPlaylist(pItem
->GetPath()))
1106 // remove the directory cache if the folder is not normally cached
1107 CFileItemList
items(pItem
->GetPath());
1108 if (!items
.AlwaysCache())
1109 items
.RemoveDiscCache(GetID());
1111 // if we have a filtered list, we need to add the filtered
1112 // path to be able to come back to the filtered view
1113 std::string strCurrentDirectory
= m_vecItems
->GetPath();
1114 if (m_canFilterAdvanced
&& !m_filter
.IsEmpty() &&
1115 !URIUtils::PathEquals(m_strFilterPath
, strCurrentDirectory
))
1117 m_history
.RemoveParentPath();
1118 m_history
.AddPath(strCurrentDirectory
, m_strFilterPath
);
1121 if (m_vecItemsUpdating
)
1123 CLog::Log(LOGWARNING
, "CGUIMediaWindow::OnClick - updating in progress");
1126 CUpdateGuard
ug(m_vecItemsUpdating
);
1128 CFileItem
directory(*pItem
);
1129 if (!Update(directory
.GetPath()))
1130 ShowShareErrorMessage(&directory
);
1134 else if (pItem
->IsPlugin() && !pItem
->GetProperty("isplayable").asBoolean())
1136 bool resume
= pItem
->GetStartOffset() == STARTOFFSET_RESUME
;
1137 return XFILE::CPluginDirectory::RunScriptWithParams(pItem
->GetPath(), resume
);
1139 #if defined(TARGET_ANDROID)
1140 else if (pItem
->IsAndroidApp())
1142 std::string appName
= URIUtils::GetFileName(pItem
->GetPath());
1143 CLog::Log(LOGDEBUG
, "CGUIMediaWindow::OnClick Trying to run: {}", appName
);
1144 return CXBMCApp::StartActivity(appName
);
1149 SaveSelectedItemInHistory();
1151 if (pItem
->GetPath() == "newplaylist://")
1153 m_vecItems
->RemoveDiscCache(GetID());
1154 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_MUSIC_PLAYLIST_EDITOR
,"newplaylist://");
1157 else if (StringUtils::StartsWithNoCase(pItem
->GetPath(), "newsmartplaylist://"))
1159 m_vecItems
->RemoveDiscCache(GetID());
1160 if (CGUIDialogSmartPlaylistEditor::NewPlaylist(pItem
->GetPath().substr(19)))
1165 bool autoplay
= m_guiState
.get() && m_guiState
->AutoPlayNextItem();
1167 if (m_vecItems
->IsPlugin())
1169 CURL
url(m_vecItems
->GetPath());
1171 if (CServiceBroker::GetAddonMgr().GetAddon(url
.GetHostName(), addon
, OnlyEnabled::CHOICE_YES
))
1173 const auto plugin
= std::dynamic_pointer_cast
<CPluginSource
>(addon
);
1174 if (plugin
&& plugin
->Provides(CPluginSource::AUDIO
))
1176 CFileItemList items
;
1177 std::unique_ptr
<CGUIViewState
> state(CGUIViewState::GetViewState(GetID(), items
));
1178 autoplay
= state
.get() && state
->AutoPlayNextItem();
1183 if (autoplay
&& !g_partyModeManager
.IsEnabled())
1185 return OnPlayAndQueueMedia(pItem
, player
);
1189 return OnPlayMedia(iItem
, player
);
1196 bool CGUIMediaWindow::OnSelect(int item
)
1198 return OnClick(item
);
1202 * \brief Check disc or connection present
1204 * Checks if there is a disc in the dvd drive and whether the
1205 * network is connected or not.
1207 bool CGUIMediaWindow::HaveDiscOrConnection(const std::string
& strPath
, int iDriveType
)
1209 if (iDriveType
==CMediaSource::SOURCE_TYPE_DVD
)
1211 if (!CServiceBroker::GetMediaManager().IsDiscInDrive(strPath
))
1213 HELPERS::ShowOKDialogText(CVariant
{218}, CVariant
{219});
1217 else if (iDriveType
==CMediaSource::SOURCE_TYPE_REMOTE
)
1219 //! @todo Handle not connected to a remote share
1220 if (!CServiceBroker::GetNetwork().IsConnected())
1222 HELPERS::ShowOKDialogText(CVariant
{220}, CVariant
{221});
1231 * \brief Shows a standard error message for a given pItem.
1233 void CGUIMediaWindow::ShowShareErrorMessage(CFileItem
* pItem
) const
1235 if (!pItem
->m_bIsShareOrDrive
)
1238 int idMessageText
= 0;
1239 CURL
url(pItem
->GetPath());
1241 if (url
.IsProtocol("smb") && url
.GetHostName().empty()) // smb workgroup
1242 idMessageText
= 15303; // Workgroup not found
1243 else if (pItem
->m_iDriveType
== CMediaSource::SOURCE_TYPE_REMOTE
|| URIUtils::IsRemote(pItem
->GetPath()))
1244 idMessageText
= 15301; // Could not connect to network server
1246 idMessageText
= 15300; // Path not found or invalid
1248 HELPERS::ShowOKDialogText(CVariant
{220}, CVariant
{idMessageText
});
1252 * \brief Go one directory up on list items
1254 * The function goes up one level in the directory tree
1256 bool CGUIMediaWindow::GoParentFolder()
1258 if (m_vecItems
->IsVirtualDirectoryRoot())
1261 if (URIUtils::PathEquals(m_vecItems
->GetPath(), GetRootPath()))
1264 //m_history.DumpPathHistory();
1266 const std::string currentPath
= m_vecItems
->GetPath();
1267 std::string parentPath
= m_history
.GetParentPath();
1268 // Check if a) the current folder is on the stack more than once, (parent is
1269 // often same as current), OR
1270 // b) the parent is an xml file (happens when ActivateWindow() called with
1271 // a node file) and so current path is the result of expanding the xml.
1272 // Keep going until there's nothing left or they dont match anymore.
1273 while (!parentPath
.empty() &&
1274 (URIUtils::PathEquals(parentPath
, currentPath
, true) ||
1275 StringUtils::EndsWith(parentPath
, ".xml/") || StringUtils::EndsWith(parentPath
, ".xml")))
1277 m_history
.RemoveParentPath();
1278 parentPath
= m_history
.GetParentPath();
1281 // remove the current filter but only if the parent
1282 // item doesn't have a filter as well
1283 CURL
filterUrl(m_strFilterPath
);
1284 if (filterUrl
.HasOption("filter"))
1286 CURL
parentUrl(m_history
.GetParentPath(true));
1287 if (!parentUrl
.HasOption("filter"))
1289 // we need to overwrite m_strFilterPath because
1290 // Refresh() will set updateFilterPath to false
1291 m_strFilterPath
.clear();
1297 // pop directory path from the stack
1298 m_strFilterPath
= m_history
.GetParentPath(true);
1299 m_history
.RemoveParentPath();
1301 if (!Update(parentPath
, false))
1304 // No items to show so go another level up
1305 if (!m_vecItems
->GetPath().empty() && (m_filter
.IsEmpty() ? m_vecItems
->Size() : m_unfilteredItems
->Size()) <= 0)
1307 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info
, g_localizeStrings
.Get(2080), g_localizeStrings
.Get(2081));
1308 return GoParentFolder();
1313 void CGUIMediaWindow::SaveSelectedItemInHistory()
1315 int iItem
= m_viewControl
.GetSelectedItem();
1316 std::string strSelectedItem
;
1317 if (iItem
>= 0 && iItem
< m_vecItems
->Size())
1319 CFileItemPtr pItem
= m_vecItems
->Get(iItem
);
1320 GetDirectoryHistoryString(pItem
.get(), strSelectedItem
);
1323 m_history
.SetSelectedItem(strSelectedItem
, m_vecItems
->GetPath(), iItem
);
1326 void CGUIMediaWindow::RestoreSelectedItemFromHistory()
1328 std::string strSelectedItem
= m_history
.GetSelectedItem(m_vecItems
->GetPath());
1330 if (!strSelectedItem
.empty())
1332 for (int i
= 0; i
< m_vecItems
->Size(); ++i
)
1334 CFileItemPtr pItem
= m_vecItems
->Get(i
);
1335 std::string strHistory
;
1336 GetDirectoryHistoryString(pItem
.get(), strHistory
);
1337 // set selected item if equals with history
1338 if (strHistory
== strSelectedItem
)
1340 m_viewControl
.SetSelectedItem(i
);
1346 // Exact item not found - maybe deleted, watched status change, filtered out, ...
1347 // Attempt to restore the position of the selection
1348 int selectedItemIndex
= m_history
.GetSelectedItemIndex(m_vecItems
->GetPath());
1349 if (selectedItemIndex
>= 0 && m_vecItems
->Size() > 0)
1351 int newIndex
= std::min(selectedItemIndex
, m_vecItems
->Size() - 1);
1352 m_viewControl
.SetSelectedItem(newIndex
);
1356 // Fallback: select the first item
1357 m_viewControl
.SetSelectedItem(0);
1361 * \brief Get history string for given file item
1363 * \note Override the function to change the default behavior on how
1364 * a selected item history should look like
1366 void CGUIMediaWindow::GetDirectoryHistoryString(const CFileItem
* pItem
, std::string
& strHistoryString
) const
1368 if (pItem
->m_bIsShareOrDrive
)
1370 // We are in the virtual directory
1372 // History string of the DVD drive
1373 // must be handled separately
1374 if (pItem
->m_iDriveType
== CMediaSource::SOURCE_TYPE_DVD
)
1376 // Remove disc label from item label
1377 // and use as history string, m_strPath
1378 // can change for new discs
1379 std::string strLabel
= pItem
->GetLabel();
1380 size_t nPosOpen
= strLabel
.find('(');
1381 size_t nPosClose
= strLabel
.rfind(')');
1382 if (nPosOpen
!= std::string::npos
&&
1383 nPosClose
!= std::string::npos
&&
1384 nPosClose
> nPosOpen
)
1386 strLabel
.erase(nPosOpen
+ 1, (nPosClose
) - (nPosOpen
+ 1));
1387 strHistoryString
= strLabel
;
1390 strHistoryString
= strLabel
;
1394 // Other items in virtual directory
1395 std::string strPath
= pItem
->GetPath();
1396 URIUtils::RemoveSlashAtEnd(strPath
);
1398 strHistoryString
= pItem
->GetLabel() + strPath
;
1401 else if (pItem
->GetEndOffset() > pItem
->GetStartOffset() &&
1402 pItem
->GetStartOffset() != STARTOFFSET_RESUME
)
1404 // Could be a cue item, all items of a cue share the same filename
1405 // so add the offsets to build the history string
1406 strHistoryString
= StringUtils::Format("{}{}", pItem
->GetStartOffset(), pItem
->GetEndOffset());
1407 strHistoryString
+= pItem
->GetPath();
1411 // Normal directory items
1412 strHistoryString
= pItem
->GetPath();
1415 // remove any filter
1416 if (CanContainFilter(strHistoryString
))
1417 strHistoryString
= RemoveParameterFromPath(strHistoryString
, "filter");
1419 URIUtils::RemoveSlashAtEnd(strHistoryString
);
1420 StringUtils::ToLower(strHistoryString
);
1424 * \brief Set history for path
1426 * Call this function to create a directory history for the
1427 * path given by strDirectory.
1429 void CGUIMediaWindow::SetHistoryForPath(const std::string
& strDirectory
)
1431 // Make sure our shares are configured
1433 if (!strDirectory
.empty())
1435 // Build the directory history for default path
1436 std::string strPath
, strParentPath
;
1437 strPath
= strDirectory
;
1438 URIUtils::RemoveSlashAtEnd(strPath
);
1440 CFileItemList items
;
1442 GetDirectoryItems(url
, items
, UseFileDirectories());
1444 m_history
.ClearPathHistory();
1446 bool originalPath
= true;
1447 while (URIUtils::GetParentPath(strPath
, strParentPath
))
1449 for (int i
= 0; i
< items
.Size(); ++i
)
1451 CFileItemPtr pItem
= items
[i
];
1452 std::string
path(pItem
->GetPath());
1453 URIUtils::RemoveSlashAtEnd(path
);
1454 if (URIUtils::PathEquals(path
, strPath
))
1456 std::string strHistory
;
1457 GetDirectoryHistoryString(pItem
.get(), strHistory
);
1458 m_history
.SetSelectedItem(strHistory
, "");
1459 URIUtils::AddSlashAtEnd(strPath
);
1460 m_history
.AddPathFront(strPath
);
1461 m_history
.AddPathFront("");
1463 //m_history.DumpPathHistory();
1468 if (URIUtils::IsVideoDb(strPath
))
1470 CURL
url(strParentPath
);
1471 url
.SetOptions(""); // clear any URL options from recreated parent path
1472 strParentPath
= url
.Get();
1475 // set the original path exactly as it was passed in
1476 if (URIUtils::PathEquals(strPath
, strDirectory
, true))
1477 strPath
= strDirectory
;
1479 URIUtils::AddSlashAtEnd(strPath
);
1481 m_history
.AddPathFront(strPath
, originalPath
? m_strFilterPath
: "");
1482 m_history
.SetSelectedItem(strPath
, strParentPath
);
1483 originalPath
= false;
1484 strPath
= strParentPath
;
1485 URIUtils::RemoveSlashAtEnd(strPath
);
1489 m_history
.ClearPathHistory();
1491 //m_history.DumpPathHistory();
1495 * \brief On media play
1497 * \note Override if you want to change the default behavior, what is done
1498 * when the user clicks on a file.
1500 * This function is called by OnClick()
1502 bool CGUIMediaWindow::OnPlayMedia(int iItem
, const std::string
&player
)
1504 // Reset Playlistplayer, playback started now does
1505 // not use the playlistplayer.
1506 CServiceBroker::GetPlaylistPlayer().Reset();
1507 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::Id::TYPE_NONE
);
1508 CFileItemPtr pItem
=m_vecItems
->Get(iItem
);
1510 CLog::Log(LOGDEBUG
, "{} {}", __FUNCTION__
, CURL::GetRedacted(pItem
->GetPath()));
1512 bool bResult
= false;
1513 if (NETWORK::IsInternetStream(*pItem
) || PLAYLIST::IsPlayList(*pItem
))
1514 bResult
= g_application
.PlayMedia(*pItem
, player
, m_guiState
->GetPlaylist());
1516 bResult
= g_application
.PlayFile(*pItem
, player
);
1518 if (pItem
->GetStartOffset() == STARTOFFSET_RESUME
)
1519 pItem
->SetStartOffset(0);
1525 * \brief On play and media queue
1527 * \note Override if you want to change the default behavior of what is done
1528 * when the user clicks on a file in a "folder" with similar files.
1530 * This function is called by OnClick()
1532 bool CGUIMediaWindow::OnPlayAndQueueMedia(const CFileItemPtr
& item
, const std::string
& player
)
1534 //play and add current directory to temporary playlist
1535 PLAYLIST::Id playlistId
= m_guiState
->GetPlaylist();
1536 if (playlistId
!= PLAYLIST::Id::TYPE_NONE
)
1538 // Remove ZIP, RAR files and folders
1539 CFileItemList playlist
;
1540 playlist
.Copy(*m_vecItems
, true);
1541 playlist
.erase(std::remove_if(playlist
.begin(), playlist
.end(),
1542 [](const std::shared_ptr
<CFileItem
>& i
)
1543 { return i
->IsZIP() || i
->IsRAR() || i
->m_bIsFolder
; }),
1548 std::distance(playlist
.begin(), std::find_if(playlist
.begin(), playlist
.end(),
1549 [&item
](const std::shared_ptr
<CFileItem
>& i
)
1550 { return i
->GetPath() == item
->GetPath(); }));
1551 /* For .mka albums, all tracks are in the same file so using path as above will always play the
1552 * first track. Use the track and disk number to ensure we start playback on the correct track.
1553 * This only applies to mka or m4b items played back via files view. Music library takes
1554 * a different play path.
1556 if (MUSIC::IsAudioBook(*item
))
1557 mediaToPlay
= std::distance(
1558 playlist
.begin(), std::find_if(playlist
.begin(), playlist
.end(),
1559 [&item
](const std::shared_ptr
<CFileItem
>& i
)
1561 return i
->GetMusicInfoTag()->GetTrackAndDiscNumber() ==
1562 item
->GetMusicInfoTag()->GetTrackAndDiscNumber();
1566 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(playlistId
);
1567 CServiceBroker::GetPlaylistPlayer().Reset();
1568 CServiceBroker::GetPlaylistPlayer().Add(playlistId
, playlist
);
1570 // Save current window and directory to know where the selected item was
1572 m_guiState
->SetPlaylistDirectory(m_vecItems
->GetPath());
1574 // figure out where we start playback
1575 if (CServiceBroker::GetPlaylistPlayer().IsShuffled(playlistId
))
1578 CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId
).FindOrder(mediaToPlay
);
1579 CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId
).Swap(0, iIndex
);
1584 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(playlistId
);
1585 CServiceBroker::GetPlaylistPlayer().Play(mediaToPlay
, player
);
1591 * \brief Update file list
1593 * Synchronize the fileitems with the playlistplayer
1594 * also recreates the playlist of the playlistplayer based
1595 * on the fileitems of the window
1597 void CGUIMediaWindow::UpdateFileList()
1599 int nItem
= m_viewControl
.GetSelectedItem();
1600 std::string strSelected
;
1602 strSelected
= m_vecItems
->Get(nItem
)->GetPath();
1604 FormatAndSort(*m_vecItems
);
1607 m_viewControl
.SetItems(*m_vecItems
);
1608 m_viewControl
.SetSelectedItem(strSelected
);
1610 // set the currently playing item as selected, if its in this directory
1611 if (m_guiState
.get() && m_guiState
->IsCurrentPlaylistDirectory(m_vecItems
->GetPath()))
1613 PLAYLIST::Id playlistId
= m_guiState
->GetPlaylist();
1614 int nSong
= CServiceBroker::GetPlaylistPlayer().GetCurrentItemIdx();
1615 CFileItem playlistItem
;
1616 if (nSong
> -1 && playlistId
!= PLAYLIST::Id::TYPE_NONE
)
1617 playlistItem
= *CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId
)[nSong
];
1619 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(playlistId
);
1620 CServiceBroker::GetPlaylistPlayer().Reset();
1622 for (int i
= 0; i
< m_vecItems
->Size(); i
++)
1624 CFileItemPtr pItem
= m_vecItems
->Get(i
);
1625 if (pItem
->m_bIsFolder
)
1628 if (!PLAYLIST::IsPlayList(*pItem
) && !pItem
->IsZIP() && !pItem
->IsRAR())
1629 CServiceBroker::GetPlaylistPlayer().Add(playlistId
, pItem
);
1631 if (pItem
->GetPath() == playlistItem
.GetPath() &&
1632 pItem
->GetStartOffset() == playlistItem
.GetStartOffset())
1633 CServiceBroker::GetPlaylistPlayer().SetCurrentItemIdx(
1634 CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId
).size() - 1);
1639 void CGUIMediaWindow::OnDeleteItem(int iItem
)
1641 if ( iItem
< 0 || iItem
>= m_vecItems
->Size()) return;
1642 CFileItemPtr item
= m_vecItems
->Get(iItem
);
1644 if (PLAYLIST::IsPlayList(*item
))
1645 item
->m_bIsFolder
= false;
1647 const std::shared_ptr
<CProfileManager
> profileManager
= CServiceBroker::GetSettingsComponent()->GetProfileManager();
1649 if (profileManager
->GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE
&& profileManager
->GetCurrentProfile().filesLocked())
1651 if (!g_passwordManager
.IsMasterLockUnlocked(true))
1655 CGUIComponent
*gui
= CServiceBroker::GetGUI();
1656 if (gui
&& gui
->ConfirmDelete(item
->GetPath()))
1658 if (!CFileUtils::DeleteItem(item
))
1665 m_viewControl
.SetSelectedItem(iItem
);
1668 void CGUIMediaWindow::OnRenameItem(int iItem
)
1670 if (iItem
< 0 || iItem
>= m_vecItems
->Size())
1673 const std::shared_ptr
<CProfileManager
> profileManager
= CServiceBroker::GetSettingsComponent()->GetProfileManager();
1675 if (profileManager
->GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE
&& profileManager
->GetCurrentProfile().filesLocked())
1677 if (!g_passwordManager
.IsMasterLockUnlocked(true))
1681 if (!CFileUtils::RenameFile(m_vecItems
->Get(iItem
)->GetPath()))
1685 m_viewControl
.SetSelectedItem(iItem
);
1688 void CGUIMediaWindow::OnInitWindow()
1690 // initial fetch is done unthreaded to ensure the items are setup prior to skin animations kicking off
1691 m_backgroundLoad
= false;
1693 // the start directory may change during Refresh
1694 bool updateStartDirectory
= URIUtils::PathEquals(m_vecItems
->GetPath(), m_startDirectory
, true);
1696 // we have python scripts hooked in everywhere :(
1697 // those scripts may open windows and we can't open a window
1698 // while opening this one.
1699 // for plugin sources delay call to Refresh
1700 if (!URIUtils::IsPlugin(m_vecItems
->GetPath()))
1706 CGUIMessage
msg(GUI_MSG_WINDOW_INIT
, 0, 0, WINDOW_INVALID
, PLUGIN_REFRESH_DELAY
);
1707 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg
, GetID());
1710 if (updateStartDirectory
)
1712 // reset the start directory to the path of the items
1713 m_startDirectory
= m_vecItems
->GetPath();
1715 // reset the history based on the path of the items
1716 SetHistoryForPath(m_startDirectory
);
1719 m_backgroundLoad
= true;
1721 CGUIWindow::OnInitWindow();
1724 void CGUIMediaWindow::SaveControlStates()
1726 CGUIWindow::SaveControlStates();
1727 SaveSelectedItemInHistory();
1730 void CGUIMediaWindow::RestoreControlStates()
1732 CGUIWindow::RestoreControlStates();
1733 RestoreSelectedItemFromHistory();
1736 CGUIControl
*CGUIMediaWindow::GetFirstFocusableControl(int id
)
1738 if (m_viewControl
.HasControl(id
))
1739 id
= m_viewControl
.GetCurrentControl();
1740 return CGUIWindow::GetFirstFocusableControl(id
);
1743 void CGUIMediaWindow::SetupShares()
1745 // Setup shares and filemasks for this window
1746 CFileItemList items
;
1747 CGUIViewState
* viewState
=CGUIViewState::GetViewState(GetID(), items
);
1750 m_rootDir
.SetMask(viewState
->GetExtensions());
1751 m_rootDir
.SetSources(viewState
->GetSources());
1756 bool CGUIMediaWindow::OnPopupMenu(int itemIdx
)
1758 auto InRange
= [](size_t i
, std::pair
<size_t, size_t> range
){ return i
>= range
.first
&& i
< range
.second
; };
1760 if (itemIdx
< 0 || itemIdx
>= m_vecItems
->Size())
1763 auto item
= m_vecItems
->Get(itemIdx
);
1767 CContextButtons buttons
;
1769 //Add items from plugin
1772 while (item
->HasProperty(StringUtils::Format("contextmenulabel({})", i
)))
1774 buttons
.emplace_back(
1776 item
->GetProperty(StringUtils::Format("contextmenulabel({})", i
)).asString());
1780 auto pluginMenuRange
= std::make_pair(static_cast<size_t>(0), buttons
.size());
1782 //Add the global menu
1783 auto globalMenu
= CServiceBroker::GetContextMenuManager().GetItems(*item
, CContextMenuManager::MAIN
);
1784 auto globalMenuRange
= std::make_pair(buttons
.size(), buttons
.size() + globalMenu
.size());
1785 for (const auto& menu
: globalMenu
)
1786 buttons
.emplace_back(~buttons
.size(), menu
->GetLabel(*item
));
1788 //Add legacy items from windows
1789 auto buttonsSize
= buttons
.size();
1790 GetContextButtons(itemIdx
, buttons
);
1791 auto windowMenuRange
= std::make_pair(buttonsSize
, buttons
.size());
1794 auto addonMenu
= CServiceBroker::GetContextMenuManager().GetAddonItems(*item
, CContextMenuManager::MAIN
);
1795 auto addonMenuRange
= std::make_pair(buttons
.size(), buttons
.size() + addonMenu
.size());
1796 for (const auto& menu
: addonMenu
)
1797 buttons
.emplace_back(~buttons
.size(), menu
->GetLabel(*item
));
1799 if (buttons
.empty())
1802 int idx
= CGUIDialogContextMenu::Show(buttons
);
1803 if (idx
< 0 || idx
>= static_cast<int>(buttons
.size()))
1806 if (InRange(static_cast<size_t>(idx
), pluginMenuRange
))
1808 bool saveVal
= m_backgroundLoad
;
1809 m_backgroundLoad
= false;
1810 CServiceBroker::GetAppMessenger()->SendMsg(
1811 TMSG_EXECUTE_BUILT_IN
, -1, -1, nullptr,
1812 item
->GetProperty(StringUtils::Format("contextmenuaction({})", idx
- pluginMenuRange
.first
))
1814 m_backgroundLoad
= saveVal
;
1818 if (InRange(idx
, windowMenuRange
))
1819 return OnContextButton(itemIdx
, static_cast<CONTEXT_BUTTON
>(buttons
[idx
].first
));
1821 if (InRange(idx
, globalMenuRange
))
1822 return CONTEXTMENU::LoopFrom(*globalMenu
[idx
- globalMenuRange
.first
], item
);
1824 return CONTEXTMENU::LoopFrom(*addonMenu
[idx
- addonMenuRange
.first
], item
);
1827 const CGUIViewState
*CGUIMediaWindow::GetViewState() const
1829 return m_guiState
.get();
1832 const CFileItemList
& CGUIMediaWindow::CurrentDirectory() const
1837 bool CGUIMediaWindow::WaitForNetwork() const
1839 if (CServiceBroker::GetNetwork().IsAvailable())
1842 CGUIDialogProgress
*progress
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogProgress
>(WINDOW_DIALOG_PROGRESS
);
1846 CURL
url(m_vecItems
->GetPath());
1847 progress
->SetHeading(CVariant
{1040}); // Loading Directory
1848 progress
->SetLine(1, CVariant
{url
.GetWithoutUserDetails()});
1849 progress
->ShowProgressBar(false);
1851 while (!CServiceBroker::GetNetwork().IsAvailable())
1853 progress
->Progress();
1854 if (progress
->IsCanceled())
1864 void CGUIMediaWindow::UpdateFilterPath(const std::string
&strDirectory
, const CFileItemList
&items
, bool updateFilterPath
)
1866 bool canfilter
= CanContainFilter(strDirectory
);
1869 CURL
url(strDirectory
);
1870 if (canfilter
&& url
.HasOption("filter"))
1871 filter
= url
.GetOption("filter");
1873 // only set the filter path if it hasn't been marked
1874 // as preset or if it's empty
1875 if (updateFilterPath
|| m_strFilterPath
.empty())
1877 if (items
.HasProperty(PROPERTY_PATH_DB
))
1878 m_strFilterPath
= items
.GetProperty(PROPERTY_PATH_DB
).asString();
1880 m_strFilterPath
= items
.GetPath();
1883 // maybe the filter path can contain a filter
1884 if (!canfilter
&& CanContainFilter(m_strFilterPath
))
1887 // check if the filter path contains a filter
1888 CURL
filterPathUrl(m_strFilterPath
);
1889 if (canfilter
&& filter
.empty())
1891 if (filterPathUrl
.HasOption("filter"))
1892 filter
= filterPathUrl
.GetOption("filter");
1895 // check if there is a filter and re-apply it
1896 if (canfilter
&& !filter
.empty())
1898 if (!m_filter
.LoadFromJson(filter
))
1900 CLog::Log(LOGWARNING
,
1901 "CGUIMediaWindow::UpdateFilterPath(): unable to load existing filter ({})", filter
);
1903 m_strFilterPath
= m_vecItems
->GetPath();
1907 // add the filter to the filter path
1908 filterPathUrl
.SetOption("filter", filter
);
1909 m_strFilterPath
= filterPathUrl
.Get();
1914 void CGUIMediaWindow::OnFilterItems(const std::string
&filter
)
1916 m_viewControl
.Clear();
1918 CFileItemList items
;
1919 items
.Copy(*m_vecItems
, false); // use the original path - it'll likely be relied on for other things later.
1920 items
.Append(*m_unfilteredItems
);
1921 bool filtered
= GetFilteredItems(filter
, items
);
1923 m_vecItems
->ClearItems();
1924 // we need to clear the sort state and re-sort the items
1925 m_vecItems
->ClearSortState();
1926 m_vecItems
->Append(items
);
1928 // if the filter has changed, get the new filter path
1929 if (filtered
&& m_canFilterAdvanced
)
1931 if (items
.HasProperty(PROPERTY_PATH_DB
))
1932 m_strFilterPath
= items
.GetProperty(PROPERTY_PATH_DB
).asString();
1933 // only set m_strFilterPath if it hasn't been set before
1934 // otherwise we might overwrite it with a non-filter path
1935 // in case GetFilteredItems() returns true even though no
1936 // db-based filter (e.g. watched filter) has been applied
1937 else if (m_strFilterPath
.empty())
1938 m_strFilterPath
= items
.GetPath();
1941 GetGroupedItems(*m_vecItems
);
1942 FormatAndSort(*m_vecItems
);
1944 CFileItemPtr currentItem
;
1945 std::string currentItemPath
;
1946 int item
= m_viewControl
.GetSelectedItem();
1947 if (item
>= 0 && item
< m_vecItems
->Size())
1949 currentItem
= m_vecItems
->Get(item
);
1950 currentItemPath
= currentItem
->GetPath();
1953 // get the "filter" option
1954 std::string filterOption
;
1955 CURL
filterUrl(m_strFilterPath
);
1956 if (filterUrl
.HasOption("filter"))
1957 filterOption
= filterUrl
.GetOption("filter");
1959 // apply the "filter" option to any folder item so that
1960 // the filter can be passed down to the sub-directory
1961 for (int index
= 0; index
< m_vecItems
->Size(); index
++)
1963 CFileItemPtr pItem
= m_vecItems
->Get(index
);
1964 // if the item is a folder we need to copy the path of
1965 // the filtered item to be able to keep the applied filters
1966 if (pItem
->m_bIsFolder
)
1968 CURL
itemUrl(pItem
->GetPath());
1969 if (!filterOption
.empty())
1970 itemUrl
.SetOption("filter", filterOption
);
1972 itemUrl
.RemoveOption("filter");
1973 pItem
->SetPath(itemUrl
.Get());
1977 SetProperty("filter", filter
);
1978 if (filtered
&& m_canFilterAdvanced
)
1980 // to be able to select the same item as before we need to adjust
1981 // the path of the item i.e. add or remove the "filter=" URL option
1982 // but that's only necessary for folder items
1983 if (currentItem
.get() && currentItem
->m_bIsFolder
)
1985 CURL
curUrl(currentItemPath
), newUrl(m_strFilterPath
);
1986 if (newUrl
.HasOption("filter"))
1987 curUrl
.SetOption("filter", newUrl
.GetOption("filter"));
1988 else if (curUrl
.HasOption("filter"))
1989 curUrl
.RemoveOption("filter");
1991 currentItemPath
= curUrl
.Get();
1995 // The idea here is to ensure we have something to focus if our file list
1996 // is empty. As such, this check MUST be last and ignore the hide parent
1997 // fileitems settings.
1998 if (m_vecItems
->IsEmpty())
2000 CFileItemPtr
pItem(new CFileItem(".."));
2001 pItem
->SetPath(m_history
.GetParentPath());
2002 pItem
->m_bIsFolder
= true;
2003 pItem
->m_bIsShareOrDrive
= false;
2004 m_vecItems
->AddFront(pItem
, 0);
2007 // and update our view control + buttons
2008 m_viewControl
.SetItems(*m_vecItems
);
2009 m_viewControl
.SetSelectedItem(currentItemPath
);
2012 bool CGUIMediaWindow::GetFilteredItems(const std::string
&filter
, CFileItemList
&items
)
2014 bool result
= false;
2015 if (m_canFilterAdvanced
)
2016 result
= GetAdvanceFilteredItems(items
);
2018 std::string
trimmedFilter(filter
);
2019 StringUtils::TrimLeft(trimmedFilter
);
2020 StringUtils::ToLower(trimmedFilter
);
2022 if (trimmedFilter
.empty())
2025 CFileItemList
filteredItems(items
.GetPath()); // use the original path - it'll likely be relied on for other things later.
2026 bool numericMatch
= StringUtils::IsNaturalNumber(trimmedFilter
);
2027 for (int i
= 0; i
< items
.Size(); i
++)
2029 CFileItemPtr item
= items
.Get(i
);
2030 if (item
->IsParentFolder())
2032 filteredItems
.Add(item
);
2035 //! @todo Need to update this to get all labels, ideally out of the displayed info (ie from m_layout and m_focusedLayout)
2036 //! though that isn't practical. Perhaps a better idea would be to just grab the info that we should filter on based on
2037 //! where we are in the library tree.
2038 //! Another idea is tying the filter string to the current level of the tree, so that going deeper disables the filter,
2039 //! but it's re-enabled on the way back out.
2041 /* if (item->GetFocusedLayout())
2042 match = item->GetFocusedLayout()->GetAllText();
2043 else if (item->GetLayout())
2044 match = item->GetLayout()->GetAllText();
2046 match
= item
->GetLabel(); // Filter label only for now
2049 StringUtils::WordToDigits(match
);
2051 size_t pos
= StringUtils::FindWords(match
.c_str(), trimmedFilter
.c_str());
2052 if (pos
!= std::string::npos
)
2053 filteredItems
.Add(item
);
2057 items
.Append(filteredItems
);
2059 return items
.GetObjectCount() > 0;
2062 bool CGUIMediaWindow::GetAdvanceFilteredItems(CFileItemList
&items
)
2064 // don't run the advanced filter if the filter is empty
2065 // and there hasn't been a filter applied before which
2066 // would have to be removed
2067 CURL
url(m_strFilterPath
);
2068 if (m_filter
.IsEmpty() && !url
.HasOption("filter"))
2071 CFileItemList resultItems
;
2072 XFILE::CSmartPlaylistDirectory::GetDirectory(m_filter
, resultItems
, m_strFilterPath
, true);
2074 // put together a lookup map for faster path comparison
2075 std::map
<std::string
, CFileItemPtr
> lookup
;
2076 for (int j
= 0; j
< resultItems
.Size(); j
++)
2078 std::string itemPath
= CURL(resultItems
[j
]->GetPath()).GetWithoutOptions();
2079 StringUtils::ToLower(itemPath
);
2081 lookup
[itemPath
] = resultItems
[j
];
2084 // loop through all the original items and find
2085 // those which are still part of the filter
2086 CFileItemList filteredItems
;
2087 for (int i
= 0; i
< items
.Size(); i
++)
2089 CFileItemPtr item
= items
.Get(i
);
2090 if (item
->IsParentFolder())
2092 filteredItems
.Add(item
);
2096 // check if the item is part of the resultItems list
2097 // by comparing their paths (but ignoring any special
2098 // options because they differ from filter to filter)
2099 std::string path
= CURL(item
->GetPath()).GetWithoutOptions();
2100 StringUtils::ToLower(path
);
2102 std::map
<std::string
, CFileItemPtr
>::iterator itItem
= lookup
.find(path
);
2103 if (itItem
!= lookup
.end())
2105 // add the item to the list of filtered items
2106 filteredItems
.Add(item
);
2108 // remove the item from the lists
2109 resultItems
.Remove(itItem
->second
.get());
2110 lookup
.erase(itItem
);
2114 if (resultItems
.Size() > 0)
2115 CLog::Log(LOGWARNING
, "CGUIMediaWindow::GetAdvanceFilteredItems(): {} unknown items",
2116 resultItems
.Size());
2119 items
.Append(filteredItems
);
2120 items
.SetPath(resultItems
.GetPath());
2121 if (resultItems
.HasProperty(PROPERTY_PATH_DB
))
2122 items
.SetProperty(PROPERTY_PATH_DB
, resultItems
.GetProperty(PROPERTY_PATH_DB
));
2126 bool CGUIMediaWindow::IsFiltered()
2128 return (!m_canFilterAdvanced
&& !GetProperty("filter").empty()) ||
2129 (m_canFilterAdvanced
&& !m_filter
.IsEmpty());
2132 bool CGUIMediaWindow::IsSameStartFolder(const std::string
&dir
)
2134 const std::string startFolder
= GetStartFolder(dir
);
2135 return URIUtils::PathHasParent(m_vecItems
->GetPath(), startFolder
);
2138 bool CGUIMediaWindow::Filter(bool advanced
/* = true */)
2141 if (!m_canFilterAdvanced
|| !advanced
)
2143 const CGUIControl
*btnFilter
= GetControl(CONTROL_BTN_FILTER
);
2144 if (btnFilter
&& btnFilter
->GetControlType() == CGUIControl::GUICONTROL_EDIT
)
2146 CGUIMessage
selected(GUI_MSG_ITEM_SELECTED
, GetID(), CONTROL_BTN_FILTER
);
2147 OnMessage(selected
);
2148 OnFilterItems(selected
.GetLabel());
2152 if (GetProperty("filter").empty())
2154 std::string filter
= GetProperty("filter").asString();
2155 CGUIKeyboardFactory::ShowAndGetFilter(filter
, false);
2156 SetProperty("filter", filter
);
2164 // advanced filtering
2166 CGUIDialogMediaFilter::ShowAndEditMediaFilter(m_strFilterPath
, m_filter
);
2171 std::string
CGUIMediaWindow::GetStartFolder(const std::string
&dir
)
2173 if (StringUtils::EqualsNoCase(dir
, "$root") ||
2174 StringUtils::EqualsNoCase(dir
, "root"))
2177 // Let plugins handle their own urls themselves
2178 if (StringUtils::StartsWith(dir
, "plugin://"))
2181 //! @todo This ifdef block probably belongs somewhere else. Move it to a better place!
2182 #if defined(TARGET_ANDROID)
2183 // Hack for Android items (numbered id's) on the leanback screen
2185 std::string fileName
;
2186 URIUtils::Split(dir
, path
, fileName
);
2187 URIUtils::RemoveExtension(fileName
);
2188 if (StringUtils::IsInteger(fileName
))
2195 std::string
CGUIMediaWindow::RemoveParameterFromPath(const std::string
&strDirectory
, const std::string
&strParameter
)
2197 CURL
url(strDirectory
);
2198 if (url
.HasOption(strParameter
))
2200 url
.RemoveOption(strParameter
);
2204 return strDirectory
;
2207 bool CGUIMediaWindow::ProcessRenderLoop(bool renderOnly
)
2209 return CServiceBroker::GetGUI()->GetWindowManager().ProcessRenderLoop(renderOnly
);
2212 bool CGUIMediaWindow::GetDirectoryItems(CURL
&url
, CFileItemList
&items
, bool useDir
)
2214 if (m_backgroundLoad
)
2217 CGetDirectoryItems
getItems(m_rootDir
, url
, items
, useDir
);
2219 if (!WaitGetDirectoryItems(getItems
))
2224 else if (!getItems
.m_result
)
2226 if (CServiceBroker::GetAppMessenger()->IsProcessThread() && m_rootDir
.GetDirImpl() &&
2227 !m_rootDir
.GetDirImpl()->ProcessRequirements())
2231 else if (!WaitGetDirectoryItems(getItems
) || !getItems
.m_result
)
2237 m_updateJobActive
= false;
2238 m_rootDir
.ReleaseDirImpl();
2243 return m_rootDir
.GetDirectory(url
, items
, useDir
, false);
2247 bool CGUIMediaWindow::WaitGetDirectoryItems(CGetDirectoryItems
&items
)
2250 CGUIDialogBusy
* dialog
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogBusy
>(WINDOW_DIALOG_BUSY
);
2251 if (dialog
&& !dialog
->IsDialogRunning())
2253 if (!CGUIDialogBusy::Wait(&items
, 100, true))
2261 m_updateJobActive
= true;
2262 m_updateAborted
= false;
2263 m_updateEvent
.Reset();
2264 CServiceBroker::GetJobManager()->Submit(
2267 m_updateEvent
.Set();
2269 nullptr, CJob::PRIORITY_NORMAL
);
2271 // Loop until either the job ended or update canceled via CGUIMediaWindow::CancelUpdateItems.
2272 while (!m_updateAborted
&& !m_updateEvent
.Wait(1ms
))
2274 if (!ProcessRenderLoop(false))
2278 if (m_updateAborted
)
2280 CLog::LogF(LOGDEBUG
, "Get directory items job was canceled.");
2283 else if (!items
.m_result
)
2285 CLog::LogF(LOGDEBUG
, "Get directory items job was unsuccessful.");
2292 void CGUIMediaWindow::CancelUpdateItems()
2294 if (m_updateJobActive
)
2296 m_rootDir
.CancelDirectory();
2297 m_updateAborted
= true;
2298 if (!m_updateEvent
.Wait(5000ms
))
2300 CLog::Log(LOGERROR
, "CGUIMediaWindow::CancelUpdateItems - error cancel update");
2302 m_updateJobActive
= false;