summaryrefslogtreecommitdiffstats
path: root/xbmc/view
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/view')
-rw-r--r--xbmc/view/CMakeLists.txt12
-rw-r--r--xbmc/view/GUIViewControl.cpp354
-rw-r--r--xbmc/view/GUIViewControl.h67
-rw-r--r--xbmc/view/GUIViewState.cpp637
-rw-r--r--xbmc/view/GUIViewState.h126
-rw-r--r--xbmc/view/ViewDatabase.cpp208
-rw-r--r--xbmc/view/ViewDatabase.h34
-rw-r--r--xbmc/view/ViewState.h40
-rw-r--r--xbmc/view/ViewStateSettings.cpp271
-rw-r--r--xbmc/view/ViewStateSettings.h62
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);
+};