diff options
Diffstat (limited to 'xbmc/view')
-rw-r--r-- | xbmc/view/CMakeLists.txt | 12 | ||||
-rw-r--r-- | xbmc/view/GUIViewControl.cpp | 354 | ||||
-rw-r--r-- | xbmc/view/GUIViewControl.h | 67 | ||||
-rw-r--r-- | xbmc/view/GUIViewState.cpp | 637 | ||||
-rw-r--r-- | xbmc/view/GUIViewState.h | 126 | ||||
-rw-r--r-- | xbmc/view/ViewDatabase.cpp | 208 | ||||
-rw-r--r-- | xbmc/view/ViewDatabase.h | 34 | ||||
-rw-r--r-- | xbmc/view/ViewState.h | 40 | ||||
-rw-r--r-- | xbmc/view/ViewStateSettings.cpp | 271 | ||||
-rw-r--r-- | xbmc/view/ViewStateSettings.h | 62 |
10 files changed, 1811 insertions, 0 deletions
diff --git a/xbmc/view/CMakeLists.txt b/xbmc/view/CMakeLists.txt new file mode 100644 index 0000000..1370a73 --- /dev/null +++ b/xbmc/view/CMakeLists.txt @@ -0,0 +1,12 @@ +set(SOURCES GUIViewControl.cpp + GUIViewState.cpp + ViewDatabase.cpp + ViewStateSettings.cpp) + +set(HEADERS GUIViewControl.h + GUIViewState.h + ViewDatabase.h + ViewState.h + ViewStateSettings.h) + +core_add_library(view) diff --git a/xbmc/view/GUIViewControl.cpp b/xbmc/view/GUIViewControl.cpp new file mode 100644 index 0000000..3d4801b --- /dev/null +++ b/xbmc/view/GUIViewControl.cpp @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "GUIViewControl.h" + +#include "FileItem.h" +#include "GUIInfoManager.h" +#include "ServiceBroker.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/IGUIContainer.h" +#include "guilib/LocalizeStrings.h" +#include "guilib/WindowIDs.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" + +#include <utility> + +CGUIViewControl::CGUIViewControl(void) +{ + m_viewAsControl = -1; + m_parentWindow = WINDOW_INVALID; + m_fileItems = nullptr; + Reset(); +} + +CGUIViewControl::~CGUIViewControl(void) = default; + +void CGUIViewControl::Reset() +{ + m_currentView = -1; + m_visibleViews.clear(); + m_allViews.clear(); +} + +void CGUIViewControl::AddView(const CGUIControl *control) +{ + if (!control || !control->IsContainer()) return; + m_allViews.push_back(const_cast<CGUIControl*>(control)); +} + +void CGUIViewControl::SetViewControlID(int control) +{ + m_viewAsControl = control; +} + +void CGUIViewControl::SetParentWindow(int window) +{ + m_parentWindow = window; +} + +void CGUIViewControl::SetCurrentView(int viewMode, bool bRefresh /* = false */) +{ + // grab the previous control + CGUIControl *previousView = nullptr; + if (m_currentView >= 0 && m_currentView < (int)m_visibleViews.size()) + previousView = m_visibleViews[m_currentView]; + + UpdateViewVisibility(); + + // viewMode is of the form TYPE << 16 | ID + VIEW_TYPE type = (VIEW_TYPE)(viewMode >> 16); + int id = viewMode & 0xffff; + + // first find a view that matches this view, if possible... + int newView = GetView(type, id); + if (newView < 0) // no suitable view that matches both id and type, so try just type + newView = GetView(type, 0); + if (newView < 0 && type == VIEW_TYPE_BIG_ICON) // try icon view if they want big icon + newView = GetView(VIEW_TYPE_ICON, 0); + if (newView < 0 && type == VIEW_TYPE_BIG_INFO) + newView = GetView(VIEW_TYPE_INFO, 0); + if (newView < 0) // try a list view + newView = GetView(VIEW_TYPE_LIST, 0); + if (newView < 0) // try anything! + newView = GetView(VIEW_TYPE_NONE, 0); + + if (newView < 0) + return; + + m_currentView = newView; + CGUIControl *pNewView = m_visibleViews[m_currentView]; + + // make only current control visible... + for (ciViews view = m_allViews.begin(); view != m_allViews.end(); ++view) + (*view)->SetVisible(false); + pNewView->SetVisible(true); + + if (!bRefresh && pNewView == previousView) + return; // no need to actually update anything (other than visibility above) + + // CLog::Log(LOGDEBUG,"SetCurrentView: Oldview: {}, Newview :{}", m_currentView, viewMode); + + bool hasFocus(false); + int item = -1; + if (previousView) + { // have an old view - let's clear it out and hide it. + hasFocus = previousView->HasFocus(); + item = GetSelectedItem(previousView); + CGUIMessage msg(GUI_MSG_LABEL_RESET, m_parentWindow, previousView->GetID()); + previousView->OnMessage(msg); + } + + // Update it with the contents + UpdateContents(pNewView, item); + + // and focus if necessary + if (hasFocus) + { + CGUIMessage msg(GUI_MSG_SETFOCUS, m_parentWindow, pNewView->GetID(), 0); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg, m_parentWindow); + } + + UpdateViewAsControl(static_cast<IGUIContainer*>(pNewView)->GetLabel()); +} + +void CGUIViewControl::SetItems(CFileItemList &items) +{ + // CLog::Log(LOGDEBUG,"SetItems: {}", m_currentView); + m_fileItems = &items; + // update our current view control... + UpdateView(); +} + +void CGUIViewControl::UpdateContents(const CGUIControl *control, int currentItem) const +{ + if (!control || !m_fileItems) return; + CGUIMessage msg(GUI_MSG_LABEL_BIND, m_parentWindow, control->GetID(), currentItem, 0, m_fileItems); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg, m_parentWindow); +} + +void CGUIViewControl::UpdateView() +{ + // CLog::Log(LOGDEBUG,"UpdateView: {}", m_currentView); + if (m_currentView < 0 || m_currentView >= (int)m_visibleViews.size()) + return; // no valid current view! + + CGUIControl *pControl = m_visibleViews[m_currentView]; + // get the currently selected item + int item = GetSelectedItem(pControl); + UpdateContents(pControl, item < 0 ? 0 : item); +} + +int CGUIViewControl::GetSelectedItem(const CGUIControl *control) const +{ + if (!control || !m_fileItems) + return -1; + + CGUIMessage msg(GUI_MSG_ITEM_SELECTED, m_parentWindow, control->GetID()); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg, m_parentWindow); + + int iItem = msg.GetParam1(); + if (iItem >= m_fileItems->Size()) + return -1; + + return iItem; +} + +int CGUIViewControl::GetSelectedItem() const +{ + if (m_currentView < 0 || m_currentView >= (int)m_visibleViews.size()) + return -1; // no valid current view! + + return GetSelectedItem(m_visibleViews[m_currentView]); +} + +std::string CGUIViewControl::GetSelectedItemPath() const +{ + if (m_currentView < 0 || (size_t)m_currentView >= m_visibleViews.size()) + return ""; + + int selectedItem = GetSelectedItem(m_visibleViews[m_currentView]); + if (selectedItem > -1) + { + CFileItemPtr fileItem = m_fileItems->Get(selectedItem); + if (fileItem) + return fileItem->GetPath(); + } + + return ""; +} + +void CGUIViewControl::SetSelectedItem(int item) +{ + if (!m_fileItems || item < 0 || item >= m_fileItems->Size()) + return; + + if (m_currentView < 0 || m_currentView >= (int)m_visibleViews.size()) + return; // no valid current view! + + CGUIMessage msg(GUI_MSG_ITEM_SELECT, m_parentWindow, m_visibleViews[m_currentView]->GetID(), item); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg, m_parentWindow); +} + +void CGUIViewControl::SetSelectedItem(const std::string &itemPath) +{ + if (!m_fileItems || itemPath.empty()) + return; + + std::string comparePath(itemPath); + URIUtils::RemoveSlashAtEnd(comparePath); + + int item = -1; + for (int i = 0; i < m_fileItems->Size(); ++i) + { + std::string strPath =(*m_fileItems)[i]->GetPath(); + URIUtils::RemoveSlashAtEnd(strPath); + if (strPath == comparePath) + { + item = i; + break; + } + } + SetSelectedItem(item); +} + +void CGUIViewControl::SetFocused() +{ + if (m_currentView < 0 || m_currentView >= (int)m_visibleViews.size()) + return; // no valid current view! + + CGUIMessage msg(GUI_MSG_SETFOCUS, m_parentWindow, m_visibleViews[m_currentView]->GetID(), 0); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg, m_parentWindow); +} + +bool CGUIViewControl::HasControl(int viewControlID) const +{ + // run through our controls, checking for the id + for (ciViews it = m_allViews.begin(); it != m_allViews.end(); ++it) + { + if ((*it)->GetID() == viewControlID) + return true; + } + return false; +} + +int CGUIViewControl::GetCurrentControl() const +{ + if (m_currentView < 0 || m_currentView >= (int)m_visibleViews.size()) + return -1; // no valid current view! + + return m_visibleViews[m_currentView]->GetID(); +} + +// returns the number-th view's viewmode (type and id) +int CGUIViewControl::GetViewModeNumber(int number) const +{ + IGUIContainer *nextView = nullptr; + if (number >= 0 && number < (int)m_visibleViews.size()) + nextView = static_cast<IGUIContainer*>(m_visibleViews[number]); + else if (m_visibleViews.size()) + nextView = static_cast<IGUIContainer*>(m_visibleViews[0]); + if (nextView) + return (nextView->GetType() << 16) | nextView->GetID(); + return 0; // no view modes :( +} + +// returns the amount of visible views +int CGUIViewControl::GetViewModeCount() const +{ + return static_cast<int>(m_visibleViews.size()); +} + +int CGUIViewControl::GetViewModeByID(int id) const +{ + for (unsigned int i = 0; i < m_visibleViews.size(); ++i) + { + IGUIContainer *view = static_cast<IGUIContainer*>(m_visibleViews[i]); + if (view->GetID() == id) + return (view->GetType() << 16) | view->GetID(); + } + return 0; // no view modes :( +} + +// returns the next viewmode in the cycle +int CGUIViewControl::GetNextViewMode(int direction) const +{ + if (!m_visibleViews.size()) + return 0; // no view modes :( + + int viewNumber = (m_currentView + direction) % (int)m_visibleViews.size(); + if (viewNumber < 0) viewNumber += m_visibleViews.size(); + IGUIContainer *nextView = static_cast<IGUIContainer*>(m_visibleViews[viewNumber]); + return (nextView->GetType() << 16) | nextView->GetID(); +} + +void CGUIViewControl::Clear() +{ + if (m_currentView < 0 || m_currentView >= (int)m_visibleViews.size()) + return; // no valid current view! + + CGUIMessage msg(GUI_MSG_LABEL_RESET, m_parentWindow, m_visibleViews[m_currentView]->GetID(), 0); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg, m_parentWindow); +} + +int CGUIViewControl::GetView(VIEW_TYPE type, int id) const +{ + for (int i = 0; i < (int)m_visibleViews.size(); i++) + { + IGUIContainer *view = static_cast<IGUIContainer*>(m_visibleViews[i]); + if ((type == VIEW_TYPE_NONE || type == view->GetType()) && (!id || view->GetID() == id)) + return i; + } + return -1; +} + +void CGUIViewControl::UpdateViewAsControl(const std::string &viewLabel) +{ + // the view as control could be a select/spin/dropdown button + std::vector< std::pair<std::string, int> > labels; + for (unsigned int i = 0; i < m_visibleViews.size(); i++) + { + IGUIContainer *view = static_cast<IGUIContainer*>(m_visibleViews[i]); + std::string label = StringUtils::Format(g_localizeStrings.Get(534), + view->GetLabel()); // View: {} + labels.emplace_back(std::move(label), i); + } + CGUIMessage msg(GUI_MSG_SET_LABELS, m_parentWindow, m_viewAsControl, m_currentView); + msg.SetPointer(&labels); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg, m_parentWindow); + + // otherwise it's just a normal button + std::string label = StringUtils::Format(g_localizeStrings.Get(534), viewLabel); // View: {} + CGUIMessage msgSet(GUI_MSG_LABEL_SET, m_parentWindow, m_viewAsControl); + msgSet.SetLabel(label); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msgSet, m_parentWindow); +} + +void CGUIViewControl::UpdateViewVisibility() +{ + // first reset our infomanager cache, as it's likely that the vis conditions + // used for views (i.e. based on contenttype) may have changed + CGUIInfoManager& infoMgr = CServiceBroker::GetGUI()->GetInfoManager(); + infoMgr.ResetCache(); + infoMgr.GetInfoProviders().GetGUIControlsInfoProvider().ResetContainerMovingCache(); + m_visibleViews.clear(); + for (unsigned int i = 0; i < m_allViews.size(); i++) + { + CGUIControl *view = m_allViews[i]; + if (view->HasVisibleCondition()) + { + view->UpdateVisibility(nullptr); + if (view->IsVisibleFromSkin()) + m_visibleViews.push_back(view); + } + else + m_visibleViews.push_back(view); + } +} diff --git a/xbmc/view/GUIViewControl.h b/xbmc/view/GUIViewControl.h new file mode 100644 index 0000000..40a70f7 --- /dev/null +++ b/xbmc/view/GUIViewControl.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "windowing/GraphicContext.h" // for VIEW_TYPE + +#include <string> +#include <vector> + +class CGUIControl; +class CFileItemList; + +class CGUIViewControl +{ +public: + CGUIViewControl(); + virtual ~CGUIViewControl(); + + void Reset(); + void SetParentWindow(int window); + void AddView(const CGUIControl *control); + void SetViewControlID(int control); + + void SetCurrentView(int viewMode, bool bRefresh = false); + + void SetItems(CFileItemList &items); + + void SetSelectedItem(int item); + void SetSelectedItem(const std::string &itemPath); + + int GetSelectedItem() const; + std::string GetSelectedItemPath() const; + void SetFocused(); + + bool HasControl(int controlID) const; + int GetNextViewMode(int direction = 1) const; + int GetViewModeNumber(int number) const; + int GetViewModeCount() const; + int GetViewModeByID(int id) const; + + int GetCurrentControl() const; + + void Clear(); + +protected: + int GetSelectedItem(const CGUIControl *control) const; + void UpdateContents(const CGUIControl *control, int currentItem) const; + void UpdateView(); + void UpdateViewAsControl(const std::string &viewLabel); + void UpdateViewVisibility(); + int GetView(VIEW_TYPE type, int id) const; + + std::vector<CGUIControl*> m_allViews; + std::vector<CGUIControl*> m_visibleViews; + typedef std::vector<CGUIControl*>::const_iterator ciViews; + + CFileItemList* m_fileItems; + int m_viewAsControl; + int m_parentWindow; + int m_currentView; +}; diff --git a/xbmc/view/GUIViewState.cpp b/xbmc/view/GUIViewState.cpp new file mode 100644 index 0000000..358bb63 --- /dev/null +++ b/xbmc/view/GUIViewState.cpp @@ -0,0 +1,637 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "view/GUIViewState.h" + +#include "AutoSwitch.h" +#include "FileItem.h" +#include "GUIPassword.h" +#include "PlayListPlayer.h" +#include "ServiceBroker.h" +#include "URL.h" +#include "ViewDatabase.h" +#include "addons/Addon.h" +#include "addons/AddonManager.h" +#include "addons/PluginSource.h" +#include "addons/addoninfo/AddonType.h" +#include "addons/gui/GUIViewStateAddonBrowser.h" +#include "dialogs/GUIDialogSelect.h" +#include "events/windows/GUIViewStateEventLog.h" +#include "favourites/GUIViewStateFavourites.h" +#include "games/windows/GUIViewStateWindowGames.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "guilib/TextureManager.h" +#include "music/GUIViewStateMusic.h" +#include "pictures/GUIViewStatePictures.h" +#include "profiles/ProfileManager.h" +#include "programs/GUIViewStatePrograms.h" +#include "pvr/windows/GUIViewStatePVR.h" +#include "settings/MediaSourceSettings.h" +#include "settings/SettingUtils.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "settings/lib/Setting.h" +#include "utils/URIUtils.h" +#include "video/GUIViewStateVideo.h" +#include "view/ViewState.h" + +#define PROPERTY_SORT_ORDER "sort.order" +#define PROPERTY_SORT_ASCENDING "sort.ascending" + +using namespace KODI; +using namespace ADDON; +using namespace PVR; + +std::string CGUIViewState::m_strPlaylistDirectory; +VECSOURCES CGUIViewState::m_sources; + +static const int SETTING_AUTOPLAYNEXT_MUSICVIDEOS = 0; +static const int SETTING_AUTOPLAYNEXT_TVSHOWS = 1; +static const int SETTING_AUTOPLAYNEXT_EPISODES = 2; +static const int SETTING_AUTOPLAYNEXT_MOVIES = 3; +static const int SETTING_AUTOPLAYNEXT_UNCATEGORIZED = 4; + +CGUIViewState* CGUIViewState::GetViewState(int windowId, const CFileItemList& items) +{ + // don't expect derived classes to clear the sources + m_sources.clear(); + + if (windowId == 0) + return GetViewState(CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow(),items); + + const CURL url=items.GetURL(); + + if (items.IsAddonsPath()) + return new CGUIViewStateAddonBrowser(items); + + if (items.HasSortDetails()) + return new CGUIViewStateFromItems(items); + + if (url.IsProtocol("musicdb")) + return new CGUIViewStateMusicDatabase(items); + + if (url.IsProtocol("musicsearch")) + return new CGUIViewStateMusicSearch(items); + + if (items.IsSmartPlayList() || url.IsProtocol("upnp") || + items.IsLibraryFolder()) + { + if (items.GetContent() == "songs" || + items.GetContent() == "albums" || + items.GetContent() == "mixed") + return new CGUIViewStateMusicSmartPlaylist(items); + else if (items.GetContent() == "musicvideos") + return new CGUIViewStateVideoMusicVideos(items); + else if (items.GetContent() == "tvshows") + return new CGUIViewStateVideoTVShows(items); + else if (items.GetContent() == "episodes") + return new CGUIViewStateVideoEpisodes(items); + else if (items.GetContent() == "movies") + return new CGUIViewStateVideoMovies(items); + } + + if (url.IsProtocol("library")) + return new CGUIViewStateLibrary(items); + + if (items.IsPlayList()) + { + // Playlists (like .strm) can be music or video type + if (windowId == WINDOW_VIDEO_NAV) + return new CGUIViewStateVideoPlaylist(items); + else + return new CGUIViewStateMusicPlaylist(items); + } + + if (items.GetPath() == "special://musicplaylists/") + return new CGUIViewStateWindowMusicNav(items); + + if (url.IsProtocol("androidapp")) + return new CGUIViewStateWindowPrograms(items); + + if (url.IsProtocol("activities")) + return new CGUIViewStateEventLog(items); + + if (windowId == WINDOW_MUSIC_NAV) + return new CGUIViewStateWindowMusicNav(items); + + if (windowId == WINDOW_MUSIC_PLAYLIST) + return new CGUIViewStateWindowMusicPlaylist(items); + + if (windowId == WINDOW_MUSIC_PLAYLIST_EDITOR) + return new CGUIViewStateWindowMusicNav(items); + + if (windowId == WINDOW_VIDEO_NAV) + return new CGUIViewStateWindowVideoNav(items); + + if (windowId == WINDOW_VIDEO_PLAYLIST) + return new CGUIViewStateWindowVideoPlaylist(items); + + if (windowId == WINDOW_TV_CHANNELS) + return new CGUIViewStateWindowPVRChannels(windowId, items); + + if (windowId == WINDOW_TV_RECORDINGS) + return new CGUIViewStateWindowPVRRecordings(windowId, items); + + if (windowId == WINDOW_TV_GUIDE) + return new CGUIViewStateWindowPVRGuide(windowId, items); + + if (windowId == WINDOW_TV_TIMERS) + return new CGUIViewStateWindowPVRTimers(windowId, items); + + if (windowId == WINDOW_TV_TIMER_RULES) + return new CGUIViewStateWindowPVRTimers(windowId, items); + + if (windowId == WINDOW_TV_SEARCH) + return new CGUIViewStateWindowPVRSearch(windowId, items); + + if (windowId == WINDOW_RADIO_CHANNELS) + return new CGUIViewStateWindowPVRChannels(windowId, items); + + if (windowId == WINDOW_RADIO_RECORDINGS) + return new CGUIViewStateWindowPVRRecordings(windowId, items); + + if (windowId == WINDOW_RADIO_GUIDE) + return new CGUIViewStateWindowPVRGuide(windowId, items); + + if (windowId == WINDOW_RADIO_TIMERS) + return new CGUIViewStateWindowPVRTimers(windowId, items); + + if (windowId == WINDOW_RADIO_TIMER_RULES) + return new CGUIViewStateWindowPVRTimers(windowId, items); + + if (windowId == WINDOW_RADIO_SEARCH) + return new CGUIViewStateWindowPVRSearch(windowId, items); + + if (windowId == WINDOW_PICTURES) + return new CGUIViewStateWindowPictures(items); + + if (windowId == WINDOW_PROGRAMS) + return new CGUIViewStateWindowPrograms(items); + + if (windowId == WINDOW_GAMES) + return new GAME::CGUIViewStateWindowGames(items); + + if (windowId == WINDOW_ADDON_BROWSER) + return new CGUIViewStateAddonBrowser(items); + + if (windowId == WINDOW_EVENT_LOG) + return new CGUIViewStateEventLog(items); + + if (windowId == WINDOW_FAVOURITES) + return new CGUIViewStateFavourites(items); + + // Use as fallback/default + return new CGUIViewStateGeneral(items); +} + +CGUIViewState::CGUIViewState(const CFileItemList& items) : m_items(items) +{ + m_currentViewAsControl = 0; + m_currentSortMethod = 0; + m_playlist = PLAYLIST::TYPE_NONE; +} + +CGUIViewState::~CGUIViewState() = default; + +SortOrder CGUIViewState::SetNextSortOrder() +{ + if (GetSortOrder() == SortOrderAscending) + SetSortOrder(SortOrderDescending); + else + SetSortOrder(SortOrderAscending); + + SaveViewState(); + + return GetSortOrder(); +} + +SortOrder CGUIViewState::GetSortOrder() const +{ + if (m_currentSortMethod >= 0 && m_currentSortMethod < (int)m_sortMethods.size()) + return m_sortMethods[m_currentSortMethod].m_sortDescription.sortOrder; + + return SortOrderAscending; +} + +int CGUIViewState::GetSortOrderLabel() const +{ + if (m_currentSortMethod >= 0 && m_currentSortMethod < (int)m_sortMethods.size()) + if (m_sortMethods[m_currentSortMethod].m_sortDescription.sortOrder == SortOrderDescending) + return 585; + + return 584; // default sort order label 'Ascending' +} + +int CGUIViewState::GetViewAsControl() const +{ + return m_currentViewAsControl; +} + +void CGUIViewState::SetViewAsControl(int viewAsControl) +{ + if (viewAsControl == DEFAULT_VIEW_AUTO) + m_currentViewAsControl = CAutoSwitch::GetView(m_items); + else + m_currentViewAsControl = viewAsControl; +} + +void CGUIViewState::SaveViewAsControl(int viewAsControl) +{ + SetViewAsControl(viewAsControl); + SaveViewState(); +} + +SortDescription CGUIViewState::GetSortMethod() const +{ + SortDescription sorting; + if (m_currentSortMethod >= 0 && m_currentSortMethod < (int)m_sortMethods.size()) + sorting = m_sortMethods[m_currentSortMethod].m_sortDescription; + + return sorting; +} + +bool CGUIViewState::HasMultipleSortMethods() const +{ + return m_sortMethods.size() > 1; +} + +int CGUIViewState::GetSortMethodLabel() const +{ + if (m_currentSortMethod >= 0 && m_currentSortMethod < (int)m_sortMethods.size()) + return m_sortMethods[m_currentSortMethod].m_buttonLabel; + + return 551; // default sort method label 'Name' +} + +void CGUIViewState::GetSortMethodLabelMasks(LABEL_MASKS& masks) const +{ + if (m_currentSortMethod >= 0 && m_currentSortMethod < (int)m_sortMethods.size()) + { + masks = m_sortMethods[m_currentSortMethod].m_labelMasks; + return; + } + + masks.m_strLabelFile.clear(); + masks.m_strLabel2File.clear(); + masks.m_strLabelFolder.clear(); + masks.m_strLabel2Folder.clear(); +} + +std::vector<SortDescription> CGUIViewState::GetSortDescriptions() const +{ + std::vector<SortDescription> descriptions; + for (const auto& desc : m_sortMethods) + { + descriptions.emplace_back(desc.m_sortDescription); + } + return descriptions; +} + +void CGUIViewState::AddSortMethod(SortBy sortBy, int buttonLabel, const LABEL_MASKS &labelMasks, SortAttribute sortAttributes /* = SortAttributeNone */, SortOrder sortOrder /* = SortOrderNone */) +{ + AddSortMethod(sortBy, sortAttributes, buttonLabel, labelMasks, sortOrder); +} + +void CGUIViewState::AddSortMethod(SortBy sortBy, SortAttribute sortAttributes, int buttonLabel, const LABEL_MASKS &labelMasks, SortOrder sortOrder /* = SortOrderNone */) +{ + for (size_t i = 0; i < m_sortMethods.size(); ++i) + if (m_sortMethods[i].m_sortDescription.sortBy == sortBy) + return; + + // handle unspecified sort order + if (sortBy != SortByNone && sortOrder == SortOrderNone) + { + // the following sort methods are sorted in descending order by default + if (sortBy == SortByDate || sortBy == SortBySize || sortBy == SortByPlaycount || + sortBy == SortByRating || sortBy == SortByProgramCount || + sortBy == SortByBitrate || sortBy == SortByListeners || + sortBy == SortByUserRating || sortBy == SortByLastPlayed) + sortOrder = SortOrderDescending; + else + sortOrder = SortOrderAscending; + } + + GUIViewSortDetails sort; + sort.m_sortDescription.sortBy = sortBy; + sort.m_sortDescription.sortOrder = sortOrder; + sort.m_sortDescription.sortAttributes = sortAttributes; + sort.m_buttonLabel = buttonLabel; + sort.m_labelMasks = labelMasks; + m_sortMethods.push_back(sort); +} + +void CGUIViewState::AddSortMethod(SortDescription sortDescription, int buttonLabel, const LABEL_MASKS &labelMasks) +{ + AddSortMethod(sortDescription.sortBy, sortDescription.sortAttributes, buttonLabel, labelMasks, sortDescription.sortOrder); +} + +void CGUIViewState::SetCurrentSortMethod(int method) +{ + SortBy sortBy = (SortBy)method; + if (sortBy < SortByNone || sortBy > SortByLastUsed) + return; // invalid + + SetSortMethod(sortBy); + SaveViewState(); +} + +void CGUIViewState::SetSortMethod(SortBy sortBy, SortOrder sortOrder /* = SortOrderNone */) +{ + for (int i = 0; i < (int)m_sortMethods.size(); ++i) + { + if (m_sortMethods[i].m_sortDescription.sortBy == sortBy) + { + m_currentSortMethod = i; + break; + } + } + + if (sortOrder != SortOrderNone) + SetSortOrder(sortOrder); +} + +void CGUIViewState::SetSortMethod(SortDescription sortDescription) +{ + return SetSortMethod(sortDescription.sortBy, sortDescription.sortOrder); +} + +bool CGUIViewState::ChooseSortMethod() +{ + + CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); + if (!dialog) + return false; + dialog->Reset(); + dialog->SetHeading(CVariant{ 39010 }); // Label "Sort by" + for (auto &sortMethod : m_sortMethods) + dialog->Add(g_localizeStrings.Get(sortMethod.m_buttonLabel)); + dialog->SetSelected(m_currentSortMethod); + dialog->Open(); + int newSelected = dialog->GetSelectedItem(); + // check if selection has changed + if (!dialog->IsConfirmed() || newSelected < 0 || newSelected == m_currentSortMethod) + return false; + + m_currentSortMethod = newSelected; + SaveViewState(); + return true; +} + +SortDescription CGUIViewState::SetNextSortMethod(int direction /* = 1 */) +{ + m_currentSortMethod += direction; + + if (m_currentSortMethod >= (int)m_sortMethods.size()) + m_currentSortMethod = 0; + if (m_currentSortMethod < 0) + m_currentSortMethod = m_sortMethods.size() ? (int)m_sortMethods.size() - 1 : 0; + + SaveViewState(); + + return GetSortMethod(); +} + +bool CGUIViewState::HideExtensions() +{ + return !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWEXTENSIONS); +} + +bool CGUIViewState::HideParentDirItems() +{ + return !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWPARENTDIRITEMS); +} + +bool CGUIViewState::DisableAddSourceButtons() +{ + const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager(); + + if (profileManager->GetCurrentProfile().canWriteSources() || g_passwordManager.bMasterUser) + return !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWADDSOURCEBUTTONS); + + return true; +} + +PLAYLIST::Id CGUIViewState::GetPlaylist() const +{ + return m_playlist; +} + +const std::string& CGUIViewState::GetPlaylistDirectory() +{ + return m_strPlaylistDirectory; +} + +void CGUIViewState::SetPlaylistDirectory(const std::string& strDirectory) +{ + m_strPlaylistDirectory = strDirectory; + URIUtils::RemoveSlashAtEnd(m_strPlaylistDirectory); +} + +bool CGUIViewState::IsCurrentPlaylistDirectory(const std::string& strDirectory) +{ + if (CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist()!=GetPlaylist()) + return false; + + std::string strDir = strDirectory; + URIUtils::RemoveSlashAtEnd(strDir); + + return m_strPlaylistDirectory == strDir; +} + +bool CGUIViewState::AutoPlayNextItem() +{ + return false; +} + +std::string CGUIViewState::GetLockType() +{ + return ""; +} + +std::string CGUIViewState::GetExtensions() +{ + return ""; +} + +VECSOURCES& CGUIViewState::GetSources() +{ + return m_sources; +} + +void CGUIViewState::AddLiveTVSources() +{ + VECSOURCES *sources = CMediaSourceSettings::GetInstance().GetSources("video"); + for (IVECSOURCES it = sources->begin(); it != sources->end(); it++) + { + if (URIUtils::IsLiveTV((*it).strPath)) + { + CMediaSource source; + source.strPath = (*it).strPath; + source.strName = (*it).strName; + source.vecPaths = (*it).vecPaths; + source.m_strThumbnailImage = ""; + source.FromNameAndPaths("video", source.strName, source.vecPaths); + m_sources.push_back(source); + } + } +} + +void CGUIViewState::SetSortOrder(SortOrder sortOrder) +{ + if (sortOrder == SortOrderNone) + return; + + if (m_currentSortMethod < 0 || m_currentSortMethod >= (int)m_sortMethods.size()) + return; + + m_sortMethods[m_currentSortMethod].m_sortDescription.sortOrder = sortOrder; +} + +bool CGUIViewState::AutoPlayNextVideoItem() const +{ + if (GetPlaylist() != PLAYLIST::TYPE_VIDEO) + return false; + + int settingValue(-1); + if (m_items.GetContent() == "musicvideos") + settingValue = SETTING_AUTOPLAYNEXT_MUSICVIDEOS; + else if (m_items.GetContent() == "tvshows") + settingValue = SETTING_AUTOPLAYNEXT_TVSHOWS; + else if (m_items.GetContent() == "episodes") + settingValue = SETTING_AUTOPLAYNEXT_EPISODES; + else if (m_items.GetContent() == "movies") + settingValue = SETTING_AUTOPLAYNEXT_MOVIES; + else + settingValue = SETTING_AUTOPLAYNEXT_UNCATEGORIZED; + + const auto setting = std::dynamic_pointer_cast<CSettingList>( + CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting( + CSettings::SETTING_VIDEOPLAYER_AUTOPLAYNEXTITEM)); + + return settingValue >= 0 && setting != nullptr && + CSettingUtils::FindIntInList(setting, settingValue); +} + +void CGUIViewState::LoadViewState(const std::string &path, int windowID) +{ // get our view state from the db + CViewDatabase db; + if (!db.Open()) + return; + + CViewState state; + if (db.GetViewState(path, windowID, state, CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOOKANDFEEL_SKIN)) || + db.GetViewState(path, windowID, state, "")) + { + SetViewAsControl(state.m_viewMode); + SetSortMethod(state.m_sortDescription); + } +} + +void CGUIViewState::SaveViewToDb(const std::string &path, int windowID, CViewState *viewState) +{ + CViewDatabase db; + if (!db.Open()) + return; + + SortDescription sorting = GetSortMethod(); + CViewState state(m_currentViewAsControl, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes); + if (viewState != NULL) + *viewState = state; + + const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings(); + db.SetViewState(path, windowID, state, settings->GetString(CSettings::SETTING_LOOKANDFEEL_SKIN)); + db.Close(); + + if (viewState != NULL) + settings->Save(); +} + +void CGUIViewState::AddPlaylistOrder(const CFileItemList& items, const LABEL_MASKS& label_masks) +{ + SortBy sortBy = SortByPlaylistOrder; + int sortLabel = 559; + SortOrder sortOrder = SortOrderAscending; + if (items.HasProperty(PROPERTY_SORT_ORDER)) + { + sortBy = (SortBy)items.GetProperty(PROPERTY_SORT_ORDER).asInteger(); + if (sortBy != SortByNone) + { + sortLabel = SortUtils::GetSortLabel(sortBy); + sortOrder = items.GetProperty(PROPERTY_SORT_ASCENDING).asBoolean() ? SortOrderAscending : SortOrderDescending; + } + } + + AddSortMethod(sortBy, sortLabel, label_masks, SortAttributeNone, sortOrder); + SetSortMethod(sortBy, sortOrder); +} + +CGUIViewStateGeneral::CGUIViewStateGeneral(const CFileItemList& items) : CGUIViewState(items) +{ + AddSortMethod(SortByLabel, 551, LABEL_MASKS("%F", "%I", "%L", "")); // Filename, size | Foldername, empty + SetSortMethod(SortByLabel); + + SetViewAsControl(DEFAULT_VIEW_LIST); +} + +CGUIViewStateFromItems::CGUIViewStateFromItems(const CFileItemList &items) : CGUIViewState(items) +{ + const std::vector<GUIViewSortDetails> &details = items.GetSortDetails(); + for (unsigned int i = 0; i < details.size(); i++) + { + const GUIViewSortDetails& sort = details[i]; + AddSortMethod(sort.m_sortDescription, sort.m_buttonLabel, sort.m_labelMasks); + } + //! @todo Should default sort/view mode be specified? + m_currentSortMethod = 0; + + SetViewAsControl(DEFAULT_VIEW_LIST); + + if (items.IsPlugin()) + { + CURL url(items.GetPath()); + AddonPtr addon; + if (CServiceBroker::GetAddonMgr().GetAddon(url.GetHostName(), addon, AddonType::PLUGIN, + OnlyEnabled::CHOICE_YES)) + { + const auto plugin = std::static_pointer_cast<CPluginSource>(addon); + if (plugin->Provides(CPluginSource::AUDIO)) + m_playlist = PLAYLIST::TYPE_MUSIC; + if (plugin->Provides(CPluginSource::VIDEO)) + m_playlist = PLAYLIST::TYPE_VIDEO; + } + } + + LoadViewState(items.GetPath(), CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow()); +} + +bool CGUIViewStateFromItems::AutoPlayNextItem() +{ + return AutoPlayNextVideoItem(); +} + +void CGUIViewStateFromItems::SaveViewState() +{ + SaveViewToDb(m_items.GetPath(), CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow()); +} + +CGUIViewStateLibrary::CGUIViewStateLibrary(const CFileItemList &items) : CGUIViewState(items) +{ + AddSortMethod(SortByNone, 551, LABEL_MASKS("%F", "%I", "%L", "")); // Filename, Size | Foldername, empty + SetSortMethod(SortByNone); + + SetViewAsControl(DEFAULT_VIEW_LIST); + + LoadViewState(items.GetPath(), CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow()); +} + +void CGUIViewStateLibrary::SaveViewState() +{ + SaveViewToDb(m_items.GetPath(), CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow()); +} diff --git a/xbmc/view/GUIViewState.h b/xbmc/view/GUIViewState.h new file mode 100644 index 0000000..8723081 --- /dev/null +++ b/xbmc/view/GUIViewState.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "MediaSource.h" +#include "utils/LabelFormatter.h" +#include "utils/SortUtils.h" + +#include <vector> + +class CViewState; // forward +class CFileItemList; + +namespace PLAYLIST +{ +using Id = int; +} // namespace PLAYLIST + +class CGUIViewState +{ +public: + virtual ~CGUIViewState(); + static CGUIViewState* GetViewState(int windowId, const CFileItemList& items); + + void SetViewAsControl(int viewAsControl); + void SaveViewAsControl(int viewAsControl); + int GetViewAsControl() const; + + bool ChooseSortMethod(); + SortDescription SetNextSortMethod(int direction = 1); + void SetCurrentSortMethod(int method); + SortDescription GetSortMethod() const; + bool HasMultipleSortMethods() const; + int GetSortMethodLabel() const; + int GetSortOrderLabel() const; + void GetSortMethodLabelMasks(LABEL_MASKS& masks) const; + + std::vector<SortDescription> GetSortDescriptions() const; + + SortOrder SetNextSortOrder(); + SortOrder GetSortOrder() const; + + virtual bool HideExtensions(); + virtual bool HideParentDirItems(); + virtual bool DisableAddSourceButtons(); + + virtual PLAYLIST::Id GetPlaylist() const; + const std::string& GetPlaylistDirectory(); + void SetPlaylistDirectory(const std::string& strDirectory); + bool IsCurrentPlaylistDirectory(const std::string& strDirectory); + virtual bool AutoPlayNextItem(); + + virtual std::string GetLockType(); + virtual std::string GetExtensions(); + virtual VECSOURCES& GetSources(); + +protected: + explicit CGUIViewState(const CFileItemList& items); // no direct object creation, use GetViewState() + + virtual void SaveViewState() = 0; + virtual void SaveViewToDb(const std::string &path, int windowID, CViewState *viewState = NULL); + void LoadViewState(const std::string &path, int windowID); + + void AddLiveTVSources(); + + /*! \brief Add the sort order defined in a smartplaylist + Defaults to SORT_METHOD_PLAYLIST_ORDER if no order is defined. + \param items the list of items for the view state. + \param label_mask the label masks for formatting items. + */ + void AddPlaylistOrder(const CFileItemList& items, const LABEL_MASKS& label_masks); + + void AddSortMethod(SortBy sortBy, int buttonLabel, const LABEL_MASKS &labelMasks, SortAttribute sortAttributes = SortAttributeNone, SortOrder sortOrder = SortOrderNone); + void AddSortMethod(SortBy sortBy, SortAttribute sortAttributes, int buttonLabel, const LABEL_MASKS &labelMasks, SortOrder sortOrder = SortOrderNone); + void AddSortMethod(SortDescription sortDescription, int buttonLabel, const LABEL_MASKS &labelMasks); + void SetSortMethod(SortBy sortBy, SortOrder sortOrder = SortOrderNone); + void SetSortMethod(SortDescription sortDescription); + void SetSortOrder(SortOrder sortOrder); + + bool AutoPlayNextVideoItem() const; + + const CFileItemList& m_items; + + int m_currentViewAsControl; + PLAYLIST::Id m_playlist; + + std::vector<GUIViewSortDetails> m_sortMethods; + int m_currentSortMethod; + + static VECSOURCES m_sources; + static std::string m_strPlaylistDirectory; +}; + +class CGUIViewStateGeneral : public CGUIViewState +{ +public: + explicit CGUIViewStateGeneral(const CFileItemList& items); + +protected: + void SaveViewState() override { } +}; + +class CGUIViewStateFromItems : public CGUIViewState +{ +public: + explicit CGUIViewStateFromItems(const CFileItemList& items); + bool AutoPlayNextItem() override; + +protected: + void SaveViewState() override; +}; + +class CGUIViewStateLibrary : public CGUIViewState +{ +public: + explicit CGUIViewStateLibrary(const CFileItemList& items); + +protected: + void SaveViewState() override; +}; diff --git a/xbmc/view/ViewDatabase.cpp b/xbmc/view/ViewDatabase.cpp new file mode 100644 index 0000000..7605cfb --- /dev/null +++ b/xbmc/view/ViewDatabase.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "ViewDatabase.h" + +#include <utility> + +#include "dbwrappers/dataset.h" +#include "SortFileItem.h" +#include "utils/LegacyPathTranslation.h" +#include "utils/log.h" +#include "utils/SortUtils.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "view/ViewState.h" + +#ifdef TARGET_POSIX +#include "platform/posix/ConvUtils.h" +#endif +CViewDatabase::CViewDatabase(void) = default; + +CViewDatabase::~CViewDatabase(void) = default; + +bool CViewDatabase::Open() +{ + return CDatabase::Open(); +} + +void CViewDatabase::CreateTables() +{ + CLog::Log(LOGINFO, "create view table"); + m_pDS->exec("CREATE TABLE view (" + "idView integer primary key," + "window integer," + "path text," + "viewMode integer," + "sortMethod integer," + "sortOrder integer," + "sortAttributes integer," + "skin text)"); +} + +void CViewDatabase::CreateAnalytics() +{ + CLog::Log(LOGINFO, "{} - creating indices", __FUNCTION__); + m_pDS->exec("CREATE INDEX idxViews ON view(path)"); + m_pDS->exec("CREATE INDEX idxViewsWindow ON view(window)"); +} + +void CViewDatabase::UpdateTables(int version) +{ + if (version < 4) + m_pDS->exec("alter table view add skin text"); + if (version < 5) + { + // translate legacy videodb:// and musicdb:// paths + std::vector< std::pair<int, std::string> > paths; + if (m_pDS->query("SELECT idView, path FROM view")) + { + while (!m_pDS->eof()) + { + std::string originalPath = m_pDS->fv(1).get_asString(); + std::string path = originalPath; + if (StringUtils::StartsWithNoCase(path, "musicdb://")) + path = CLegacyPathTranslation::TranslateMusicDbPath(path); + else if (StringUtils::StartsWithNoCase(path, "videodb://")) + path = CLegacyPathTranslation::TranslateVideoDbPath(path); + + if (!StringUtils::EqualsNoCase(path, originalPath)) + paths.emplace_back(m_pDS->fv(0).get_asInt(), path); + m_pDS->next(); + } + m_pDS->close(); + + for (std::vector< std::pair<int, std::string> >::const_iterator it = paths.begin(); it != paths.end(); ++it) + m_pDS->exec(PrepareSQL("UPDATE view SET path='%s' WHERE idView=%d", it->second.c_str(), it->first)); + } + } + if (version < 6) + { + // convert the "path" table + m_pDS->exec("ALTER TABLE view RENAME TO tmp_view"); + + m_pDS->exec("CREATE TABLE view (" + "idView integer primary key," + "window integer," + "path text," + "viewMode integer," + "sortMethod integer," + "sortOrder integer," + "sortAttributes integer," + "skin text)\n"); + + m_pDS->query("SELECT * FROM tmp_view"); + while (!m_pDS->eof()) + { + SortDescription sorting = SortUtils::TranslateOldSortMethod((SORT_METHOD)m_pDS->fv(4).get_asInt()); + + std::string sql = PrepareSQL("INSERT INTO view (idView, window, path, viewMode, sortMethod, sortOrder, sortAttributes, skin) VALUES (%i, %i, '%s', %i, %i, %i, %i, '%s')", + m_pDS->fv(0).get_asInt(), m_pDS->fv(1).get_asInt(), m_pDS->fv(2).get_asString().c_str(), m_pDS->fv(3).get_asInt(), + (int)sorting.sortBy, m_pDS->fv(5).get_asInt(), (int)sorting.sortAttributes, m_pDS->fv(6).get_asString().c_str()); + m_pDS2->exec(sql); + + m_pDS->next(); + } + m_pDS->exec("DROP TABLE tmp_view"); + } +} + +bool CViewDatabase::GetViewState(const std::string &path, int window, CViewState &state, const std::string &skin) +{ + try + { + if (nullptr == m_pDB) + return false; + if (nullptr == m_pDS) + return false; + + std::string path1(path); + URIUtils::AddSlashAtEnd(path1); + if (path1.empty()) path1 = "root://"; + + std::string sql; + if (skin.empty()) + sql = PrepareSQL("select * from view where window = %i and path='%s'", window, path1.c_str()); + else + sql = PrepareSQL("select * from view where window = %i and path='%s' and skin='%s'", window, path1.c_str(), skin.c_str()); + m_pDS->query(sql); + + if (!m_pDS->eof()) + { // have some information + state.m_viewMode = m_pDS->fv("viewMode").get_asInt(); + state.m_sortDescription.sortBy = (SortBy)m_pDS->fv("sortMethod").get_asInt(); + state.m_sortDescription.sortOrder = (SortOrder)m_pDS->fv("sortOrder").get_asInt(); + state.m_sortDescription.sortAttributes = (SortAttribute)m_pDS->fv("sortAttributes").get_asInt(); + m_pDS->close(); + return true; + } + m_pDS->close(); + } + catch (...) + { + CLog::Log(LOGERROR, "{}, failed on path '{}'", __FUNCTION__, path); + } + return false; +} + +bool CViewDatabase::SetViewState(const std::string &path, int window, const CViewState &state, const std::string &skin) +{ + try + { + if (nullptr == m_pDB) + return false; + if (nullptr == m_pDS) + return false; + + std::string path1(path); + URIUtils::AddSlashAtEnd(path1); + if (path1.empty()) path1 = "root://"; + + std::string sql = PrepareSQL("select idView from view where window = %i and path='%s' and skin='%s'", window, path1.c_str(), skin.c_str()); + m_pDS->query(sql); + if (!m_pDS->eof()) + { // update the view + int idView = m_pDS->fv("idView").get_asInt(); + m_pDS->close(); + sql = PrepareSQL("update view set viewMode=%i,sortMethod=%i,sortOrder=%i,sortAttributes=%i where idView=%i", + state.m_viewMode, (int)state.m_sortDescription.sortBy, (int)state.m_sortDescription.sortOrder, (int)state.m_sortDescription.sortAttributes, idView); + m_pDS->exec(sql); + } + else + { // add the view + m_pDS->close(); + sql = PrepareSQL("insert into view (idView, path, window, viewMode, sortMethod, sortOrder, sortAttributes, skin) values(NULL, '%s', %i, %i, %i, %i, %i, '%s')", + path1.c_str(), window, state.m_viewMode, (int)state.m_sortDescription.sortBy, (int)state.m_sortDescription.sortOrder, (int)state.m_sortDescription.sortAttributes, skin.c_str()); + m_pDS->exec(sql); + } + } + catch (...) + { + CLog::Log(LOGERROR, "{} failed on path '{}'", __FUNCTION__, path); + } + return true; +} + +bool CViewDatabase::ClearViewStates(int windowID) +{ + try + { + if (nullptr == m_pDB) + return false; + if (nullptr == m_pDS) + return false; + + std::string sql = PrepareSQL("delete from view where window = %i", windowID); + m_pDS->exec(sql); + } + catch (...) + { + CLog::Log(LOGERROR, "{} failed on window '{}'", __FUNCTION__, windowID); + } + return true; +} diff --git a/xbmc/view/ViewDatabase.h b/xbmc/view/ViewDatabase.h new file mode 100644 index 0000000..701d1f6 --- /dev/null +++ b/xbmc/view/ViewDatabase.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "dbwrappers/Database.h" + +#include <string> + +class CViewState; + +class CViewDatabase : public CDatabase +{ +public: + CViewDatabase(); + ~CViewDatabase() override; + bool Open() override; + + bool GetViewState(const std::string &path, int windowID, CViewState &state, const std::string &skin); + bool SetViewState(const std::string &path, int windowID, const CViewState &state, const std::string &skin); + bool ClearViewStates(int windowID); + +protected: + void CreateTables() override; + void CreateAnalytics() override; + void UpdateTables(int version) override; + int GetSchemaVersion() const override { return 6; } + const char *GetBaseDBName() const override { return "ViewModes"; } +}; diff --git a/xbmc/view/ViewState.h b/xbmc/view/ViewState.h new file mode 100644 index 0000000..1ae0737 --- /dev/null +++ b/xbmc/view/ViewState.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "utils/SortUtils.h" + +#define DEFAULT_VIEW_AUTO (VIEW_TYPE_AUTO << 16) +#define DEFAULT_VIEW_LIST (VIEW_TYPE_LIST << 16) +#define DEFAULT_VIEW_ICONS (VIEW_TYPE_ICON << 16) +#define DEFAULT_VIEW_BIG_ICONS (VIEW_TYPE_BIG_ICON << 16) +#define DEFAULT_VIEW_INFO (VIEW_TYPE_INFO << 16) +#define DEFAULT_VIEW_BIG_INFO (VIEW_TYPE_BIG_INFO << 16) +#define DEFAULT_VIEW_MAX (((VIEW_TYPE_MAX - 1) << 16) | 60) + +class CViewState +{ +public: + CViewState(int viewMode, SortBy sortMethod, SortOrder sortOrder, SortAttribute sortAttributes = SortAttributeNone) + { + m_viewMode = viewMode; + m_sortDescription.sortBy = sortMethod; + m_sortDescription.sortOrder = sortOrder; + m_sortDescription.sortAttributes = sortAttributes; + }; + CViewState() + { + m_viewMode = 0; + m_sortDescription.sortBy = SortByLabel; + m_sortDescription.sortOrder = SortOrderAscending; + }; + + int m_viewMode; + SortDescription m_sortDescription; +}; diff --git a/xbmc/view/ViewStateSettings.cpp b/xbmc/view/ViewStateSettings.cpp new file mode 100644 index 0000000..d883393 --- /dev/null +++ b/xbmc/view/ViewStateSettings.cpp @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2013-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "ViewStateSettings.h" + +#include "utils/SortUtils.h" +#include "utils/XBMCTinyXML.h" +#include "utils/XMLUtils.h" +#include "utils/log.h" + +#include <cstring> +#include <mutex> +#include <utility> + +#define XML_VIEWSTATESETTINGS "viewstates" +#define XML_VIEWMODE "viewmode" +#define XML_SORTMETHOD "sortmethod" +#define XML_SORTORDER "sortorder" +#define XML_SORTATTRIBUTES "sortattributes" +#define XML_GENERAL "general" +#define XML_SETTINGLEVEL "settinglevel" +#define XML_EVENTLOG "eventlog" +#define XML_EVENTLOG_LEVEL "level" +#define XML_EVENTLOG_LEVEL_HIGHER "showhigherlevels" + +CViewStateSettings::CViewStateSettings() +{ + AddViewState("musicnavartists"); + AddViewState("musicnavalbums"); + AddViewState("musicnavsongs", DEFAULT_VIEW_LIST, SortByTrackNumber); + AddViewState("musiclastfm"); + AddViewState("videonavactors"); + AddViewState("videonavyears"); + AddViewState("videonavgenres"); + AddViewState("videonavtitles"); + AddViewState("videonavepisodes", DEFAULT_VIEW_AUTO, SortByEpisodeNumber); + AddViewState("videonavtvshows"); + AddViewState("videonavseasons"); + AddViewState("videonavmusicvideos"); + + AddViewState("programs", DEFAULT_VIEW_AUTO); + AddViewState("pictures", DEFAULT_VIEW_AUTO); + AddViewState("videofiles", DEFAULT_VIEW_AUTO); + AddViewState("musicfiles", DEFAULT_VIEW_AUTO); + AddViewState("games", DEFAULT_VIEW_AUTO); + + Clear(); +} + +CViewStateSettings::~CViewStateSettings() +{ + for (std::map<std::string, CViewState*>::const_iterator viewState = m_viewStates.begin(); viewState != m_viewStates.end(); ++viewState) + delete viewState->second; + m_viewStates.clear(); +} + +CViewStateSettings& CViewStateSettings::GetInstance() +{ + static CViewStateSettings sViewStateSettings; + return sViewStateSettings; +} + +bool CViewStateSettings::Load(const TiXmlNode *settings) +{ + if (settings == NULL) + return false; + + std::unique_lock<CCriticalSection> lock(m_critical); + const TiXmlNode *pElement = settings->FirstChildElement(XML_VIEWSTATESETTINGS); + if (pElement == NULL) + { + CLog::Log(LOGWARNING, "CViewStateSettings: no <viewstates> tag found"); + return false; + } + + for (std::map<std::string, CViewState*>::iterator viewState = m_viewStates.begin(); viewState != m_viewStates.end(); ++viewState) + { + const TiXmlNode* pViewState = pElement->FirstChildElement(viewState->first); + if (pViewState == NULL) + continue; + + XMLUtils::GetInt(pViewState, XML_VIEWMODE, viewState->second->m_viewMode, DEFAULT_VIEW_LIST, DEFAULT_VIEW_MAX); + + // keep backwards compatibility to the old sorting methods + if (pViewState->FirstChild(XML_SORTATTRIBUTES) == NULL) + { + int sortMethod; + if (XMLUtils::GetInt(pViewState, XML_SORTMETHOD, sortMethod, SORT_METHOD_NONE, SORT_METHOD_MAX)) + viewState->second->m_sortDescription = SortUtils::TranslateOldSortMethod((SORT_METHOD)sortMethod); + } + else + { + int sortMethod; + if (XMLUtils::GetInt(pViewState, XML_SORTMETHOD, sortMethod, SortByNone, SortByLastUsed)) + viewState->second->m_sortDescription.sortBy = (SortBy)sortMethod; + if (XMLUtils::GetInt(pViewState, XML_SORTATTRIBUTES, sortMethod, SortAttributeNone, SortAttributeIgnoreFolders)) + viewState->second->m_sortDescription.sortAttributes = (SortAttribute)sortMethod; + } + + int sortOrder; + if (XMLUtils::GetInt(pViewState, XML_SORTORDER, sortOrder, SortOrderNone, SortOrderDescending)) + viewState->second->m_sortDescription.sortOrder = (SortOrder)sortOrder; + } + + pElement = settings->FirstChild(XML_GENERAL); + if (pElement != NULL) + { + int settingLevel; + if (XMLUtils::GetInt(pElement, XML_SETTINGLEVEL, settingLevel, static_cast<int>(SettingLevel::Basic), static_cast<int>(SettingLevel::Expert))) + m_settingLevel = (SettingLevel)settingLevel; + else + m_settingLevel = SettingLevel::Standard; + + const TiXmlNode* pEventLogNode = pElement->FirstChild(XML_EVENTLOG); + if (pEventLogNode != NULL) + { + int eventLevel; + if (XMLUtils::GetInt(pEventLogNode, XML_EVENTLOG_LEVEL, eventLevel, static_cast<int>(EventLevel::Basic), static_cast<int>(EventLevel::Error))) + m_eventLevel = (EventLevel)eventLevel; + else + m_eventLevel = EventLevel::Basic; + + if (!XMLUtils::GetBoolean(pEventLogNode, XML_EVENTLOG_LEVEL_HIGHER, m_eventShowHigherLevels)) + m_eventShowHigherLevels = true; + } + } + + return true; +} + +bool CViewStateSettings::Save(TiXmlNode *settings) const +{ + if (settings == NULL) + return false; + + std::unique_lock<CCriticalSection> lock(m_critical); + // add the <viewstates> tag + TiXmlElement xmlViewStateElement(XML_VIEWSTATESETTINGS); + TiXmlNode *pViewStateNode = settings->InsertEndChild(xmlViewStateElement); + if (pViewStateNode == NULL) + { + CLog::Log(LOGWARNING, "CViewStateSettings: could not create <viewstates> tag"); + return false; + } + + for (std::map<std::string, CViewState*>::const_iterator viewState = m_viewStates.begin(); viewState != m_viewStates.end(); ++viewState) + { + TiXmlElement newElement(viewState->first); + TiXmlNode *pNewNode = pViewStateNode->InsertEndChild(newElement); + if (pNewNode == NULL) + continue; + + XMLUtils::SetInt(pNewNode, XML_VIEWMODE, viewState->second->m_viewMode); + XMLUtils::SetInt(pNewNode, XML_SORTMETHOD, (int)viewState->second->m_sortDescription.sortBy); + XMLUtils::SetInt(pNewNode, XML_SORTORDER, (int)viewState->second->m_sortDescription.sortOrder); + XMLUtils::SetInt(pNewNode, XML_SORTATTRIBUTES, (int)viewState->second->m_sortDescription.sortAttributes); + } + + TiXmlNode *generalNode = settings->FirstChild(XML_GENERAL); + if (generalNode == NULL) + { + TiXmlElement generalElement(XML_GENERAL); + generalNode = settings->InsertEndChild(generalElement); + if (generalNode == NULL) + return false; + } + + XMLUtils::SetInt(generalNode, XML_SETTINGLEVEL, (int)m_settingLevel); + + TiXmlNode *eventLogNode = generalNode->FirstChild(XML_EVENTLOG); + if (eventLogNode == NULL) + { + TiXmlElement eventLogElement(XML_EVENTLOG); + eventLogNode = generalNode->InsertEndChild(eventLogElement); + if (eventLogNode == NULL) + return false; + } + + XMLUtils::SetInt(eventLogNode, XML_EVENTLOG_LEVEL, (int)m_eventLevel); + XMLUtils::SetBoolean(eventLogNode, XML_EVENTLOG_LEVEL_HIGHER, (int)m_eventShowHigherLevels); + + return true; +} + +void CViewStateSettings::Clear() +{ + m_settingLevel = SettingLevel::Standard; +} + +const CViewState* CViewStateSettings::Get(const std::string &viewState) const +{ + std::unique_lock<CCriticalSection> lock(m_critical); + std::map<std::string, CViewState*>::const_iterator view = m_viewStates.find(viewState); + if (view != m_viewStates.end()) + return view->second; + + return NULL; +} + +CViewState* CViewStateSettings::Get(const std::string &viewState) +{ + std::unique_lock<CCriticalSection> lock(m_critical); + std::map<std::string, CViewState*>::iterator view = m_viewStates.find(viewState); + if (view != m_viewStates.end()) + return view->second; + + return NULL; +} + +void CViewStateSettings::SetSettingLevel(SettingLevel settingLevel) +{ + if (settingLevel < SettingLevel::Basic) + m_settingLevel = SettingLevel::Basic; + if (settingLevel > SettingLevel::Expert) + m_settingLevel = SettingLevel::Expert; + else + m_settingLevel = settingLevel; +} + +void CViewStateSettings::CycleSettingLevel() +{ + m_settingLevel = GetNextSettingLevel(); +} + +SettingLevel CViewStateSettings::GetNextSettingLevel() const +{ + SettingLevel level = (SettingLevel)((int)m_settingLevel + 1); + if (level > SettingLevel::Expert) + level = SettingLevel::Basic; + return level; +} + +void CViewStateSettings::SetEventLevel(EventLevel eventLevel) +{ + if (eventLevel < EventLevel::Basic) + m_eventLevel = EventLevel::Basic; + if (eventLevel > EventLevel::Error) + m_eventLevel = EventLevel::Error; + else + m_eventLevel = eventLevel; +} + +void CViewStateSettings::CycleEventLevel() +{ + m_eventLevel = GetNextEventLevel(); +} + +EventLevel CViewStateSettings::GetNextEventLevel() const +{ + EventLevel level = (EventLevel)((int)m_eventLevel + 1); + if (level > EventLevel::Error) + level = EventLevel::Basic; + return level; +} + +void CViewStateSettings::AddViewState(const std::string& strTagName, int defaultView /* = DEFAULT_VIEW_LIST */, SortBy defaultSort /* = SortByLabel */) +{ + if (strTagName.empty() || m_viewStates.find(strTagName) != m_viewStates.end()) + return; + + CViewState *viewState = new CViewState(defaultView, defaultSort, SortOrderAscending); + if (viewState == NULL) + return; + + m_viewStates.insert(make_pair(strTagName, viewState)); +} diff --git a/xbmc/view/ViewStateSettings.h b/xbmc/view/ViewStateSettings.h new file mode 100644 index 0000000..2a18fc9 --- /dev/null +++ b/xbmc/view/ViewStateSettings.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "ViewState.h" +#include "events/IEvent.h" +#include "settings/ISubSettings.h" +#include "settings/lib/Setting.h" +#include "threads/CriticalSection.h" +#include "windowing/GraphicContext.h" + +#include <map> +#include <string> + +class TiXmlNode; + +class CViewStateSettings : public ISubSettings +{ +public: + static CViewStateSettings& GetInstance(); + + bool Load(const TiXmlNode *settings) override; + bool Save(TiXmlNode *settings) const override; + void Clear() override; + + const CViewState* Get(const std::string &viewState) const; + CViewState* Get(const std::string &viewState); + + SettingLevel GetSettingLevel() const { return m_settingLevel; } + void SetSettingLevel(SettingLevel settingLevel); + void CycleSettingLevel(); + SettingLevel GetNextSettingLevel() const; + + EventLevel GetEventLevel() const { return m_eventLevel; } + void SetEventLevel(EventLevel eventLevel); + void CycleEventLevel(); + EventLevel GetNextEventLevel() const; + bool ShowHigherEventLevels() const { return m_eventShowHigherLevels; } + void SetShowHigherEventLevels(bool showHigherEventLevels) { m_eventShowHigherLevels = showHigherEventLevels; } + void ToggleShowHigherEventLevels() { m_eventShowHigherLevels = !m_eventShowHigherLevels; } + +protected: + CViewStateSettings(); + CViewStateSettings(const CViewStateSettings&) = delete; + CViewStateSettings& operator=(CViewStateSettings const&) = delete; + ~CViewStateSettings() override; + +private: + std::map<std::string, CViewState*> m_viewStates; + SettingLevel m_settingLevel = SettingLevel::Standard; + EventLevel m_eventLevel = EventLevel::Basic; + bool m_eventShowHigherLevels = true; + mutable CCriticalSection m_critical; + + void AddViewState(const std::string& strTagName, int defaultView = DEFAULT_VIEW_LIST, SortBy defaultSort = SortByLabel); +}; |