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 "FileItemListModification.h"
14 #include "GUIPassword.h"
15 #include "GUIUserMessages.h"
16 #include "PartyModeManager.h"
17 #include "PlayListPlayer.h"
18 #include "ServiceBroker.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"
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"
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
;
85 class CGetDirectoryItems
: public IRunnable
88 CGetDirectoryItems(XFILE::CVirtualDirectory
&dir
, CURL
&url
, CFileItemList
&items
, bool useDir
)
89 : m_dir(dir
), m_url(url
), m_items(items
), m_useDir(useDir
)
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;
106 XFILE::CVirtualDirectory
&m_dir
;
108 CFileItemList
&m_items
;
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("?");
121 m_canFilterAdvanced
= false;
123 m_guiState
.reset(CGUIViewState::GetViewState(GetID(), *m_vecItems
));
126 CGUIMediaWindow::~CGUIMediaWindow()
129 delete m_unfilteredItems
;
132 bool CGUIMediaWindow::Load(TiXmlElement
*pRootElement
)
134 bool retVal
= CGUIWindow::Load(pRootElement
);
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
);
160 void CGUIMediaWindow::OnWindowLoaded()
162 SendMessage(GUI_MSG_SET_TYPE
, CONTROL_BTN_FILTER
, CGUIEditControl::INPUT_TYPE_FILTER
);
163 CGUIWindow::OnWindowLoaded();
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
)
191 if (CGUIWindow::OnAction(action
))
194 if (action
.GetID() == ACTION_FILTER
)
198 if (action
.GetID() == ACTION_FILTER_CLEAR
)
200 CGUIMessage
message(GUI_MSG_NOTIFY_ALL
, GetID(), 0, GUI_MSG_FILTER_ITEMS
);
201 message
.SetStringParam("");
206 if (action
.GetID() == ACTION_BACKSPACE
)
208 CGUIMessage
message(GUI_MSG_NOTIFY_ALL
, GetID(), 0, GUI_MSG_FILTER_ITEMS
, 2); // 2 for delete
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
);
225 bool CGUIMediaWindow::OnBack(int actionID
)
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())
238 return CGUIWindow::OnBack(actionID
);
241 bool CGUIMediaWindow::OnMessage(CGUIMessage
& message
)
243 switch ( message
.GetMessage() )
245 case GUI_MSG_WINDOW_DEINIT
:
249 m_iLastControl
= GetFocusedControlID();
250 CGUIWindow::OnMessage(message
);
252 // get rid of any active filtering
253 if (m_canFilterAdvanced
)
255 m_canFilterAdvanced
= false;
258 m_strFilterPath
.clear();
260 // Call ClearFileItems() after our window has finished doing any WindowClose
267 case GUI_MSG_CLICKED
:
269 int iControl
= message
.GetSenderId();
270 if (iControl
== CONTROL_BTNVIEWASICONS
)
272 // view as control could be a select button
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
);
279 viewMode
= m_viewControl
.GetViewModeNumber(msg
.GetParam1());
282 viewMode
= m_viewControl
.GetNextViewMode();
285 m_guiState
->SaveViewAsControl(viewMode
);
290 else if (iControl
== CONTROL_BTNSORTASC
) // sort asc
293 m_guiState
->SetNextSortOrder();
297 else if (iControl
== CONTROL_BTNSORTBY
) // sort by
299 if (m_guiState
.get() && m_guiState
->ChooseSortMethod())
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
)
314 else if (iAction
== ACTION_CONTEXT_MENU
|| iAction
== ACTION_MOUSE_RIGHT_CLICK
)
323 case GUI_MSG_SETFOCUS
:
325 if (m_viewControl
.HasControl(message
.GetControlId()) && m_viewControl
.GetCurrentControl() != message
.GetControlId())
327 m_viewControl
.SetFocused();
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("?");
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();
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("");
362 m_history
.ClearPathHistory();
363 m_vecItems
->SetPath("");
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");
380 CUpdateGuard
ug(m_vecItemsUpdating
);
381 int iItem
= m_viewControl
.GetSelectedItem();
383 m_viewControl
.SetSelectedItem(iItem
);
387 else if (message
.GetParam1()==GUI_MSG_UPDATE
&& IsActive())
389 if (m_vecItemsUpdating
)
391 CLog::Log(LOGWARNING
, "CGUIMediaWindow::OnMessage - updating in progress");
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());
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
422 { // need to remove the disc cache
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
);
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
)
440 if((message
.GetStringParam() == m_vecItems
->GetPath()) ||
441 (m_vecItems
->IsMultiPath() && XFILE::CMultiPathDirectory::HasPath(m_vecItems
->GetPath(), message
.GetStringParam())))
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)
456 filter
.erase(filter
.size() - 1);
459 filter
= message
.GetStringParam();
461 OnFilterItems(filter
);
466 return CGUIWindow::OnMessage(message
);
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
);
483 case GUI_MSG_CHANGE_VIEW_MODE
:
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());
492 m_guiState
->SaveViewAsControl(viewMode
);
497 case GUI_MSG_CHANGE_SORT_METHOD
:
501 if (message
.GetParam1())
502 m_guiState
->SetCurrentSortMethod(message
.GetParam1());
503 else if (message
.GetParam2())
504 m_guiState
->SetNextSortMethod(message
.GetParam2());
510 case GUI_MSG_CHANGE_SORT_DIRECTION
:
513 m_guiState
->SetNextSortOrder();
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");
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
);
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("");
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)
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
)
572 SetInitialVisibility();
573 RestoreControlStates();
574 SetInitialVisibility();
581 return CGUIWindow::OnMessage(message
);
585 * \brief Updates the states
587 * This updates the states (enable, disable, visible...) of the controls defined
590 * \note Override this function in a derived class to add new controls
592 void CGUIMediaWindow::UpdateButtons()
596 // Update sorting controls
597 if (m_guiState
->GetSortOrder() == SortOrderNone
)
599 CONTROL_DISABLE(CONTROL_BTNSORTASC
);
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
);
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
);
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();
632 m_unfilteredItems
->Clear();
636 * \brief Sort file items
638 * This sorts file items based on the sort method and sort order provided by
641 void CGUIMediaWindow::SortItems(CFileItemList
&items
)
643 std::unique_ptr
<CGUIViewState
> guiState(CGUIViewState::GetViewState(GetID(), items
));
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
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
;
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())
690 if (pItem
->m_bIsFolder
)
691 folderFormatter
.FormatLabels(pItem
.get());
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
));
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
);
746 auto start
= std::chrono::steady_clock::now();
748 if (strDirectory
.empty())
751 CFileItemList dirItems
;
752 if (!GetDirectoryItems(pathToUrl
, dirItems
, UseFileDirectories()))
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())
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())
777 if (items
.GetPath() == GetRootPath())
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
;
802 for (int i
=0; i
< items
.Size();)
804 if (CUtil::ExcludeFileOrFolder(items
[i
]->GetPath(), regexps
))
812 SetProperty("filter", "");
813 m_canFilterAdvanced
= false;
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
== "?")
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
856 if (m_vecItems
->GetLabel().empty())
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
);
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();
884 if (URIUtils::PathEquals(path
, GetRootPath()))
886 if (iWindow
== WINDOW_PICTURES
)
888 else if (iWindow
== WINDOW_FILES
)
890 else if (iWindow
== WINDOW_GAMES
)
891 showLabel
= 35250; // "Add games..."
893 if (m_vecItems
->IsPath("sources://video/"))
895 else if (m_vecItems
->IsPath("sources://music/"))
897 else if (m_vecItems
->IsPath("sources://pictures/"))
899 else if (m_vecItems
->IsPath("sources://files/"))
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());
942 // Restore selected item from history
943 RestoreSelectedItemFromHistory();
945 m_history
.AddPath(m_vecItems
->GetPath(), m_strFilterPath
);
947 //m_history.DumpPathHistory();
952 bool CGUIMediaWindow::Refresh(bool clearCache
/* = false */)
954 std::string strCurrentDirectory
= m_vecItems
->GetPath();
955 if (strCurrentDirectory
== "?")
959 m_vecItems
->RemoveDiscCache(GetID());
963 // get the original number of items
964 if (!Update(strCurrentDirectory
, false))
973 * \brief On prepare file items
975 * This function will be called by Update() before the labels of the fileitems
978 * \note Override this function to set custom thumbs or load additional media
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());
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())
1015 const std::shared_ptr
<CProfileManager
> profileManager
= CServiceBroker::GetSettingsComponent()->GetProfileManager();
1017 CFileItemPtr pItem
= m_vecItems
->Get(iItem
);
1019 if (pItem
->IsParentFolder())
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))
1032 else if (!profileManager
->GetCurrentProfile().canWriteSources() && !g_passwordManager
.IsProfileLockUnlocked())
1035 if (OnAddMediaSource())
1041 if (!pItem
->m_bIsFolder
&& pItem
->IsFileFolder(EFILEFOLDER_MASK_ONCLICK
))
1043 XFILE::IFileDirectory
*pFileDirectory
= nullptr;
1044 pFileDirectory
= XFILE::CFileDirectoryFactory::Create(pItem
->GetURL(), pItem
.get(), "");
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());
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
);
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
))
1078 if (!HaveDiscOrConnection(pItem
->GetPath(), pItem
->m_iDriveType
))
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()))
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");
1116 CUpdateGuard
ug(m_vecItemsUpdating
);
1118 CFileItem
directory(*pItem
);
1119 if (!Update(directory
.GetPath()))
1120 ShowShareErrorMessage(&directory
);
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
);
1139 SaveSelectedItemInHistory();
1141 if (pItem
->GetPath() == "newplaylist://")
1143 m_vecItems
->RemoveDiscCache(GetID());
1144 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_MUSIC_PLAYLIST_EDITOR
,"newplaylist://");
1147 else if (StringUtils::StartsWithNoCase(pItem
->GetPath(), "newsmartplaylist://"))
1149 m_vecItems
->RemoveDiscCache(GetID());
1150 if (CGUIDialogSmartPlaylistEditor::NewPlaylist(pItem
->GetPath().substr(19)))
1155 bool autoplay
= m_guiState
.get() && m_guiState
->AutoPlayNextItem();
1157 if (m_vecItems
->IsPlugin())
1159 CURL
url(m_vecItems
->GetPath());
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
);
1179 return OnPlayMedia(iItem
, player
);
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});
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});
1221 * \brief Shows a standard error message for a given pItem.
1223 void CGUIMediaWindow::ShowShareErrorMessage(CFileItem
* pItem
) const
1225 if (!pItem
->m_bIsShareOrDrive
)
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
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())
1251 if (URIUtils::PathEquals(m_vecItems
->GetPath(), GetRootPath()))
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();
1287 // pop directory path from the stack
1288 m_strFilterPath
= m_history
.GetParentPath(true);
1289 m_history
.RemoveParentPath();
1291 if (!Update(parentPath
, 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();
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
);
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
;
1370 strHistoryString
= strLabel
;
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();
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
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
;
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();
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
;
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
);
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());
1496 bResult
= g_application
.PlayFile(*pItem
, player
);
1498 if (pItem
->GetStartOffset() == STARTOFFSET_RESUME
)
1499 pItem
->SetStartOffset(0);
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"))
1536 for ( int i
= 0; i
< m_vecItems
->Size(); i
++ )
1538 CFileItemPtr nItem
= m_vecItems
->Get(i
);
1540 if (nItem
->m_bIsFolder
)
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
1554 m_guiState
->SetPlaylistDirectory(m_vecItems
->GetPath());
1556 // figure out where we start playback
1557 if (CServiceBroker::GetPlaylistPlayer().IsShuffled(playlistId
))
1560 CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId
).FindOrder(mediaToPlay
);
1561 CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId
).Swap(0, iIndex
);
1566 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(playlistId
);
1567 CServiceBroker::GetPlaylistPlayer().Play(mediaToPlay
, player
);
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
;
1584 strSelected
= m_vecItems
->Get(nItem
)->GetPath();
1586 FormatAndSort(*m_vecItems
);
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
)
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))
1637 CGUIComponent
*gui
= CServiceBroker::GetGUI();
1638 if (gui
&& gui
->ConfirmDelete(item
->GetPath()))
1640 if (!CFileUtils::DeleteItem(item
))
1647 m_viewControl
.SetSelectedItem(iItem
);
1650 void CGUIMediaWindow::OnRenameItem(int iItem
)
1652 if (iItem
< 0 || iItem
>= m_vecItems
->Size())
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))
1663 if (!CFileUtils::RenameFile(m_vecItems
->Get(iItem
)->GetPath()))
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()))
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
);
1732 m_rootDir
.SetMask(viewState
->GetExtensions());
1733 m_rootDir
.SetSources(viewState
->GetSources());
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())
1745 auto item
= m_vecItems
->Get(itemIdx
);
1749 item
->SetProperty("ParentPath", m_vecItems
->GetPath());
1751 CContextButtons buttons
;
1753 //Add items from plugin
1756 while (item
->HasProperty(StringUtils::Format("contextmenulabel({})", i
)))
1758 buttons
.emplace_back(
1760 item
->GetProperty(StringUtils::Format("contextmenulabel({})", i
)).asString());
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());
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())
1786 int idx
= CGUIDialogContextMenu::Show(buttons
);
1787 if (idx
< 0 || idx
>= static_cast<int>(buttons
.size()))
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
))
1798 m_backgroundLoad
= saveVal
;
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())
1818 if (item
->IsFileFolder(EFILEFOLDER_MASK_ONBROWSE
))
1819 buttons
.Add(CONTEXT_BUTTON_BROWSE_INTO
, 37015);
1823 bool CGUIMediaWindow::OnContextButton(int itemNumber
, CONTEXT_BUTTON button
)
1827 case CONTEXT_BUTTON_BROWSE_INTO
:
1829 CFileItemPtr item
= m_vecItems
->Get(itemNumber
);
1830 Update(item
->GetPath());
1839 const CGUIViewState
*CGUIMediaWindow::GetViewState() const
1841 return m_guiState
.get();
1844 const CFileItemList
& CGUIMediaWindow::CurrentDirectory() const
1849 bool CGUIMediaWindow::WaitForNetwork() const
1851 if (CServiceBroker::GetNetwork().IsAvailable())
1854 CGUIDialogProgress
*progress
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogProgress
>(WINDOW_DIALOG_PROGRESS
);
1858 CURL
url(m_vecItems
->GetPath());
1859 progress
->SetHeading(CVariant
{1040}); // Loading Directory
1860 progress
->SetLine(1, CVariant
{url
.GetWithoutUserDetails()});
1861 progress
->ShowProgressBar(false);
1863 while (!CServiceBroker::GetNetwork().IsAvailable())
1865 progress
->Progress();
1866 if (progress
->IsCanceled())
1876 void CGUIMediaWindow::UpdateFilterPath(const std::string
&strDirectory
, const CFileItemList
&items
, bool updateFilterPath
)
1878 bool canfilter
= CanContainFilter(strDirectory
);
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();
1892 m_strFilterPath
= items
.GetPath();
1895 // maybe the filter path can contain a filter
1896 if (!canfilter
&& CanContainFilter(m_strFilterPath
))
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
);
1915 m_strFilterPath
= m_vecItems
->GetPath();
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
);
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())
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
);
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.
2053 /* if (item->GetFocusedLayout())
2054 match = item->GetFocusedLayout()->GetAllText();
2055 else if (item->GetLayout())
2056 match = item->GetLayout()->GetAllText();
2058 match
= item
->GetLabel(); // Filter label only for now
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
);
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"))
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
);
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());
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
));
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 */)
2153 if (!m_canFilterAdvanced
|| !advanced
)
2155 const CGUIControl
*btnFilter
= GetControl(CONTROL_BTN_FILTER
);
2156 if (btnFilter
&& btnFilter
->GetControlType() == CGUIControl::GUICONTROL_EDIT
)
2158 CGUIMessage
selected(GUI_MSG_ITEM_SELECTED
, GetID(), CONTROL_BTN_FILTER
);
2159 OnMessage(selected
);
2160 OnFilterItems(selected
.GetLabel());
2164 if (GetProperty("filter").empty())
2166 std::string filter
= GetProperty("filter").asString();
2167 CGUIKeyboardFactory::ShowAndGetFilter(filter
, false);
2168 SetProperty("filter", filter
);
2176 // advanced filtering
2178 CGUIDialogMediaFilter::ShowAndEditMediaFilter(m_strFilterPath
, m_filter
);
2183 std::string
CGUIMediaWindow::GetStartFolder(const std::string
&dir
)
2185 if (StringUtils::EqualsNoCase(dir
, "$root") ||
2186 StringUtils::EqualsNoCase(dir
, "root"))
2189 // Let plugins handle their own urls themselves
2190 if (StringUtils::StartsWith(dir
, "plugin://"))
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
2197 std::string fileName
;
2198 URIUtils::Split(dir
, path
, fileName
);
2199 URIUtils::RemoveExtension(fileName
);
2200 if (StringUtils::IsInteger(fileName
))
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
);
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
)
2229 CGetDirectoryItems
getItems(m_rootDir
, url
, items
, useDir
);
2231 if (!WaitGetDirectoryItems(getItems
))
2236 else if (!getItems
.m_result
)
2238 if (CServiceBroker::GetAppMessenger()->IsProcessThread() && m_rootDir
.GetDirImpl() &&
2239 !m_rootDir
.GetDirImpl()->ProcessRequirements())
2243 else if (!WaitGetDirectoryItems(getItems
) || !getItems
.m_result
)
2249 m_updateJobActive
= false;
2250 m_rootDir
.ReleaseDirImpl();
2255 return m_rootDir
.GetDirectory(url
, items
, useDir
, false);
2259 bool CGUIMediaWindow::WaitGetDirectoryItems(CGetDirectoryItems
&items
)
2262 CGUIDialogBusy
* dialog
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogBusy
>(WINDOW_DIALOG_BUSY
);
2263 if (dialog
&& !dialog
->IsDialogRunning())
2265 if (!CGUIDialogBusy::Wait(&items
, 100, true))
2273 m_updateJobActive
= true;
2274 m_updateEvent
.Reset();
2275 CServiceBroker::GetJobManager()->Submit(
2278 m_updateEvent
.Set();
2280 nullptr, CJob::PRIORITY_NORMAL
);
2282 while (!m_updateEvent
.Wait(1ms
))
2284 if (!ProcessRenderLoop(false))
2288 if (m_updateAborted
|| !items
.m_result
)
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;