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 "view/GUIViewState.h"
11 #include "AutoSwitch.h"
13 #include "FileItemList.h"
14 #include "GUIPassword.h"
15 #include "PlayListPlayer.h"
16 #include "ServiceBroker.h"
18 #include "ViewDatabase.h"
19 #include "addons/Addon.h"
20 #include "addons/AddonManager.h"
21 #include "addons/PluginSource.h"
22 #include "addons/addoninfo/AddonType.h"
23 #include "addons/gui/GUIViewStateAddonBrowser.h"
24 #include "dialogs/GUIDialogSelect.h"
25 #include "events/windows/GUIViewStateEventLog.h"
26 #include "favourites/GUIViewStateFavourites.h"
27 #include "games/windows/GUIViewStateWindowGames.h"
28 #include "guilib/GUIComponent.h"
29 #include "guilib/GUIWindowManager.h"
30 #include "guilib/LocalizeStrings.h"
31 #include "guilib/TextureManager.h"
32 #include "music/GUIViewStateMusic.h"
33 #include "pictures/GUIViewStatePictures.h"
34 #include "playlists/PlayListFileItemClassify.h"
35 #include "profiles/ProfileManager.h"
36 #include "programs/GUIViewStatePrograms.h"
37 #include "pvr/windows/GUIViewStatePVR.h"
38 #include "settings/MediaSourceSettings.h"
39 #include "settings/Settings.h"
40 #include "settings/SettingsComponent.h"
41 #include "settings/lib/Setting.h"
42 #include "utils/URIUtils.h"
43 #include "video/GUIViewStateVideo.h"
44 #include "video/VideoUtils.h"
45 #include "view/ViewState.h"
47 #define PROPERTY_SORT_ORDER "sort.order"
48 #define PROPERTY_SORT_ASCENDING "sort.ascending"
51 using namespace ADDON
;
54 std::string
CGUIViewState::m_strPlaylistDirectory
;
55 std::vector
<CMediaSource
> CGUIViewState::m_sources
;
57 CGUIViewState
* CGUIViewState::GetViewState(int windowId
, const CFileItemList
& items
)
59 // don't expect derived classes to clear the sources
63 return GetViewState(CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow(),items
);
65 const CURL url
=items
.GetURL();
67 if (items
.IsAddonsPath())
68 return new CGUIViewStateAddonBrowser(items
);
70 if (items
.HasSortDetails())
71 return new CGUIViewStateFromItems(items
);
73 if (url
.IsProtocol("musicdb"))
74 return new CGUIViewStateMusicDatabase(items
);
76 if (url
.IsProtocol("musicsearch"))
77 return new CGUIViewStateMusicSearch(items
);
79 if (PLAYLIST::IsSmartPlayList(items
) || url
.IsProtocol("upnp") || items
.IsLibraryFolder())
81 if (items
.GetContent() == "songs" ||
82 items
.GetContent() == "albums" ||
83 items
.GetContent() == "mixed")
84 return new CGUIViewStateMusicSmartPlaylist(items
);
85 else if (items
.GetContent() == "musicvideos")
86 return new CGUIViewStateVideoMusicVideos(items
);
87 else if (items
.GetContent() == "tvshows")
88 return new CGUIViewStateVideoTVShows(items
);
89 else if (items
.GetContent() == "episodes")
90 return new CGUIViewStateVideoEpisodes(items
);
91 else if (items
.GetContent() == "movies")
92 return new CGUIViewStateVideoMovies(items
);
95 if (url
.IsProtocol("library"))
96 return new CGUIViewStateLibrary(items
);
98 if (PLAYLIST::IsPlayList(items
))
100 // Playlists (like .strm) can be music or video type
101 if (windowId
== WINDOW_VIDEO_NAV
)
102 return new CGUIViewStateVideoPlaylist(items
);
104 return new CGUIViewStateMusicPlaylist(items
);
107 if (items
.GetPath() == "special://musicplaylists/")
108 return new CGUIViewStateWindowMusicNav(items
);
110 if (url
.IsProtocol("androidapp"))
111 return new CGUIViewStateWindowPrograms(items
);
113 if (url
.IsProtocol("activities"))
114 return new CGUIViewStateEventLog(items
);
116 if (windowId
== WINDOW_MUSIC_NAV
)
117 return new CGUIViewStateWindowMusicNav(items
);
119 if (windowId
== WINDOW_MUSIC_PLAYLIST
)
120 return new CGUIViewStateWindowMusicPlaylist(items
);
122 if (windowId
== WINDOW_MUSIC_PLAYLIST_EDITOR
)
123 return new CGUIViewStateWindowMusicNav(items
);
125 if (windowId
== WINDOW_VIDEO_NAV
)
126 return new CGUIViewStateWindowVideoNav(items
);
128 if (windowId
== WINDOW_VIDEO_PLAYLIST
)
129 return new CGUIViewStateWindowVideoPlaylist(items
);
131 if (windowId
== WINDOW_TV_CHANNELS
)
132 return new CGUIViewStateWindowPVRChannels(windowId
, items
);
134 if (windowId
== WINDOW_TV_RECORDINGS
)
135 return new CGUIViewStateWindowPVRRecordings(windowId
, items
);
137 if (windowId
== WINDOW_TV_GUIDE
)
138 return new CGUIViewStateWindowPVRGuide(windowId
, items
);
140 if (windowId
== WINDOW_TV_TIMERS
)
141 return new CGUIViewStateWindowPVRTimers(windowId
, items
);
143 if (windowId
== WINDOW_TV_TIMER_RULES
)
144 return new CGUIViewStateWindowPVRTimers(windowId
, items
);
146 if (windowId
== WINDOW_TV_SEARCH
)
147 return new CGUIViewStateWindowPVRSearch(windowId
, items
);
149 if (windowId
== WINDOW_TV_PROVIDERS
)
150 return new CGUIViewStateWindowPVRProviders(windowId
, items
);
152 if (windowId
== WINDOW_RADIO_CHANNELS
)
153 return new CGUIViewStateWindowPVRChannels(windowId
, items
);
155 if (windowId
== WINDOW_RADIO_RECORDINGS
)
156 return new CGUIViewStateWindowPVRRecordings(windowId
, items
);
158 if (windowId
== WINDOW_RADIO_GUIDE
)
159 return new CGUIViewStateWindowPVRGuide(windowId
, items
);
161 if (windowId
== WINDOW_RADIO_TIMERS
)
162 return new CGUIViewStateWindowPVRTimers(windowId
, items
);
164 if (windowId
== WINDOW_RADIO_TIMER_RULES
)
165 return new CGUIViewStateWindowPVRTimers(windowId
, items
);
167 if (windowId
== WINDOW_RADIO_SEARCH
)
168 return new CGUIViewStateWindowPVRSearch(windowId
, items
);
170 if (windowId
== WINDOW_RADIO_PROVIDERS
)
171 return new CGUIViewStateWindowPVRProviders(windowId
, items
);
173 if (windowId
== WINDOW_PICTURES
)
174 return new CGUIViewStateWindowPictures(items
);
176 if (windowId
== WINDOW_PROGRAMS
)
177 return new CGUIViewStateWindowPrograms(items
);
179 if (windowId
== WINDOW_GAMES
)
180 return new GAME::CGUIViewStateWindowGames(items
);
182 if (windowId
== WINDOW_ADDON_BROWSER
)
183 return new CGUIViewStateAddonBrowser(items
);
185 if (windowId
== WINDOW_EVENT_LOG
)
186 return new CGUIViewStateEventLog(items
);
188 if (windowId
== WINDOW_FAVOURITES
)
189 return new CGUIViewStateFavourites(items
);
191 // Use as fallback/default
192 return new CGUIViewStateGeneral(items
);
195 CGUIViewState::CGUIViewState(const CFileItemList
& items
) : m_items(items
)
197 m_currentViewAsControl
= 0;
198 m_currentSortMethod
= 0;
199 m_playlist
= PLAYLIST::Id::TYPE_NONE
;
202 CGUIViewState::~CGUIViewState() = default;
204 SortOrder
CGUIViewState::SetNextSortOrder()
206 if (GetSortOrder() == SortOrderAscending
)
207 SetSortOrder(SortOrderDescending
);
209 SetSortOrder(SortOrderAscending
);
213 return GetSortOrder();
216 SortOrder
CGUIViewState::GetSortOrder() const
218 if (m_currentSortMethod
>= 0 && m_currentSortMethod
< (int)m_sortMethods
.size())
219 return m_sortMethods
[m_currentSortMethod
].m_sortDescription
.sortOrder
;
221 return SortOrderAscending
;
224 int CGUIViewState::GetSortOrderLabel() const
226 if (m_currentSortMethod
>= 0 && m_currentSortMethod
< (int)m_sortMethods
.size())
227 if (m_sortMethods
[m_currentSortMethod
].m_sortDescription
.sortOrder
== SortOrderDescending
)
230 return 584; // default sort order label 'Ascending'
233 int CGUIViewState::GetViewAsControl() const
235 return m_currentViewAsControl
;
238 void CGUIViewState::SetViewAsControl(int viewAsControl
)
240 if (viewAsControl
== DEFAULT_VIEW_AUTO
)
241 m_currentViewAsControl
= CAutoSwitch::GetView(m_items
);
243 m_currentViewAsControl
= viewAsControl
;
246 void CGUIViewState::SaveViewAsControl(int viewAsControl
)
248 SetViewAsControl(viewAsControl
);
252 SortDescription
CGUIViewState::GetSortMethod() const
254 SortDescription sorting
;
255 if (m_currentSortMethod
>= 0 && m_currentSortMethod
< (int)m_sortMethods
.size())
256 sorting
= m_sortMethods
[m_currentSortMethod
].m_sortDescription
;
261 bool CGUIViewState::HasMultipleSortMethods() const
263 return m_sortMethods
.size() > 1;
266 int CGUIViewState::GetSortMethodLabel() const
268 if (m_currentSortMethod
>= 0 && m_currentSortMethod
< (int)m_sortMethods
.size())
269 return m_sortMethods
[m_currentSortMethod
].m_buttonLabel
;
271 return 551; // default sort method label 'Name'
274 void CGUIViewState::GetSortMethodLabelMasks(LABEL_MASKS
& masks
) const
276 if (m_currentSortMethod
>= 0 && m_currentSortMethod
< (int)m_sortMethods
.size())
278 masks
= m_sortMethods
[m_currentSortMethod
].m_labelMasks
;
282 masks
.m_strLabelFile
.clear();
283 masks
.m_strLabel2File
.clear();
284 masks
.m_strLabelFolder
.clear();
285 masks
.m_strLabel2Folder
.clear();
288 std::vector
<SortDescription
> CGUIViewState::GetSortDescriptions() const
290 std::vector
<SortDescription
> descriptions
;
291 descriptions
.reserve(m_sortMethods
.size());
292 for (const auto& desc
: m_sortMethods
)
294 descriptions
.emplace_back(desc
.m_sortDescription
);
299 void CGUIViewState::AddSortMethod(SortBy sortBy
, int buttonLabel
, const LABEL_MASKS
&labelMasks
, SortAttribute sortAttributes
/* = SortAttributeNone */, SortOrder sortOrder
/* = SortOrderNone */)
301 AddSortMethod(sortBy
, sortAttributes
, buttonLabel
, labelMasks
, sortOrder
);
304 void CGUIViewState::AddSortMethod(SortBy sortBy
, SortAttribute sortAttributes
, int buttonLabel
, const LABEL_MASKS
&labelMasks
, SortOrder sortOrder
/* = SortOrderNone */)
306 for (size_t i
= 0; i
< m_sortMethods
.size(); ++i
)
307 if (m_sortMethods
[i
].m_sortDescription
.sortBy
== sortBy
)
310 // handle unspecified sort order
311 if (sortBy
!= SortByNone
&& sortOrder
== SortOrderNone
)
313 // the following sort methods are sorted in descending order by default
314 if (sortBy
== SortByDate
|| sortBy
== SortBySize
|| sortBy
== SortByPlaycount
||
315 sortBy
== SortByRating
|| sortBy
== SortByProgramCount
||
316 sortBy
== SortByBitrate
|| sortBy
== SortByListeners
||
317 sortBy
== SortByUserRating
|| sortBy
== SortByLastPlayed
)
318 sortOrder
= SortOrderDescending
;
320 sortOrder
= SortOrderAscending
;
323 GUIViewSortDetails sort
;
324 sort
.m_sortDescription
.sortBy
= sortBy
;
325 sort
.m_sortDescription
.sortOrder
= sortOrder
;
326 sort
.m_sortDescription
.sortAttributes
= sortAttributes
;
327 sort
.m_buttonLabel
= buttonLabel
;
328 sort
.m_labelMasks
= labelMasks
;
329 m_sortMethods
.push_back(sort
);
332 void CGUIViewState::AddSortMethod(SortDescription sortDescription
, int buttonLabel
, const LABEL_MASKS
&labelMasks
)
334 AddSortMethod(sortDescription
.sortBy
, sortDescription
.sortAttributes
, buttonLabel
, labelMasks
, sortDescription
.sortOrder
);
337 void CGUIViewState::SetCurrentSortMethod(int method
)
339 SortBy sortBy
= (SortBy
)method
;
340 if (sortBy
< SortByNone
|| sortBy
> SortByLastUsed
)
343 SetSortMethod(sortBy
);
347 void CGUIViewState::SetSortMethod(SortBy sortBy
, SortOrder sortOrder
/* = SortOrderNone */)
349 for (int i
= 0; i
< (int)m_sortMethods
.size(); ++i
)
351 if (m_sortMethods
[i
].m_sortDescription
.sortBy
== sortBy
)
353 m_currentSortMethod
= i
;
358 if (sortOrder
!= SortOrderNone
)
359 SetSortOrder(sortOrder
);
362 void CGUIViewState::SetSortMethod(SortDescription sortDescription
)
364 return SetSortMethod(sortDescription
.sortBy
, sortDescription
.sortOrder
);
367 bool CGUIViewState::ChooseSortMethod()
370 CGUIDialogSelect
*dialog
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogSelect
>(WINDOW_DIALOG_SELECT
);
374 dialog
->SetHeading(CVariant
{ 39010 }); // Label "Sort by"
375 for (auto &sortMethod
: m_sortMethods
)
376 dialog
->Add(g_localizeStrings
.Get(sortMethod
.m_buttonLabel
));
377 dialog
->SetSelected(m_currentSortMethod
);
379 int newSelected
= dialog
->GetSelectedItem();
380 // check if selection has changed
381 if (!dialog
->IsConfirmed() || newSelected
< 0 || newSelected
== m_currentSortMethod
)
384 m_currentSortMethod
= newSelected
;
389 SortDescription
CGUIViewState::SetNextSortMethod(int direction
/* = 1 */)
391 m_currentSortMethod
+= direction
;
393 if (m_currentSortMethod
>= (int)m_sortMethods
.size())
394 m_currentSortMethod
= 0;
395 if (m_currentSortMethod
< 0)
396 m_currentSortMethod
= m_sortMethods
.size() ? (int)m_sortMethods
.size() - 1 : 0;
400 return GetSortMethod();
403 bool CGUIViewState::HideExtensions()
405 return !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWEXTENSIONS
);
408 bool CGUIViewState::HideParentDirItems()
410 return !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWPARENTDIRITEMS
);
413 bool CGUIViewState::DisableAddSourceButtons()
415 const std::shared_ptr
<CProfileManager
> profileManager
= CServiceBroker::GetSettingsComponent()->GetProfileManager();
417 if (profileManager
->GetCurrentProfile().canWriteSources() || g_passwordManager
.bMasterUser
)
418 return !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWADDSOURCEBUTTONS
);
423 PLAYLIST::Id
CGUIViewState::GetPlaylist() const
428 const std::string
& CGUIViewState::GetPlaylistDirectory()
430 return m_strPlaylistDirectory
;
433 void CGUIViewState::SetPlaylistDirectory(const std::string
& strDirectory
)
435 m_strPlaylistDirectory
= strDirectory
;
436 URIUtils::RemoveSlashAtEnd(m_strPlaylistDirectory
);
439 bool CGUIViewState::IsCurrentPlaylistDirectory(const std::string
& strDirectory
)
441 if (CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist()!=GetPlaylist())
444 std::string strDir
= strDirectory
;
445 URIUtils::RemoveSlashAtEnd(strDir
);
447 return m_strPlaylistDirectory
== strDir
;
450 bool CGUIViewState::AutoPlayNextItem()
455 std::string
CGUIViewState::GetLockType()
460 std::string
CGUIViewState::GetExtensions()
465 std::vector
<CMediaSource
>& CGUIViewState::GetSources()
470 void CGUIViewState::AddLiveTVSources()
472 std::vector
<CMediaSource
>* sources
= CMediaSourceSettings::GetInstance().GetSources("video");
473 for (std::vector
<CMediaSource
>::iterator it
= sources
->begin(); it
!= sources
->end(); ++it
)
475 if (URIUtils::IsLiveTV((*it
).strPath
))
478 source
.strPath
= (*it
).strPath
;
479 source
.strName
= (*it
).strName
;
480 source
.vecPaths
= (*it
).vecPaths
;
481 source
.m_strThumbnailImage
= "";
482 source
.FromNameAndPaths("video", source
.strName
, source
.vecPaths
);
483 m_sources
.push_back(source
);
488 void CGUIViewState::SetSortOrder(SortOrder sortOrder
)
490 if (sortOrder
== SortOrderNone
)
493 if (m_currentSortMethod
< 0 || m_currentSortMethod
>= (int)m_sortMethods
.size())
496 m_sortMethods
[m_currentSortMethod
].m_sortDescription
.sortOrder
= sortOrder
;
499 bool CGUIViewState::AutoPlayNextVideoItem() const
501 if (GetPlaylist() != PLAYLIST::Id::TYPE_VIDEO
)
504 return VIDEO::UTILS::IsAutoPlayNextItem(m_items
.GetContent());
507 void CGUIViewState::LoadViewState(const std::string
&path
, int windowID
)
508 { // get our view state from the db
514 if (db
.GetViewState(path
, windowID
, state
, CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOOKANDFEEL_SKIN
)) ||
515 db
.GetViewState(path
, windowID
, state
, ""))
517 SetViewAsControl(state
.m_viewMode
);
518 SetSortMethod(state
.m_sortDescription
);
522 void CGUIViewState::SaveViewToDb(const std::string
&path
, int windowID
, CViewState
*viewState
)
528 SortDescription sorting
= GetSortMethod();
529 CViewState
state(m_currentViewAsControl
, sorting
.sortBy
, sorting
.sortOrder
, sorting
.sortAttributes
);
530 if (viewState
!= NULL
)
533 const std::shared_ptr
<CSettings
> settings
= CServiceBroker::GetSettingsComponent()->GetSettings();
534 db
.SetViewState(path
, windowID
, state
, settings
->GetString(CSettings::SETTING_LOOKANDFEEL_SKIN
));
537 if (viewState
!= NULL
)
541 void CGUIViewState::AddPlaylistOrder(const CFileItemList
& items
, const LABEL_MASKS
& label_masks
)
543 SortBy sortBy
= SortByPlaylistOrder
;
545 SortOrder sortOrder
= SortOrderAscending
;
546 if (items
.HasProperty(PROPERTY_SORT_ORDER
))
548 sortBy
= (SortBy
)items
.GetProperty(PROPERTY_SORT_ORDER
).asInteger();
549 if (sortBy
!= SortByNone
)
551 sortLabel
= SortUtils::GetSortLabel(sortBy
);
552 sortOrder
= items
.GetProperty(PROPERTY_SORT_ASCENDING
).asBoolean() ? SortOrderAscending
: SortOrderDescending
;
556 AddSortMethod(sortBy
, sortLabel
, label_masks
, SortAttributeNone
, sortOrder
);
557 SetSortMethod(sortBy
, sortOrder
);
560 CGUIViewStateGeneral::CGUIViewStateGeneral(const CFileItemList
& items
) : CGUIViewState(items
)
562 AddSortMethod(SortByLabel
, 551, LABEL_MASKS("%F", "%I", "%L", "")); // Filename, size | Foldername, empty
563 SetSortMethod(SortByLabel
);
565 SetViewAsControl(DEFAULT_VIEW_LIST
);
568 CGUIViewStateFromItems::CGUIViewStateFromItems(const CFileItemList
&items
) : CGUIViewState(items
)
570 const std::vector
<GUIViewSortDetails
> &details
= items
.GetSortDetails();
571 for (unsigned int i
= 0; i
< details
.size(); i
++)
573 const GUIViewSortDetails
& sort
= details
[i
];
574 AddSortMethod(sort
.m_sortDescription
, sort
.m_buttonLabel
, sort
.m_labelMasks
);
576 //! @todo Should default sort/view mode be specified?
577 m_currentSortMethod
= 0;
579 SetViewAsControl(DEFAULT_VIEW_LIST
);
581 if (items
.IsPlugin())
583 CURL
url(items
.GetPath());
585 if (CServiceBroker::GetAddonMgr().GetAddon(url
.GetHostName(), addon
, AddonType::PLUGIN
,
586 OnlyEnabled::CHOICE_YES
))
588 const auto plugin
= std::static_pointer_cast
<CPluginSource
>(addon
);
589 if (plugin
->Provides(CPluginSource::AUDIO
))
590 m_playlist
= PLAYLIST::Id::TYPE_MUSIC
;
591 if (plugin
->Provides(CPluginSource::VIDEO
))
592 m_playlist
= PLAYLIST::Id::TYPE_VIDEO
;
596 LoadViewState(items
.GetPath(), CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow());
599 bool CGUIViewStateFromItems::AutoPlayNextItem()
601 return AutoPlayNextVideoItem();
604 void CGUIViewStateFromItems::SaveViewState()
606 SaveViewToDb(m_items
.GetPath(), CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow());
609 CGUIViewStateLibrary::CGUIViewStateLibrary(const CFileItemList
&items
) : CGUIViewState(items
)
611 AddSortMethod(SortByNone
, 551, LABEL_MASKS("%F", "%I", "%L", "")); // Filename, Size | Foldername, empty
612 SetSortMethod(SortByNone
);
614 SetViewAsControl(DEFAULT_VIEW_LIST
);
616 LoadViewState(items
.GetPath(), CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow());
619 void CGUIViewStateLibrary::SaveViewState()
621 SaveViewToDb(m_items
.GetPath(), CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow());