summaryrefslogtreecommitdiffstats
path: root/xbmc/windows
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/windows')
-rw-r--r--xbmc/windows/CMakeLists.txt25
-rw-r--r--xbmc/windows/GUIMediaWindow.cpp2314
-rw-r--r--xbmc/windows/GUIMediaWindow.h227
-rw-r--r--xbmc/windows/GUIWindowDebugInfo.cpp185
-rw-r--r--xbmc/windows/GUIWindowDebugInfo.h34
-rw-r--r--xbmc/windows/GUIWindowFileManager.cpp1331
-rw-r--r--xbmc/windows/GUIWindowFileManager.h105
-rw-r--r--xbmc/windows/GUIWindowHome.cpp183
-rw-r--r--xbmc/windows/GUIWindowHome.h41
-rw-r--r--xbmc/windows/GUIWindowLoginScreen.cpp269
-rw-r--r--xbmc/windows/GUIWindowLoginScreen.h43
-rw-r--r--xbmc/windows/GUIWindowPointer.cpp82
-rw-r--r--xbmc/windows/GUIWindowPointer.h27
-rw-r--r--xbmc/windows/GUIWindowScreensaver.cpp106
-rw-r--r--xbmc/windows/GUIWindowScreensaver.h42
-rw-r--r--xbmc/windows/GUIWindowScreensaverDim.cpp77
-rw-r--r--xbmc/windows/GUIWindowScreensaverDim.h28
-rw-r--r--xbmc/windows/GUIWindowSplash.cpp45
-rw-r--r--xbmc/windows/GUIWindowSplash.h29
-rw-r--r--xbmc/windows/GUIWindowStartup.cpp38
-rw-r--r--xbmc/windows/GUIWindowStartup.h23
-rw-r--r--xbmc/windows/GUIWindowSystemInfo.cpp260
-rw-r--r--xbmc/windows/GUIWindowSystemInfo.h29
23 files changed, 5543 insertions, 0 deletions
diff --git a/xbmc/windows/CMakeLists.txt b/xbmc/windows/CMakeLists.txt
new file mode 100644
index 0000000..2fe465f
--- /dev/null
+++ b/xbmc/windows/CMakeLists.txt
@@ -0,0 +1,25 @@
+set(SOURCES GUIMediaWindow.cpp
+ GUIWindowDebugInfo.cpp
+ GUIWindowFileManager.cpp
+ GUIWindowHome.cpp
+ GUIWindowLoginScreen.cpp
+ GUIWindowPointer.cpp
+ GUIWindowScreensaver.cpp
+ GUIWindowScreensaverDim.cpp
+ GUIWindowSplash.cpp
+ GUIWindowStartup.cpp
+ GUIWindowSystemInfo.cpp)
+
+set(HEADERS GUIMediaWindow.h
+ GUIWindowDebugInfo.h
+ GUIWindowFileManager.h
+ GUIWindowHome.h
+ GUIWindowLoginScreen.h
+ GUIWindowPointer.h
+ GUIWindowScreensaver.h
+ GUIWindowScreensaverDim.h
+ GUIWindowSplash.h
+ GUIWindowStartup.h
+ GUIWindowSystemInfo.h)
+
+core_add_library(windows)
diff --git a/xbmc/windows/GUIMediaWindow.cpp b/xbmc/windows/GUIMediaWindow.cpp
new file mode 100644
index 0000000..b55cb17
--- /dev/null
+++ b/xbmc/windows/GUIMediaWindow.cpp
@@ -0,0 +1,2314 @@
+/*
+ * 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 "GUIMediaWindow.h"
+
+#include "ContextMenuManager.h"
+#include "FileItem.h"
+#include "FileItemListModification.h"
+#include "GUIPassword.h"
+#include "GUIUserMessages.h"
+#include "PartyModeManager.h"
+#include "PlayListPlayer.h"
+#include "ServiceBroker.h"
+#include "URL.h"
+#include "Util.h"
+#include "addons/AddonManager.h"
+#include "addons/PluginSource.h"
+#include "addons/addoninfo/AddonType.h"
+#include "application/Application.h"
+#include "messaging/ApplicationMessenger.h"
+#if defined(TARGET_ANDROID)
+#include "platform/android/activity/XBMCApp.h"
+#endif
+#include "dialogs/GUIDialogBusy.h"
+#include "dialogs/GUIDialogKaiToast.h"
+#include "dialogs/GUIDialogMediaFilter.h"
+#include "dialogs/GUIDialogProgress.h"
+#include "dialogs/GUIDialogSmartPlaylistEditor.h"
+#include "filesystem/FileDirectoryFactory.h"
+#include "filesystem/MultiPathDirectory.h"
+#include "filesystem/PluginDirectory.h"
+#include "filesystem/SmartPlaylistDirectory.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIEditControl.h"
+#include "guilib/GUIKeyboardFactory.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "input/actions/Action.h"
+#include "input/actions/ActionIDs.h"
+#include "interfaces/generic/ScriptInvocationManager.h"
+#include "messaging/helpers/DialogOKHelper.h"
+#include "network/Network.h"
+#include "playlists/PlayList.h"
+#include "profiles/ProfileManager.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "storage/MediaManager.h"
+#include "threads/IRunnable.h"
+#include "utils/FileUtils.h"
+#include "utils/LabelFormatter.h"
+#include "utils/SortUtils.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+#include "view/GUIViewState.h"
+
+#include <inttypes.h>
+
+#define CONTROL_BTNVIEWASICONS 2
+#define CONTROL_BTNSORTBY 3
+#define CONTROL_BTNSORTASC 4
+#define CONTROL_BTN_FILTER 19
+
+#define CONTROL_LABELFILES 12
+
+#define PROPERTY_PATH_DB "path.db"
+#define PROPERTY_SORT_ORDER "sort.order"
+#define PROPERTY_SORT_ASCENDING "sort.ascending"
+
+#define PLUGIN_REFRESH_DELAY 200
+
+using namespace ADDON;
+using namespace KODI::MESSAGING;
+using namespace std::chrono_literals;
+
+namespace
+{
+class CGetDirectoryItems : public IRunnable
+{
+public:
+ CGetDirectoryItems(XFILE::CVirtualDirectory &dir, CURL &url, CFileItemList &items, bool useDir)
+ : m_dir(dir), m_url(url), m_items(items), m_useDir(useDir)
+ {
+ }
+
+ void Run() override
+ {
+ m_result = m_dir.GetDirectory(m_url, m_items, m_useDir, true);
+ }
+
+ void Cancel() override
+ {
+ m_dir.CancelDirectory();
+ }
+
+ bool m_result = false;
+
+protected:
+ XFILE::CVirtualDirectory &m_dir;
+ CURL m_url;
+ CFileItemList &m_items;
+ bool m_useDir;
+};
+}
+
+CGUIMediaWindow::CGUIMediaWindow(int id, const char *xmlFile)
+ : CGUIWindow(id, xmlFile)
+{
+ m_loadType = KEEP_IN_MEMORY;
+ m_vecItems = new CFileItemList;
+ m_unfilteredItems = new CFileItemList;
+ m_vecItems->SetPath("?");
+ m_iLastControl = -1;
+ m_canFilterAdvanced = false;
+
+ m_guiState.reset(CGUIViewState::GetViewState(GetID(), *m_vecItems));
+}
+
+CGUIMediaWindow::~CGUIMediaWindow()
+{
+ delete m_vecItems;
+ delete m_unfilteredItems;
+}
+
+bool CGUIMediaWindow::Load(TiXmlElement *pRootElement)
+{
+ bool retVal = CGUIWindow::Load(pRootElement);
+
+ if (!retVal)
+ return false;
+
+ // configure our view control
+ m_viewControl.Reset();
+ m_viewControl.SetParentWindow(GetID());
+ TiXmlElement *element = pRootElement->FirstChildElement("views");
+ if (element && element->FirstChild())
+ { // format is <views>50,29,51,95</views>
+ const std::string &allViews = element->FirstChild()->ValueStr();
+ std::vector<std::string> views = StringUtils::Split(allViews, ",");
+ for (std::vector<std::string>::const_iterator i = views.begin(); i != views.end(); ++i)
+ {
+ int controlID = atol(i->c_str());
+ CGUIControl *control = GetControl(controlID);
+ if (control && control->IsContainer())
+ m_viewControl.AddView(control);
+ }
+ }
+ m_viewControl.SetViewControlID(CONTROL_BTNVIEWASICONS);
+
+ return true;
+}
+
+void CGUIMediaWindow::OnWindowLoaded()
+{
+ SendMessage(GUI_MSG_SET_TYPE, CONTROL_BTN_FILTER, CGUIEditControl::INPUT_TYPE_FILTER);
+ CGUIWindow::OnWindowLoaded();
+ SetupShares();
+}
+
+void CGUIMediaWindow::OnWindowUnload()
+{
+ CGUIWindow::OnWindowUnload();
+ m_viewControl.Reset();
+}
+
+CFileItemPtr CGUIMediaWindow::GetCurrentListItem(int offset)
+{
+ int item = m_viewControl.GetSelectedItem();
+ if (!m_vecItems->Size() || item < 0)
+ return CFileItemPtr();
+ item = (item + offset) % m_vecItems->Size();
+ if (item < 0) item += m_vecItems->Size();
+ return m_vecItems->Get(item);
+}
+
+bool CGUIMediaWindow::OnAction(const CAction &action)
+{
+ if (action.GetID() == ACTION_PARENT_DIR)
+ {
+ GoParentFolder();
+ return true;
+ }
+
+ if (CGUIWindow::OnAction(action))
+ return true;
+
+ if (action.GetID() == ACTION_FILTER)
+ return Filter();
+
+ // live filtering
+ if (action.GetID() == ACTION_FILTER_CLEAR)
+ {
+ CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS);
+ message.SetStringParam("");
+ OnMessage(message);
+ return true;
+ }
+
+ if (action.GetID() == ACTION_BACKSPACE)
+ {
+ CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS, 2); // 2 for delete
+ OnMessage(message);
+ return true;
+ }
+
+ if (action.GetID() >= ACTION_FILTER_SMS2 && action.GetID() <= ACTION_FILTER_SMS9)
+ {
+ std::string filter = std::to_string(action.GetID() - ACTION_FILTER_SMS2 + 2);
+ CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS, 1); // 1 for append
+ message.SetStringParam(filter);
+ OnMessage(message);
+ return true;
+ }
+
+ return false;
+}
+
+bool CGUIMediaWindow::OnBack(int actionID)
+{
+ CancelUpdateItems();
+
+ CURL filterUrl(m_strFilterPath);
+ if (actionID == ACTION_NAV_BACK &&
+ !m_vecItems->IsVirtualDirectoryRoot() &&
+ !URIUtils::PathEquals(m_vecItems->GetPath(), GetRootPath(), true) &&
+ (!URIUtils::PathEquals(m_vecItems->GetPath(), m_startDirectory, true) || (m_canFilterAdvanced && filterUrl.HasOption("filter"))))
+ {
+ if (GoParentFolder())
+ return true;
+ }
+ return CGUIWindow::OnBack(actionID);
+}
+
+bool CGUIMediaWindow::OnMessage(CGUIMessage& message)
+{
+ switch ( message.GetMessage() )
+ {
+ case GUI_MSG_WINDOW_DEINIT:
+ {
+ CancelUpdateItems();
+
+ m_iLastControl = GetFocusedControlID();
+ CGUIWindow::OnMessage(message);
+
+ // get rid of any active filtering
+ if (m_canFilterAdvanced)
+ {
+ m_canFilterAdvanced = false;
+ m_filter.Reset();
+ }
+ m_strFilterPath.clear();
+
+ // Call ClearFileItems() after our window has finished doing any WindowClose
+ // animations
+ ClearFileItems();
+ return true;
+ }
+ break;
+
+ case GUI_MSG_CLICKED:
+ {
+ int iControl = message.GetSenderId();
+ if (iControl == CONTROL_BTNVIEWASICONS)
+ {
+ // view as control could be a select button
+ int viewMode = 0;
+ const CGUIControl *control = GetControl(CONTROL_BTNVIEWASICONS);
+ if (control && control->GetControlType() != CGUIControl::GUICONTROL_BUTTON)
+ {
+ CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTNVIEWASICONS);
+ OnMessage(msg);
+ viewMode = m_viewControl.GetViewModeNumber(msg.GetParam1());
+ }
+ else
+ viewMode = m_viewControl.GetNextViewMode();
+
+ if (m_guiState)
+ m_guiState->SaveViewAsControl(viewMode);
+
+ UpdateButtons();
+ return true;
+ }
+ else if (iControl == CONTROL_BTNSORTASC) // sort asc
+ {
+ if (m_guiState)
+ m_guiState->SetNextSortOrder();
+ UpdateFileList();
+ return true;
+ }
+ else if (iControl == CONTROL_BTNSORTBY) // sort by
+ {
+ if (m_guiState.get() && m_guiState->ChooseSortMethod())
+ UpdateFileList();
+ return true;
+ }
+ else if (iControl == CONTROL_BTN_FILTER)
+ return Filter(false);
+ else if (m_viewControl.HasControl(iControl)) // list/thumb control
+ {
+ int iItem = m_viewControl.GetSelectedItem();
+ int iAction = message.GetParam1();
+ if (iItem < 0) break;
+ if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
+ {
+ OnSelect(iItem);
+ }
+ else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
+ {
+ OnPopupMenu(iItem);
+ return true;
+ }
+ }
+ }
+ break;
+
+ case GUI_MSG_SETFOCUS:
+ {
+ if (m_viewControl.HasControl(message.GetControlId()) && m_viewControl.GetCurrentControl() != message.GetControlId())
+ {
+ m_viewControl.SetFocused();
+ return true;
+ }
+ }
+ break;
+
+ case GUI_MSG_NOTIFY_ALL:
+ { // Message is received even if this window is inactive
+ if (message.GetParam1() == GUI_MSG_WINDOW_RESET)
+ {
+ m_vecItems->SetPath("?");
+ return true;
+ }
+ else if ( message.GetParam1() == GUI_MSG_REFRESH_THUMBS )
+ {
+ for (int i = 0; i < m_vecItems->Size(); i++)
+ m_vecItems->Get(i)->FreeMemory(true);
+ break; // the window will take care of any info images
+ }
+ else if (message.GetParam1() == GUI_MSG_REMOVED_MEDIA)
+ {
+ if ((m_vecItems->IsVirtualDirectoryRoot() ||
+ m_vecItems->IsSourcesPath()) && IsActive())
+ {
+ int iItem = m_viewControl.GetSelectedItem();
+ Refresh();
+ m_viewControl.SetSelectedItem(iItem);
+ }
+ else if (m_vecItems->IsRemovable())
+ { // check that we have this removable share still
+ if (!m_rootDir.IsInSource(m_vecItems->GetPath()))
+ { // don't have this share any more
+ if (IsActive()) Update("");
+ else
+ {
+ m_history.ClearPathHistory();
+ m_vecItems->SetPath("");
+ }
+ }
+ }
+
+ return true;
+ }
+ else if (message.GetParam1()==GUI_MSG_UPDATE_SOURCES)
+ { // State of the sources changed, so update our view
+ if ((m_vecItems->IsVirtualDirectoryRoot() ||
+ m_vecItems->IsSourcesPath()) && IsActive())
+ {
+ if (m_vecItemsUpdating)
+ {
+ CLog::Log(LOGWARNING, "CGUIMediaWindow::OnMessage - updating in progress");
+ return true;
+ }
+ CUpdateGuard ug(m_vecItemsUpdating);
+ int iItem = m_viewControl.GetSelectedItem();
+ Refresh(true);
+ m_viewControl.SetSelectedItem(iItem);
+ }
+ return true;
+ }
+ else if (message.GetParam1()==GUI_MSG_UPDATE && IsActive())
+ {
+ if (m_vecItemsUpdating)
+ {
+ CLog::Log(LOGWARNING, "CGUIMediaWindow::OnMessage - updating in progress");
+ return true;
+ }
+ CUpdateGuard ug(m_vecItemsUpdating);
+ if (message.GetNumStringParams())
+ {
+ if (message.GetParam2()) // param2 is used for resetting the history
+ SetHistoryForPath(message.GetStringParam());
+
+ CFileItemList list(message.GetStringParam());
+ list.RemoveDiscCache(GetID());
+ Update(message.GetStringParam());
+ }
+ else
+ Refresh(true); // refresh the listing
+ }
+ else if (message.GetParam1()==GUI_MSG_UPDATE_ITEM && message.GetItem())
+ {
+ int flag = message.GetParam2();
+ CFileItemPtr newItem = std::static_pointer_cast<CFileItem>(message.GetItem());
+
+ if (IsActive() || (flag & GUI_MSG_FLAG_FORCE_UPDATE))
+ {
+ m_vecItems->UpdateItem(newItem.get());
+
+ if (flag & GUI_MSG_FLAG_UPDATE_LIST)
+ { // need the list updated as well
+ UpdateFileList();
+ }
+ }
+ else if (newItem)
+ { // need to remove the disc cache
+ CFileItemList items;
+ items.SetPath(URIUtils::GetDirectory(newItem->GetPath()));
+ if (newItem->HasProperty("cachefilename"))
+ {
+ // Use stored cache file name
+ std::string crcfile = newItem->GetProperty("cachefilename").asString();
+ items.RemoveDiscCacheCRC(crcfile);
+ }
+ else
+ // No stored cache file name, attempt using truncated item path as list path
+ items.RemoveDiscCache(GetID());
+ }
+ }
+ else if (message.GetParam1()==GUI_MSG_UPDATE_PATH)
+ {
+ if (IsActive())
+ {
+ if((message.GetStringParam() == m_vecItems->GetPath()) ||
+ (m_vecItems->IsMultiPath() && XFILE::CMultiPathDirectory::HasPath(m_vecItems->GetPath(), message.GetStringParam())))
+ Refresh();
+ }
+ }
+ else if (message.GetParam1() == GUI_MSG_FILTER_ITEMS && IsActive())
+ {
+ std::string filter = GetProperty("filter").asString();
+ // check if this is meant for advanced filtering
+ if (message.GetParam2() != 10)
+ {
+ if (message.GetParam2() == 1) // append
+ filter += message.GetStringParam();
+ else if (message.GetParam2() == 2)
+ { // delete
+ if (filter.size())
+ filter.erase(filter.size() - 1);
+ }
+ else
+ filter = message.GetStringParam();
+ }
+ OnFilterItems(filter);
+ UpdateButtons();
+ return true;
+ }
+ else
+ return CGUIWindow::OnMessage(message);
+
+ return true;
+ }
+ break;
+ case GUI_MSG_PLAYBACK_STARTED:
+ case GUI_MSG_PLAYBACK_ENDED:
+ case GUI_MSG_PLAYBACK_STOPPED:
+ case GUI_MSG_PLAYLIST_CHANGED:
+ case GUI_MSG_PLAYLISTPLAYER_STOPPED:
+ case GUI_MSG_PLAYLISTPLAYER_STARTED:
+ case GUI_MSG_PLAYLISTPLAYER_CHANGED:
+ { // send a notify all to all controls on this window
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_REFRESH_LIST);
+ OnMessage(msg);
+ break;
+ }
+ case GUI_MSG_CHANGE_VIEW_MODE:
+ {
+ int viewMode = 0;
+ if (message.GetParam1()) // we have an id
+ viewMode = m_viewControl.GetViewModeByID(message.GetParam1());
+ else if (message.GetParam2())
+ viewMode = m_viewControl.GetNextViewMode(message.GetParam2());
+
+ if (m_guiState)
+ m_guiState->SaveViewAsControl(viewMode);
+ UpdateButtons();
+ return true;
+ }
+ break;
+ case GUI_MSG_CHANGE_SORT_METHOD:
+ {
+ if (m_guiState)
+ {
+ if (message.GetParam1())
+ m_guiState->SetCurrentSortMethod(message.GetParam1());
+ else if (message.GetParam2())
+ m_guiState->SetNextSortMethod(message.GetParam2());
+ }
+ UpdateFileList();
+ return true;
+ }
+ break;
+ case GUI_MSG_CHANGE_SORT_DIRECTION:
+ {
+ if (m_guiState)
+ m_guiState->SetNextSortOrder();
+ UpdateFileList();
+ return true;
+ }
+ break;
+ case GUI_MSG_WINDOW_INIT:
+ {
+ if (m_vecItems->GetPath() == "?")
+ m_vecItems->SetPath("");
+
+ std::string dir = message.GetStringParam(0);
+ const std::string& ret = message.GetStringParam(1);
+ const std::string& swap = message.GetStringParam(message.GetNumStringParams() - 1);
+ const bool returning = StringUtils::EqualsNoCase(ret, "return");
+ const bool replacing = StringUtils::EqualsNoCase(swap, "replace");
+
+ if (!dir.empty())
+ {
+ // ensure our directory is valid
+ dir = GetStartFolder(dir);
+ bool resetHistory = false;
+ if (!returning || !URIUtils::PathEquals(dir, m_startDirectory, true))
+ { // we're not returning to the same path, so set our directory to the requested path
+ m_vecItems->SetPath(dir);
+ resetHistory = true;
+ }
+ else if (m_vecItems->GetPath().empty() && URIUtils::PathEquals(dir, m_startDirectory, true))
+ m_vecItems->SetPath(dir);
+
+ // check for network up
+ if (URIUtils::IsRemote(m_vecItems->GetPath()) && !WaitForNetwork())
+ {
+ m_vecItems->SetPath("");
+ resetHistory = true;
+ }
+ if (resetHistory)
+ {
+ m_vecItems->RemoveDiscCache(GetID());
+ // only compute the history for the provided path if "return" is not defined
+ // (otherwise the root level for the path will be added by default to the path history
+ // and we won't be able to move back to the path we came from)
+ if (!returning)
+ SetHistoryForPath(m_vecItems->GetPath());
+ }
+ }
+ if (message.GetParam1() != WINDOW_INVALID)
+ {
+ // if this is the first time to this window - make sure we set the root path
+ // if "return" is defined make sure we set the startDirectory to the directory we are
+ // moving to (so that we can move back to where we were onBack). If we are activating
+ // the same window but with a different path, do nothing - we are simply adding to the
+ // window history. Note that if the window is just being replaced, the start directory
+ // also needs to be set as the manager has just popped the previous window.
+ if (message.GetParam1() != message.GetParam2() || replacing)
+ m_startDirectory = returning ? dir : GetRootPath();
+ }
+ if (message.GetParam2() == PLUGIN_REFRESH_DELAY)
+ {
+ Refresh();
+ SetInitialVisibility();
+ RestoreControlStates();
+ SetInitialVisibility();
+ return true;
+ }
+ }
+ break;
+ }
+
+ return CGUIWindow::OnMessage(message);
+}
+
+/*!
+ * \brief Updates the states
+ *
+ * This updates the states (enable, disable, visible...) of the controls defined
+ * by this window.
+ *
+ * \note Override this function in a derived class to add new controls
+ */
+void CGUIMediaWindow::UpdateButtons()
+{
+ if (m_guiState)
+ {
+ // Update sorting controls
+ if (m_guiState->GetSortOrder() == SortOrderNone)
+ {
+ CONTROL_DISABLE(CONTROL_BTNSORTASC);
+ }
+ else
+ {
+ CONTROL_ENABLE(CONTROL_BTNSORTASC);
+ SET_CONTROL_SELECTED(GetID(), CONTROL_BTNSORTASC, m_guiState->GetSortOrder() != SortOrderAscending);
+ }
+
+ // Update list/thumb control
+ m_viewControl.SetCurrentView(m_guiState->GetViewAsControl());
+
+ // Update sort by button
+ if (!m_guiState->HasMultipleSortMethods())
+ CONTROL_DISABLE(CONTROL_BTNSORTBY);
+ else
+ CONTROL_ENABLE(CONTROL_BTNSORTBY);
+
+ std::string sortLabel = StringUtils::Format(
+ g_localizeStrings.Get(550), g_localizeStrings.Get(m_guiState->GetSortMethodLabel()));
+ SET_CONTROL_LABEL(CONTROL_BTNSORTBY, sortLabel);
+ }
+
+ std::string items =
+ StringUtils::Format("{} {}", m_vecItems->GetObjectCount(), g_localizeStrings.Get(127));
+ SET_CONTROL_LABEL(CONTROL_LABELFILES, items);
+
+ SET_CONTROL_LABEL2(CONTROL_BTN_FILTER, GetProperty("filter").asString());
+}
+
+void CGUIMediaWindow::ClearFileItems()
+{
+ m_viewControl.Clear();
+ m_vecItems->Clear();
+ m_unfilteredItems->Clear();
+}
+
+/*!
+ * \brief Sort file items
+ *
+ * This sorts file items based on the sort method and sort order provided by
+ * guiViewState.
+ */
+void CGUIMediaWindow::SortItems(CFileItemList &items)
+{
+ std::unique_ptr<CGUIViewState> guiState(CGUIViewState::GetViewState(GetID(), items));
+
+ if (guiState)
+ {
+ SortDescription sorting = guiState->GetSortMethod();
+ sorting.sortOrder = guiState->GetSortOrder();
+ // If the sort method is "sort by playlist" and we have a specific
+ // sort order available we can use the specified sort order to do the sorting
+ // We do this as the new SortBy methods are a superset of the SORT_METHOD methods, thus
+ // not all are available. This may be removed once SORT_METHOD_* have been replaced by
+ // SortBy.
+ if ((sorting.sortBy == SortByPlaylistOrder) && items.HasProperty(PROPERTY_SORT_ORDER))
+ {
+ SortBy sortBy = (SortBy)items.GetProperty(PROPERTY_SORT_ORDER).asInteger();
+ if (sortBy != SortByNone && sortBy != SortByPlaylistOrder && sortBy != SortByProgramCount)
+ {
+ sorting.sortBy = sortBy;
+ sorting.sortOrder = items.GetProperty(PROPERTY_SORT_ASCENDING).asBoolean() ? SortOrderAscending : SortOrderDescending;
+ sorting.sortAttributes = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING) ? SortAttributeIgnoreArticle : SortAttributeNone;
+
+ // if the sort order is descending, we need to switch the original sort order, as we assume
+ // in CGUIViewState::AddPlaylistOrder that SortByPlaylistOrder is ascending.
+ if (guiState->GetSortOrder() == SortOrderDescending)
+ sorting.sortOrder = sorting.sortOrder == SortOrderDescending ? SortOrderAscending : SortOrderDescending;
+ }
+ }
+
+ items.Sort(sorting);
+ }
+}
+
+/*!
+ * \brief Formats item labels
+ *
+ * This is based on the formatting provided by guiViewState.
+ */
+void CGUIMediaWindow::FormatItemLabels(CFileItemList &items, const LABEL_MASKS &labelMasks)
+{
+ CLabelFormatter fileFormatter(labelMasks.m_strLabelFile, labelMasks.m_strLabel2File);
+ CLabelFormatter folderFormatter(labelMasks.m_strLabelFolder, labelMasks.m_strLabel2Folder);
+ for (int i=0; i<items.Size(); ++i)
+ {
+ CFileItemPtr pItem=items[i];
+
+ if (pItem->IsLabelPreformatted())
+ continue;
+
+ if (pItem->m_bIsFolder)
+ folderFormatter.FormatLabels(pItem.get());
+ else
+ fileFormatter.FormatLabels(pItem.get());
+ }
+
+ if (items.GetSortMethod() == SortByLabel)
+ items.ClearSortState();
+}
+
+/*!
+ * \brief Format and sort file items
+ *
+ * Prepares and adds the fileitems to list/thumb panel
+ */
+void CGUIMediaWindow::FormatAndSort(CFileItemList &items)
+{
+ std::unique_ptr<CGUIViewState> viewState(CGUIViewState::GetViewState(GetID(), items));
+
+ if (viewState)
+ {
+ LABEL_MASKS labelMasks;
+ viewState->GetSortMethodLabelMasks(labelMasks);
+ FormatItemLabels(items, labelMasks);
+
+ items.Sort(viewState->GetSortMethod().sortBy, viewState->GetSortOrder(), viewState->GetSortMethod().sortAttributes);
+ }
+}
+
+/*!
+ * \brief Overwrite to fill fileitems from a source
+ *
+ * \param[in] strDirectory Path to read
+ * \param[out] items Fill with items specified in \e strDirectory
+ * \return false if given directory not present
+ */
+bool CGUIMediaWindow::GetDirectory(const std::string &strDirectory, CFileItemList &items)
+{
+ CURL pathToUrl(strDirectory);
+
+ std::string strParentPath = m_history.GetParentPath();
+
+ CLog::Log(LOGDEBUG, "CGUIMediaWindow::GetDirectory ({})", CURL::GetRedacted(strDirectory));
+ CLog::Log(LOGDEBUG, " ParentPath = [{}]", CURL::GetRedacted(strParentPath));
+
+ if (pathToUrl.IsProtocol("plugin") && !pathToUrl.GetHostName().empty())
+ CServiceBroker::GetAddonMgr().UpdateLastUsed(pathToUrl.GetHostName());
+
+ // see if we can load a previously cached folder
+ CFileItemList cachedItems(strDirectory);
+ if (!strDirectory.empty() && cachedItems.Load(GetID()))
+ {
+ items.Assign(cachedItems);
+ }
+ else
+ {
+ auto start = std::chrono::steady_clock::now();
+
+ if (strDirectory.empty())
+ SetupShares();
+
+ CFileItemList dirItems;
+ if (!GetDirectoryItems(pathToUrl, dirItems, UseFileDirectories()))
+ return false;
+
+ // assign fetched directory items
+ items.Assign(dirItems);
+
+ // took over a second, and not normally cached, so cache it
+ auto end = std::chrono::steady_clock::now();
+ auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
+
+ if (duration.count() > 1000 && items.CacheToDiscIfSlow())
+ items.Save(GetID());
+
+ // if these items should replace the current listing, then pop it off the top
+ if (items.GetReplaceListing())
+ m_history.RemoveParentPath();
+ }
+
+ // update the view state's reference to the current items
+ m_guiState.reset(CGUIViewState::GetViewState(GetID(), items));
+
+ bool bHideParent = false;
+
+ if (m_guiState && m_guiState->HideParentDirItems())
+ bHideParent = true;
+ if (items.GetPath() == GetRootPath())
+ bHideParent = true;
+
+ if (!bHideParent)
+ {
+ CFileItemPtr pItem(new CFileItem(".."));
+ pItem->SetPath(strParentPath);
+ pItem->m_bIsFolder = true;
+ pItem->m_bIsShareOrDrive = false;
+ items.AddFront(pItem, 0);
+ }
+
+ int iWindow = GetID();
+ std::vector<std::string> regexps;
+
+ //! @todo Do we want to limit the directories we apply the video ones to?
+ if (iWindow == WINDOW_VIDEO_NAV)
+ regexps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoExcludeFromListingRegExps;
+ if (iWindow == WINDOW_MUSIC_NAV)
+ regexps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_audioExcludeFromListingRegExps;
+ if (iWindow == WINDOW_PICTURES)
+ regexps = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_pictureExcludeFromListingRegExps;
+
+ if (regexps.size())
+ {
+ for (int i=0; i < items.Size();)
+ {
+ if (CUtil::ExcludeFileOrFolder(items[i]->GetPath(), regexps))
+ items.Remove(i);
+ else
+ i++;
+ }
+ }
+
+ // clear the filter
+ SetProperty("filter", "");
+ m_canFilterAdvanced = false;
+ m_filter.Reset();
+ return true;
+}
+
+bool CGUIMediaWindow::Update(const std::string &strDirectory, bool updateFilterPath /* = true */)
+{
+ //! @todo OnInitWindow calls Update() before window path has been set properly.
+ if (strDirectory == "?")
+ return false;
+
+ // The path to load. Empty string is used in various places to denote root, so translate to the
+ // real root path first
+ const std::string path = strDirectory.empty() ? GetRootPath() : strDirectory;
+
+ // stores the selected item in history
+ SaveSelectedItemInHistory();
+
+ const std::string previousPath = m_vecItems->GetPath();
+
+ // check if the path contains a filter and temporarily remove it
+ // so that the retrieved list of items is unfiltered
+ std::string pathNoFilter = path;
+ if (CanContainFilter(pathNoFilter) && CURL(pathNoFilter).HasOption("filter"))
+ pathNoFilter = RemoveParameterFromPath(pathNoFilter, "filter");
+
+ if (!GetDirectory(pathNoFilter, *m_vecItems))
+ {
+ CLog::Log(LOGERROR, "CGUIMediaWindow::GetDirectory({}) failed", CURL(path).GetRedacted());
+
+ if (URIUtils::PathEquals(path, GetRootPath()))
+ return false; // Nothing to fallback to
+
+ // Try to return to the previous directory, if not the same
+ // else fallback to root
+ if (URIUtils::PathEquals(path, previousPath) || !Update(m_history.RemoveParentPath()))
+ Update(""); // Fallback to root
+
+ // Return false to be able to eg. show
+ // an error message.
+ return false;
+ }
+
+ if (m_vecItems->GetLabel().empty())
+ {
+ // Removable sources
+ VECSOURCES removables;
+ CServiceBroker::GetMediaManager().GetRemovableDrives(removables);
+ for (const auto& s : removables)
+ {
+ if (URIUtils::CompareWithoutSlashAtEnd(s.strPath, m_vecItems->GetPath()))
+ {
+ m_vecItems->SetLabel(s.strName);
+ break;
+ }
+ }
+ }
+
+ if (m_vecItems->GetLabel().empty())
+ m_vecItems->SetLabel(CUtil::GetTitleFromPath(m_vecItems->GetPath(), true));
+
+ // check the given path for filter data
+ UpdateFilterPath(path, *m_vecItems, updateFilterPath);
+
+ // if we're getting the root source listing
+ // make sure the path history is clean
+ if (URIUtils::PathEquals(path, GetRootPath()))
+ m_history.ClearPathHistory();
+
+ int iWindow = GetID();
+ int showLabel = 0;
+ if (URIUtils::PathEquals(path, GetRootPath()))
+ {
+ if (iWindow == WINDOW_PICTURES)
+ showLabel = 997;
+ else if (iWindow == WINDOW_FILES)
+ showLabel = 1026;
+ else if (iWindow == WINDOW_GAMES)
+ showLabel = 35250; // "Add games..."
+ }
+ if (m_vecItems->IsPath("sources://video/"))
+ showLabel = 999;
+ else if (m_vecItems->IsPath("sources://music/"))
+ showLabel = 998;
+ else if (m_vecItems->IsPath("sources://pictures/"))
+ showLabel = 997;
+ else if (m_vecItems->IsPath("sources://files/"))
+ showLabel = 1026;
+ else if (m_vecItems->IsPath("sources://games/"))
+ showLabel = 35250; // "Add games..."
+ // Add 'Add source ' item
+ if (showLabel && (m_vecItems->Size() == 0 || !m_guiState->DisableAddSourceButtons()) &&
+ iWindow != WINDOW_MUSIC_PLAYLIST_EDITOR)
+ {
+ const std::string& strLabel = g_localizeStrings.Get(showLabel);
+ CFileItemPtr pItem(new CFileItem(strLabel));
+ pItem->SetPath("add");
+ pItem->SetArt("icon", "DefaultAddSource.png");
+ pItem->SetLabel(strLabel);
+ pItem->SetLabelPreformatted(true);
+ pItem->m_bIsFolder = true;
+ pItem->SetSpecialSort(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_addSourceOnTop ?
+ SortSpecialOnTop : SortSpecialOnBottom);
+ m_vecItems->Add(pItem);
+ }
+ m_iLastControl = GetFocusedControlID();
+
+ // Check whether to enabled advanced filtering based on the content type
+ m_canFilterAdvanced = CheckFilterAdvanced(*m_vecItems);
+ if (m_canFilterAdvanced)
+ m_filter.SetType(m_vecItems->GetContent());
+
+ // Ask the derived class if it wants to load additional info
+ // for the fileitems like media info or additional
+ // filtering on the items, setting thumbs.
+ OnPrepareFileItems(*m_vecItems);
+
+ m_vecItems->FillInDefaultIcons();
+
+ // remember the original (untouched) list of items (for filtering etc)
+ m_unfilteredItems->Assign(*m_vecItems);
+
+ // Cache the list of items if possible
+ OnCacheFileItems(*m_vecItems);
+
+ // Filter and group the items if necessary
+ OnFilterItems(GetProperty("filter").asString());
+ UpdateButtons();
+
+ // Restore selected item from history
+ RestoreSelectedItemFromHistory();
+
+ m_history.AddPath(m_vecItems->GetPath(), m_strFilterPath);
+
+ //m_history.DumpPathHistory();
+
+ return true;
+}
+
+bool CGUIMediaWindow::Refresh(bool clearCache /* = false */)
+{
+ std::string strCurrentDirectory = m_vecItems->GetPath();
+ if (strCurrentDirectory == "?")
+ return false;
+
+ if (clearCache)
+ m_vecItems->RemoveDiscCache(GetID());
+
+ bool ret = true;
+
+ // get the original number of items
+ if (!Update(strCurrentDirectory, false))
+ {
+ ret = false;
+ }
+
+ return ret;
+}
+
+/*!
+ * \brief On prepare file items
+ *
+ * This function will be called by Update() before the labels of the fileitems
+ * are formatted.
+ *
+ * \note Override this function to set custom thumbs or load additional media
+ * info.
+ *
+ * It's used to load tag info for music.
+ */
+void CGUIMediaWindow::OnPrepareFileItems(CFileItemList &items)
+{
+ CFileItemListModification::GetInstance().Modify(items);
+}
+
+/*!
+ * \brief On cache file items
+ *
+ * This function will be called by Update() before
+ * any additional formatting, filtering or sorting is applied.
+ *
+ * \note Override this function to define a custom caching behaviour.
+ */
+void CGUIMediaWindow::OnCacheFileItems(CFileItemList &items)
+{
+ // Should these items be saved to the hdd
+ if (items.CacheToDiscAlways() && !IsFiltered())
+ items.Save(GetID());
+}
+
+/*!
+ * \brief On click
+ *
+ * With this function you can react on a users click in the list/thumb panel.
+ * It returns true, if the click is handled.
+ * This function calls OnPlayMedia()
+ */
+bool CGUIMediaWindow::OnClick(int iItem, const std::string &player)
+{
+ if (iItem < 0 || iItem >= m_vecItems->Size())
+ return true;
+
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ CFileItemPtr pItem = m_vecItems->Get(iItem);
+
+ if (pItem->IsParentFolder())
+ {
+ GoParentFolder();
+ return true;
+ }
+
+ if (pItem->GetPath() == "add" || pItem->GetPath() == "sources://add/") // 'add source button' in empty root
+ {
+ if (profileManager->IsMasterProfile())
+ {
+ if (!g_passwordManager.IsMasterLockUnlocked(true))
+ return false;
+ }
+ else if (!profileManager->GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked())
+ return false;
+
+ if (OnAddMediaSource())
+ Refresh(true);
+
+ return true;
+ }
+
+ if (!pItem->m_bIsFolder && pItem->IsFileFolder(EFILEFOLDER_MASK_ONCLICK))
+ {
+ XFILE::IFileDirectory *pFileDirectory = nullptr;
+ pFileDirectory = XFILE::CFileDirectoryFactory::Create(pItem->GetURL(), pItem.get(), "");
+ if(pFileDirectory)
+ pItem->m_bIsFolder = true;
+ else if(pItem->m_bIsFolder)
+ pItem->m_bIsFolder = false;
+ delete pFileDirectory;
+ }
+
+ if (pItem->IsScript())
+ {
+ // execute the script
+ CURL url(pItem->GetPath());
+ AddonPtr addon;
+ if (CServiceBroker::GetAddonMgr().GetAddon(url.GetHostName(), addon, AddonType::SCRIPT,
+ OnlyEnabled::CHOICE_YES))
+ {
+ if (!CScriptInvocationManager::GetInstance().Stop(addon->LibPath()))
+ {
+ CServiceBroker::GetAddonMgr().UpdateLastUsed(addon->ID());
+ CScriptInvocationManager::GetInstance().ExecuteAsync(addon->LibPath(), addon);
+ }
+ return true;
+ }
+ }
+
+ if (pItem->m_bIsFolder)
+ {
+ if ( pItem->m_bIsShareOrDrive )
+ {
+ const std::string& strLockType=m_guiState->GetLockType();
+ if (profileManager->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE)
+ if (!strLockType.empty() && !g_passwordManager.IsItemUnlocked(pItem.get(), strLockType))
+ return true;
+
+ if (!HaveDiscOrConnection(pItem->GetPath(), pItem->m_iDriveType))
+ return true;
+ }
+
+ // check for the partymode playlist items - they may not exist yet
+ if ((pItem->GetPath() == profileManager->GetUserDataItem("PartyMode.xsp")) ||
+ (pItem->GetPath() == profileManager->GetUserDataItem("PartyMode-Video.xsp")))
+ {
+ // party mode playlist item - if it doesn't exist, prompt for user to define it
+ if (!CFileUtils::Exists(pItem->GetPath()))
+ {
+ m_vecItems->RemoveDiscCache(GetID());
+ if (CGUIDialogSmartPlaylistEditor::EditPlaylist(pItem->GetPath()))
+ Refresh();
+ return true;
+ }
+ }
+
+ // remove the directory cache if the folder is not normally cached
+ CFileItemList items(pItem->GetPath());
+ if (!items.AlwaysCache())
+ items.RemoveDiscCache(GetID());
+
+ // if we have a filtered list, we need to add the filtered
+ // path to be able to come back to the filtered view
+ std::string strCurrentDirectory = m_vecItems->GetPath();
+ if (m_canFilterAdvanced && !m_filter.IsEmpty() &&
+ !URIUtils::PathEquals(m_strFilterPath, strCurrentDirectory))
+ {
+ m_history.RemoveParentPath();
+ m_history.AddPath(strCurrentDirectory, m_strFilterPath);
+ }
+
+ if (m_vecItemsUpdating)
+ {
+ CLog::Log(LOGWARNING, "CGUIMediaWindow::OnClick - updating in progress");
+ return true;
+ }
+ CUpdateGuard ug(m_vecItemsUpdating);
+
+ CFileItem directory(*pItem);
+ if (!Update(directory.GetPath()))
+ ShowShareErrorMessage(&directory);
+
+ return true;
+ }
+ else if (pItem->IsPlugin() && !pItem->GetProperty("isplayable").asBoolean())
+ {
+ bool resume = pItem->GetStartOffset() == STARTOFFSET_RESUME;
+ return XFILE::CPluginDirectory::RunScriptWithParams(pItem->GetPath(), resume);
+ }
+#if defined(TARGET_ANDROID)
+ else if (pItem->IsAndroidApp())
+ {
+ std::string appName = URIUtils::GetFileName(pItem->GetPath());
+ CLog::Log(LOGDEBUG, "CGUIMediaWindow::OnClick Trying to run: {}", appName);
+ return CXBMCApp::StartActivity(appName);
+ }
+#endif
+ else
+ {
+ SaveSelectedItemInHistory();
+
+ if (pItem->GetPath() == "newplaylist://")
+ {
+ m_vecItems->RemoveDiscCache(GetID());
+ CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_MUSIC_PLAYLIST_EDITOR,"newplaylist://");
+ return true;
+ }
+ else if (StringUtils::StartsWithNoCase(pItem->GetPath(), "newsmartplaylist://"))
+ {
+ m_vecItems->RemoveDiscCache(GetID());
+ if (CGUIDialogSmartPlaylistEditor::NewPlaylist(pItem->GetPath().substr(19)))
+ Refresh();
+ return true;
+ }
+
+ bool autoplay = m_guiState.get() && m_guiState->AutoPlayNextItem();
+
+ if (m_vecItems->IsPlugin())
+ {
+ CURL url(m_vecItems->GetPath());
+ AddonPtr addon;
+ if (CServiceBroker::GetAddonMgr().GetAddon(url.GetHostName(), addon, OnlyEnabled::CHOICE_YES))
+ {
+ const auto plugin = std::dynamic_pointer_cast<CPluginSource>(addon);
+ if (plugin && plugin->Provides(CPluginSource::AUDIO))
+ {
+ CFileItemList items;
+ std::unique_ptr<CGUIViewState> state(CGUIViewState::GetViewState(GetID(), items));
+ autoplay = state.get() && state->AutoPlayNextItem();
+ }
+ }
+ }
+
+ if (autoplay && !g_partyModeManager.IsEnabled())
+ {
+ return OnPlayAndQueueMedia(pItem, player);
+ }
+ else
+ {
+ return OnPlayMedia(iItem, player);
+ }
+ }
+
+ return false;
+}
+
+bool CGUIMediaWindow::OnSelect(int item)
+{
+ return OnClick(item);
+}
+
+/*!
+ * \brief Check disc or connection present
+ *
+ * Checks if there is a disc in the dvd drive and whether the
+ * network is connected or not.
+ */
+bool CGUIMediaWindow::HaveDiscOrConnection(const std::string& strPath, int iDriveType)
+{
+ if (iDriveType==CMediaSource::SOURCE_TYPE_DVD)
+ {
+ if (!CServiceBroker::GetMediaManager().IsDiscInDrive(strPath))
+ {
+ HELPERS::ShowOKDialogText(CVariant{218}, CVariant{219});
+ return false;
+ }
+ }
+ else if (iDriveType==CMediaSource::SOURCE_TYPE_REMOTE)
+ {
+ //! @todo Handle not connected to a remote share
+ if (!CServiceBroker::GetNetwork().IsConnected())
+ {
+ HELPERS::ShowOKDialogText(CVariant{220}, CVariant{221});
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*!
+ * \brief Shows a standard error message for a given pItem.
+ */
+void CGUIMediaWindow::ShowShareErrorMessage(CFileItem* pItem) const
+{
+ if (!pItem->m_bIsShareOrDrive)
+ return;
+
+ int idMessageText = 0;
+ CURL url(pItem->GetPath());
+
+ if (url.IsProtocol("smb") && url.GetHostName().empty()) // smb workgroup
+ idMessageText = 15303; // Workgroup not found
+ else if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_REMOTE || URIUtils::IsRemote(pItem->GetPath()))
+ idMessageText = 15301; // Could not connect to network server
+ else
+ idMessageText = 15300; // Path not found or invalid
+
+ HELPERS::ShowOKDialogText(CVariant{220}, CVariant{idMessageText});
+}
+
+/*!
+ * \brief Go one directory up on list items
+ *
+ * The function goes up one level in the directory tree
+ */
+bool CGUIMediaWindow::GoParentFolder()
+{
+ if (m_vecItems->IsVirtualDirectoryRoot())
+ return false;
+
+ if (URIUtils::PathEquals(m_vecItems->GetPath(), GetRootPath()))
+ return false;
+
+ //m_history.DumpPathHistory();
+
+ const std::string currentPath = m_vecItems->GetPath();
+ std::string parentPath = m_history.GetParentPath();
+ // Check if a) the current folder is on the stack more than once, (parent is
+ // often same as current), OR
+ // b) the parent is an xml file (happens when ActivateWindow() called with
+ // a node file) and so current path is the result of expanding the xml.
+ // Keep going until there's nothing left or they dont match anymore.
+ while (!parentPath.empty() &&
+ (URIUtils::PathEquals(parentPath, currentPath, true) ||
+ StringUtils::EndsWith(parentPath, ".xml/") || StringUtils::EndsWith(parentPath, ".xml")))
+ {
+ m_history.RemoveParentPath();
+ parentPath = m_history.GetParentPath();
+ }
+
+ // remove the current filter but only if the parent
+ // item doesn't have a filter as well
+ CURL filterUrl(m_strFilterPath);
+ if (filterUrl.HasOption("filter"))
+ {
+ CURL parentUrl(m_history.GetParentPath(true));
+ if (!parentUrl.HasOption("filter"))
+ {
+ // we need to overwrite m_strFilterPath because
+ // Refresh() will set updateFilterPath to false
+ m_strFilterPath.clear();
+ Refresh();
+ return true;
+ }
+ }
+
+ // pop directory path from the stack
+ m_strFilterPath = m_history.GetParentPath(true);
+ m_history.RemoveParentPath();
+
+ if (!Update(parentPath, false))
+ return false;
+
+ // No items to show so go another level up
+ if (!m_vecItems->GetPath().empty() && (m_filter.IsEmpty() ? m_vecItems->Size() : m_unfilteredItems->Size()) <= 0)
+ {
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(2080), g_localizeStrings.Get(2081));
+ return GoParentFolder();
+ }
+ return true;
+}
+
+void CGUIMediaWindow::SaveSelectedItemInHistory()
+{
+ int iItem = m_viewControl.GetSelectedItem();
+ std::string strSelectedItem;
+ if (iItem >= 0 && iItem < m_vecItems->Size())
+ {
+ CFileItemPtr pItem = m_vecItems->Get(iItem);
+ GetDirectoryHistoryString(pItem.get(), strSelectedItem);
+ }
+
+ m_history.SetSelectedItem(strSelectedItem, m_vecItems->GetPath());
+}
+
+void CGUIMediaWindow::RestoreSelectedItemFromHistory()
+{
+ std::string strSelectedItem = m_history.GetSelectedItem(m_vecItems->GetPath());
+
+ if (!strSelectedItem.empty())
+ {
+ for (int i = 0; i < m_vecItems->Size(); ++i)
+ {
+ CFileItemPtr pItem = m_vecItems->Get(i);
+ std::string strHistory;
+ GetDirectoryHistoryString(pItem.get(), strHistory);
+ // set selected item if equals with history
+ if (strHistory == strSelectedItem)
+ {
+ m_viewControl.SetSelectedItem(i);
+ return;
+ }
+ }
+ }
+
+ // if we haven't found the selected item, select the first item
+ m_viewControl.SetSelectedItem(0);
+}
+
+/*!
+ * \brief Get history string for given file item
+ *
+ * \note Override the function to change the default behavior on how
+ * a selected item history should look like
+ */
+void CGUIMediaWindow::GetDirectoryHistoryString(const CFileItem* pItem, std::string& strHistoryString) const
+{
+ if (pItem->m_bIsShareOrDrive)
+ {
+ // We are in the virtual directory
+
+ // History string of the DVD drive
+ // must be handled separately
+ if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_DVD)
+ {
+ // Remove disc label from item label
+ // and use as history string, m_strPath
+ // can change for new discs
+ std::string strLabel = pItem->GetLabel();
+ size_t nPosOpen = strLabel.find('(');
+ size_t nPosClose = strLabel.rfind(')');
+ if (nPosOpen != std::string::npos &&
+ nPosClose != std::string::npos &&
+ nPosClose > nPosOpen)
+ {
+ strLabel.erase(nPosOpen + 1, (nPosClose) - (nPosOpen + 1));
+ strHistoryString = strLabel;
+ }
+ else
+ strHistoryString = strLabel;
+ }
+ else
+ {
+ // Other items in virtual directory
+ std::string strPath = pItem->GetPath();
+ URIUtils::RemoveSlashAtEnd(strPath);
+
+ strHistoryString = pItem->GetLabel() + strPath;
+ }
+ }
+ else if (pItem->GetEndOffset() > pItem->GetStartOffset() &&
+ pItem->GetStartOffset() != STARTOFFSET_RESUME)
+ {
+ // Could be a cue item, all items of a cue share the same filename
+ // so add the offsets to build the history string
+ strHistoryString = StringUtils::Format("{}{}", pItem->GetStartOffset(), pItem->GetEndOffset());
+ strHistoryString += pItem->GetPath();
+ }
+ else
+ {
+ // Normal directory items
+ strHistoryString = pItem->GetPath();
+ }
+
+ // remove any filter
+ if (CanContainFilter(strHistoryString))
+ strHistoryString = RemoveParameterFromPath(strHistoryString, "filter");
+
+ URIUtils::RemoveSlashAtEnd(strHistoryString);
+ StringUtils::ToLower(strHistoryString);
+}
+
+/*!
+ * \brief Set history for path
+ *
+ * Call this function to create a directory history for the
+ * path given by strDirectory.
+ */
+void CGUIMediaWindow::SetHistoryForPath(const std::string& strDirectory)
+{
+ // Make sure our shares are configured
+ SetupShares();
+ if (!strDirectory.empty())
+ {
+ // Build the directory history for default path
+ std::string strPath, strParentPath;
+ strPath = strDirectory;
+ URIUtils::RemoveSlashAtEnd(strPath);
+
+ CFileItemList items;
+ CURL url;
+ GetDirectoryItems(url, items, UseFileDirectories());
+
+ m_history.ClearPathHistory();
+
+ bool originalPath = true;
+ while (URIUtils::GetParentPath(strPath, strParentPath))
+ {
+ for (int i = 0; i < items.Size(); ++i)
+ {
+ CFileItemPtr pItem = items[i];
+ std::string path(pItem->GetPath());
+ URIUtils::RemoveSlashAtEnd(path);
+ if (URIUtils::PathEquals(path, strPath))
+ {
+ std::string strHistory;
+ GetDirectoryHistoryString(pItem.get(), strHistory);
+ m_history.SetSelectedItem(strHistory, "");
+ URIUtils::AddSlashAtEnd(strPath);
+ m_history.AddPathFront(strPath);
+ m_history.AddPathFront("");
+
+ //m_history.DumpPathHistory();
+ return ;
+ }
+ }
+
+ if (URIUtils::IsVideoDb(strPath))
+ {
+ CURL url(strParentPath);
+ url.SetOptions(""); // clear any URL options from recreated parent path
+ strParentPath = url.Get();
+ }
+
+ // set the original path exactly as it was passed in
+ if (URIUtils::PathEquals(strPath, strDirectory, true))
+ strPath = strDirectory;
+ else
+ URIUtils::AddSlashAtEnd(strPath);
+
+ m_history.AddPathFront(strPath, originalPath ? m_strFilterPath : "");
+ m_history.SetSelectedItem(strPath, strParentPath);
+ originalPath = false;
+ strPath = strParentPath;
+ URIUtils::RemoveSlashAtEnd(strPath);
+ }
+ }
+ else
+ m_history.ClearPathHistory();
+
+ //m_history.DumpPathHistory();
+}
+
+/*!
+ * \brief On media play
+ *
+ * \note Override if you want to change the default behavior, what is done
+ * when the user clicks on a file.
+ *
+ * This function is called by OnClick()
+ */
+bool CGUIMediaWindow::OnPlayMedia(int iItem, const std::string &player)
+{
+ // Reset Playlistplayer, playback started now does
+ // not use the playlistplayer.
+ CServiceBroker::GetPlaylistPlayer().Reset();
+ CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_NONE);
+ CFileItemPtr pItem=m_vecItems->Get(iItem);
+
+ CLog::Log(LOGDEBUG, "{} {}", __FUNCTION__, CURL::GetRedacted(pItem->GetPath()));
+
+ bool bResult = false;
+ if (pItem->IsInternetStream() || pItem->IsPlayList())
+ bResult = g_application.PlayMedia(*pItem, player, m_guiState->GetPlaylist());
+ else
+ bResult = g_application.PlayFile(*pItem, player);
+
+ if (pItem->GetStartOffset() == STARTOFFSET_RESUME)
+ pItem->SetStartOffset(0);
+
+ return bResult;
+}
+
+/*!
+ * \brief On play and media queue
+ *
+ * \note Override if you want to change the default behavior of what is done
+ * when the user clicks on a file in a "folder" with similar files.
+ *
+ * This function is called by OnClick()
+ */
+bool CGUIMediaWindow::OnPlayAndQueueMedia(const CFileItemPtr& item, const std::string& player)
+{
+ //play and add current directory to temporary playlist
+ PLAYLIST::Id playlistId = m_guiState->GetPlaylist();
+ if (playlistId != PLAYLIST::TYPE_NONE)
+ {
+ CServiceBroker::GetPlaylistPlayer().ClearPlaylist(playlistId);
+ CServiceBroker::GetPlaylistPlayer().Reset();
+ int mediaToPlay = 0;
+
+ // first try to find mainDVD file (VIDEO_TS.IFO).
+ // If we find this we should not allow to queue VOB files
+ std::string mainDVD;
+ for (int i = 0; i < m_vecItems->Size(); i++)
+ {
+ std::string path = URIUtils::GetFileName(m_vecItems->Get(i)->GetDynPath());
+ if (StringUtils::EqualsNoCase(path, "VIDEO_TS.IFO"))
+ {
+ mainDVD = path;
+ break;
+ }
+ }
+
+ // now queue...
+ for ( int i = 0; i < m_vecItems->Size(); i++ )
+ {
+ CFileItemPtr nItem = m_vecItems->Get(i);
+
+ if (nItem->m_bIsFolder)
+ continue;
+
+ if (!nItem->IsZIP() && !nItem->IsRAR() && (!nItem->IsDVDFile() || (URIUtils::GetFileName(nItem->GetDynPath()) == mainDVD)))
+ CServiceBroker::GetPlaylistPlayer().Add(playlistId, nItem);
+
+ if (item->IsSamePath(nItem.get()))
+ { // item that was clicked
+ mediaToPlay = CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId).size() - 1;
+ }
+ }
+
+ // Save current window and directory to know where the selected item was
+ if (m_guiState)
+ m_guiState->SetPlaylistDirectory(m_vecItems->GetPath());
+
+ // figure out where we start playback
+ if (CServiceBroker::GetPlaylistPlayer().IsShuffled(playlistId))
+ {
+ int iIndex =
+ CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId).FindOrder(mediaToPlay);
+ CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId).Swap(0, iIndex);
+ mediaToPlay = 0;
+ }
+
+ // play
+ CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(playlistId);
+ CServiceBroker::GetPlaylistPlayer().Play(mediaToPlay, player);
+ }
+ return true;
+}
+
+/*!
+ * \brief Update file list
+ *
+ * Synchronize the fileitems with the playlistplayer
+ * also recreates the playlist of the playlistplayer based
+ * on the fileitems of the window
+ */
+void CGUIMediaWindow::UpdateFileList()
+{
+ int nItem = m_viewControl.GetSelectedItem();
+ std::string strSelected;
+ if (nItem >= 0)
+ strSelected = m_vecItems->Get(nItem)->GetPath();
+
+ FormatAndSort(*m_vecItems);
+ UpdateButtons();
+
+ m_viewControl.SetItems(*m_vecItems);
+ m_viewControl.SetSelectedItem(strSelected);
+
+ // set the currently playing item as selected, if its in this directory
+ if (m_guiState.get() && m_guiState->IsCurrentPlaylistDirectory(m_vecItems->GetPath()))
+ {
+ PLAYLIST::Id playlistId = m_guiState->GetPlaylist();
+ int nSong = CServiceBroker::GetPlaylistPlayer().GetCurrentSong();
+ CFileItem playlistItem;
+ if (nSong > -1 && playlistId != PLAYLIST::TYPE_NONE)
+ playlistItem = *CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId)[nSong];
+
+ CServiceBroker::GetPlaylistPlayer().ClearPlaylist(playlistId);
+ CServiceBroker::GetPlaylistPlayer().Reset();
+
+ for (int i = 0; i < m_vecItems->Size(); i++)
+ {
+ CFileItemPtr pItem = m_vecItems->Get(i);
+ if (pItem->m_bIsFolder)
+ continue;
+
+ if (!pItem->IsPlayList() && !pItem->IsZIP() && !pItem->IsRAR())
+ CServiceBroker::GetPlaylistPlayer().Add(playlistId, pItem);
+
+ if (pItem->GetPath() == playlistItem.GetPath() &&
+ pItem->GetStartOffset() == playlistItem.GetStartOffset())
+ CServiceBroker::GetPlaylistPlayer().SetCurrentSong(
+ CServiceBroker::GetPlaylistPlayer().GetPlaylist(playlistId).size() - 1);
+ }
+ }
+}
+
+void CGUIMediaWindow::OnDeleteItem(int iItem)
+{
+ if ( iItem < 0 || iItem >= m_vecItems->Size()) return;
+ CFileItemPtr item = m_vecItems->Get(iItem);
+
+ if (item->IsPlayList())
+ item->m_bIsFolder = false;
+
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ if (profileManager->GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE && profileManager->GetCurrentProfile().filesLocked())
+ {
+ if (!g_passwordManager.IsMasterLockUnlocked(true))
+ return;
+ }
+
+ CGUIComponent *gui = CServiceBroker::GetGUI();
+ if (gui && gui->ConfirmDelete(item->GetPath()))
+ {
+ if (!CFileUtils::DeleteItem(item))
+ return;
+ }
+ else
+ return;
+
+ Refresh(true);
+ m_viewControl.SetSelectedItem(iItem);
+}
+
+void CGUIMediaWindow::OnRenameItem(int iItem)
+{
+ if (iItem < 0 || iItem >= m_vecItems->Size())
+ return;
+
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ if (profileManager->GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE && profileManager->GetCurrentProfile().filesLocked())
+ {
+ if (!g_passwordManager.IsMasterLockUnlocked(true))
+ return;
+ }
+
+ if (!CFileUtils::RenameFile(m_vecItems->Get(iItem)->GetPath()))
+ return;
+
+ Refresh(true);
+ m_viewControl.SetSelectedItem(iItem);
+}
+
+void CGUIMediaWindow::OnInitWindow()
+{
+ // initial fetch is done unthreaded to ensure the items are setup prior to skin animations kicking off
+ m_backgroundLoad = false;
+
+ // the start directory may change during Refresh
+ bool updateStartDirectory = URIUtils::PathEquals(m_vecItems->GetPath(), m_startDirectory, true);
+
+ // we have python scripts hooked in everywhere :(
+ // those scripts may open windows and we can't open a window
+ // while opening this one.
+ // for plugin sources delay call to Refresh
+ if (!URIUtils::IsPlugin(m_vecItems->GetPath()))
+ {
+ Refresh();
+ }
+ else
+ {
+ CGUIMessage msg(GUI_MSG_WINDOW_INIT, 0, 0, WINDOW_INVALID, PLUGIN_REFRESH_DELAY);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, GetID());
+ }
+
+ if (updateStartDirectory)
+ {
+ // reset the start directory to the path of the items
+ m_startDirectory = m_vecItems->GetPath();
+
+ // reset the history based on the path of the items
+ SetHistoryForPath(m_startDirectory);
+ }
+
+ m_backgroundLoad = true;
+
+ CGUIWindow::OnInitWindow();
+}
+
+void CGUIMediaWindow::SaveControlStates()
+{
+ CGUIWindow::SaveControlStates();
+ SaveSelectedItemInHistory();
+}
+
+void CGUIMediaWindow::RestoreControlStates()
+{
+ CGUIWindow::RestoreControlStates();
+ RestoreSelectedItemFromHistory();
+}
+
+CGUIControl *CGUIMediaWindow::GetFirstFocusableControl(int id)
+{
+ if (m_viewControl.HasControl(id))
+ id = m_viewControl.GetCurrentControl();
+ return CGUIWindow::GetFirstFocusableControl(id);
+}
+
+void CGUIMediaWindow::SetupShares()
+{
+ // Setup shares and filemasks for this window
+ CFileItemList items;
+ CGUIViewState* viewState=CGUIViewState::GetViewState(GetID(), items);
+ if (viewState)
+ {
+ m_rootDir.SetMask(viewState->GetExtensions());
+ m_rootDir.SetSources(viewState->GetSources());
+ delete viewState;
+ }
+}
+
+bool CGUIMediaWindow::OnPopupMenu(int itemIdx)
+{
+ auto InRange = [](size_t i, std::pair<size_t, size_t> range){ return i >= range.first && i < range.second; };
+
+ if (itemIdx < 0 || itemIdx >= m_vecItems->Size())
+ return false;
+
+ auto item = m_vecItems->Get(itemIdx);
+ if (!item)
+ return false;
+
+ CContextButtons buttons;
+
+ //Add items from plugin
+ {
+ int i = 0;
+ while (item->HasProperty(StringUtils::Format("contextmenulabel({})", i)))
+ {
+ buttons.emplace_back(
+ ~buttons.size(),
+ item->GetProperty(StringUtils::Format("contextmenulabel({})", i)).asString());
+ ++i;
+ }
+ }
+ auto pluginMenuRange = std::make_pair(static_cast<size_t>(0), buttons.size());
+
+ //Add the global menu
+ auto globalMenu = CServiceBroker::GetContextMenuManager().GetItems(*item, CContextMenuManager::MAIN);
+ auto globalMenuRange = std::make_pair(buttons.size(), buttons.size() + globalMenu.size());
+ for (const auto& menu : globalMenu)
+ buttons.emplace_back(~buttons.size(), menu->GetLabel(*item));
+
+ //Add legacy items from windows
+ auto buttonsSize = buttons.size();
+ GetContextButtons(itemIdx, buttons);
+ auto windowMenuRange = std::make_pair(buttonsSize, buttons.size());
+
+ //Add addon menus
+ auto addonMenu = CServiceBroker::GetContextMenuManager().GetAddonItems(*item, CContextMenuManager::MAIN);
+ auto addonMenuRange = std::make_pair(buttons.size(), buttons.size() + addonMenu.size());
+ for (const auto& menu : addonMenu)
+ buttons.emplace_back(~buttons.size(), menu->GetLabel(*item));
+
+ if (buttons.empty())
+ return true;
+
+ int idx = CGUIDialogContextMenu::Show(buttons);
+ if (idx < 0 || idx >= static_cast<int>(buttons.size()))
+ return false;
+
+ if (InRange(static_cast<size_t>(idx), pluginMenuRange))
+ {
+ bool saveVal = m_backgroundLoad;
+ m_backgroundLoad = false;
+ CServiceBroker::GetAppMessenger()->SendMsg(
+ TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr,
+ item->GetProperty(StringUtils::Format("contextmenuaction({})", idx - pluginMenuRange.first))
+ .asString());
+ m_backgroundLoad = saveVal;
+ return true;
+ }
+
+ if (InRange(idx, windowMenuRange))
+ return OnContextButton(itemIdx, static_cast<CONTEXT_BUTTON>(buttons[idx].first));
+
+ if (InRange(idx, globalMenuRange))
+ return CONTEXTMENU::LoopFrom(*globalMenu[idx - globalMenuRange.first], item);
+
+ return CONTEXTMENU::LoopFrom(*addonMenu[idx - addonMenuRange.first], item);
+}
+
+void CGUIMediaWindow::GetContextButtons(int itemNumber, CContextButtons &buttons)
+{
+ CFileItemPtr item = (itemNumber >= 0 && itemNumber < m_vecItems->Size()) ? m_vecItems->Get(itemNumber) : CFileItemPtr();
+
+ if (!item || item->IsParentFolder())
+ return;
+
+ if (item->IsFileFolder(EFILEFOLDER_MASK_ONBROWSE))
+ buttons.Add(CONTEXT_BUTTON_BROWSE_INTO, 37015);
+
+}
+
+bool CGUIMediaWindow::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
+{
+ switch (button)
+ {
+ case CONTEXT_BUTTON_BROWSE_INTO:
+ {
+ CFileItemPtr item = m_vecItems->Get(itemNumber);
+ Update(item->GetPath());
+ return true;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+const CGUIViewState *CGUIMediaWindow::GetViewState() const
+{
+ return m_guiState.get();
+}
+
+const CFileItemList& CGUIMediaWindow::CurrentDirectory() const
+{
+ return *m_vecItems;
+}
+
+bool CGUIMediaWindow::WaitForNetwork() const
+{
+ if (CServiceBroker::GetNetwork().IsAvailable())
+ return true;
+
+ CGUIDialogProgress *progress = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS);
+ if (!progress)
+ return true;
+
+ CURL url(m_vecItems->GetPath());
+ progress->SetHeading(CVariant{1040}); // Loading Directory
+ progress->SetLine(1, CVariant{url.GetWithoutUserDetails()});
+ progress->ShowProgressBar(false);
+ progress->Open();
+ while (!CServiceBroker::GetNetwork().IsAvailable())
+ {
+ progress->Progress();
+ if (progress->IsCanceled())
+ {
+ progress->Close();
+ return false;
+ }
+ }
+ progress->Close();
+ return true;
+}
+
+void CGUIMediaWindow::UpdateFilterPath(const std::string &strDirectory, const CFileItemList &items, bool updateFilterPath)
+{
+ bool canfilter = CanContainFilter(strDirectory);
+
+ std::string filter;
+ CURL url(strDirectory);
+ if (canfilter && url.HasOption("filter"))
+ filter = url.GetOption("filter");
+
+ // only set the filter path if it hasn't been marked
+ // as preset or if it's empty
+ if (updateFilterPath || m_strFilterPath.empty())
+ {
+ if (items.HasProperty(PROPERTY_PATH_DB))
+ m_strFilterPath = items.GetProperty(PROPERTY_PATH_DB).asString();
+ else
+ m_strFilterPath = items.GetPath();
+ }
+
+ // maybe the filter path can contain a filter
+ if (!canfilter && CanContainFilter(m_strFilterPath))
+ canfilter = true;
+
+ // check if the filter path contains a filter
+ CURL filterPathUrl(m_strFilterPath);
+ if (canfilter && filter.empty())
+ {
+ if (filterPathUrl.HasOption("filter"))
+ filter = filterPathUrl.GetOption("filter");
+ }
+
+ // check if there is a filter and re-apply it
+ if (canfilter && !filter.empty())
+ {
+ if (!m_filter.LoadFromJson(filter))
+ {
+ CLog::Log(LOGWARNING,
+ "CGUIMediaWindow::UpdateFilterPath(): unable to load existing filter ({})", filter);
+ m_filter.Reset();
+ m_strFilterPath = m_vecItems->GetPath();
+ }
+ else
+ {
+ // add the filter to the filter path
+ filterPathUrl.SetOption("filter", filter);
+ m_strFilterPath = filterPathUrl.Get();
+ }
+ }
+}
+
+void CGUIMediaWindow::OnFilterItems(const std::string &filter)
+{
+ m_viewControl.Clear();
+
+ CFileItemList items;
+ items.Copy(*m_vecItems, false); // use the original path - it'll likely be relied on for other things later.
+ items.Append(*m_unfilteredItems);
+ bool filtered = GetFilteredItems(filter, items);
+
+ m_vecItems->ClearItems();
+ // we need to clear the sort state and re-sort the items
+ m_vecItems->ClearSortState();
+ m_vecItems->Append(items);
+
+ // if the filter has changed, get the new filter path
+ if (filtered && m_canFilterAdvanced)
+ {
+ if (items.HasProperty(PROPERTY_PATH_DB))
+ m_strFilterPath = items.GetProperty(PROPERTY_PATH_DB).asString();
+ // only set m_strFilterPath if it hasn't been set before
+ // otherwise we might overwrite it with a non-filter path
+ // in case GetFilteredItems() returns true even though no
+ // db-based filter (e.g. watched filter) has been applied
+ else if (m_strFilterPath.empty())
+ m_strFilterPath = items.GetPath();
+ }
+
+ GetGroupedItems(*m_vecItems);
+ FormatAndSort(*m_vecItems);
+
+ CFileItemPtr currentItem;
+ std::string currentItemPath;
+ int item = m_viewControl.GetSelectedItem();
+ if (item >= 0 && item < m_vecItems->Size())
+ {
+ currentItem = m_vecItems->Get(item);
+ currentItemPath = currentItem->GetPath();
+ }
+
+ // get the "filter" option
+ std::string filterOption;
+ CURL filterUrl(m_strFilterPath);
+ if (filterUrl.HasOption("filter"))
+ filterOption = filterUrl.GetOption("filter");
+
+ // apply the "filter" option to any folder item so that
+ // the filter can be passed down to the sub-directory
+ for (int index = 0; index < m_vecItems->Size(); index++)
+ {
+ CFileItemPtr pItem = m_vecItems->Get(index);
+ // if the item is a folder we need to copy the path of
+ // the filtered item to be able to keep the applied filters
+ if (pItem->m_bIsFolder)
+ {
+ CURL itemUrl(pItem->GetPath());
+ if (!filterOption.empty())
+ itemUrl.SetOption("filter", filterOption);
+ else
+ itemUrl.RemoveOption("filter");
+ pItem->SetPath(itemUrl.Get());
+ }
+ }
+
+ SetProperty("filter", filter);
+ if (filtered && m_canFilterAdvanced)
+ {
+ // to be able to select the same item as before we need to adjust
+ // the path of the item i.e. add or remove the "filter=" URL option
+ // but that's only necessary for folder items
+ if (currentItem.get() && currentItem->m_bIsFolder)
+ {
+ CURL curUrl(currentItemPath), newUrl(m_strFilterPath);
+ if (newUrl.HasOption("filter"))
+ curUrl.SetOption("filter", newUrl.GetOption("filter"));
+ else if (curUrl.HasOption("filter"))
+ curUrl.RemoveOption("filter");
+
+ currentItemPath = curUrl.Get();
+ }
+ }
+
+ // The idea here is to ensure we have something to focus if our file list
+ // is empty. As such, this check MUST be last and ignore the hide parent
+ // fileitems settings.
+ if (m_vecItems->IsEmpty())
+ {
+ CFileItemPtr pItem(new CFileItem(".."));
+ pItem->SetPath(m_history.GetParentPath());
+ pItem->m_bIsFolder = true;
+ pItem->m_bIsShareOrDrive = false;
+ m_vecItems->AddFront(pItem, 0);
+ }
+
+ // and update our view control + buttons
+ m_viewControl.SetItems(*m_vecItems);
+ m_viewControl.SetSelectedItem(currentItemPath);
+}
+
+bool CGUIMediaWindow::GetFilteredItems(const std::string &filter, CFileItemList &items)
+{
+ bool result = false;
+ if (m_canFilterAdvanced)
+ result = GetAdvanceFilteredItems(items);
+
+ std::string trimmedFilter(filter);
+ StringUtils::TrimLeft(trimmedFilter);
+ StringUtils::ToLower(trimmedFilter);
+
+ if (trimmedFilter.empty())
+ return result;
+
+ CFileItemList filteredItems(items.GetPath()); // use the original path - it'll likely be relied on for other things later.
+ bool numericMatch = StringUtils::IsNaturalNumber(trimmedFilter);
+ for (int i = 0; i < items.Size(); i++)
+ {
+ CFileItemPtr item = items.Get(i);
+ if (item->IsParentFolder())
+ {
+ filteredItems.Add(item);
+ continue;
+ }
+ //! @todo Need to update this to get all labels, ideally out of the displayed info (ie from m_layout and m_focusedLayout)
+ //! though that isn't practical. Perhaps a better idea would be to just grab the info that we should filter on based on
+ //! where we are in the library tree.
+ //! Another idea is tying the filter string to the current level of the tree, so that going deeper disables the filter,
+ //! but it's re-enabled on the way back out.
+ std::string match;
+ /* if (item->GetFocusedLayout())
+ match = item->GetFocusedLayout()->GetAllText();
+ else if (item->GetLayout())
+ match = item->GetLayout()->GetAllText();
+ else*/
+ match = item->GetLabel(); // Filter label only for now
+
+ if (numericMatch)
+ StringUtils::WordToDigits(match);
+
+ size_t pos = StringUtils::FindWords(match.c_str(), trimmedFilter.c_str());
+ if (pos != std::string::npos)
+ filteredItems.Add(item);
+ }
+
+ items.ClearItems();
+ items.Append(filteredItems);
+
+ return items.GetObjectCount() > 0;
+}
+
+bool CGUIMediaWindow::GetAdvanceFilteredItems(CFileItemList &items)
+{
+ // don't run the advanced filter if the filter is empty
+ // and there hasn't been a filter applied before which
+ // would have to be removed
+ CURL url(m_strFilterPath);
+ if (m_filter.IsEmpty() && !url.HasOption("filter"))
+ return false;
+
+ CFileItemList resultItems;
+ XFILE::CSmartPlaylistDirectory::GetDirectory(m_filter, resultItems, m_strFilterPath, true);
+
+ // put together a lookup map for faster path comparison
+ std::map<std::string, CFileItemPtr> lookup;
+ for (int j = 0; j < resultItems.Size(); j++)
+ {
+ std::string itemPath = CURL(resultItems[j]->GetPath()).GetWithoutOptions();
+ StringUtils::ToLower(itemPath);
+
+ lookup[itemPath] = resultItems[j];
+ }
+
+ // loop through all the original items and find
+ // those which are still part of the filter
+ CFileItemList filteredItems;
+ for (int i = 0; i < items.Size(); i++)
+ {
+ CFileItemPtr item = items.Get(i);
+ if (item->IsParentFolder())
+ {
+ filteredItems.Add(item);
+ continue;
+ }
+
+ // check if the item is part of the resultItems list
+ // by comparing their paths (but ignoring any special
+ // options because they differ from filter to filter)
+ std::string path = CURL(item->GetPath()).GetWithoutOptions();
+ StringUtils::ToLower(path);
+
+ std::map<std::string, CFileItemPtr>::iterator itItem = lookup.find(path);
+ if (itItem != lookup.end())
+ {
+ // add the item to the list of filtered items
+ filteredItems.Add(item);
+
+ // remove the item from the lists
+ resultItems.Remove(itItem->second.get());
+ lookup.erase(itItem);
+ }
+ }
+
+ if (resultItems.Size() > 0)
+ CLog::Log(LOGWARNING, "CGUIMediaWindow::GetAdvanceFilteredItems(): {} unknown items",
+ resultItems.Size());
+
+ items.ClearItems();
+ items.Append(filteredItems);
+ items.SetPath(resultItems.GetPath());
+ if (resultItems.HasProperty(PROPERTY_PATH_DB))
+ items.SetProperty(PROPERTY_PATH_DB, resultItems.GetProperty(PROPERTY_PATH_DB));
+ return true;
+}
+
+bool CGUIMediaWindow::IsFiltered()
+{
+ return (!m_canFilterAdvanced && !GetProperty("filter").empty()) ||
+ (m_canFilterAdvanced && !m_filter.IsEmpty());
+}
+
+bool CGUIMediaWindow::IsSameStartFolder(const std::string &dir)
+{
+ const std::string startFolder = GetStartFolder(dir);
+ return URIUtils::PathHasParent(m_vecItems->GetPath(), startFolder);
+}
+
+bool CGUIMediaWindow::Filter(bool advanced /* = true */)
+{
+ // basic filtering
+ if (!m_canFilterAdvanced || !advanced)
+ {
+ const CGUIControl *btnFilter = GetControl(CONTROL_BTN_FILTER);
+ if (btnFilter && btnFilter->GetControlType() == CGUIControl::GUICONTROL_EDIT)
+ { // filter updated
+ CGUIMessage selected(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTN_FILTER);
+ OnMessage(selected);
+ OnFilterItems(selected.GetLabel());
+ UpdateButtons();
+ return true;
+ }
+ if (GetProperty("filter").empty())
+ {
+ std::string filter = GetProperty("filter").asString();
+ CGUIKeyboardFactory::ShowAndGetFilter(filter, false);
+ SetProperty("filter", filter);
+ }
+ else
+ {
+ OnFilterItems("");
+ UpdateButtons();
+ }
+ }
+ // advanced filtering
+ else
+ CGUIDialogMediaFilter::ShowAndEditMediaFilter(m_strFilterPath, m_filter);
+
+ return true;
+}
+
+std::string CGUIMediaWindow::GetStartFolder(const std::string &dir)
+{
+ if (StringUtils::EqualsNoCase(dir, "$root") ||
+ StringUtils::EqualsNoCase(dir, "root"))
+ return "";
+
+ // Let plugins handle their own urls themselves
+ if (StringUtils::StartsWith(dir, "plugin://"))
+ return dir;
+
+//! @todo This ifdef block probably belongs somewhere else. Move it to a better place!
+#if defined(TARGET_ANDROID)
+ // Hack for Android items (numbered id's) on the leanback screen
+ std::string path;
+ std::string fileName;
+ URIUtils::Split(dir, path, fileName);
+ URIUtils::RemoveExtension(fileName);
+ if (StringUtils::IsInteger(fileName))
+ return path;
+#endif
+
+ return dir;
+}
+
+std::string CGUIMediaWindow::RemoveParameterFromPath(const std::string &strDirectory, const std::string &strParameter)
+{
+ CURL url(strDirectory);
+ if (url.HasOption(strParameter))
+ {
+ url.RemoveOption(strParameter);
+ return url.Get();
+ }
+
+ return strDirectory;
+}
+
+bool CGUIMediaWindow::ProcessRenderLoop(bool renderOnly)
+{
+ return CServiceBroker::GetGUI()->GetWindowManager().ProcessRenderLoop(renderOnly);
+}
+
+bool CGUIMediaWindow::GetDirectoryItems(CURL &url, CFileItemList &items, bool useDir)
+{
+ if (m_backgroundLoad)
+ {
+ bool ret = true;
+ CGetDirectoryItems getItems(m_rootDir, url, items, useDir);
+
+ if (!WaitGetDirectoryItems(getItems))
+ {
+ // cancelled
+ ret = false;
+ }
+ else if (!getItems.m_result)
+ {
+ if (CServiceBroker::GetAppMessenger()->IsProcessThread() && m_rootDir.GetDirImpl() &&
+ !m_rootDir.GetDirImpl()->ProcessRequirements())
+ {
+ ret = false;
+ }
+ else if (!WaitGetDirectoryItems(getItems) || !getItems.m_result)
+ {
+ ret = false;
+ }
+ }
+
+ m_updateJobActive = false;
+ m_rootDir.ReleaseDirImpl();
+ return ret;
+ }
+ else
+ {
+ return m_rootDir.GetDirectory(url, items, useDir, false);
+ }
+}
+
+bool CGUIMediaWindow::WaitGetDirectoryItems(CGetDirectoryItems &items)
+{
+ bool ret = true;
+ CGUIDialogBusy* dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogBusy>(WINDOW_DIALOG_BUSY);
+ if (dialog && !dialog->IsDialogRunning())
+ {
+ if (!CGUIDialogBusy::Wait(&items, 100, true))
+ {
+ // cancelled
+ ret = false;
+ }
+ }
+ else
+ {
+ m_updateJobActive = true;
+ m_updateAborted = false;
+ m_updateEvent.Reset();
+ CServiceBroker::GetJobManager()->Submit(
+ [&]() {
+ items.Run();
+ m_updateEvent.Set();
+ },
+ nullptr, CJob::PRIORITY_NORMAL);
+
+ // Loop until either the job ended or update canceled via CGUIMediaWindow::CancelUpdateItems.
+ while (!m_updateAborted && !m_updateEvent.Wait(1ms))
+ {
+ if (!ProcessRenderLoop(false))
+ break;
+ }
+
+ if (m_updateAborted)
+ {
+ CLog::LogF(LOGDEBUG, "Get directory items job was canceled.");
+ ret = false;
+ }
+ else if (!items.m_result)
+ {
+ CLog::LogF(LOGDEBUG, "Get directory items job was unsuccessful.");
+ ret = false;
+ }
+ }
+ return ret;
+}
+
+void CGUIMediaWindow::CancelUpdateItems()
+{
+ if (m_updateJobActive)
+ {
+ m_rootDir.CancelDirectory();
+ m_updateAborted = true;
+ if (!m_updateEvent.Wait(5000ms))
+ {
+ CLog::Log(LOGERROR, "CGUIMediaWindow::CancelUpdateItems - error cancel update");
+ }
+ m_updateJobActive = false;
+ }
+}
diff --git a/xbmc/windows/GUIMediaWindow.h b/xbmc/windows/GUIMediaWindow.h
new file mode 100644
index 0000000..1fc1dfc
--- /dev/null
+++ b/xbmc/windows/GUIMediaWindow.h
@@ -0,0 +1,227 @@
+/*
+ * 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 "dialogs/GUIDialogContextMenu.h"
+#include "filesystem/DirectoryHistory.h"
+#include "filesystem/VirtualDirectory.h"
+#include "guilib/GUIWindow.h"
+#include "playlists/SmartPlayList.h"
+#include "view/GUIViewControl.h"
+
+#include <atomic>
+
+class CFileItemList;
+class CGUIViewState;
+namespace
+{
+class CGetDirectoryItems;
+}
+
+// base class for all media windows
+class CGUIMediaWindow : public CGUIWindow
+{
+public:
+ CGUIMediaWindow(int id, const char *xmlFile);
+ ~CGUIMediaWindow(void) override;
+
+ // specializations of CGUIControl
+ bool OnAction(const CAction &action) override;
+ bool OnBack(int actionID) override;
+ bool OnMessage(CGUIMessage& message) override;
+
+ // specializations of CGUIWindow
+ void OnWindowLoaded() override;
+ void OnWindowUnload() override;
+ void OnInitWindow() override;
+ bool IsMediaWindow() const override { return true; }
+ int GetViewContainerID() const override { return m_viewControl.GetCurrentControl(); }
+ int GetViewCount() const override { return m_viewControl.GetViewModeCount(); }
+ bool HasListItems() const override { return true; }
+ CFileItemPtr GetCurrentListItem(int offset = 0) override;
+
+ // custom methods
+ virtual bool CanFilterAdvanced() { return m_canFilterAdvanced; }
+ virtual bool IsFiltered();
+ virtual bool IsSameStartFolder(const std::string &dir);
+
+ virtual std::string GetRootPath() const { return ""; }
+
+ const CFileItemList &CurrentDirectory() const;
+ const CGUIViewState *GetViewState() const;
+ virtual bool UseFileDirectories() { return true; }
+
+protected:
+ // specializations of CGUIControlGroup
+ CGUIControl *GetFirstFocusableControl(int id) override;
+
+ bool Load(TiXmlElement *pRootElement) override;
+
+ // custom methods
+ virtual void SetupShares();
+ virtual bool GoParentFolder();
+ virtual bool OnClick(int iItem, const std::string &player = "");
+
+ /* \brief React to a "Select" action on an item in a view.
+ \param item selected item.
+ \return true if the action is handled, false otherwise.
+ */
+ virtual bool OnSelect(int item);
+ virtual bool OnPopupMenu(int iItem);
+
+ virtual void GetContextButtons(int itemNumber, CContextButtons &buttons);
+ virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button);
+ virtual bool OnAddMediaSource() { return false; }
+
+ virtual void FormatItemLabels(CFileItemList &items, const LABEL_MASKS &labelMasks);
+ virtual void UpdateButtons();
+ void SaveControlStates() override;
+ void RestoreControlStates() override;
+
+ virtual bool GetDirectory(const std::string &strDirectory, CFileItemList &items);
+ /*! \brief Retrieves the items from the given path and updates the list
+ \param strDirectory The path to the directory to get the items from
+ \param updateFilterPath Whether to update the filter path in m_strFilterPath or not
+ \return true if the list was successfully updated otherwise false
+ \sa GetDirectory
+ \sa m_vecItems
+ \sa m_strFilterPath
+ */
+ virtual bool Update(const std::string &strDirectory, bool updateFilterPath = true);
+ /*! \brief Refreshes the current list by retrieving the lists's path
+ \return true if the list was successfully refreshed otherwise false
+ \sa Update
+ \sa GetDirectory
+ */
+ virtual bool Refresh(bool clearCache = false);
+
+ virtual void FormatAndSort(CFileItemList &items);
+ virtual void OnPrepareFileItems(CFileItemList &items);
+ virtual void OnCacheFileItems(CFileItemList &items);
+ virtual void GetGroupedItems(CFileItemList &items) { }
+
+ void ClearFileItems();
+ virtual void SortItems(CFileItemList &items);
+
+ /*! \brief Check if the given list can be advance filtered or not
+ \param items List of items to check
+ \return true if the list can be advance filtered otherwise false
+ */
+ virtual bool CheckFilterAdvanced(CFileItemList &items) const { return false; }
+ /*! \brief Check if the given path can contain a "filter" parameter
+ \param strDirectory Path to check
+ \return true if the given path can contain a "filter" parameter otherwise false
+ */
+ virtual bool CanContainFilter(const std::string &strDirectory) const { return false; }
+ virtual void UpdateFilterPath(const std::string &strDirector, const CFileItemList &items, bool updateFilterPath);
+ virtual bool Filter(bool advanced = true);
+
+ /* \brief Called on response to a GUI_MSG_FILTER_ITEMS message
+ Filters the current list with the given filter using FilterItems()
+ \param filter the filter to use.
+ \sa FilterItems
+ */
+ void OnFilterItems(const std::string &filter);
+
+ /* \brief Retrieve the filtered item list
+ \param filter filter to apply
+ \param items CFileItemList to filter
+ \sa OnFilterItems
+ */
+ virtual bool GetFilteredItems(const std::string &filter, CFileItemList &items);
+
+ /* \brief Retrieve the advance filtered item list
+ \param items CFileItemList to filter
+ \param hasNewItems Whether the filtered item list contains new items
+ which were not present in the original list
+ \sa GetFilteredItems
+ */
+ virtual bool GetAdvanceFilteredItems(CFileItemList &items);
+
+ // check for a disc or connection
+ virtual bool HaveDiscOrConnection(const std::string& strPath, int iDriveType);
+ void ShowShareErrorMessage(CFileItem* pItem) const;
+
+ void SaveSelectedItemInHistory();
+ void RestoreSelectedItemFromHistory();
+ void GetDirectoryHistoryString(const CFileItem* pItem, std::string& strHistoryString) const;
+ void SetHistoryForPath(const std::string& strDirectory);
+ virtual void LoadPlayList(const std::string& strFileName) {}
+ virtual bool OnPlayMedia(int iItem, const std::string &player = "");
+ virtual bool OnPlayAndQueueMedia(const CFileItemPtr& item, const std::string& player = "");
+ void UpdateFileList();
+ virtual void OnDeleteItem(int iItem);
+ void OnRenameItem(int iItem);
+ bool WaitForNetwork() const;
+ bool GetDirectoryItems(CURL &url, CFileItemList &items, bool useDir);
+ bool WaitGetDirectoryItems(CGetDirectoryItems &items);
+ void CancelUpdateItems();
+
+ /*! \brief Translate the folder to start in from the given quick path
+ \param url the folder the user wants
+ \return the resulting path */
+ virtual std::string GetStartFolder(const std::string &url);
+
+ /*! \brief Utility method to remove the given parameter from a path/URL
+ \param strDirectory Path/URL from which to remove the given parameter
+ \param strParameter Parameter to remove from the given path/URL
+ \return Path/URL without the given parameter
+ */
+ static std::string RemoveParameterFromPath(const std::string &strDirectory, const std::string &strParameter);
+
+ bool ProcessRenderLoop(bool renderOnly);
+
+ XFILE::CVirtualDirectory m_rootDir;
+ CGUIViewControl m_viewControl;
+
+ // current path and history
+ CFileItemList* m_vecItems;
+ CFileItemList* m_unfilteredItems; ///< \brief items prior to filtering using FilterItems()
+ CDirectoryHistory m_history;
+ std::unique_ptr<CGUIViewState> m_guiState;
+ std::atomic_bool m_vecItemsUpdating = {false};
+ class CUpdateGuard
+ {
+ public:
+ CUpdateGuard(std::atomic_bool &update) : m_update(update)
+ {
+ m_update = true;
+ }
+ ~CUpdateGuard()
+ {
+ m_update = false;
+ }
+ protected:
+ std::atomic_bool &m_update;
+ };
+ CEvent m_updateEvent;
+ std::atomic_bool m_updateAborted = {false};
+ std::atomic_bool m_updateJobActive = {false};
+
+ // save control state on window exit
+ int m_iLastControl;
+ std::string m_startDirectory;
+
+ CSmartPlaylist m_filter;
+ bool m_canFilterAdvanced;
+ /*! \brief Contains the path used for filtering (including any active filter)
+
+ When Update() is called with a path to e.g. a smartplaylist or
+ a library node filter, that "original" path will be stored in
+ m_vecItems->m_strPath. But the path used by XBMC to retrieve
+ those items from the database (Videodb:// or musicdb://)
+ is stored in this member variable to still have access to it
+ because it is used for filtering by appending the currently active
+ filter as a "filter" parameter to the filter path/URL.
+
+ \sa Update
+ */
+ std::string m_strFilterPath;
+ bool m_backgroundLoad = false;
+};
diff --git a/xbmc/windows/GUIWindowDebugInfo.cpp b/xbmc/windows/GUIWindowDebugInfo.cpp
new file mode 100644
index 0000000..764e6b0
--- /dev/null
+++ b/xbmc/windows/GUIWindowDebugInfo.cpp
@@ -0,0 +1,185 @@
+/*
+ * 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 "GUIWindowDebugInfo.h"
+
+#include "CompileInfo.h"
+#include "GUIInfoManager.h"
+#include "ServiceBroker.h"
+#include "addons/Skin.h"
+#include "filesystem/SpecialProtocol.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIControlFactory.h"
+#include "guilib/GUIControlProfiler.h"
+#include "guilib/GUIFontManager.h"
+#include "guilib/GUITextLayout.h"
+#include "guilib/GUIWindowManager.h"
+#include "input/WindowTranslator.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/CPUInfo.h"
+#include "utils/MemUtils.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+
+#include <inttypes.h>
+
+CGUIWindowDebugInfo::CGUIWindowDebugInfo(void)
+ : CGUIDialog(WINDOW_DEBUG_INFO, "", DialogModalityType::MODELESS)
+{
+ m_needsScaling = false;
+ m_layout = nullptr;
+ m_renderOrder = RENDER_ORDER_WINDOW_DEBUG;
+}
+
+CGUIWindowDebugInfo::~CGUIWindowDebugInfo(void) = default;
+
+void CGUIWindowDebugInfo::UpdateVisibility()
+{
+ if (LOG_LEVEL_DEBUG_FREEMEM <= CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_logLevel || g_SkinInfo->IsDebugging())
+ Open();
+ else
+ Close();
+}
+
+bool CGUIWindowDebugInfo::OnMessage(CGUIMessage &message)
+{
+ if (message.GetMessage() == GUI_MSG_WINDOW_DEINIT)
+ {
+ delete m_layout;
+ m_layout = nullptr;
+ }
+ else if (message.GetMessage() == GUI_MSG_REFRESH_TIMER)
+ MarkDirtyRegion();
+
+ return CGUIDialog::OnMessage(message);
+}
+
+void CGUIWindowDebugInfo::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
+{
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetRenderingResolution(CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(), false);
+
+ CServiceBroker::GetCPUInfo()->GetUsedPercentage(); // must call it to recalculate pct values
+
+ static int yShift = 20;
+ static int xShift = 40;
+ static unsigned int lastShift = time(nullptr);
+ time_t now = time(nullptr);
+ if (now - lastShift > 10)
+ {
+ yShift *= -1;
+ if (now % 5 == 0)
+ xShift *= -1;
+ lastShift = now;
+ MarkDirtyRegion();
+ }
+
+ if (!m_layout)
+ {
+ CGUIFont *font13 = g_fontManager.GetDefaultFont();
+ CGUIFont *font13border = g_fontManager.GetDefaultFont(true);
+ if (font13)
+ m_layout = new CGUITextLayout(font13, true, 0, font13border);
+ }
+ if (!m_layout)
+ return;
+
+ std::string info;
+ if (LOG_LEVEL_DEBUG_FREEMEM <= CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_logLevel)
+ {
+ KODI::MEMORY::MemoryStatus stat;
+ KODI::MEMORY::GetMemoryStatus(&stat);
+ std::string profiling = CGUIControlProfiler::IsRunning() ? " (profiling)" : "";
+ std::string strCores;
+ if (CServiceBroker::GetCPUInfo()->SupportsCPUUsage())
+ strCores = CServiceBroker::GetCPUInfo()->GetCoresUsageString();
+ else
+ strCores = "N/A";
+ std::string lcAppName = CCompileInfo::GetAppName();
+ StringUtils::ToLower(lcAppName);
+#if !defined(TARGET_POSIX)
+ info = StringUtils::Format("LOG: {}{}.log\nMEM: {}/{} KB - FPS: {:2.1f} fps\nCPU: {}{}",
+ CSpecialProtocol::TranslatePath("special://logpath"), lcAppName,
+ stat.availPhys / 1024, stat.totalPhys / 1024,
+ CServiceBroker::GetGUI()
+ ->GetInfoManager()
+ .GetInfoProviders()
+ .GetSystemInfoProvider()
+ .GetFPS(),
+ strCores, profiling);
+#else
+ double dCPU = m_resourceCounter.GetCPUUsage();
+ std::string ucAppName = lcAppName;
+ StringUtils::ToUpper(ucAppName);
+ info = StringUtils::Format("LOG: {}{}.log\n"
+ "MEM: {}/{} KB - FPS: {:2.1f} fps\n"
+ "CPU: {} (CPU-{} {:4.2f}%{})",
+ CSpecialProtocol::TranslatePath("special://logpath"), lcAppName,
+ stat.availPhys / 1024, stat.totalPhys / 1024,
+ CServiceBroker::GetGUI()
+ ->GetInfoManager()
+ .GetInfoProviders()
+ .GetSystemInfoProvider()
+ .GetFPS(),
+ strCores, ucAppName, dCPU, profiling);
+#endif
+ }
+
+ // render the skin debug info
+ if (g_SkinInfo->IsDebugging())
+ {
+ if (!info.empty())
+ info += "\n";
+ CGUIWindow *window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog());
+ CGUIWindow *pointer = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_DIALOG_POINTER);
+ CPoint point;
+ if (pointer)
+ point = CPoint(pointer->GetXPosition(), pointer->GetYPosition());
+ if (window)
+ {
+ std::string windowName = CWindowTranslator::TranslateWindow(window->GetID());
+ if (!windowName.empty())
+ windowName += " (" + window->GetProperty("xmlfile").asString() + ")";
+ else
+ windowName = window->GetProperty("xmlfile").asString();
+ info += "Window: " + windowName + "\n";
+ // transform the mouse coordinates to this window's coordinates
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetScalingResolution(window->GetCoordsRes(), true);
+ point.x *= CServiceBroker::GetWinSystem()->GetGfxContext().GetGUIScaleX();
+ point.y *= CServiceBroker::GetWinSystem()->GetGfxContext().GetGUIScaleY();
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetRenderingResolution(CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(), false);
+ }
+ info += StringUtils::Format("Mouse: ({},{}) ", static_cast<int>(point.x),
+ static_cast<int>(point.y));
+ if (window)
+ {
+ CGUIControl *control = window->GetFocusedControl();
+ if (control)
+ info += StringUtils::Format(
+ "Focused: {} ({})", control->GetID(),
+ CGUIControlFactory::TranslateControlType(control->GetControlType()));
+ }
+ }
+
+ float w, h;
+ if (m_layout->Update(info))
+ MarkDirtyRegion();
+ m_layout->GetTextExtent(w, h);
+
+ float x = xShift + 0.04f * CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth();
+ float y = yShift + 0.04f * CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight();
+ m_renderRegion.SetRect(x, y, x+w, y+h);
+}
+
+void CGUIWindowDebugInfo::Render()
+{
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetRenderingResolution(CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(), false);
+ if (m_layout)
+ m_layout->RenderOutline(m_renderRegion.x1, m_renderRegion.y1, 0xffffffff, 0xff000000, 0, 0);
+}
diff --git a/xbmc/windows/GUIWindowDebugInfo.h b/xbmc/windows/GUIWindowDebugInfo.h
new file mode 100644
index 0000000..b29405c
--- /dev/null
+++ b/xbmc/windows/GUIWindowDebugInfo.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 "guilib/GUIDialog.h"
+#ifdef TARGET_POSIX
+#include "platform/posix/PosixResourceCounter.h"
+#endif
+
+class CGUITextLayout;
+
+class CGUIWindowDebugInfo :
+ public CGUIDialog
+{
+public:
+ CGUIWindowDebugInfo();
+ ~CGUIWindowDebugInfo() override;
+ void Process(unsigned int currentTime, CDirtyRegionList &dirtyregions) override;
+ void Render() override;
+ bool OnMessage(CGUIMessage &message) override;
+protected:
+ void UpdateVisibility() override;
+private:
+ CGUITextLayout *m_layout;
+#ifdef TARGET_POSIX
+ CPosixResourceCounter m_resourceCounter;
+#endif
+};
diff --git a/xbmc/windows/GUIWindowFileManager.cpp b/xbmc/windows/GUIWindowFileManager.cpp
new file mode 100644
index 0000000..4d3f399
--- /dev/null
+++ b/xbmc/windows/GUIWindowFileManager.cpp
@@ -0,0 +1,1331 @@
+/*
+ * 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 "GUIWindowFileManager.h"
+
+#include "Autorun.h"
+#include "GUIPassword.h"
+#include "GUIUserMessages.h"
+#include "PlayListPlayer.h"
+#include "ServiceBroker.h"
+#include "URL.h"
+#include "Util.h"
+#include "application/Application.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
+#include "cores/playercorefactory/PlayerCoreFactory.h"
+#include "dialogs/GUIDialogBusy.h"
+#include "dialogs/GUIDialogContextMenu.h"
+#include "dialogs/GUIDialogMediaSource.h"
+#include "dialogs/GUIDialogProgress.h"
+#include "dialogs/GUIDialogTextViewer.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "favourites/FavouritesService.h"
+#include "filesystem/Directory.h"
+#include "filesystem/FileDirectoryFactory.h"
+#include "filesystem/ZipManager.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIKeyboardFactory.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "input/InputManager.h"
+#include "interfaces/generic/ScriptInvocationManager.h"
+#include "messaging/ApplicationMessenger.h"
+#include "messaging/helpers/DialogOKHelper.h"
+#include "network/Network.h"
+#include "pictures/GUIWindowSlideShow.h"
+#include "platform/Filesystem.h"
+#include "playlists/PlayList.h"
+#include "playlists/PlayListFactory.h"
+#include "settings/MediaSourceSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "storage/MediaManager.h"
+#include "threads/IRunnable.h"
+#include "utils/FileOperationJob.h"
+#include "utils/FileUtils.h"
+#include "utils/JobManager.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+
+using namespace XFILE;
+using namespace KODI::MESSAGING;
+
+#define CONTROL_BTNSELECTALL 1
+#define CONTROL_BTNFAVOURITES 2
+#define CONTROL_BTNPLAYWITH 3
+#define CONTROL_BTNRENAME 4
+#define CONTROL_BTNDELETE 5
+#define CONTROL_BTNCOPY 6
+#define CONTROL_BTNMOVE 7
+#define CONTROL_BTNNEWFOLDER 8
+#define CONTROL_BTNCALCSIZE 9
+#define CONTROL_BTNSWITCHMEDIA 11
+#define CONTROL_BTNCANCELJOB 12
+#define CONTROL_BTNVIEW 13
+
+
+#define CONTROL_NUMFILES_LEFT 12
+#define CONTROL_NUMFILES_RIGHT 13
+
+#define CONTROL_LEFT_LIST 20
+#define CONTROL_RIGHT_LIST 21
+
+#define CONTROL_CURRENTDIRLABEL_LEFT 101
+#define CONTROL_CURRENTDIRLABEL_RIGHT 102
+
+namespace
+{
+class CGetDirectoryItems : public IRunnable
+{
+public:
+ CGetDirectoryItems(XFILE::CVirtualDirectory &dir, CURL &url, CFileItemList &items)
+ : m_result(false), m_dir(dir), m_url(url), m_items(items)
+ {
+ }
+ void Run() override
+ {
+ m_result = m_dir.GetDirectory(m_url, m_items, false, false);
+ }
+ void Cancel() override
+ {
+ m_dir.CancelDirectory();
+ }
+ bool m_result;
+protected:
+ XFILE::CVirtualDirectory &m_dir;
+ CURL m_url;
+ CFileItemList &m_items;
+};
+}
+
+CGUIWindowFileManager::CGUIWindowFileManager(void)
+ : CGUIWindow(WINDOW_FILES, "FileManager.xml"),
+ CJobQueue(false,2)
+{
+ m_Directory[0] = new CFileItem;
+ m_Directory[1] = new CFileItem;
+ m_vecItems[0] = new CFileItemList;
+ m_vecItems[1] = new CFileItemList;
+ m_Directory[0]->SetPath("?");
+ m_Directory[1]->SetPath("?");
+ m_Directory[0]->m_bIsFolder = true;
+ m_Directory[1]->m_bIsFolder = true;
+ bCheckShareConnectivity = true;
+ m_loadType = KEEP_IN_MEMORY;
+}
+
+CGUIWindowFileManager::~CGUIWindowFileManager(void)
+{
+ delete m_Directory[0];
+ delete m_Directory[1];
+ delete m_vecItems[0];
+ delete m_vecItems[1];
+}
+
+bool CGUIWindowFileManager::OnAction(const CAction &action)
+{
+ int list = GetFocusedList();
+ if (list >= 0 && list <= 1)
+ {
+ int item;
+
+ // the non-contextual menu can be called at any time
+ if (action.GetID() == ACTION_CONTEXT_MENU && m_vecItems[list]->Size() == 0)
+ {
+ OnPopupMenu(list,-1, false);
+ return true;
+ }
+ if (action.GetID() == ACTION_DELETE_ITEM)
+ {
+ if (CanDelete(list))
+ {
+ bool bDeselect = SelectItem(list, item);
+ OnDelete(list);
+ if (bDeselect) m_vecItems[list]->Get(item)->Select(false);
+ }
+ return true;
+ }
+ if (action.GetID() == ACTION_COPY_ITEM)
+ {
+ if (CanCopy(list))
+ {
+ bool bDeselect = SelectItem(list, item);
+ OnCopy(list);
+ if (bDeselect) m_vecItems[list]->Get(item)->Select(false);
+ }
+ return true;
+ }
+ if (action.GetID() == ACTION_MOVE_ITEM)
+ {
+ if (CanMove(list))
+ {
+ bool bDeselect = SelectItem(list, item);
+ OnMove(list);
+ if (bDeselect) m_vecItems[list]->Get(item)->Select(false);
+ }
+ return true;
+ }
+ if (action.GetID() == ACTION_RENAME_ITEM)
+ {
+ if (CanRename(list))
+ {
+ bool bDeselect = SelectItem(list, item);
+ OnRename(list);
+ if (bDeselect) m_vecItems[list]->Get(item)->Select(false);
+ }
+ return true;
+ }
+ if (action.GetID() == ACTION_PARENT_DIR)
+ {
+ GoParentFolder(list);
+ return true;
+ }
+ if (action.GetID() == ACTION_PLAYER_PLAY)
+ {
+#ifdef HAS_DVD_DRIVE
+ if (m_vecItems[list]->Get(GetSelectedItem(list))->IsDVD())
+ return MEDIA_DETECT::CAutorun::PlayDiscAskResume(m_vecItems[list]->Get(GetSelectedItem(list))->GetPath());
+#endif
+ }
+ }
+ return CGUIWindow::OnAction(action);
+}
+
+bool CGUIWindowFileManager::OnBack(int actionID)
+{
+ int list = GetFocusedList();
+ if (list >= 0 && list <= 1 && actionID == ACTION_NAV_BACK && !m_vecItems[list]->IsVirtualDirectoryRoot())
+ {
+ GoParentFolder(list);
+ return true;
+ }
+ return CGUIWindow::OnBack(actionID);
+}
+
+bool CGUIWindowFileManager::OnMessage(CGUIMessage& message)
+{
+ switch ( message.GetMessage() )
+ {
+ case GUI_MSG_NOTIFY_ALL:
+ { // Message is received even if window is inactive
+ if (message.GetParam1() == GUI_MSG_WINDOW_RESET)
+ {
+ m_Directory[0]->SetPath("?");
+ m_Directory[1]->SetPath("?");
+ m_Directory[0]->m_bIsFolder = true;
+ m_Directory[1]->m_bIsFolder = true;
+ return true;
+ }
+
+ // handle removable media
+ if (message.GetParam1() == GUI_MSG_REMOVED_MEDIA)
+ {
+ for (int i = 0; i < 2; i++)
+ {
+ if (m_Directory[i]->IsVirtualDirectoryRoot() && IsActive())
+ {
+ int iItem = GetSelectedItem(i);
+ Update(i, m_Directory[i]->GetPath());
+ CONTROL_SELECT_ITEM(CONTROL_LEFT_LIST + i, iItem);
+ }
+ else if (m_Directory[i]->IsRemovable() && !m_rootDir.IsInSource(m_Directory[i]->GetPath()))
+ { //
+ if (IsActive())
+ Update(i, "");
+ else
+ m_Directory[i]->SetPath("");
+ }
+ }
+ return true;
+ }
+ else if (message.GetParam1()==GUI_MSG_UPDATE_SOURCES)
+ { // State of the sources changed, so update our view
+ for (int i = 0; i < 2; i++)
+ {
+ if (m_Directory[i]->IsVirtualDirectoryRoot() && IsActive())
+ {
+ int iItem = GetSelectedItem(i);
+ Update(i, m_Directory[i]->GetPath());
+ CONTROL_SELECT_ITEM(CONTROL_LEFT_LIST + i, iItem);
+ }
+ }
+ return true;
+ }
+ else if (message.GetParam1()==GUI_MSG_UPDATE && IsActive())
+ {
+ Refresh();
+ return true;
+ }
+ }
+ break;
+ case GUI_MSG_PLAYBACK_STARTED:
+ case GUI_MSG_PLAYBACK_ENDED:
+ case GUI_MSG_PLAYBACK_STOPPED:
+ case GUI_MSG_PLAYLIST_CHANGED:
+ case GUI_MSG_PLAYLISTPLAYER_STOPPED:
+ case GUI_MSG_PLAYLISTPLAYER_STARTED:
+ case GUI_MSG_PLAYLISTPLAYER_CHANGED:
+ { // send a notify all to all controls on this window
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_REFRESH_LIST);
+ OnMessage(msg);
+ break;
+ }
+ case GUI_MSG_WINDOW_DEINIT:
+ {
+ CGUIWindow::OnMessage(message);
+ ClearFileItems(0);
+ ClearFileItems(1);
+ return true;
+ }
+ break;
+
+ case GUI_MSG_WINDOW_INIT:
+ {
+ SetInitialPath(message.GetStringParam());
+ message.SetStringParam("");
+
+ return CGUIWindow::OnMessage(message);
+ }
+ break;
+ case GUI_MSG_CLICKED:
+ {
+ int iControl = message.GetSenderId();
+
+ if (iControl == CONTROL_LEFT_LIST || iControl == CONTROL_RIGHT_LIST) // list/thumb control
+ {
+ // get selected item
+ int list = iControl - CONTROL_LEFT_LIST;
+ int iItem = GetSelectedItem(list);
+ int iAction = message.GetParam1();
+
+ // iItem is checked for validity inside these routines
+ if (iAction == ACTION_HIGHLIGHT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
+ {
+ OnMark(list, iItem);
+ if (!CServiceBroker::GetInputManager().IsMouseActive())
+ {
+ //move to next item
+ CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), iControl, iItem + 1);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+ }
+ }
+ else if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_DOUBLE_CLICK)
+ {
+ OnClick(list, iItem);
+ }
+ else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
+ {
+ OnPopupMenu(list, iItem);
+ }
+ }
+ }
+ break;
+ // prevent touch/gesture unfocussing ..
+ case GUI_MSG_GESTURE_NOTIFY:
+ case GUI_MSG_UNFOCUS_ALL:
+ return true;
+ }
+ return CGUIWindow::OnMessage(message);
+}
+
+void CGUIWindowFileManager::OnSort(int iList)
+{
+ using namespace KODI::PLATFORM::FILESYSTEM;
+ // always sort the list by label in ascending order
+ for (int i = 0; i < m_vecItems[iList]->Size(); i++)
+ {
+ CFileItemPtr pItem = m_vecItems[iList]->Get(i);
+ if (pItem->m_bIsFolder && (!pItem->m_dwSize || pItem->IsPath("add")))
+ pItem->SetLabel2("");
+ else
+ pItem->SetFileSizeLabel();
+
+ // Set free space on disc
+ if (pItem->m_bIsShareOrDrive)
+ {
+ if (pItem->IsHD())
+ {
+ std::error_code ec;
+ auto freeSpace = space(pItem->GetPath(), ec);
+ if (ec.value() == 0)
+ {
+ pItem->m_dwSize = freeSpace.free;
+ pItem->SetFileSizeLabel();
+ }
+ }
+ else if (pItem->IsDVD() && CServiceBroker::GetMediaManager().IsDiscInDrive())
+ {
+ std::error_code ec;
+ auto freeSpace = space(pItem->GetPath(), ec);
+ if (ec.value() == 0)
+ {
+ pItem->m_dwSize = freeSpace.capacity;
+ pItem->SetFileSizeLabel();
+ }
+ }
+ } // if (pItem->m_bIsShareOrDrive)
+
+ }
+
+ m_vecItems[iList]->Sort(SortByLabel, SortOrderAscending);
+}
+
+void CGUIWindowFileManager::ClearFileItems(int iList)
+{
+ CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), iList + CONTROL_LEFT_LIST);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+
+ m_vecItems[iList]->Clear(); // will clean up everything
+}
+
+void CGUIWindowFileManager::UpdateButtons()
+{
+ // update our current directory labels
+ std::string strDir = CURL(m_Directory[0]->GetPath()).GetWithoutUserDetails();
+ if (strDir.empty())
+ {
+ SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_LEFT,g_localizeStrings.Get(20108));
+ }
+ else
+ {
+ SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_LEFT, strDir);
+ }
+ strDir = CURL(m_Directory[1]->GetPath()).GetWithoutUserDetails();
+ if (strDir.empty())
+ {
+ SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_RIGHT,g_localizeStrings.Get(20108));
+ }
+ else
+ {
+ SET_CONTROL_LABEL(CONTROL_CURRENTDIRLABEL_RIGHT, strDir);
+ }
+
+ // update the number of items in each list
+ UpdateItemCounts();
+}
+
+void CGUIWindowFileManager::UpdateItemCounts()
+{
+ for (int i = 0; i < 2; i++)
+ {
+ unsigned int selectedCount = 0;
+ unsigned int totalCount = 0;
+ int64_t selectedSize = 0;
+ for (int j = 0; j < m_vecItems[i]->Size(); j++)
+ {
+ CFileItemPtr item = m_vecItems[i]->Get(j);
+ if (item->IsParentFolder()) continue;
+ if (item->IsSelected())
+ {
+ selectedCount++;
+ selectedSize += item->m_dwSize;
+ }
+ totalCount++;
+ }
+ std::string items;
+ if (selectedCount > 0)
+ items =
+ StringUtils::Format("{}/{} {} ({})", selectedCount, totalCount,
+ g_localizeStrings.Get(127), StringUtils::SizeToString(selectedSize));
+ else
+ items = StringUtils::Format("{} {}", totalCount, g_localizeStrings.Get(127));
+ SET_CONTROL_LABEL(CONTROL_NUMFILES_LEFT + i, items);
+ }
+}
+
+bool CGUIWindowFileManager::Update(int iList, const std::string &strDirectory)
+{
+ if (m_updating)
+ {
+ CLog::Log(LOGWARNING, "CGUIWindowFileManager::Update - updating in progress");
+ return true;
+ }
+ CUpdateGuard ug(m_updating);
+
+ // get selected item
+ int iItem = GetSelectedItem(iList);
+ std::string strSelectedItem = "";
+
+ if (iItem >= 0 && iItem < m_vecItems[iList]->Size())
+ {
+ CFileItemPtr pItem = m_vecItems[iList]->Get(iItem);
+ if (!pItem->IsParentFolder())
+ {
+ GetDirectoryHistoryString(pItem.get(), strSelectedItem);
+ m_history[iList].SetSelectedItem(strSelectedItem, m_Directory[iList]->GetPath());
+ }
+ }
+
+ std::string strOldDirectory=m_Directory[iList]->GetPath();
+ m_Directory[iList]->SetPath(strDirectory);
+
+ CFileItemList items;
+ if (!GetDirectory(iList, m_Directory[iList]->GetPath(), items))
+ {
+ if (strDirectory != strOldDirectory && GetDirectory(iList, strOldDirectory, items))
+ m_Directory[iList]->SetPath(strOldDirectory); // Fallback to old (previous) path)
+ else
+ Update(iList, ""); // Fallback to root
+
+ return false;
+ }
+
+ m_history[iList].SetSelectedItem(strSelectedItem, strOldDirectory);
+
+ ClearFileItems(iList);
+
+ m_vecItems[iList]->Append(items);
+ m_vecItems[iList]->SetPath(items.GetPath());
+
+ std::string strParentPath;
+ URIUtils::GetParentPath(strDirectory, strParentPath);
+ if (strDirectory.empty() && (m_vecItems[iList]->Size() == 0 || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWADDSOURCEBUTTONS)))
+ { // add 'add source button'
+ const std::string& strLabel = g_localizeStrings.Get(1026);
+ CFileItemPtr pItem(new CFileItem(strLabel));
+ pItem->SetPath("add");
+ pItem->SetArt("icon", "DefaultAddSource.png");
+ pItem->SetLabel(strLabel);
+ pItem->SetLabelPreformatted(true);
+ pItem->m_bIsFolder = true;
+ pItem->SetSpecialSort(SortSpecialOnBottom);
+ m_vecItems[iList]->Add(pItem);
+ }
+ else if (items.IsEmpty() || CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWPARENTDIRITEMS))
+ {
+ CFileItemPtr pItem(new CFileItem(".."));
+ pItem->SetPath(m_rootDir.IsSource(strDirectory) ? "" : strParentPath);
+ pItem->m_bIsFolder = true;
+ pItem->m_bIsShareOrDrive = false;
+ m_vecItems[iList]->AddFront(pItem, 0);
+ }
+
+ m_strParentPath[iList] = (m_rootDir.IsSource(strDirectory) ? "" : strParentPath);
+
+ if (strDirectory.empty())
+ {
+ CFileItemPtr pItem(new CFileItem("special://profile/", true));
+ pItem->SetLabel(g_localizeStrings.Get(20070));
+ pItem->SetArt("thumb", "DefaultFolder.png");
+ pItem->SetLabelPreformatted(true);
+ m_vecItems[iList]->Add(pItem);
+
+ #ifdef TARGET_DARWIN_EMBEDDED
+ CFileItemPtr iItem(new CFileItem("special://envhome/Documents/Inbox", true));
+ iItem->SetLabel("Inbox");
+ iItem->SetArt("thumb", "DefaultFolder.png");
+ iItem->SetLabelPreformatted(true);
+ m_vecItems[iList]->Add(iItem);
+ #endif
+ }
+
+ // if we have a .tbn file, use itself as the thumb
+ for (int i = 0; i < m_vecItems[iList]->Size(); i++)
+ {
+ CFileItemPtr pItem = m_vecItems[iList]->Get(i);
+ if (pItem->IsHD() &&
+ URIUtils::HasExtension(pItem->GetPath(), ".tbn"))
+ {
+ pItem->SetArt("thumb", pItem->GetPath());
+ }
+ }
+ m_vecItems[iList]->FillInDefaultIcons();
+
+ OnSort(iList);
+ UpdateButtons();
+
+ int item = 0;
+ strSelectedItem = m_history[iList].GetSelectedItem(m_Directory[iList]->GetPath());
+ for (int i = 0; i < m_vecItems[iList]->Size(); ++i)
+ {
+ CFileItemPtr pItem = m_vecItems[iList]->Get(i);
+ std::string strHistory;
+ GetDirectoryHistoryString(pItem.get(), strHistory);
+ if (strHistory == strSelectedItem)
+ {
+ item = i;
+ break;
+ }
+ }
+ UpdateControl(iList, item);
+ return true;
+}
+
+
+void CGUIWindowFileManager::OnClick(int iList, int iItem)
+{
+ if ( iList < 0 || iList >= 2) return ;
+ if ( iItem < 0 || iItem >= m_vecItems[iList]->Size() ) return ;
+
+ CFileItemPtr pItem = m_vecItems[iList]->Get(iItem);
+ if (pItem->GetPath() == "add" && pItem->GetLabel() == g_localizeStrings.Get(1026)) // 'add source button' in empty root
+ {
+ if (CGUIDialogMediaSource::ShowAndAddMediaSource("files"))
+ {
+ m_rootDir.SetSources(*CMediaSourceSettings::GetInstance().GetSources("files"));
+ Update(0,m_Directory[0]->GetPath());
+ Update(1,m_Directory[1]->GetPath());
+ }
+ return;
+ }
+
+ if (!pItem->m_bIsFolder && pItem->IsFileFolder(EFILEFOLDER_MASK_ALL))
+ {
+ XFILE::IFileDirectory *pFileDirectory = NULL;
+ pFileDirectory = XFILE::CFileDirectoryFactory::Create(pItem->GetURL(), pItem.get(), "");
+ if(pFileDirectory)
+ pItem->m_bIsFolder = true;
+ else if(pItem->m_bIsFolder)
+ pItem->m_bIsFolder = false;
+ delete pFileDirectory;
+ }
+
+ if (pItem->m_bIsFolder)
+ {
+ // save path + drive type because of the possible refresh
+ std::string strPath = pItem->GetPath();
+ int iDriveType = pItem->m_iDriveType;
+ if ( pItem->m_bIsShareOrDrive )
+ {
+ if ( !g_passwordManager.IsItemUnlocked( pItem.get(), "files" ) )
+ {
+ Refresh();
+ return ;
+ }
+
+ if ( !HaveDiscOrConnection( strPath, iDriveType ) )
+ return ;
+ }
+ if (!Update(iList, strPath))
+ ShowShareErrorMessage(pItem.get());
+ }
+ else if (pItem->IsZIP() || pItem->IsCBZ()) // mount zip archive
+ {
+ CURL pathToUrl = URIUtils::CreateArchivePath("zip", pItem->GetURL(), "");
+ Update(iList, pathToUrl.Get());
+ }
+ else if (pItem->IsRAR() || pItem->IsCBR())
+ {
+ CURL pathToUrl = URIUtils::CreateArchivePath("rar", pItem->GetURL(), "");
+ Update(iList, pathToUrl.Get());
+ }
+ else
+ {
+ OnStart(pItem.get(), "");
+ return ;
+ }
+ // UpdateButtons();
+}
+
+//! @todo 2.0: Can this be removed, or should we run without the "special" file directories while
+// in filemanager view.
+void CGUIWindowFileManager::OnStart(CFileItem *pItem, const std::string &player)
+{
+ // start playlists from file manager
+ if (pItem->IsPlayList())
+ {
+ const std::string& strPlayList = pItem->GetPath();
+ std::unique_ptr<PLAYLIST::CPlayList> pPlayList(PLAYLIST::CPlayListFactory::Create(strPlayList));
+ if (nullptr != pPlayList)
+ {
+ if (!pPlayList->Load(strPlayList))
+ {
+ HELPERS::ShowOKDialogText(CVariant{6}, CVariant{477});
+ return;
+ }
+ }
+ g_application.ProcessAndStartPlaylist(strPlayList, *pPlayList, PLAYLIST::TYPE_MUSIC);
+ return;
+ }
+ if (pItem->IsAudio() || pItem->IsVideo())
+ {
+ CServiceBroker::GetPlaylistPlayer().Play(std::make_shared<CFileItem>(*pItem), player);
+ return;
+ }
+ if (pItem->IsGame())
+ {
+ g_application.PlayFile(*pItem, player);
+ return ;
+ }
+#ifdef HAS_PYTHON
+ if (pItem->IsPythonScript())
+ {
+ CScriptInvocationManager::GetInstance().ExecuteAsync(pItem->GetPath());
+ return ;
+ }
+#endif
+ if (pItem->IsPicture())
+ {
+ CGUIWindowSlideShow *pSlideShow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
+ if (!pSlideShow)
+ return ;
+
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (appPlayer->IsPlayingVideo())
+ g_application.StopPlaying();
+
+ pSlideShow->Reset();
+ pSlideShow->Add(pItem);
+ pSlideShow->Select(pItem->GetPath());
+
+ CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW);
+ return;
+ }
+ if (pItem->IsType(".txt") || pItem->IsType(".xml"))
+ CGUIDialogTextViewer::ShowForFile(pItem->GetPath(), true);
+}
+
+bool CGUIWindowFileManager::HaveDiscOrConnection( std::string& strPath, int iDriveType )
+{
+ if ( iDriveType == CMediaSource::SOURCE_TYPE_DVD )
+ {
+ if (!CServiceBroker::GetMediaManager().IsDiscInDrive(strPath))
+ {
+ HELPERS::ShowOKDialogText(CVariant{218}, CVariant{219});
+ int iList = GetFocusedList();
+ int iItem = GetSelectedItem(iList);
+ Update(iList, "");
+ CONTROL_SELECT_ITEM(iList + CONTROL_LEFT_LIST, iItem);
+ return false;
+ }
+ }
+ else if ( iDriveType == CMediaSource::SOURCE_TYPE_REMOTE )
+ {
+ //! @todo Handle not connected to a remote share
+ if (!CServiceBroker::GetNetwork().IsConnected())
+ {
+ HELPERS::ShowOKDialogText(CVariant{220}, CVariant{221});
+ return false;
+ }
+ }
+ else
+ return true;
+ return true;
+}
+
+void CGUIWindowFileManager::UpdateControl(int iList, int item)
+{
+ CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), iList + CONTROL_LEFT_LIST, item, 0, m_vecItems[iList]);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+}
+
+void CGUIWindowFileManager::OnMark(int iList, int iItem)
+{
+ CFileItemPtr pItem = m_vecItems[iList]->Get(iItem);
+
+ if (!pItem->m_bIsShareOrDrive)
+ {
+ if (!pItem->IsParentFolder())
+ {
+ // MARK file
+ pItem->Select(!pItem->IsSelected());
+ }
+ }
+
+ UpdateItemCounts();
+ // UpdateButtons();
+}
+
+void CGUIWindowFileManager::OnCopy(int iList)
+{
+ if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{120}, CVariant{123}))
+ return;
+
+ AddJob(new CFileOperationJob(CFileOperationJob::ActionCopy,
+ *m_vecItems[iList],
+ m_Directory[1 - iList]->GetPath(),
+ true, 16201, 16202));
+}
+
+void CGUIWindowFileManager::OnMove(int iList)
+{
+ if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{121}, CVariant{124}))
+ return;
+
+ AddJob(new CFileOperationJob(CFileOperationJob::ActionMove,
+ *m_vecItems[iList],
+ m_Directory[1 - iList]->GetPath(),
+ true, 16203, 16204));
+}
+
+void CGUIWindowFileManager::OnDelete(int iList)
+{
+ if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{122}, CVariant{125}))
+ return;
+
+ AddJob(new CFileOperationJob(CFileOperationJob::ActionDelete,
+ *m_vecItems[iList],
+ m_Directory[iList]->GetPath(),
+ true, 16205, 16206));
+}
+
+void CGUIWindowFileManager::OnRename(int iList)
+{
+ std::string strFile;
+ for (int i = 0; i < m_vecItems[iList]->Size();++i)
+ {
+ CFileItemPtr pItem = m_vecItems[iList]->Get(i);
+ if (pItem->IsSelected())
+ {
+ strFile = pItem->GetPath();
+ break;
+ }
+ }
+
+ CFileUtils::RenameFile(strFile);
+
+ Refresh(iList);
+}
+
+void CGUIWindowFileManager::OnSelectAll(int iList)
+{
+ for (int i = 0; i < m_vecItems[iList]->Size();++i)
+ {
+ CFileItemPtr pItem = m_vecItems[iList]->Get(i);
+ if (!pItem->IsParentFolder())
+ {
+ pItem->Select(true);
+ }
+ }
+}
+
+void CGUIWindowFileManager::OnNewFolder(int iList)
+{
+ std::string strNewFolder = "";
+ if (CGUIKeyboardFactory::ShowAndGetInput(strNewFolder, CVariant{g_localizeStrings.Get(16014)}, false))
+ {
+ std::string strNewPath = m_Directory[iList]->GetPath();
+ URIUtils::AddSlashAtEnd(strNewPath);
+ strNewPath += strNewFolder;
+ CDirectory::Create(strNewPath);
+ Refresh(iList);
+
+ // select the new folder
+ for (int i=0; i<m_vecItems[iList]->Size(); ++i)
+ {
+ CFileItemPtr pItem=m_vecItems[iList]->Get(i);
+ std::string strPath=pItem->GetPath();
+ URIUtils::RemoveSlashAtEnd(strPath);
+ if (strPath==strNewPath)
+ {
+ CONTROL_SELECT_ITEM(iList + CONTROL_LEFT_LIST, i);
+ break;
+ }
+ }
+ }
+}
+
+void CGUIWindowFileManager::Refresh(int iList)
+{
+ int nSel = GetSelectedItem(iList);
+ // update the list views
+ Update(iList, m_Directory[iList]->GetPath());
+
+ while (nSel > m_vecItems[iList]->Size())
+ nSel--;
+
+ CONTROL_SELECT_ITEM(iList + CONTROL_LEFT_LIST, nSel);
+}
+
+
+void CGUIWindowFileManager::Refresh()
+{
+ int iList = GetFocusedList();
+ int nSel = GetSelectedItem(iList);
+ // update the list views
+ Update(0, m_Directory[0]->GetPath());
+ Update(1, m_Directory[1]->GetPath());
+
+ while (nSel > m_vecItems[iList]->Size())
+ nSel--;
+
+ CONTROL_SELECT_ITEM(iList + CONTROL_LEFT_LIST, nSel);
+}
+
+int CGUIWindowFileManager::GetSelectedItem(int iControl)
+{
+ if (iControl < 0 || iControl > 1 || m_vecItems[iControl]->IsEmpty())
+ return -1;
+ CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl + CONTROL_LEFT_LIST);
+ if (OnMessage(msg))
+ return msg.GetParam1();
+ return -1;
+}
+
+void CGUIWindowFileManager::GoParentFolder(int iList)
+{
+ CURL url(m_Directory[iList]->GetPath());
+ if (url.IsProtocol("rar") || url.IsProtocol("zip"))
+ {
+ // check for step-below, if, unmount rar
+ if (url.GetFileName().empty())
+ if (url.IsProtocol("zip"))
+ g_ZipManager.release(m_Directory[iList]->GetPath()); // release resources
+ }
+
+ std::string strPath(m_strParentPath[iList]), strOldPath(m_Directory[iList]->GetPath());
+ Update(iList, strPath);
+}
+
+/// \brief Build a directory history string
+/// \param pItem Item to build the history string from
+/// \param strHistoryString History string build as return value
+void CGUIWindowFileManager::GetDirectoryHistoryString(const CFileItem* pItem, std::string& strHistoryString)
+{
+ if (pItem->m_bIsShareOrDrive)
+ {
+ // We are in the virtual directory
+
+ // History string of the DVD drive
+ // must be handled separately
+ if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_DVD)
+ {
+ // Remove disc label from item label
+ // and use as history string, m_strPath
+ // can change for new discs
+ std::string strLabel = pItem->GetLabel();
+ size_t nPosOpen = strLabel.find('(');
+ size_t nPosClose = strLabel.rfind(')');
+ if (nPosOpen != std::string::npos &&
+ nPosClose != std::string::npos &&
+ nPosClose > nPosOpen)
+ {
+ strLabel.erase(nPosOpen + 1, (nPosClose) - (nPosOpen + 1));
+ strHistoryString = strLabel;
+ }
+ else
+ strHistoryString = strLabel;
+ }
+ else
+ {
+ // Other items in virtual directory
+ strHistoryString = pItem->GetLabel() + pItem->GetPath();
+ URIUtils::RemoveSlashAtEnd(strHistoryString);
+ }
+ }
+ else
+ {
+ // Normal directory items
+ strHistoryString = pItem->GetPath();
+ URIUtils::RemoveSlashAtEnd(strHistoryString);
+ }
+}
+
+bool CGUIWindowFileManager::GetDirectory(int iList, const std::string &strDirectory, CFileItemList &items)
+{
+ CURL pathToUrl(strDirectory);
+
+ CGetDirectoryItems getItems(m_rootDir, pathToUrl, items);
+ if (!CGUIDialogBusy::Wait(&getItems, 100, true))
+ {
+ return false;
+ }
+ return getItems.m_result;
+}
+
+bool CGUIWindowFileManager::CanRename(int iList)
+{
+ //! @todo Renaming of shares (requires writing to sources.xml)
+ //! this might be able to be done via the webserver code stuff...
+ if (m_Directory[iList]->IsVirtualDirectoryRoot()) return false;
+ if (m_Directory[iList]->IsReadOnly()) return false;
+
+ return true;
+}
+
+bool CGUIWindowFileManager::CanCopy(int iList)
+{
+ // can't copy if the destination is not writeable, or if the source is a share!
+ //! @todo Perhaps if the source is removeable media (DVD/CD etc.) we could
+ //! put ripping/backup in here.
+ if (!CUtil::SupportsReadFileOperations(m_Directory[iList]->GetPath())) return false;
+ if (m_Directory[iList]->IsVirtualDirectoryRoot()) return false;
+ if (m_Directory[1 - iList]->IsVirtualDirectoryRoot()) return false;
+ if (m_Directory[iList]->IsVirtualDirectoryRoot()) return false;
+ if (m_Directory[1 -iList]->IsReadOnly()) return false;
+ return true;
+}
+
+bool CGUIWindowFileManager::CanMove(int iList)
+{
+ // can't move if the destination is not writeable, or if the source is a share or not writeable!
+ if (m_Directory[0]->IsVirtualDirectoryRoot() || m_Directory[0]->IsReadOnly()) return false;
+ if (m_Directory[1]->IsVirtualDirectoryRoot() || m_Directory[1]->IsReadOnly()) return false;
+ return true;
+}
+
+bool CGUIWindowFileManager::CanDelete(int iList)
+{
+ if (m_Directory[iList]->IsVirtualDirectoryRoot()) return false;
+ if (m_Directory[iList]->IsReadOnly()) return false;
+ return true;
+}
+
+bool CGUIWindowFileManager::CanNewFolder(int iList)
+{
+ if (m_Directory[iList]->IsVirtualDirectoryRoot()) return false;
+ if (m_Directory[iList]->IsReadOnly()) return false;
+ return true;
+}
+
+int CGUIWindowFileManager::NumSelected(int iList)
+{
+ int iSelectedItems = 0;
+ for (int iItem = 0; iItem < m_vecItems[iList]->Size(); ++iItem)
+ {
+ if (m_vecItems[iList]->Get(iItem)->IsSelected()) iSelectedItems++;
+ }
+ return iSelectedItems;
+}
+
+int CGUIWindowFileManager::GetFocusedList() const
+{
+ return GetFocusedControlID() - CONTROL_LEFT_LIST;
+}
+
+void CGUIWindowFileManager::OnPopupMenu(int list, int item, bool bContextDriven /* = true */)
+{
+ if (list < 0 || list >= 2) return ;
+ bool bDeselect = SelectItem(list, item);
+ // calculate the position for our menu
+ float posX = 200;
+ float posY = 100;
+ const CGUIControl *pList = GetControl(CONTROL_LEFT_LIST + list);
+ if (pList)
+ {
+ posX = pList->GetXPosition() + pList->GetWidth() / 2;
+ posY = pList->GetYPosition() + pList->GetHeight() / 2;
+ }
+
+ CFileItemPtr pItem = m_vecItems[list]->Get(item);
+ if (!pItem.get())
+ return;
+
+ if (m_Directory[list]->IsVirtualDirectoryRoot())
+ {
+ if (item < 0)
+ { //! @todo We should add the option here for shares to be added if there aren't any
+ return ;
+ }
+
+ // and do the popup menu
+ if (CGUIDialogContextMenu::SourcesMenu("files", pItem, posX, posY))
+ {
+ m_rootDir.SetSources(*CMediaSourceSettings::GetInstance().GetSources("files"));
+ if (m_Directory[1 - list]->IsVirtualDirectoryRoot())
+ Refresh();
+ else
+ Refresh(list);
+ return ;
+ }
+ pItem->Select(false);
+ return ;
+ }
+
+ const CPlayerCoreFactory &playerCoreFactory = CServiceBroker::GetPlayerCoreFactory();
+
+ // popup the context menu
+
+ bool showEntry = false;
+ if (item >= m_vecItems[list]->Size()) item = -1;
+ if (item >= 0)
+ showEntry=(!pItem->IsParentFolder() || (pItem->IsParentFolder() && m_vecItems[list]->GetSelectedCount()>0));
+
+ // determine available players
+ std::vector<std::string>players;
+ playerCoreFactory.GetPlayers(*pItem, players);
+
+ // add the needed buttons
+ CContextButtons choices;
+ if (item >= 0)
+ {
+ //The ".." item is not selectable. Take that into account when figuring out if all items are selected
+ int notSelectable = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWPARENTDIRITEMS) ? 1 : 0;
+ if (NumSelected(list) < m_vecItems[list]->Size() - notSelectable)
+ choices.Add(CONTROL_BTNSELECTALL, 188); // SelectAll
+ if (!pItem->IsParentFolder())
+ choices.Add(CONTROL_BTNFAVOURITES, CServiceBroker::GetFavouritesService().IsFavourited(*pItem.get(), GetID()) ? 14077 : 14076); // Add/Remove Favourite
+ if (players.size() > 1)
+ choices.Add(CONTROL_BTNPLAYWITH, 15213);
+ if (CanRename(list) && !pItem->IsParentFolder())
+ choices.Add(CONTROL_BTNRENAME, 118);
+ if (CanDelete(list) && showEntry)
+ choices.Add(CONTROL_BTNDELETE, 117);
+ if (CanCopy(list) && showEntry)
+ choices.Add(CONTROL_BTNCOPY, 115);
+ if (CanMove(list) && showEntry)
+ choices.Add(CONTROL_BTNMOVE, 116);
+ }
+ if (CanNewFolder(list))
+ choices.Add(CONTROL_BTNNEWFOLDER, 20309);
+ if (item >= 0 && pItem->m_bIsFolder && !pItem->IsParentFolder())
+ choices.Add(CONTROL_BTNCALCSIZE, 13393);
+ choices.Add(CONTROL_BTNSWITCHMEDIA, 523);
+ if (CServiceBroker::GetJobManager()->IsProcessing("filemanager"))
+ choices.Add(CONTROL_BTNCANCELJOB, 167);
+
+ if (!pItem->m_bIsFolder)
+ choices.Add(CONTROL_BTNVIEW, 39104);
+
+ int btnid = CGUIDialogContextMenu::ShowAndGetChoice(choices);
+ if (btnid == CONTROL_BTNSELECTALL)
+ {
+ OnSelectAll(list);
+ bDeselect=false;
+ }
+ if (btnid == CONTROL_BTNFAVOURITES)
+ {
+ CServiceBroker::GetFavouritesService().AddOrRemove(*pItem.get(), GetID());
+ return;
+ }
+ if (btnid == CONTROL_BTNPLAYWITH)
+ {
+ std::vector<std::string>players;
+ playerCoreFactory.GetPlayers(*pItem, players);
+ std::string player = playerCoreFactory.SelectPlayerDialog(players);
+ if (!player.empty())
+ OnStart(pItem.get(), player);
+ }
+ if (btnid == CONTROL_BTNRENAME)
+ OnRename(list);
+ if (btnid == CONTROL_BTNDELETE)
+ OnDelete(list);
+ if (btnid == CONTROL_BTNCOPY)
+ OnCopy(list);
+ if (btnid == CONTROL_BTNMOVE)
+ OnMove(list);
+ if (btnid == CONTROL_BTNNEWFOLDER)
+ OnNewFolder(list);
+ if (btnid == CONTROL_BTNCALCSIZE)
+ {
+ // setup the progress dialog, and show it
+ CGUIDialogProgress *progress = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS);
+ if (progress)
+ {
+ progress->SetHeading(CVariant{13394});
+ for (int i=0; i < 3; i++)
+ progress->SetLine(i, CVariant{""});
+ progress->Open();
+ }
+
+ // Calculate folder size for each selected item
+ for (int i=0; i<m_vecItems[list]->Size(); ++i)
+ {
+ CFileItemPtr pItem2=m_vecItems[list]->Get(i);
+ if (pItem2->m_bIsFolder && pItem2->IsSelected())
+ {
+ int64_t folderSize = CalculateFolderSize(pItem2->GetPath(), progress);
+ if (folderSize >= 0)
+ {
+ pItem2->m_dwSize = folderSize;
+ if (folderSize == 0)
+ pItem2->SetLabel2(StringUtils::SizeToString(folderSize));
+ else
+ pItem2->SetFileSizeLabel();
+ }
+ }
+ }
+ if (progress)
+ progress->Close();
+ }
+ if (btnid == CONTROL_BTNSWITCHMEDIA)
+ {
+ CGUIDialogContextMenu::SwitchMedia("files", m_vecItems[list]->GetPath());
+ return;
+ }
+ if (btnid == CONTROL_BTNCANCELJOB)
+ CancelJobs();
+ if (btnid == CONTROL_BTNVIEW)
+ CGUIDialogTextViewer::ShowForFile(pItem->GetPath(), true);
+
+ if (bDeselect && item >= 0 && item < m_vecItems[list]->Size())
+ { // deselect item as we didn't do anything
+ pItem->Select(false);
+ }
+}
+
+// Highlights the item in the list under the cursor
+// returns true if we should deselect the item, false otherwise
+bool CGUIWindowFileManager::SelectItem(int list, int &item)
+{
+ // get the currently selected item in the list
+ item = GetSelectedItem(list);
+
+ // select the item if we need to
+ if (item > -1 && !NumSelected(list) && !m_vecItems[list]->Get(item)->IsParentFolder())
+ {
+ m_vecItems[list]->Get(item)->Select(true);
+ return true;
+ }
+ return false;
+}
+
+// recursively calculates the selected folder size
+int64_t CGUIWindowFileManager::CalculateFolderSize(const std::string &strDirectory, CGUIDialogProgress *pProgress)
+{
+ const CURL pathToUrl(strDirectory);
+ if (pProgress)
+ { // update our progress control
+ pProgress->Progress();
+ pProgress->SetLine(1, strDirectory);
+ if (pProgress->IsCanceled())
+ return -1;
+ }
+ // start by calculating the size of the files in this folder...
+ int64_t totalSize = 0;
+ CFileItemList items;
+ CVirtualDirectory rootDir;
+ rootDir.SetSources(*CMediaSourceSettings::GetInstance().GetSources("files"));
+ rootDir.GetDirectory(pathToUrl, items, false, false);
+ for (int i=0; i < items.Size(); i++)
+ {
+ if (items[i]->m_bIsFolder && !items[i]->IsParentFolder()) // folder
+ {
+ int64_t folderSize = CalculateFolderSize(items[i]->GetPath(), pProgress);
+ if (folderSize < 0) return -1;
+ totalSize += folderSize;
+ }
+ else // file
+ totalSize += items[i]->m_dwSize;
+ }
+ return totalSize;
+}
+
+void CGUIWindowFileManager::OnJobComplete(unsigned int jobID, bool success, CJob *job)
+{
+ if(!success)
+ {
+ CFileOperationJob* fileJob = static_cast<CFileOperationJob*>(job);
+ HELPERS::ShowOKDialogLines(CVariant{fileJob->GetHeading()},
+ CVariant{fileJob->GetLine()}, CVariant{16200}, CVariant{0});
+ }
+
+ if (IsActive())
+ {
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_UPDATE);
+ CServiceBroker::GetAppMessenger()->SendGUIMessage(msg, GetID(), false);
+ }
+
+ CJobQueue::OnJobComplete(jobID, success, job);
+}
+
+void CGUIWindowFileManager::ShowShareErrorMessage(CFileItem* pItem)
+{
+ int idMessageText = 0;
+ CURL url(pItem->GetPath());
+
+ if (url.IsProtocol("smb") && url.GetHostName().empty()) // smb workgroup
+ idMessageText = 15303; // Workgroup not found
+ else if (pItem->m_iDriveType == CMediaSource::SOURCE_TYPE_REMOTE || URIUtils::IsRemote(pItem->GetPath()))
+ idMessageText = 15301; // Could not connect to network server
+ else
+ idMessageText = 15300; // Path not found or invalid
+
+ HELPERS::ShowOKDialogText(CVariant{220}, CVariant{idMessageText});
+}
+
+void CGUIWindowFileManager::OnInitWindow()
+{
+ bool bResult0 = Update(0, m_Directory[0]->GetPath());
+ bool bResult1 = Update(1, m_Directory[1]->GetPath());
+
+ CGUIWindow::OnInitWindow();
+
+ if (!bCheckShareConnectivity)
+ {
+ bCheckShareConnectivity = true; //reset
+ CFileItem pItem(strCheckSharePath, true);
+ ShowShareErrorMessage(&pItem); //show the error message after window is loaded!
+ Update(0,""); // reset view to root
+ }
+ else if (!bResult0)
+ {
+ ShowShareErrorMessage(m_Directory[0]); //show the error message after window is loaded!
+ Update(0, ""); // reset view to root
+ }
+
+ if (!bResult1)
+ {
+ ShowShareErrorMessage(m_Directory[1]); //show the error message after window is loaded!
+ Update(1, ""); // reset view to root
+ }
+}
+
+void CGUIWindowFileManager::SetInitialPath(const std::string &path)
+{
+ // check for a passed destination path
+ std::string strDestination = path;
+ m_rootDir.SetSources(*CMediaSourceSettings::GetInstance().GetSources("files"));
+ if (!strDestination.empty())
+ {
+ CLog::Log(LOGINFO, "Attempting to quickpath to: {}", strDestination);
+ }
+ // otherwise, is this the first time accessing this window?
+ else if (m_Directory[0]->GetPath() == "?")
+ {
+ m_Directory[0]->SetPath(strDestination = CMediaSourceSettings::GetInstance().GetDefaultSource("files"));
+ CLog::Log(LOGINFO, "Attempting to default to: {}", strDestination);
+ }
+ // try to open the destination path
+ if (!strDestination.empty())
+ {
+ // open root
+ if (StringUtils::EqualsNoCase(strDestination, "$ROOT"))
+ {
+ m_Directory[0]->SetPath("");
+ CLog::Log(LOGINFO, " Success! Opening root listing.");
+ }
+ else
+ {
+ // default parameters if the jump fails
+ m_Directory[0]->SetPath("");
+
+ bool bIsSourceName = false;
+ VECSOURCES shares;
+ m_rootDir.GetSources(shares);
+ int iIndex = CUtil::GetMatchingSource(strDestination, shares, bIsSourceName);
+ if (iIndex > -1
+#if defined(TARGET_DARWIN_EMBEDDED)
+ || URIUtils::PathHasParent(strDestination, "special://envhome/Documents/Inbox/")
+#endif
+ || URIUtils::PathHasParent(strDestination, "special://profile/"))
+ {
+ // set current directory to matching share
+ std::string path;
+ if (bIsSourceName && iIndex < (int)shares.size())
+ path = shares[iIndex].strPath;
+ else
+ path = strDestination;
+ URIUtils::RemoveSlashAtEnd(path);
+ m_Directory[0]->SetPath(path);
+ CLog::Log(LOGINFO, " Success! Opened destination path: {}", strDestination);
+
+ // outside call: check the share for connectivity
+ bCheckShareConnectivity = Update(0, m_Directory[0]->GetPath());
+ if(!bCheckShareConnectivity)
+ strCheckSharePath = m_Directory[0]->GetPath();
+ }
+ else
+ {
+ CLog::Log(LOGERROR, " Failed! Destination parameter ({}) does not match a valid share!",
+ strDestination);
+ }
+ }
+ }
+
+ if (m_Directory[1]->GetPath() == "?") m_Directory[1]->SetPath("");
+}
+
+const CFileItem& CGUIWindowFileManager::CurrentDirectory(int indx) const
+{
+ return *m_Directory[indx];
+}
diff --git a/xbmc/windows/GUIWindowFileManager.h b/xbmc/windows/GUIWindowFileManager.h
new file mode 100644
index 0000000..425e96e
--- /dev/null
+++ b/xbmc/windows/GUIWindowFileManager.h
@@ -0,0 +1,105 @@
+/*
+ * 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 "filesystem/DirectoryHistory.h"
+#include "filesystem/VirtualDirectory.h"
+#include "guilib/GUIWindow.h"
+#include "utils/JobManager.h"
+
+#include <atomic>
+#include <string>
+#include <vector>
+
+class CFileItem;
+class CFileItemList;
+class CGUIDialogProgress;
+
+class CGUIWindowFileManager :
+ public CGUIWindow,
+ public CJobQueue
+{
+public:
+
+ CGUIWindowFileManager(void);
+ ~CGUIWindowFileManager(void) override;
+ bool OnMessage(CGUIMessage& message) override;
+ bool OnAction(const CAction &action) override;
+ bool OnBack(int actionID) override;
+ const CFileItem &CurrentDirectory(int indx) const;
+
+ static int64_t CalculateFolderSize(const std::string &strDirectory, CGUIDialogProgress *pProgress = NULL);
+
+ void OnJobComplete(unsigned int jobID, bool success, CJob *job) override;
+protected:
+ void OnInitWindow() override;
+ void SetInitialPath(const std::string &path);
+ void GoParentFolder(int iList);
+ void UpdateControl(int iList, int item);
+ bool Update(int iList, const std::string &strDirectory); //???
+ void OnStart(CFileItem *pItem, const std::string &player);
+ bool SelectItem(int iList, int &item);
+ void ClearFileItems(int iList);
+ void OnClick(int iList, int iItem);
+ void OnMark(int iList, int iItem);
+ void OnSort(int iList);
+ void UpdateButtons();
+ void OnCopy(int iList);
+ void OnMove(int iList);
+ void OnDelete(int iList);
+ void OnRename(int iList);
+ void OnSelectAll(int iList);
+ void OnNewFolder(int iList);
+ void Refresh();
+ void Refresh(int iList);
+ int GetSelectedItem(int iList);
+ bool HaveDiscOrConnection( std::string& strPath, int iDriveType );
+ void GetDirectoryHistoryString(const CFileItem* pItem, std::string& strHistoryString);
+ bool GetDirectory(int iList, const std::string &strDirectory, CFileItemList &items);
+ int NumSelected(int iList);
+ int GetFocusedList() const;
+ // functions to check for actions that we can perform
+ bool CanRename(int iList);
+ bool CanCopy(int iList);
+ bool CanMove(int iList);
+ bool CanDelete(int iList);
+ bool CanNewFolder(int iList);
+ void OnPopupMenu(int iList, int iItem, bool bContextDriven = true);
+ void ShowShareErrorMessage(CFileItem* pItem);
+ void UpdateItemCounts();
+
+ //
+ bool bCheckShareConnectivity;
+ std::string strCheckSharePath;
+
+ XFILE::CVirtualDirectory m_rootDir;
+ CFileItemList* m_vecItems[2];
+ typedef std::vector <CFileItem*> ::iterator ivecItems;
+ CFileItem* m_Directory[2];
+ std::string m_strParentPath[2];
+ CDirectoryHistory m_history[2];
+
+ int m_errorHeading, m_errorLine;
+private:
+ std::atomic_bool m_updating = {false};
+ class CUpdateGuard
+ {
+ public:
+ CUpdateGuard(std::atomic_bool &update) : m_update(update)
+ {
+ m_update = true;
+ }
+ ~CUpdateGuard()
+ {
+ m_update = false;
+ }
+ private:
+ std::atomic_bool &m_update;
+ };
+};
diff --git a/xbmc/windows/GUIWindowHome.cpp b/xbmc/windows/GUIWindowHome.cpp
new file mode 100644
index 0000000..abc6cfc
--- /dev/null
+++ b/xbmc/windows/GUIWindowHome.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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 "GUIWindowHome.h"
+
+#include "ServiceBroker.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/WindowIDs.h"
+#include "input/actions/Action.h"
+#include "input/actions/ActionIDs.h"
+#include "interfaces/AnnouncementManager.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/JobManager.h"
+#include "utils/RecentlyAddedJob.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+
+#include <mutex>
+
+CGUIWindowHome::CGUIWindowHome(void) : CGUIWindow(WINDOW_HOME, "Home.xml")
+{
+ m_updateRA = (Audio | Video | Totals);
+ m_loadType = KEEP_IN_MEMORY;
+
+ CServiceBroker::GetAnnouncementManager()->AddAnnouncer(this);
+}
+
+CGUIWindowHome::~CGUIWindowHome(void)
+{
+ CServiceBroker::GetAnnouncementManager()->RemoveAnnouncer(this);
+}
+
+bool CGUIWindowHome::OnAction(const CAction &action)
+{
+ static unsigned int min_hold_time = 1000;
+ if (action.GetID() == ACTION_NAV_BACK && action.GetHoldTime() < min_hold_time)
+ {
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (appPlayer->IsPlaying())
+ {
+ CGUIComponent* gui = CServiceBroker::GetGUI();
+ if (gui)
+ gui->GetWindowManager().SwitchToFullScreen();
+
+ return true;
+ }
+ }
+ return CGUIWindow::OnAction(action);
+}
+
+void CGUIWindowHome::OnInitWindow()
+{
+ // for shared databases (ie mysql) always force an update on return to home
+ // this is a temporary solution until remote announcements can be delivered
+ if (StringUtils::EqualsNoCase(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_databaseVideo.type, "mysql") ||
+ StringUtils::EqualsNoCase(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_databaseMusic.type, "mysql") )
+ m_updateRA = (Audio | Video | Totals);
+ AddRecentlyAddedJobs( m_updateRA );
+
+ CGUIWindow::OnInitWindow();
+}
+
+void CGUIWindowHome::Announce(ANNOUNCEMENT::AnnouncementFlag flag,
+ const std::string& sender,
+ const std::string& message,
+ const CVariant& data)
+{
+ int ra_flag = 0;
+
+ CLog::Log(LOGDEBUG, LOGANNOUNCE, "GOT ANNOUNCEMENT, type: {}, from {}, message {}",
+ AnnouncementFlagToString(flag), sender, message);
+
+ // we are only interested in library changes
+ if ((flag & (ANNOUNCEMENT::VideoLibrary | ANNOUNCEMENT::AudioLibrary)) == 0)
+ return;
+
+ if (data.isMember("transaction") && data["transaction"].asBoolean())
+ return;
+
+ if (message == "OnScanStarted" || message == "OnCleanStarted")
+ return;
+
+ bool onUpdate = message == "OnUpdate";
+ // always update Totals except on an OnUpdate with no playcount update
+ if (!onUpdate || data.isMember("playcount"))
+ ra_flag |= Totals;
+
+ // always update the full list except on an OnUpdate
+ if (!onUpdate)
+ {
+ if (flag & ANNOUNCEMENT::VideoLibrary)
+ ra_flag |= Video;
+ else if (flag & ANNOUNCEMENT::AudioLibrary)
+ ra_flag |= Audio;
+ }
+
+ CGUIMessage reload(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_REFRESH_THUMBS, ra_flag);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(reload, GetID());
+}
+
+void CGUIWindowHome::AddRecentlyAddedJobs(int flag)
+{
+ bool getAJob = false;
+
+ // this block checks to see if another one is running
+ // and keeps track of the flag
+ {
+ std::unique_lock<CCriticalSection> lockMe(*this);
+ if (!m_recentlyAddedRunning)
+ {
+ getAJob = true;
+
+ flag |= m_cumulativeUpdateFlag; // add the flags from previous calls to AddRecentlyAddedJobs
+
+ m_cumulativeUpdateFlag = 0; // now taken care of in flag.
+ // reset this since we're going to execute a job
+
+ // we're about to add one so set the indicator
+ if (flag)
+ m_recentlyAddedRunning = true; // this will happen in the if clause below
+ }
+ else
+ // since we're going to skip a job, mark that one came in and ...
+ m_cumulativeUpdateFlag |= flag; // this will be used later
+ }
+
+ if (flag && getAJob)
+ CServiceBroker::GetJobManager()->AddJob(new CRecentlyAddedJob(flag), this);
+
+ m_updateRA = 0;
+}
+
+void CGUIWindowHome::OnJobComplete(unsigned int jobID, bool success, CJob *job)
+{
+ int flag = 0;
+
+ {
+ std::unique_lock<CCriticalSection> lockMe(*this);
+
+ // the job is finished.
+ // did one come in the meantime?
+ flag = m_cumulativeUpdateFlag;
+ m_recentlyAddedRunning = false; /// we're done.
+ }
+
+ if (flag)
+ AddRecentlyAddedJobs(0 /* the flag will be set inside AddRecentlyAddedJobs via m_cumulativeUpdateFlag */ );
+}
+
+
+bool CGUIWindowHome::OnMessage(CGUIMessage& message)
+{
+ switch ( message.GetMessage() )
+ {
+ case GUI_MSG_NOTIFY_ALL:
+ if (message.GetParam1() == GUI_MSG_WINDOW_RESET || message.GetParam1() == GUI_MSG_REFRESH_THUMBS)
+ {
+ int updateRA = (message.GetSenderId() == GetID()) ? message.GetParam2() : (Video | Audio | Totals);
+
+ if (IsActive())
+ AddRecentlyAddedJobs(updateRA);
+ else
+ m_updateRA |= updateRA;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return CGUIWindow::OnMessage(message);
+}
diff --git a/xbmc/windows/GUIWindowHome.h b/xbmc/windows/GUIWindowHome.h
new file mode 100644
index 0000000..08e0066
--- /dev/null
+++ b/xbmc/windows/GUIWindowHome.h
@@ -0,0 +1,41 @@
+/*
+ * 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 "guilib/GUIWindow.h"
+#include "interfaces/IAnnouncer.h"
+#include "utils/Job.h"
+
+class CVariant;
+
+class CGUIWindowHome :
+ public CGUIWindow,
+ public ANNOUNCEMENT::IAnnouncer,
+ public IJobCallback
+{
+public:
+ CGUIWindowHome(void);
+ ~CGUIWindowHome(void) override;
+ void OnInitWindow() override;
+ void Announce(ANNOUNCEMENT::AnnouncementFlag flag,
+ const std::string& sender,
+ const std::string& message,
+ const CVariant& data) override;
+
+ bool OnMessage(CGUIMessage& message) override;
+ bool OnAction(const CAction &action) override;
+
+ void OnJobComplete(unsigned int jobID, bool success, CJob *job) override;
+private:
+ int m_updateRA; // flag for which recently added items needs to be queried
+ void AddRecentlyAddedJobs(int flag);
+
+ bool m_recentlyAddedRunning = false;
+ int m_cumulativeUpdateFlag = 0;
+};
diff --git a/xbmc/windows/GUIWindowLoginScreen.cpp b/xbmc/windows/GUIWindowLoginScreen.cpp
new file mode 100644
index 0000000..b00c2ae
--- /dev/null
+++ b/xbmc/windows/GUIWindowLoginScreen.cpp
@@ -0,0 +1,269 @@
+/*
+ * 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 "GUIWindowLoginScreen.h"
+
+#include "FileItem.h"
+#include "GUIPassword.h"
+#include "ServiceBroker.h"
+#include "addons/Skin.h"
+#include "dialogs/GUIDialogContextMenu.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIMessage.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "input/Key.h"
+#include "interfaces/builtins/Builtins.h"
+#include "messaging/ApplicationMessenger.h"
+#include "messaging/helpers/DialogOKHelper.h"
+#include "profiles/Profile.h"
+#include "profiles/ProfileManager.h"
+#include "profiles/dialogs/GUIDialogProfileSettings.h"
+#include "pvr/PVRManager.h"
+#include "pvr/guilib/PVRGUIActionsPowerManagement.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "view/ViewState.h"
+
+using namespace KODI::MESSAGING;
+
+#define CONTROL_BIG_LIST 52
+#define CONTROL_LABEL_HEADER 2
+#define CONTROL_LABEL_SELECTED_PROFILE 3
+
+CGUIWindowLoginScreen::CGUIWindowLoginScreen(void)
+ : CGUIWindow(WINDOW_LOGIN_SCREEN, "LoginScreen.xml")
+{
+ watch.StartZero();
+ m_vecItems = new CFileItemList;
+ m_iSelectedItem = -1;
+ m_loadType = KEEP_IN_MEMORY;
+}
+
+CGUIWindowLoginScreen::~CGUIWindowLoginScreen(void)
+{
+ delete m_vecItems;
+}
+
+bool CGUIWindowLoginScreen::OnMessage(CGUIMessage& message)
+{
+ switch ( message.GetMessage() )
+ {
+ case GUI_MSG_WINDOW_DEINIT:
+ {
+ m_vecItems->Clear();
+ }
+ break;
+
+ case GUI_MSG_CLICKED:
+ {
+ int iControl = message.GetSenderId();
+ if (iControl == CONTROL_BIG_LIST)
+ {
+ int iAction = message.GetParam1();
+
+ // iItem is checked for validity inside these routines
+ if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
+ {
+ int iItem = m_viewControl.GetSelectedItem();
+ bool bResult = OnPopupMenu(m_viewControl.GetSelectedItem());
+ if (bResult)
+ {
+ Update();
+ CGUIMessage msg(GUI_MSG_ITEM_SELECT,GetID(),CONTROL_BIG_LIST,iItem);
+ OnMessage(msg);
+ }
+
+ return bResult;
+ }
+ else if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
+ {
+ int iItem = m_viewControl.GetSelectedItem();
+ bool bCanceled;
+ bool bOkay = g_passwordManager.IsProfileLockUnlocked(iItem, bCanceled);
+
+ if (bOkay)
+ {
+ if (iItem >= 0)
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_LOADPROFILE, iItem);
+ }
+ else
+ {
+ if (!bCanceled && iItem != 0)
+ HELPERS::ShowOKDialogText(CVariant{20068}, CVariant{20117});
+ }
+ }
+ }
+ }
+ break;
+ case GUI_MSG_SETFOCUS:
+ {
+ if (m_viewControl.HasControl(message.GetControlId()) && m_viewControl.GetCurrentControl() != message.GetControlId())
+ {
+ m_viewControl.SetFocused();
+ return true;
+ }
+ }
+ default:
+ break;
+
+ }
+
+ return CGUIWindow::OnMessage(message);
+}
+
+bool CGUIWindowLoginScreen::OnAction(const CAction &action)
+{
+ // don't allow built in actions to act here except shutdown related ones.
+ // this forces only navigation type actions to be performed.
+ if (action.GetID() == ACTION_BUILT_IN_FUNCTION)
+ {
+ std::string actionName = action.GetName();
+ StringUtils::ToLower(actionName);
+ if ((actionName.find("shutdown") != std::string::npos) &&
+ CServiceBroker::GetPVRManager().Get<PVR::GUI::PowerManagement>().CanSystemPowerdown())
+ CBuiltins::GetInstance().Execute(action.GetName());
+ return true;
+ }
+ return CGUIWindow::OnAction(action);
+}
+
+bool CGUIWindowLoginScreen::OnBack(int actionID)
+{
+ // no escape from the login window
+ return false;
+}
+
+void CGUIWindowLoginScreen::FrameMove()
+{
+ if (GetFocusedControlID() == CONTROL_BIG_LIST && !CServiceBroker::GetGUI()->GetWindowManager().HasModalDialog(true))
+ {
+ if (m_viewControl.HasControl(CONTROL_BIG_LIST))
+ m_iSelectedItem = m_viewControl.GetSelectedItem();
+ }
+
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ std::string strLabel = StringUtils::Format(g_localizeStrings.Get(20114), m_iSelectedItem + 1,
+ profileManager->GetNumberOfProfiles());
+ SET_CONTROL_LABEL(CONTROL_LABEL_SELECTED_PROFILE,strLabel);
+ CGUIWindow::FrameMove();
+}
+
+void CGUIWindowLoginScreen::OnInitWindow()
+{
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ m_iSelectedItem = static_cast<int>(profileManager->GetLastUsedProfileIndex());
+
+ // Update list/thumb control
+ m_viewControl.SetCurrentView(DEFAULT_VIEW_LIST);
+ Update();
+ m_viewControl.SetFocused();
+ SET_CONTROL_LABEL(CONTROL_LABEL_HEADER,g_localizeStrings.Get(20115));
+ SET_CONTROL_VISIBLE(CONTROL_BIG_LIST);
+
+ CGUIWindow::OnInitWindow();
+}
+
+void CGUIWindowLoginScreen::OnWindowLoaded()
+{
+ CGUIWindow::OnWindowLoaded();
+ m_viewControl.Reset();
+ m_viewControl.SetParentWindow(GetID());
+ m_viewControl.AddView(GetControl(CONTROL_BIG_LIST));
+}
+
+void CGUIWindowLoginScreen::OnWindowUnload()
+{
+ CGUIWindow::OnWindowUnload();
+ m_viewControl.Reset();
+}
+
+void CGUIWindowLoginScreen::Update()
+{
+ m_vecItems->Clear();
+
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ for (unsigned int i = 0; i < profileManager->GetNumberOfProfiles(); ++i)
+ {
+ const CProfile *profile = profileManager->GetProfile(i);
+
+ CFileItemPtr item(new CFileItem(profile->getName()));
+
+ std::string strLabel;
+ if (profile->getDate().empty())
+ strLabel = g_localizeStrings.Get(20113);
+ else
+ strLabel = StringUtils::Format(g_localizeStrings.Get(20112), profile->getDate());
+
+ item->SetLabel2(strLabel);
+ item->SetArt("thumb", profile->getThumb());
+ if (profile->getThumb().empty())
+ item->SetArt("thumb", "DefaultUser.png");
+ item->SetLabelPreformatted(true);
+
+ m_vecItems->Add(item);
+ }
+ m_viewControl.SetItems(*m_vecItems);
+ m_viewControl.SetSelectedItem(m_iSelectedItem);
+}
+
+bool CGUIWindowLoginScreen::OnPopupMenu(int iItem)
+{
+ if (iItem < 0 || iItem >= m_vecItems->Size())
+ return false;
+
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ CFileItemPtr pItem = m_vecItems->Get(iItem);
+ bool bSelect = pItem->IsSelected();
+
+ // mark the item
+ pItem->Select(true);
+
+ CContextButtons choices;
+ choices.Add(1, 20067);
+
+ if (iItem == 0 && g_passwordManager.iMasterLockRetriesLeft == 0)
+ choices.Add(2, 12334);
+
+ int choice = CGUIDialogContextMenu::ShowAndGetChoice(choices);
+ if (choice == 2)
+ {
+ if (g_passwordManager.CheckLock(profileManager->GetMasterProfile().getLockMode(), profileManager->GetMasterProfile().getLockCode(), 20075))
+ g_passwordManager.iMasterLockRetriesLeft = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_MASTERLOCK_MAXRETRIES);
+ else // be inconvenient
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_SHUTDOWN);
+
+ return true;
+ }
+
+ // Edit the profile after checking if the correct master lock password was given.
+ if (choice == 1 && g_passwordManager.IsMasterLockUnlocked(true))
+ CGUIDialogProfileSettings::ShowForProfile(m_viewControl.GetSelectedItem());
+
+ //NOTE: this can potentially (de)select the wrong item if the filelisting has changed because of an action above.
+ if (iItem < static_cast<int>(profileManager->GetNumberOfProfiles()))
+ m_vecItems->Get(iItem)->Select(bSelect);
+
+ return false;
+}
+
+CFileItemPtr CGUIWindowLoginScreen::GetCurrentListItem(int offset)
+{
+ int item = m_viewControl.GetSelectedItem();
+ if (item < 0 || !m_vecItems->Size()) return CFileItemPtr();
+
+ item = (item + offset) % m_vecItems->Size();
+ if (item < 0) item += m_vecItems->Size();
+ return m_vecItems->Get(item);
+}
diff --git a/xbmc/windows/GUIWindowLoginScreen.h b/xbmc/windows/GUIWindowLoginScreen.h
new file mode 100644
index 0000000..500bee7
--- /dev/null
+++ b/xbmc/windows/GUIWindowLoginScreen.h
@@ -0,0 +1,43 @@
+/*
+ * 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 "guilib/GUIDialog.h"
+#include "utils/Stopwatch.h"
+#include "view/GUIViewControl.h"
+
+class CFileItemList;
+
+class CGUIWindowLoginScreen : public CGUIWindow
+{
+public:
+ CGUIWindowLoginScreen(void);
+ ~CGUIWindowLoginScreen(void) override;
+ bool OnMessage(CGUIMessage& message) override;
+ bool OnAction(const CAction &action) override;
+ bool OnBack(int actionID) override;
+ void FrameMove() override;
+ bool HasListItems() const override { return true; }
+ CFileItemPtr GetCurrentListItem(int offset = 0) override;
+ int GetViewContainerID() const override { return m_viewControl.GetCurrentControl(); }
+
+protected:
+ void OnInitWindow() override;
+ void OnWindowLoaded() override;
+ void OnWindowUnload() override;
+ void Update();
+ void SetLabel(int iControl, const std::string& strLabel);
+
+ bool OnPopupMenu(int iItem);
+ CGUIViewControl m_viewControl;
+ CFileItemList* m_vecItems;
+
+ int m_iSelectedItem;
+ CStopWatch watch;
+};
diff --git a/xbmc/windows/GUIWindowPointer.cpp b/xbmc/windows/GUIWindowPointer.cpp
new file mode 100644
index 0000000..d9e1bda
--- /dev/null
+++ b/xbmc/windows/GUIWindowPointer.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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 "GUIWindowPointer.h"
+
+#include "ServiceBroker.h"
+#include "input/InputManager.h"
+#include "input/mouse/MouseStat.h"
+#include "windowing/WinSystem.h"
+
+#define ID_POINTER 10
+
+CGUIWindowPointer::CGUIWindowPointer(void)
+ : CGUIDialog(WINDOW_DIALOG_POINTER, "Pointer.xml", DialogModalityType::MODELESS)
+{
+ m_pointer = 0;
+ m_loadType = LOAD_ON_GUI_INIT;
+ m_needsScaling = false;
+ m_active = false;
+ m_renderOrder = RENDER_ORDER_WINDOW_POINTER;
+}
+
+CGUIWindowPointer::~CGUIWindowPointer(void) = default;
+
+void CGUIWindowPointer::SetPointer(int pointer)
+{
+ if (m_pointer == pointer) return;
+ // set the new pointer visible
+ CGUIControl *pControl = GetControl(pointer);
+ if (pControl)
+ {
+ pControl->SetVisible(true);
+ // disable the old pointer
+ pControl = GetControl(m_pointer);
+ if (pControl) pControl->SetVisible(false);
+ // set pointer to the new one
+ m_pointer = pointer;
+ }
+}
+
+void CGUIWindowPointer::UpdateVisibility()
+{
+ if(CServiceBroker::GetWinSystem()->HasCursor())
+ {
+ if (CServiceBroker::GetInputManager().IsMouseActive())
+ Open();
+ else
+ Close();
+ }
+}
+
+void CGUIWindowPointer::OnWindowLoaded()
+{ // set all our pointer images invisible
+ for (iControls i = m_children.begin();i != m_children.end(); ++i)
+ {
+ CGUIControl* pControl = *i;
+ pControl->SetVisible(false);
+ }
+ CGUIWindow::OnWindowLoaded();
+ DynamicResourceAlloc(false);
+ m_pointer = 0;
+ m_renderOrder = RENDER_ORDER_WINDOW_POINTER;
+}
+
+void CGUIWindowPointer::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
+{
+ bool active = CServiceBroker::GetInputManager().IsMouseActive();
+ if (active != m_active)
+ {
+ MarkDirtyRegion();
+ m_active = active;
+ }
+ MousePosition pos = CServiceBroker::GetInputManager().GetMousePosition();
+ SetPosition((float)pos.x, (float)pos.y);
+ SetPointer(CServiceBroker::GetInputManager().GetMouseState());
+ return CGUIWindow::Process(currentTime, dirtyregions);
+}
diff --git a/xbmc/windows/GUIWindowPointer.h b/xbmc/windows/GUIWindowPointer.h
new file mode 100644
index 0000000..476f0a2
--- /dev/null
+++ b/xbmc/windows/GUIWindowPointer.h
@@ -0,0 +1,27 @@
+/*
+ * 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 "guilib/GUIDialog.h"
+
+class CGUIWindowPointer :
+ public CGUIDialog
+{
+public:
+ CGUIWindowPointer(void);
+ ~CGUIWindowPointer(void) override;
+ void Process(unsigned int currentTime, CDirtyRegionList &dirtyregions) override;
+protected:
+ void SetPointer(int pointer);
+ void OnWindowLoaded() override;
+ void UpdateVisibility() override;
+private:
+ int m_pointer;
+ bool m_active;
+};
diff --git a/xbmc/windows/GUIWindowScreensaver.cpp b/xbmc/windows/GUIWindowScreensaver.cpp
new file mode 100644
index 0000000..76eaa96
--- /dev/null
+++ b/xbmc/windows/GUIWindowScreensaver.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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 "GUIWindowScreensaver.h"
+
+#include "GUIPassword.h"
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "addons/AddonManager.h"
+#include "addons/ScreenSaver.h"
+#include "addons/addoninfo/AddonType.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPowerHandling.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+
+CGUIWindowScreensaver::CGUIWindowScreensaver() : CGUIWindow(WINDOW_SCREENSAVER, "")
+{
+}
+
+void CGUIWindowScreensaver::Process(unsigned int currentTime, CDirtyRegionList& regions)
+{
+ MarkDirtyRegion();
+ CGUIWindow::Process(currentTime, regions);
+ const auto& context = CServiceBroker::GetWinSystem()->GetGfxContext();
+ m_renderRegion.SetRect(0, 0, static_cast<float>(context.GetWidth()),
+ static_cast<float>(context.GetHeight()));
+}
+
+void CGUIWindowScreensaver::Render()
+{
+ if (m_addon)
+ {
+ auto& context = CServiceBroker::GetWinSystem()->GetGfxContext();
+
+ context.CaptureStateBlock();
+ m_addon->Render();
+ context.ApplyStateBlock();
+ return;
+ }
+
+ CGUIWindow::Render();
+}
+
+// called when the mouse is moved/clicked etc. etc.
+EVENT_RESULT CGUIWindowScreensaver::OnMouseEvent(const CPoint& point, const CMouseEvent& event)
+{
+ CServiceBroker::GetGUI()->GetWindowManager().PreviousWindow();
+ return EVENT_RESULT_HANDLED;
+}
+
+bool CGUIWindowScreensaver::OnMessage(CGUIMessage& message)
+{
+ switch (message.GetMessage())
+ {
+ case GUI_MSG_WINDOW_DEINIT:
+ {
+ if (m_addon)
+ {
+ m_addon->Stop();
+ m_addon.reset();
+ }
+
+ CServiceBroker::GetWinSystem()->GetGfxContext().ApplyStateBlock();
+ }
+ break;
+
+ case GUI_MSG_WINDOW_INIT:
+ {
+ CGUIWindow::OnMessage(message);
+
+ CServiceBroker::GetWinSystem()->GetGfxContext().CaptureStateBlock();
+
+ const std::string addon = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
+ CSettings::SETTING_SCREENSAVER_MODE);
+ const ADDON::AddonInfoPtr addonBase =
+ CServiceBroker::GetAddonMgr().GetAddonInfo(addon, ADDON::AddonType::SCREENSAVER);
+ if (!addonBase)
+ return false;
+ m_addon = std::make_unique<KODI::ADDONS::CScreenSaver>(addonBase);
+ return m_addon->Start();
+ }
+
+ case GUI_MSG_CHECK_LOCK:
+ {
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ if (!g_passwordManager.IsProfileLockUnlocked())
+ {
+ appPower->SetScreenSaverLockFailed();
+ return false;
+ }
+ appPower->SetScreenSaverUnlocked();
+ return true;
+ }
+ }
+
+ return CGUIWindow::OnMessage(message);
+}
diff --git a/xbmc/windows/GUIWindowScreensaver.h b/xbmc/windows/GUIWindowScreensaver.h
new file mode 100644
index 0000000..903efde
--- /dev/null
+++ b/xbmc/windows/GUIWindowScreensaver.h
@@ -0,0 +1,42 @@
+/*
+ * 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 "guilib/GUIWindow.h"
+
+#include <memory>
+
+namespace KODI
+{
+namespace ADDONS
+{
+class CScreenSaver;
+} // namespace ADDONS
+} // namespace KODI
+
+class CGUIWindowScreensaver : public CGUIWindow
+{
+public:
+ CGUIWindowScreensaver();
+
+ bool OnMessage(CGUIMessage& message) override;
+ bool OnAction(const CAction& action) override
+ {
+ // We're just a screen saver, nothing to do here
+ return false;
+ }
+ void Render() override;
+ void Process(unsigned int currentTime, CDirtyRegionList& regions) override;
+
+protected:
+ EVENT_RESULT OnMouseEvent(const CPoint& point, const CMouseEvent& event) override;
+
+private:
+ std::unique_ptr<KODI::ADDONS::CScreenSaver> m_addon;
+};
diff --git a/xbmc/windows/GUIWindowScreensaverDim.cpp b/xbmc/windows/GUIWindowScreensaverDim.cpp
new file mode 100644
index 0000000..f617fba
--- /dev/null
+++ b/xbmc/windows/GUIWindowScreensaverDim.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 "GUIWindowScreensaverDim.h"
+
+#include "ServiceBroker.h"
+#include "addons/AddonManager.h"
+#include "addons/IAddon.h"
+#include "addons/addoninfo/AddonType.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPowerHandling.h"
+#include "guilib/GUITexture.h"
+#include "utils/ColorUtils.h"
+#include "windowing/GraphicContext.h"
+
+CGUIWindowScreensaverDim::CGUIWindowScreensaverDim(void)
+ : CGUIDialog(WINDOW_SCREENSAVER_DIM, "", DialogModalityType::MODELESS)
+{
+ m_needsScaling = false;
+ m_animations.push_back(CAnimation::CreateFader(0, 100, 0, 1000, ANIM_TYPE_WINDOW_OPEN));
+ m_animations.push_back(CAnimation::CreateFader(100, 0, 0, 1000, ANIM_TYPE_WINDOW_CLOSE));
+ m_renderOrder = RENDER_ORDER_WINDOW_SCREENSAVER;
+}
+
+void CGUIWindowScreensaverDim::UpdateVisibility()
+{
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ if (appPower->IsInScreenSaver())
+ {
+ if (m_visible)
+ return;
+
+ std::string usedId = appPower->ScreensaverIdInUse();
+ if (usedId == "screensaver.xbmc.builtin.dim" ||
+ usedId == "screensaver.xbmc.builtin.black")
+ {
+ m_visible = true;
+ ADDON::AddonPtr info;
+ bool success = CServiceBroker::GetAddonMgr().GetAddon(
+ usedId, info, ADDON::AddonType::SCREENSAVER, ADDON::OnlyEnabled::CHOICE_YES);
+ if (success && info && !info->GetSetting("level").empty())
+ m_newDimLevel = 100.0f - (float)atof(info->GetSetting("level").c_str());
+ else
+ m_newDimLevel = 100.0f;
+ Open();
+ }
+ }
+ else if (m_visible)
+ {
+ m_visible = false;
+ Close();
+ }
+}
+
+void CGUIWindowScreensaverDim::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
+{
+ if (m_newDimLevel != m_dimLevel && !IsAnimating(ANIM_TYPE_WINDOW_CLOSE))
+ m_dimLevel = m_newDimLevel;
+ CGUIDialog::Process(currentTime, dirtyregions);
+ m_renderRegion.SetRect(0, 0, (float)CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth(), (float)CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight());
+}
+
+void CGUIWindowScreensaverDim::Render()
+{
+ // draw a translucent black quad - fading is handled by the window animation
+ UTILS::COLOR::Color color = (static_cast<UTILS::COLOR::Color>(m_dimLevel * 2.55f) & 0xff) << 24;
+ color = CServiceBroker::GetWinSystem()->GetGfxContext().MergeAlpha(color);
+ CRect rect(0, 0, (float)CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth(), (float)CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight());
+ CGUITexture::DrawQuad(rect, color);
+ CGUIDialog::Render();
+}
diff --git a/xbmc/windows/GUIWindowScreensaverDim.h b/xbmc/windows/GUIWindowScreensaverDim.h
new file mode 100644
index 0000000..c4d5a78
--- /dev/null
+++ b/xbmc/windows/GUIWindowScreensaverDim.h
@@ -0,0 +1,28 @@
+/*
+ * 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 "guilib/GUIDialog.h"
+
+class CGUIWindowScreensaverDim : public CGUIDialog
+{
+public:
+ CGUIWindowScreensaverDim();
+
+ void Process(unsigned int currentTime, CDirtyRegionList &dirtyregions) override;
+ void Render() override;
+
+protected:
+ void UpdateVisibility() override;
+
+private:
+ float m_dimLevel = 100.0f;
+ float m_newDimLevel = 100.0f;
+ bool m_visible = false;
+};
diff --git a/xbmc/windows/GUIWindowSplash.cpp b/xbmc/windows/GUIWindowSplash.cpp
new file mode 100644
index 0000000..dadd61e
--- /dev/null
+++ b/xbmc/windows/GUIWindowSplash.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015-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 "GUIWindowSplash.h"
+
+#include "Util.h"
+#include "guilib/GUIImage.h"
+#include "guilib/GUIWindowManager.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/SettingsComponent.h"
+
+CGUIWindowSplash::CGUIWindowSplash(void) : CGUIWindow(WINDOW_SPLASH, ""), m_image(nullptr)
+{
+ m_loadType = LOAD_ON_GUI_INIT;
+}
+
+CGUIWindowSplash::~CGUIWindowSplash(void) = default;
+
+void CGUIWindowSplash::OnInitWindow()
+{
+ if (!CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_splashImage)
+ return;
+
+ m_image = std::unique_ptr<CGUIImage>(new CGUIImage(0, 0, 0, 0, CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth(), CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight(), CTextureInfo(CUtil::GetSplashPath())));
+ m_image->SetAspectRatio(CAspectRatio::AR_SCALE);
+}
+
+void CGUIWindowSplash::Render()
+{
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetRenderingResolution(CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(), true);
+
+ if (!m_image)
+ return;
+
+ m_image->SetWidth(CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth());
+ m_image->SetHeight(CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight());
+ m_image->AllocResources();
+ m_image->Render();
+ m_image->FreeResources();
+}
diff --git a/xbmc/windows/GUIWindowSplash.h b/xbmc/windows/GUIWindowSplash.h
new file mode 100644
index 0000000..f5f54ee
--- /dev/null
+++ b/xbmc/windows/GUIWindowSplash.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015-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 "guilib/GUIWindow.h"
+
+#include <memory>
+
+class CGUITextLayout;
+class CGUIImage;
+
+class CGUIWindowSplash : public CGUIWindow
+{
+public:
+ CGUIWindowSplash(void);
+ ~CGUIWindowSplash(void) override;
+ bool OnAction(const CAction& action) override { return false; }
+ void Render() override;
+protected:
+ void OnInitWindow() override;
+private:
+ std::unique_ptr<CGUIImage> m_image;
+};
diff --git a/xbmc/windows/GUIWindowStartup.cpp b/xbmc/windows/GUIWindowStartup.cpp
new file mode 100644
index 0000000..ffa3d79
--- /dev/null
+++ b/xbmc/windows/GUIWindowStartup.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 "GUIWindowStartup.h"
+
+#include "ServiceBroker.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/WindowIDs.h"
+#include "input/Key.h"
+
+CGUIWindowStartup::CGUIWindowStartup(void)
+ : CGUIWindow(WINDOW_STARTUP_ANIM, "Startup.xml")
+{
+}
+
+CGUIWindowStartup::~CGUIWindowStartup(void) = default;
+
+bool CGUIWindowStartup::OnAction(const CAction &action)
+{
+ if (action.IsMouse())
+ return true;
+ return CGUIWindow::OnAction(action);
+}
+
+void CGUIWindowStartup::OnDeinitWindow(int nextWindowID)
+{
+ CGUIWindow::OnDeinitWindow(nextWindowID);
+
+ // let everyone know that the user interface is now ready for usage
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UI_READY);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
+}
diff --git a/xbmc/windows/GUIWindowStartup.h b/xbmc/windows/GUIWindowStartup.h
new file mode 100644
index 0000000..c2e35ad
--- /dev/null
+++ b/xbmc/windows/GUIWindowStartup.h
@@ -0,0 +1,23 @@
+/*
+ * 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 "guilib/GUIWindow.h"
+
+class CGUIWindowStartup :
+ public CGUIWindow
+{
+public:
+ CGUIWindowStartup(void);
+ ~CGUIWindowStartup(void) override;
+ bool OnAction(const CAction &action) override;
+
+ // specialization of CGUIWindow
+ void OnDeinitWindow(int nextWindowID) override;
+};
diff --git a/xbmc/windows/GUIWindowSystemInfo.cpp b/xbmc/windows/GUIWindowSystemInfo.cpp
new file mode 100644
index 0000000..157b7b8
--- /dev/null
+++ b/xbmc/windows/GUIWindowSystemInfo.cpp
@@ -0,0 +1,260 @@
+/*
+ * 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 "GUIWindowSystemInfo.h"
+
+#include "GUIInfoManager.h"
+#include "ServiceBroker.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIMessage.h"
+#include "guilib/LocalizeStrings.h"
+#include "guilib/WindowIDs.h"
+#include "guilib/guiinfo/GUIInfoLabels.h"
+#include "pvr/PVRManager.h"
+#include "storage/MediaManager.h"
+#include "utils/CPUInfo.h"
+#include "utils/StringUtils.h"
+#include "utils/SystemInfo.h"
+
+#define CONTROL_TB_POLICY 30
+#define CONTROL_BT_STORAGE 94
+#define CONTROL_BT_DEFAULT 95
+#define CONTROL_BT_NETWORK 96
+#define CONTROL_BT_VIDEO 97
+#define CONTROL_BT_HARDWARE 98
+#define CONTROL_BT_PVR 99
+#define CONTROL_BT_POLICY 100
+
+#define CONTROL_START CONTROL_BT_STORAGE
+#define CONTROL_END CONTROL_BT_POLICY
+
+CGUIWindowSystemInfo::CGUIWindowSystemInfo(void) :
+ CGUIWindow(WINDOW_SYSTEM_INFORMATION, "SettingsSystemInfo.xml")
+{
+ m_section = CONTROL_BT_DEFAULT;
+ m_loadType = KEEP_IN_MEMORY;
+}
+
+CGUIWindowSystemInfo::~CGUIWindowSystemInfo(void) = default;
+
+bool CGUIWindowSystemInfo::OnMessage(CGUIMessage& message)
+{
+ switch (message.GetMessage())
+ {
+ case GUI_MSG_WINDOW_INIT:
+ {
+ CGUIWindow::OnMessage(message);
+ SET_CONTROL_LABEL(52, CSysInfo::GetAppName() + " " + CSysInfo::GetVersion());
+ SET_CONTROL_LABEL(53, CSysInfo::GetBuildDate());
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BT_PVR, CServiceBroker::GetPVRManager().IsStarted());
+ return true;
+ }
+ break;
+
+ case GUI_MSG_WINDOW_DEINIT:
+ {
+ CGUIWindow::OnMessage(message);
+ m_diskUsage.clear();
+ return true;
+ }
+ break;
+
+ case GUI_MSG_FOCUSED:
+ {
+ CGUIWindow::OnMessage(message);
+ int focusedControl = GetFocusedControlID();
+ if (m_section != focusedControl && focusedControl >= CONTROL_START && focusedControl <= CONTROL_END)
+ {
+ ResetLabels();
+ m_section = focusedControl;
+ }
+ if (m_section >= CONTROL_BT_STORAGE && m_section <= CONTROL_BT_PVR)
+ SET_CONTROL_HIDDEN(CONTROL_TB_POLICY);
+ else if (m_section == CONTROL_BT_POLICY)
+ {
+ SET_CONTROL_LABEL(CONTROL_TB_POLICY, CServiceBroker::GetGUI()->GetInfoManager().GetLabel(
+ SYSTEM_PRIVACY_POLICY, INFO::DEFAULT_CONTEXT));
+ SET_CONTROL_VISIBLE(CONTROL_TB_POLICY);
+ }
+ return true;
+ }
+ break;
+ }
+ return CGUIWindow::OnMessage(message);
+}
+
+void CGUIWindowSystemInfo::FrameMove()
+{
+ int i = 2;
+ if (m_section == CONTROL_BT_DEFAULT)
+ {
+ SET_CONTROL_LABEL(40, g_localizeStrings.Get(20154));
+ SetControlLabel(i++, "{}: {}", 158, SYSTEM_FREE_MEMORY);
+ SetControlLabel(i++, "{}: {}", 150, NETWORK_IP_ADDRESS);
+ SetControlLabel(i++, "{} {}", 13287, SYSTEM_SCREEN_RESOLUTION);
+ SetControlLabel(i++, "{} {}", 13283, SYSTEM_OS_VERSION_INFO);
+ SetControlLabel(i++, "{}: {}", 12390, SYSTEM_UPTIME);
+ SetControlLabel(i++, "{}: {}", 12394, SYSTEM_TOTALUPTIME);
+ SetControlLabel(i++, "{}: {}", 12395, SYSTEM_BATTERY_LEVEL);
+ }
+
+ else if (m_section == CONTROL_BT_STORAGE)
+ {
+ SET_CONTROL_LABEL(40, g_localizeStrings.Get(20155));
+ if (m_diskUsage.empty())
+ m_diskUsage = CServiceBroker::GetMediaManager().GetDiskUsage();
+
+ for (size_t d = 0; d < m_diskUsage.size(); d++)
+ {
+ SET_CONTROL_LABEL(i++, m_diskUsage[d]);
+ }
+ }
+
+ else if (m_section == CONTROL_BT_NETWORK)
+ {
+ SET_CONTROL_LABEL(40,g_localizeStrings.Get(20158));
+ SET_CONTROL_LABEL(i++, CServiceBroker::GetGUI()->GetInfoManager().GetLabel(
+ NETWORK_LINK_STATE, INFO::DEFAULT_CONTEXT));
+ SetControlLabel(i++, "{}: {}", 149, NETWORK_MAC_ADDRESS);
+ SetControlLabel(i++, "{}: {}", 150, NETWORK_IP_ADDRESS);
+ SetControlLabel(i++, "{}: {}", 13159, NETWORK_SUBNET_MASK);
+ SetControlLabel(i++, "{}: {}", 13160, NETWORK_GATEWAY_ADDRESS);
+ SetControlLabel(i++, "{}: {}", 13161, NETWORK_DNS1_ADDRESS);
+ SetControlLabel(i++, "{}: {}", 20307, NETWORK_DNS2_ADDRESS);
+ SetControlLabel(i++, "{} {}", 13295, SYSTEM_INTERNET_STATE);
+ }
+
+ else if (m_section == CONTROL_BT_VIDEO)
+ {
+ SET_CONTROL_LABEL(40,g_localizeStrings.Get(20159));
+ SET_CONTROL_LABEL(i++, CServiceBroker::GetGUI()->GetInfoManager().GetLabel(
+ SYSTEM_VIDEO_ENCODER_INFO, INFO::DEFAULT_CONTEXT));
+ SetControlLabel(i++, "{} {}", 13287, SYSTEM_SCREEN_RESOLUTION);
+
+ auto renderingSystem = CServiceBroker::GetRenderSystem();
+ if (renderingSystem)
+ {
+ static std::string vendor = renderingSystem->GetRenderVendor();
+ if (!vendor.empty())
+ SET_CONTROL_LABEL(i++, StringUtils::Format("{} {}", g_localizeStrings.Get(22007), vendor));
+
+#if defined(HAS_DX)
+ int renderVersionLabel = 22024;
+#else
+ int renderVersionLabel = 22009;
+#endif
+ static std::string version = renderingSystem->GetRenderVersionString();
+ if (!version.empty())
+ SET_CONTROL_LABEL(
+ i++, StringUtils::Format("{} {}", g_localizeStrings.Get(renderVersionLabel), version));
+ }
+
+ auto windowSystem = CServiceBroker::GetWinSystem();
+ if (windowSystem)
+ {
+ static std::string platform = windowSystem->GetName();
+ if (platform != "platform default")
+ SET_CONTROL_LABEL(i++,
+ StringUtils::Format("{} {}", g_localizeStrings.Get(39153), platform));
+ }
+
+ SetControlLabel(i++, "{} {}", 22010, SYSTEM_GPU_TEMPERATURE);
+
+ const std::string hdrTypes = CServiceBroker::GetGUI()->GetInfoManager().GetLabel(
+ SYSTEM_SUPPORTED_HDR_TYPES, INFO::DEFAULT_CONTEXT);
+ SET_CONTROL_LABEL(
+ i++, StringUtils::Format("{}: {}", g_localizeStrings.Get(39174),
+ hdrTypes.empty() ? g_localizeStrings.Get(231) : hdrTypes));
+ }
+
+ else if (m_section == CONTROL_BT_HARDWARE)
+ {
+ SET_CONTROL_LABEL(40,g_localizeStrings.Get(20160));
+
+ auto cpuInfo = CServiceBroker::GetCPUInfo();
+ if (cpuInfo)
+ {
+ static std::string model = cpuInfo->GetCPUModel();
+ if (!model.empty())
+ SET_CONTROL_LABEL(i++, "CPU: " + model);
+
+ static std::string mips = cpuInfo->GetCPUBogoMips();
+ if (!mips.empty())
+ SET_CONTROL_LABEL(i++, "BogoMips: " + mips);
+
+ static std::string soc = cpuInfo->GetCPUSoC();
+ if (!soc.empty())
+ SET_CONTROL_LABEL(i++, "SoC: " + soc);
+
+ static std::string hardware = cpuInfo->GetCPUHardware();
+ if (!hardware.empty())
+ SET_CONTROL_LABEL(i++, "Hardware: " + hardware);
+
+ static std::string revision = cpuInfo->GetCPURevision();
+ if (!revision.empty())
+ SET_CONTROL_LABEL(i++, "Revision: " + revision);
+
+ static std::string serial = cpuInfo->GetCPUSerial();
+ if (!serial.empty())
+ SET_CONTROL_LABEL(i++, "Serial: " + serial);
+
+ // temperature can't really be conditional because of localization units
+ SetControlLabel(i++, "{} {}", 22011, SYSTEM_CPU_TEMPERATURE);
+
+ // we can check if the cpufrequency is not 0 (default if not implemented)
+ // but we have to call through CGUIInfoManager -> CSystemGUIInfo -> CSysInfo
+ // to limit the frequency of updates
+ static float cpuFreq = cpuInfo->GetCPUFrequency();
+ if (cpuFreq > 0)
+ SetControlLabel(i++, "{} {}", 13284, SYSTEM_CPUFREQUENCY);
+ }
+ }
+
+ else if (m_section == CONTROL_BT_PVR)
+ {
+ SET_CONTROL_LABEL(40, g_localizeStrings.Get(19166));
+ int i = 2;
+
+ SetControlLabel(i++, "{}: {}", 19120, PVR_BACKEND_NUMBER);
+ i++; // empty line
+ SetControlLabel(i++, "{}: {}", 19012, PVR_BACKEND_NAME);
+ SetControlLabel(i++, "{}: {}", 19114, PVR_BACKEND_VERSION);
+ SetControlLabel(i++, "{}: {}", 19115, PVR_BACKEND_HOST);
+ SetControlLabel(i++, "{}: {}", 19116, PVR_BACKEND_DISKSPACE);
+ SetControlLabel(i++, "{}: {}", 19334, PVR_BACKEND_PROVIDERS);
+ SetControlLabel(i++, "{}: {}", 19042, PVR_BACKEND_CHANNEL_GROUPS);
+ SetControlLabel(i++, "{}: {}", 19019, PVR_BACKEND_CHANNELS);
+ SetControlLabel(i++, "{}: {}", 19163, PVR_BACKEND_RECORDINGS);
+ SetControlLabel(i++, "{}: {}", 19168,
+ PVR_BACKEND_DELETED_RECORDINGS); // Deleted and recoverable recordings
+ SetControlLabel(i++, "{}: {}", 19025, PVR_BACKEND_TIMERS);
+ }
+
+ else if (m_section == CONTROL_BT_POLICY)
+ {
+ SET_CONTROL_LABEL(40, g_localizeStrings.Get(12389));
+ }
+ CGUIWindow::FrameMove();
+}
+
+void CGUIWindowSystemInfo::ResetLabels()
+{
+ for (int i = 2; i < 13; i++)
+ {
+ SET_CONTROL_LABEL(i, "");
+ }
+ SET_CONTROL_LABEL(CONTROL_TB_POLICY, "");
+}
+
+void CGUIWindowSystemInfo::SetControlLabel(int id, const char *format, int label, int info)
+{
+ std::string tmpStr = StringUtils::Format(
+ format, g_localizeStrings.Get(label),
+ CServiceBroker::GetGUI()->GetInfoManager().GetLabel(info, INFO::DEFAULT_CONTEXT));
+ SET_CONTROL_LABEL(id, tmpStr);
+}
diff --git a/xbmc/windows/GUIWindowSystemInfo.h b/xbmc/windows/GUIWindowSystemInfo.h
new file mode 100644
index 0000000..e2fc50a
--- /dev/null
+++ b/xbmc/windows/GUIWindowSystemInfo.h
@@ -0,0 +1,29 @@
+/*
+ * 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 "guilib/GUIWindow.h"
+
+#include <string>
+#include <vector>
+
+class CGUIWindowSystemInfo : public CGUIWindow
+{
+public:
+ CGUIWindowSystemInfo(void);
+ ~CGUIWindowSystemInfo(void) override;
+ bool OnMessage(CGUIMessage& message) override;
+ void FrameMove() override;
+private:
+ int m_section;
+ void ResetLabels();
+ void SetControlLabel(int id, const char *format, int label, int info);
+ std::vector<std::string> m_diskUsage;
+};
+