summaryrefslogtreecommitdiffstats
path: root/xbmc/dialogs/GUIDialogContextMenu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/dialogs/GUIDialogContextMenu.cpp')
-rw-r--r--xbmc/dialogs/GUIDialogContextMenu.cpp705
1 files changed, 705 insertions, 0 deletions
diff --git a/xbmc/dialogs/GUIDialogContextMenu.cpp b/xbmc/dialogs/GUIDialogContextMenu.cpp
new file mode 100644
index 0000000..dcc9ede
--- /dev/null
+++ b/xbmc/dialogs/GUIDialogContextMenu.cpp
@@ -0,0 +1,705 @@
+/*
+ * Copyright (C) 2005-2020 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 "GUIDialogContextMenu.h"
+
+#include "FileItem.h"
+#include "GUIDialogFileBrowser.h"
+#include "GUIDialogMediaSource.h"
+#include "GUIDialogYesNo.h"
+#include "GUIPassword.h"
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "TextureDatabase.h"
+#include "URL.h"
+#include "Util.h"
+#include "addons/Scraper.h"
+#include "favourites/FavouritesService.h"
+#include "guilib/GUIButtonControl.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIControlGroupList.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "input/Key.h"
+#include "media/MediaLockState.h"
+#include "profiles/ProfileManager.h"
+#include "profiles/dialogs/GUIDialogLockSettings.h"
+#include "settings/MediaSourceSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "storage/MediaManager.h"
+#include "utils/FileUtils.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+
+#define BACKGROUND_IMAGE 999
+#define GROUP_LIST 996
+#define BUTTON_TEMPLATE 1000
+#define BUTTON_START 1001
+#define BUTTON_END (BUTTON_START + (int)m_buttons.size() - 1)
+
+void CContextButtons::Add(unsigned int button, const std::string &label)
+{
+ for (const auto& i : *this)
+ if (i.first == button)
+ return; // already have this button
+ push_back(std::pair<unsigned int, std::string>(button, label));
+}
+
+void CContextButtons::Add(unsigned int button, int label)
+{
+ for (const auto& i : *this)
+ if (i.first == button)
+ return; // already have added this button
+ push_back(std::pair<unsigned int, std::string>(button, g_localizeStrings.Get(label)));
+}
+
+CGUIDialogContextMenu::CGUIDialogContextMenu(void)
+ : CGUIDialog(WINDOW_DIALOG_CONTEXT_MENU, "DialogContextMenu.xml")
+{
+ m_clickedButton = -1;
+ m_backgroundImageSize = 0;
+ m_loadType = KEEP_IN_MEMORY;
+ m_coordX = 0.0f;
+ m_coordY = 0.0f;
+}
+
+CGUIDialogContextMenu::~CGUIDialogContextMenu(void) = default;
+
+bool CGUIDialogContextMenu::OnMessage(CGUIMessage &message)
+{
+ if (message.GetMessage() == GUI_MSG_CLICKED)
+ { // someone has been clicked - deinit...
+ if (message.GetSenderId() >= BUTTON_START && message.GetSenderId() <= BUTTON_END)
+ m_clickedButton = message.GetSenderId() - BUTTON_START;
+ Close();
+ return true;
+ }
+ else if (message.GetMessage() == GUI_MSG_PLAYBACK_AVSTARTED)
+ {
+ // playback was just started from elsewhere - close the dialog
+ Close();
+ return true;
+ }
+ return CGUIDialog::OnMessage(message);
+}
+
+bool CGUIDialogContextMenu::OnAction(const CAction& action)
+{
+ if (action.GetID() == ACTION_CONTEXT_MENU ||
+ action.GetID() == ACTION_SWITCH_PLAYER)
+ {
+ Close();
+ return true;
+ }
+
+ return CGUIDialog::OnAction(action);
+}
+
+void CGUIDialogContextMenu::OnInitWindow()
+{
+ m_clickedButton = -1;
+ // set initial control focus
+ m_lastControlID = m_initiallyFocusedButtonIdx + BUTTON_START;
+ CGUIDialog::OnInitWindow();
+}
+
+void CGUIDialogContextMenu::SetupButtons()
+{
+ if (!m_buttons.size())
+ return;
+
+ // disable the template button control
+ CGUIButtonControl *pButtonTemplate = dynamic_cast<CGUIButtonControl *>(GetFirstFocusableControl(BUTTON_TEMPLATE));
+ if (!pButtonTemplate)
+ pButtonTemplate = dynamic_cast<CGUIButtonControl *>(GetControl(BUTTON_TEMPLATE));
+ if (!pButtonTemplate)
+ return;
+ pButtonTemplate->SetVisible(false);
+
+ CGUIControlGroupList* pGroupList = dynamic_cast<CGUIControlGroupList *>(GetControl(GROUP_LIST));
+
+ // add our buttons
+ if (pGroupList)
+ {
+ for (unsigned int i = 0; i < m_buttons.size(); i++)
+ {
+ CGUIButtonControl* pButton = new CGUIButtonControl(*pButtonTemplate);
+ if (pButton)
+ { // set the button's ID and position
+ int id = BUTTON_START + i;
+ pButton->SetID(id);
+ pButton->SetVisible(true);
+ pButton->SetLabel(m_buttons[i].second);
+ pButton->SetPosition(pButtonTemplate->GetXPosition(), pButtonTemplate->GetYPosition());
+ // try inserting context buttons at position specified by template
+ // button, if template button is not in grouplist fallback to adding
+ // new buttons at the end of grouplist
+ if (!pGroupList->InsertControl(pButton, pButtonTemplate))
+ pGroupList->AddControl(pButton);
+ }
+ }
+ }
+
+ // fix up background images placement and size
+ CGUIControl *pControl = GetControl(BACKGROUND_IMAGE);
+ if (pControl)
+ {
+ // first set size of background image
+ if (pGroupList)
+ {
+ if (pGroupList->GetOrientation() == VERTICAL)
+ {
+ // keep gap between bottom edges of grouplist and background image
+ pControl->SetHeight(m_backgroundImageSize - pGroupList->Size() + pGroupList->GetHeight());
+ }
+ else
+ {
+ // keep gap between right edges of grouplist and background image
+ pControl->SetWidth(m_backgroundImageSize - pGroupList->Size() + pGroupList->GetWidth());
+ }
+ }
+ }
+
+ // update our default control
+ if (pGroupList)
+ m_defaultControl = pGroupList->GetID();
+}
+
+void CGUIDialogContextMenu::SetPosition(float posX, float posY)
+{
+ if (posY + GetHeight() > m_coordsRes.iHeight)
+ posY = m_coordsRes.iHeight - GetHeight();
+ if (posY < 0) posY = 0;
+ if (posX + GetWidth() > m_coordsRes.iWidth)
+ posX = m_coordsRes.iWidth - GetWidth();
+ if (posX < 0) posX = 0;
+ CGUIDialog::SetPosition(posX, posY);
+}
+
+float CGUIDialogContextMenu::GetHeight() const
+{
+ if (m_backgroundImage)
+ return m_backgroundImage->GetHeight();
+ else
+ return CGUIDialog::GetHeight();
+}
+
+float CGUIDialogContextMenu::GetWidth() const
+{
+ if (m_backgroundImage)
+ return m_backgroundImage->GetWidth();
+ else
+ return CGUIDialog::GetWidth();
+}
+
+bool CGUIDialogContextMenu::SourcesMenu(const std::string &strType, const CFileItemPtr& item, float posX, float posY)
+{
+ //! @todo This should be callable even if we don't have any valid items
+ if (!item)
+ return false;
+
+ // grab our context menu
+ CContextButtons buttons;
+ GetContextButtons(strType, item, buttons);
+
+ int button = ShowAndGetChoice(buttons);
+ if (button >= 0)
+ return OnContextButton(strType, item, (CONTEXT_BUTTON)button);
+ return false;
+}
+
+void CGUIDialogContextMenu::GetContextButtons(const std::string &type, const CFileItemPtr& item, CContextButtons &buttons)
+{
+ // Add buttons to the ContextMenu that should be visible for both sources and autosourced items
+ // Optical removable drives automatically have the static Eject button added (see CEjectDisk).
+ // Here we only add the eject button to HDD drives
+ if (item && item->IsRemovable() && !item->IsDVD() && !item->IsCDDA())
+ {
+ buttons.Add(CONTEXT_BUTTON_EJECT_DRIVE, 13420); // Remove safely
+ }
+
+ // Next, Add buttons to the ContextMenu that should ONLY be visible for sources and not autosourced items
+ CMediaSource *share = GetShare(type, item.get());
+
+ if (CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetCurrentProfile().canWriteSources() || g_passwordManager.bMasterUser)
+ {
+ if (share)
+ {
+ // Note. from now on, remove source & disable plugin should mean the same thing
+ //! @todo might be smart to also combine editing source & plugin settings into one concept/dialog
+ // Note. Temporarily disabled ability to remove plugin sources until installer is operational
+
+ CURL url(share->strPath);
+ bool isAddon = ADDON::TranslateContent(url.GetProtocol()) != CONTENT_NONE;
+ if (!share->m_ignore && !isAddon)
+ buttons.Add(CONTEXT_BUTTON_EDIT_SOURCE, 1027); // Edit Source
+ if (type != "video")
+ buttons.Add(CONTEXT_BUTTON_SET_DEFAULT, 13335); // Set as Default
+ if (!share->m_ignore && !isAddon)
+ buttons.Add(CONTEXT_BUTTON_REMOVE_SOURCE, 522); // Remove Source
+
+ buttons.Add(CONTEXT_BUTTON_SET_THUMB, 20019);
+ }
+ if (!GetDefaultShareNameByType(type).empty())
+ buttons.Add(CONTEXT_BUTTON_CLEAR_DEFAULT, 13403); // Clear Default
+ }
+ if (share && LOCK_MODE_EVERYONE != CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetMasterProfile().getLockMode())
+ {
+ if (share->m_iHasLock == LOCK_STATE_NO_LOCK && (CServiceBroker::GetSettingsComponent()
+ ->GetProfileManager()
+ ->GetCurrentProfile()
+ .canWriteSources() ||
+ g_passwordManager.bMasterUser))
+ buttons.Add(CONTEXT_BUTTON_ADD_LOCK, 12332);
+ else if (share->m_iHasLock == LOCK_STATE_LOCK_BUT_UNLOCKED)
+ buttons.Add(CONTEXT_BUTTON_REMOVE_LOCK, 12335);
+ else if (share->m_iHasLock == LOCK_STATE_LOCKED)
+ {
+ buttons.Add(CONTEXT_BUTTON_REMOVE_LOCK, 12335);
+
+ bool maxRetryExceeded = false;
+ if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_MASTERLOCK_MAXRETRIES) != 0)
+ maxRetryExceeded = (share->m_iBadPwdCount >= CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_MASTERLOCK_MAXRETRIES));
+
+ if (maxRetryExceeded)
+ buttons.Add(CONTEXT_BUTTON_RESET_LOCK, 12334);
+ else
+ buttons.Add(CONTEXT_BUTTON_CHANGE_LOCK, 12356);
+ }
+ }
+ if (share && !g_passwordManager.bMasterUser && item->m_iHasLock == LOCK_STATE_LOCK_BUT_UNLOCKED)
+ buttons.Add(CONTEXT_BUTTON_REACTIVATE_LOCK, 12353);
+}
+
+bool CGUIDialogContextMenu::OnContextButton(const std::string &type, const CFileItemPtr& item, CONTEXT_BUTTON button)
+{
+ // buttons that are available on both sources and autosourced items
+ if (!item)
+ return false;
+
+ switch (button)
+ {
+ case CONTEXT_BUTTON_EJECT_DRIVE:
+ return CServiceBroker::GetMediaManager().Eject(item->GetPath());
+ default:
+ break;
+ }
+
+ // the rest of the operations require a valid share
+ CMediaSource *share = GetShare(type, item.get());
+ if (!share)
+ return false;
+
+ switch (button)
+ {
+ case CONTEXT_BUTTON_EDIT_SOURCE:
+ if (CServiceBroker::GetSettingsComponent()->GetProfileManager()->IsMasterProfile())
+ {
+ if (!g_passwordManager.IsMasterLockUnlocked(true))
+ return false;
+ }
+ else if (!g_passwordManager.IsProfileLockUnlocked())
+ return false;
+
+ return CGUIDialogMediaSource::ShowAndEditMediaSource(type, *share);
+
+ case CONTEXT_BUTTON_REMOVE_SOURCE:
+ {
+ if (CServiceBroker::GetSettingsComponent()->GetProfileManager()->IsMasterProfile())
+ {
+ if (!g_passwordManager.IsMasterLockUnlocked(true))
+ return false;
+ }
+ else
+ {
+ if (!CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetCurrentProfile().canWriteSources() && !g_passwordManager.IsMasterLockUnlocked(false))
+ return false;
+ if (CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked())
+ return false;
+ }
+ // prompt user if they want to really delete the source
+ if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{751}, CVariant{750}))
+ return false;
+
+ // check default before we delete, as deletion will kill the share object
+ std::string defaultSource(GetDefaultShareNameByType(type));
+ if (!defaultSource.empty())
+ {
+ if (share->strName == defaultSource)
+ ClearDefault(type);
+ }
+ CMediaSourceSettings::GetInstance().DeleteSource(type, share->strName, share->strPath);
+ return true;
+ }
+ case CONTEXT_BUTTON_SET_DEFAULT:
+ if (CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked())
+ return false;
+ else if (!g_passwordManager.IsMasterLockUnlocked(true))
+ return false;
+
+ // make share default
+ SetDefault(type, share->strName);
+ return true;
+
+ case CONTEXT_BUTTON_CLEAR_DEFAULT:
+ if (CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked())
+ return false;
+ else if (!g_passwordManager.IsMasterLockUnlocked(true))
+ return false;
+ // remove share default
+ ClearDefault(type);
+ return true;
+
+ case CONTEXT_BUTTON_SET_THUMB:
+ {
+ if (CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked())
+ return false;
+ else if (!g_passwordManager.IsMasterLockUnlocked(true))
+ return false;
+
+ // setup our thumb list
+ CFileItemList items;
+
+ // add the current thumb, if available
+ if (!share->m_strThumbnailImage.empty())
+ {
+ CFileItemPtr current(new CFileItem("thumb://Current", false));
+ current->SetArt("thumb", share->m_strThumbnailImage);
+ current->SetLabel(g_localizeStrings.Get(20016));
+ items.Add(current);
+ }
+ else if (item->HasArt("thumb"))
+ { // already have a thumb that the share doesn't know about - must be a local one, so we mayaswell reuse it.
+ CFileItemPtr current(new CFileItem("thumb://Current", false));
+ current->SetArt("thumb", item->GetArt("thumb"));
+ current->SetLabel(g_localizeStrings.Get(20016));
+ items.Add(current);
+ }
+ // see if there's a local thumb for this item
+ std::string folderThumb = item->GetFolderThumb();
+ if (CFileUtils::Exists(folderThumb))
+ {
+ CFileItemPtr local(new CFileItem("thumb://Local", false));
+ local->SetArt("thumb", folderThumb);
+ local->SetLabel(g_localizeStrings.Get(20017));
+ items.Add(local);
+ }
+ // and add a "no thumb" entry as well
+ CFileItemPtr nothumb(new CFileItem("thumb://None", false));
+ nothumb->SetArt("icon", item->GetArt("icon"));
+ nothumb->SetLabel(g_localizeStrings.Get(20018));
+ items.Add(nothumb);
+
+ std::string strThumb;
+ VECSOURCES shares;
+ CServiceBroker::GetMediaManager().GetLocalDrives(shares);
+ if (!CGUIDialogFileBrowser::ShowAndGetImage(items, shares, g_localizeStrings.Get(1030), strThumb))
+ return false;
+
+ if (strThumb == "thumb://Current")
+ return true;
+
+ if (strThumb == "thumb://Local")
+ strThumb = folderThumb;
+
+ if (strThumb == "thumb://None")
+ strThumb = "";
+
+ if (!share->m_ignore)
+ {
+ CMediaSourceSettings::GetInstance().UpdateSource(type,share->strName,"thumbnail",strThumb);
+ CMediaSourceSettings::GetInstance().Save();
+ }
+ else if (!strThumb.empty())
+ { // this is some sort of an auto-share, so store in the texture database
+ CTextureDatabase db;
+ if (db.Open())
+ db.SetTextureForPath(item->GetPath(), "thumb", strThumb);
+ }
+
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
+ return true;
+ }
+
+ case CONTEXT_BUTTON_ADD_LOCK:
+ {
+ // prompt user for mastercode when changing lock settings) only for default user
+ if (!g_passwordManager.IsMasterLockUnlocked(true))
+ return false;
+
+ std::string strNewPassword = "";
+ if (!CGUIDialogLockSettings::ShowAndGetLock(share->m_iLockMode,strNewPassword))
+ return false;
+ // password entry and re-entry succeeded, write out the lock data
+ share->m_iHasLock = LOCK_STATE_LOCKED;
+ CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockcode", strNewPassword);
+ strNewPassword = std::to_string(share->m_iLockMode);
+ CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockmode", strNewPassword);
+ CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "badpwdcount", "0");
+ CMediaSourceSettings::GetInstance().Save();
+
+ // lock of a mediasource has been added
+ // => refresh favourites due to possible visibility changes
+ CServiceBroker::GetFavouritesService().RefreshFavourites();
+
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
+ return true;
+ }
+ case CONTEXT_BUTTON_RESET_LOCK:
+ {
+ // prompt user for profile lock when changing lock settings
+ if (!g_passwordManager.IsMasterLockUnlocked(true))
+ return false;
+
+ CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "badpwdcount", "0");
+ CMediaSourceSettings::GetInstance().Save();
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
+ return true;
+ }
+ case CONTEXT_BUTTON_REMOVE_LOCK:
+ {
+ if (!g_passwordManager.IsMasterLockUnlocked(true))
+ return false;
+
+ // prompt user if they want to really remove the lock
+ if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{12335}, CVariant{750}))
+ return false;
+
+ share->m_iHasLock = LOCK_STATE_NO_LOCK;
+ CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockmode", "0");
+ CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockcode", "0");
+ CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "badpwdcount", "0");
+ CMediaSourceSettings::GetInstance().Save();
+
+ // lock of a mediasource has been removed
+ // => refresh favourites due to possible visibility changes
+ CServiceBroker::GetFavouritesService().RefreshFavourites();
+
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
+ return true;
+ }
+ case CONTEXT_BUTTON_REACTIVATE_LOCK:
+ {
+ bool maxRetryExceeded = false;
+ if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_MASTERLOCK_MAXRETRIES) != 0)
+ maxRetryExceeded = (share->m_iBadPwdCount >= CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_MASTERLOCK_MAXRETRIES));
+ if (!maxRetryExceeded)
+ {
+ // don't prompt user for mastercode when reactivating a lock
+ g_passwordManager.LockSource(type, share->strName, true);
+
+ // lock of a mediasource has been reactivated
+ // => refresh favourites due to possible visibility changes
+ CServiceBroker::GetFavouritesService().RefreshFavourites();
+ return true;
+ }
+ return false;
+ }
+ case CONTEXT_BUTTON_CHANGE_LOCK:
+ {
+ if (!g_passwordManager.IsMasterLockUnlocked(true))
+ return false;
+
+ std::string strNewPW;
+ std::string strNewLockMode;
+ if (CGUIDialogLockSettings::ShowAndGetLock(share->m_iLockMode,strNewPW))
+ strNewLockMode = std::to_string(share->m_iLockMode);
+ else
+ return false;
+ // password ReSet and re-entry succeeded, write out the lock data
+ CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockcode", strNewPW);
+ CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockmode", strNewLockMode);
+ CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "badpwdcount", "0");
+ CMediaSourceSettings::GetInstance().Save();
+
+ // lock of a mediasource has been changed
+ // => refresh favourites due to possible visibility changes
+ CServiceBroker::GetFavouritesService().RefreshFavourites();
+
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
+ return true;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+CMediaSource *CGUIDialogContextMenu::GetShare(const std::string &type, const CFileItem *item)
+{
+ VECSOURCES *shares = CMediaSourceSettings::GetInstance().GetSources(type);
+ if (!shares || !item)
+ return nullptr;
+ for (unsigned int i = 0; i < shares->size(); i++)
+ {
+ CMediaSource &testShare = shares->at(i);
+ if (URIUtils::IsDVD(testShare.strPath))
+ {
+ if (!item->IsDVD())
+ continue;
+ }
+ else
+ {
+ if (!URIUtils::CompareWithoutSlashAtEnd(testShare.strPath, item->GetPath()))
+ continue;
+ }
+ // paths match, what about share name - only match the leftmost
+ // characters as the label may contain other info (status for instance)
+ if (StringUtils::StartsWithNoCase(item->GetLabel(), testShare.strName))
+ {
+ return &testShare;
+ }
+ }
+ return nullptr;
+}
+
+void CGUIDialogContextMenu::OnWindowLoaded()
+{
+ m_coordX = m_posX;
+ m_coordY = m_posY;
+
+ const CGUIControlGroupList* pGroupList = dynamic_cast<const CGUIControlGroupList *>(GetControl(GROUP_LIST));
+ m_backgroundImage = GetControl(BACKGROUND_IMAGE);
+ if (m_backgroundImage && pGroupList)
+ {
+ if (pGroupList->GetOrientation() == VERTICAL)
+ m_backgroundImageSize = m_backgroundImage->GetHeight();
+ else
+ m_backgroundImageSize = m_backgroundImage->GetWidth();
+ }
+
+ CGUIDialog::OnWindowLoaded();
+}
+
+void CGUIDialogContextMenu::OnDeinitWindow(int nextWindowID)
+{
+ //we can't be sure that controls are removed on window unload
+ //we have to remove them to be sure that they won't stay for next use of context menu
+ for (unsigned int i = 0; i < m_buttons.size(); i++)
+ {
+ const CGUIControl *control = GetControl(BUTTON_START + i);
+ if (control)
+ {
+ RemoveControl(control);
+ delete control;
+ }
+ }
+
+ m_buttons.clear();
+ m_initiallyFocusedButtonIdx = 0;
+ CGUIDialog::OnDeinitWindow(nextWindowID);
+}
+
+std::string CGUIDialogContextMenu::GetDefaultShareNameByType(const std::string &strType)
+{
+ VECSOURCES *pShares = CMediaSourceSettings::GetInstance().GetSources(strType);
+ std::string strDefault = CMediaSourceSettings::GetInstance().GetDefaultSource(strType);
+
+ if (!pShares) return "";
+
+ bool bIsSourceName(false);
+ int iIndex = CUtil::GetMatchingSource(strDefault, *pShares, bIsSourceName);
+ if (iIndex < 0 || iIndex >= (int)pShares->size())
+ return "";
+
+ return pShares->at(iIndex).strName;
+}
+
+void CGUIDialogContextMenu::SetDefault(const std::string &strType, const std::string &strDefault)
+{
+ CMediaSourceSettings::GetInstance().SetDefaultSource(strType, strDefault);
+ CMediaSourceSettings::GetInstance().Save();
+}
+
+void CGUIDialogContextMenu::ClearDefault(const std::string &strType)
+{
+ SetDefault(strType, "");
+}
+
+void CGUIDialogContextMenu::SwitchMedia(const std::string& strType, const std::string& strPath)
+{
+ // create menu
+ CContextButtons choices;
+ if (strType != "music")
+ choices.Add(WINDOW_MUSIC_NAV, 2);
+ if (strType != "video")
+ choices.Add(WINDOW_VIDEO_NAV, 3);
+ if (strType != "pictures")
+ choices.Add(WINDOW_PICTURES, 1);
+ if (strType != "files")
+ choices.Add(WINDOW_FILES, 7);
+
+ int window = ShowAndGetChoice(choices);
+ if (window >= 0)
+ {
+ CUtil::DeleteDirectoryCache();
+ CServiceBroker::GetGUI()->GetWindowManager().ChangeActiveWindow(window, strPath);
+ }
+}
+
+int CGUIDialogContextMenu::Show(const CContextButtons& choices, int focusedButtonIdx /* = 0 */)
+{
+ auto dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogContextMenu>(WINDOW_DIALOG_CONTEXT_MENU);
+ if (!dialog)
+ return -1;
+
+ dialog->m_buttons = choices;
+ dialog->Initialize();
+ dialog->SetInitialVisibility();
+ dialog->SetupButtons();
+ dialog->PositionAtCurrentFocus();
+ dialog->m_initiallyFocusedButtonIdx = focusedButtonIdx;
+ dialog->Open();
+ return dialog->m_clickedButton;
+}
+
+int CGUIDialogContextMenu::ShowAndGetChoice(const CContextButtons &choices)
+{
+ if (choices.empty())
+ return -1;
+
+ CGUIDialogContextMenu *pMenu = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogContextMenu>(WINDOW_DIALOG_CONTEXT_MENU);
+ if (pMenu)
+ {
+ pMenu->m_buttons = choices;
+ pMenu->Initialize();
+ pMenu->SetInitialVisibility();
+ pMenu->SetupButtons();
+ pMenu->PositionAtCurrentFocus();
+ pMenu->Open();
+
+ int idx = pMenu->m_clickedButton;
+ if (idx != -1)
+ return choices[idx].first;
+ }
+ return -1;
+}
+
+void CGUIDialogContextMenu::PositionAtCurrentFocus()
+{
+ CGUIWindow *window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog());
+ if (window)
+ {
+ const CGUIControl *focusedControl = window->GetFocusedControl();
+ if (focusedControl)
+ {
+ CPoint pos = focusedControl->GetRenderPosition() + CPoint(focusedControl->GetWidth() * 0.5f, focusedControl->GetHeight() * 0.5f);
+ SetPosition(m_coordX + pos.x - GetWidth() * 0.5f, m_coordY + pos.y - GetHeight() * 0.5f);
+ return;
+ }
+ }
+ // no control to center at, so just center the window
+ CenterWindow();
+}