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