1 /*
2 * Copyright (C) 2005-2020 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "GUIDialogContextMenu.h"
10
11 #include "FileItem.h"
12 #include "GUIDialogFileBrowser.h"
13 #include "GUIDialogMediaSource.h"
14 #include "GUIDialogYesNo.h"
15 #include "GUIPassword.h"
16 #include "GUIUserMessages.h"
17 #include "ServiceBroker.h"
18 #include "TextureCache.h"
19 #include "URL.h"
20 #include "Util.h"
21 #include "addons/Scraper.h"
22 #include "filesystem/File.h"
23 #include "guilib/GUIButtonControl.h"
24 #include "guilib/GUIComponent.h"
25 #include "guilib/GUIControlGroupList.h"
26 #include "guilib/GUIWindowManager.h"
27 #include "guilib/LocalizeStrings.h"
28 #include "input/Key.h"
29 #include "media/MediaLockState.h"
30 #include "profiles/ProfileManager.h"
31 #include "profiles/dialogs/GUIDialogLockSettings.h"
32 #include "settings/MediaSourceSettings.h"
33 #include "settings/Settings.h"
34 #include "settings/SettingsComponent.h"
35 #include "storage/MediaManager.h"
36 #include "utils/StringUtils.h"
37 #include "utils/URIUtils.h"
38 #include "utils/Variant.h"
39
40 #define BACKGROUND_IMAGE 999
41 #define GROUP_LIST 996
42 #define BUTTON_TEMPLATE 1000
43 #define BUTTON_START 1001
44 #define BUTTON_END (BUTTON_START + (int)m_buttons.size() - 1)
45
Add(unsigned int button,const std::string & label)46 void CContextButtons::Add(unsigned int button, const std::string &label)
47 {
48 for (const auto& i : *this)
49 if (i.first == button)
50 return; // already have this button
51 push_back(std::pair<unsigned int, std::string>(button, label));
52 }
53
Add(unsigned int button,int label)54 void CContextButtons::Add(unsigned int button, int label)
55 {
56 for (const auto& i : *this)
57 if (i.first == button)
58 return; // already have added this button
59 push_back(std::pair<unsigned int, std::string>(button, g_localizeStrings.Get(label)));
60 }
61
CGUIDialogContextMenu(void)62 CGUIDialogContextMenu::CGUIDialogContextMenu(void)
63 : CGUIDialog(WINDOW_DIALOG_CONTEXT_MENU, "DialogContextMenu.xml")
64 {
65 m_clickedButton = -1;
66 m_backgroundImageSize = 0;
67 m_loadType = KEEP_IN_MEMORY;
68 m_coordX = 0.0f;
69 m_coordY = 0.0f;
70 }
71
72 CGUIDialogContextMenu::~CGUIDialogContextMenu(void) = default;
73
OnMessage(CGUIMessage & message)74 bool CGUIDialogContextMenu::OnMessage(CGUIMessage &message)
75 {
76 if (message.GetMessage() == GUI_MSG_CLICKED)
77 { // someone has been clicked - deinit...
78 if (message.GetSenderId() >= BUTTON_START && message.GetSenderId() <= BUTTON_END)
79 m_clickedButton = message.GetSenderId() - BUTTON_START;
80 Close();
81 return true;
82 }
83 return CGUIDialog::OnMessage(message);
84 }
85
OnAction(const CAction & action)86 bool CGUIDialogContextMenu::OnAction(const CAction& action)
87 {
88 if (action.GetID() == ACTION_CONTEXT_MENU ||
89 action.GetID() == ACTION_SWITCH_PLAYER)
90 {
91 Close();
92 return true;
93 }
94
95 return CGUIDialog::OnAction(action);
96 }
97
OnInitWindow()98 void CGUIDialogContextMenu::OnInitWindow()
99 {
100 m_clickedButton = -1;
101 // set initial control focus
102 m_lastControlID = m_initiallyFocusedButtonIdx + BUTTON_START;
103 CGUIDialog::OnInitWindow();
104 }
105
SetupButtons()106 void CGUIDialogContextMenu::SetupButtons()
107 {
108 if (!m_buttons.size())
109 return;
110
111 // disable the template button control
112 CGUIButtonControl *pButtonTemplate = dynamic_cast<CGUIButtonControl *>(GetFirstFocusableControl(BUTTON_TEMPLATE));
113 if (!pButtonTemplate)
114 pButtonTemplate = dynamic_cast<CGUIButtonControl *>(GetControl(BUTTON_TEMPLATE));
115 if (!pButtonTemplate)
116 return;
117 pButtonTemplate->SetVisible(false);
118
119 CGUIControlGroupList* pGroupList = dynamic_cast<CGUIControlGroupList *>(GetControl(GROUP_LIST));
120
121 // add our buttons
122 for (unsigned int i = 0; i < m_buttons.size(); i++)
123 {
124 CGUIButtonControl *pButton = new CGUIButtonControl(*pButtonTemplate);
125 if (pButton)
126 { // set the button's ID and position
127 int id = BUTTON_START + i;
128 pButton->SetID(id);
129 pButton->SetVisible(true);
130 pButton->SetLabel(m_buttons[i].second);
131 if (pGroupList)
132 {
133 pButton->SetPosition(pButtonTemplate->GetXPosition(), pButtonTemplate->GetYPosition());
134 // try inserting context buttons at position specified by template
135 // button, if template button is not in grouplist fallback to adding
136 // new buttons at the end of grouplist
137 if (!pGroupList->InsertControl(pButton, pButtonTemplate))
138 pGroupList->AddControl(pButton);
139 }
140 }
141 }
142
143 // fix up background images placement and size
144 CGUIControl *pControl = GetControl(BACKGROUND_IMAGE);
145 if (pControl)
146 {
147 // first set size of background image
148 if (pGroupList)
149 {
150 if (pGroupList->GetOrientation() == VERTICAL)
151 {
152 // keep gap between bottom edges of grouplist and background image
153 pControl->SetHeight(m_backgroundImageSize - pGroupList->Size() + pGroupList->GetHeight());
154 }
155 else
156 {
157 // keep gap between right edges of grouplist and background image
158 pControl->SetWidth(m_backgroundImageSize - pGroupList->Size() + pGroupList->GetWidth());
159 }
160 }
161 }
162
163 // update our default control
164 if (pGroupList)
165 m_defaultControl = pGroupList->GetID();
166 }
167
SetPosition(float posX,float posY)168 void CGUIDialogContextMenu::SetPosition(float posX, float posY)
169 {
170 if (posY + GetHeight() > m_coordsRes.iHeight)
171 posY = m_coordsRes.iHeight - GetHeight();
172 if (posY < 0) posY = 0;
173 if (posX + GetWidth() > m_coordsRes.iWidth)
174 posX = m_coordsRes.iWidth - GetWidth();
175 if (posX < 0) posX = 0;
176 CGUIDialog::SetPosition(posX, posY);
177 }
178
GetHeight() const179 float CGUIDialogContextMenu::GetHeight() const
180 {
181 if (m_backgroundImage)
182 return m_backgroundImage->GetHeight();
183 else
184 return CGUIDialog::GetHeight();
185 }
186
GetWidth() const187 float CGUIDialogContextMenu::GetWidth() const
188 {
189 if (m_backgroundImage)
190 return m_backgroundImage->GetWidth();
191 else
192 return CGUIDialog::GetWidth();
193 }
194
SourcesMenu(const std::string & strType,const CFileItemPtr & item,float posX,float posY)195 bool CGUIDialogContextMenu::SourcesMenu(const std::string &strType, const CFileItemPtr& item, float posX, float posY)
196 {
197 //! @todo This should be callable even if we don't have any valid items
198 if (!item)
199 return false;
200
201 // grab our context menu
202 CContextButtons buttons;
203 GetContextButtons(strType, item, buttons);
204
205 int button = ShowAndGetChoice(buttons);
206 if (button >= 0)
207 return OnContextButton(strType, item, (CONTEXT_BUTTON)button);
208 return false;
209 }
210
GetContextButtons(const std::string & type,const CFileItemPtr & item,CContextButtons & buttons)211 void CGUIDialogContextMenu::GetContextButtons(const std::string &type, const CFileItemPtr& item, CContextButtons &buttons)
212 {
213 // Add buttons to the ContextMenu that should be visible for both sources and autosourced items
214 if (item && item->IsRemovable())
215 {
216 if (item->IsDVD() || item->IsCDDA())
217 {
218 buttons.Add(CONTEXT_BUTTON_EJECT_DISC, 13391); // Eject / Load
219 }
220 else // Must be HDD
221 {
222 buttons.Add(CONTEXT_BUTTON_EJECT_DRIVE, 13420); // Remove safely
223 }
224 }
225
226 // Next, Add buttons to the ContextMenu that should ONLY be visible for sources and not autosourced items
227 CMediaSource *share = GetShare(type, item.get());
228
229 if (CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetCurrentProfile().canWriteSources() || g_passwordManager.bMasterUser)
230 {
231 if (share)
232 {
233 // Note. from now on, remove source & disable plugin should mean the same thing
234 //! @todo might be smart to also combine editing source & plugin settings into one concept/dialog
235 // Note. Temporarily disabled ability to remove plugin sources until installer is operational
236
237 CURL url(share->strPath);
238 bool isAddon = ADDON::TranslateContent(url.GetProtocol()) != CONTENT_NONE;
239 if (!share->m_ignore && !isAddon)
240 buttons.Add(CONTEXT_BUTTON_EDIT_SOURCE, 1027); // Edit Source
241 if (type != "video")
242 buttons.Add(CONTEXT_BUTTON_SET_DEFAULT, 13335); // Set as Default
243 if (!share->m_ignore && !isAddon)
244 buttons.Add(CONTEXT_BUTTON_REMOVE_SOURCE, 522); // Remove Source
245
246 buttons.Add(CONTEXT_BUTTON_SET_THUMB, 20019);
247 }
248 if (!GetDefaultShareNameByType(type).empty())
249 buttons.Add(CONTEXT_BUTTON_CLEAR_DEFAULT, 13403); // Clear Default
250 }
251 if (share && LOCK_MODE_EVERYONE != CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetMasterProfile().getLockMode())
252 {
253 if (share->m_iHasLock == LOCK_STATE_NO_LOCK && (CServiceBroker::GetSettingsComponent()
254 ->GetProfileManager()
255 ->GetCurrentProfile()
256 .canWriteSources() ||
257 g_passwordManager.bMasterUser))
258 buttons.Add(CONTEXT_BUTTON_ADD_LOCK, 12332);
259 else if (share->m_iHasLock == LOCK_STATE_LOCK_BUT_UNLOCKED)
260 buttons.Add(CONTEXT_BUTTON_REMOVE_LOCK, 12335);
261 else if (share->m_iHasLock == LOCK_STATE_LOCKED)
262 {
263 buttons.Add(CONTEXT_BUTTON_REMOVE_LOCK, 12335);
264
265 bool maxRetryExceeded = false;
266 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_MASTERLOCK_MAXRETRIES) != 0)
267 maxRetryExceeded = (share->m_iBadPwdCount >= CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_MASTERLOCK_MAXRETRIES));
268
269 if (maxRetryExceeded)
270 buttons.Add(CONTEXT_BUTTON_RESET_LOCK, 12334);
271 else
272 buttons.Add(CONTEXT_BUTTON_CHANGE_LOCK, 12356);
273 }
274 }
275 if (share && !g_passwordManager.bMasterUser && item->m_iHasLock == LOCK_STATE_LOCK_BUT_UNLOCKED)
276 buttons.Add(CONTEXT_BUTTON_REACTIVATE_LOCK, 12353);
277 }
278
OnContextButton(const std::string & type,const CFileItemPtr & item,CONTEXT_BUTTON button)279 bool CGUIDialogContextMenu::OnContextButton(const std::string &type, const CFileItemPtr& item, CONTEXT_BUTTON button)
280 {
281 // buttons that are available on both sources and autosourced items
282 if (!item)
283 return false;
284
285 switch (button)
286 {
287 case CONTEXT_BUTTON_EJECT_DRIVE:
288 return CServiceBroker::GetMediaManager().Eject(item->GetPath());
289 #ifdef HAS_DVD_DRIVE
290 case CONTEXT_BUTTON_EJECT_DISC:
291 CServiceBroker::GetMediaManager().ToggleTray(
292 CServiceBroker::GetMediaManager().TranslateDevicePath(item->GetPath())[0]);
293 #endif
294 return true;
295 default:
296 break;
297 }
298
299 // the rest of the operations require a valid share
300 CMediaSource *share = GetShare(type, item.get());
301 if (!share)
302 return false;
303
304 switch (button)
305 {
306 case CONTEXT_BUTTON_EDIT_SOURCE:
307 if (CServiceBroker::GetSettingsComponent()->GetProfileManager()->IsMasterProfile())
308 {
309 if (!g_passwordManager.IsMasterLockUnlocked(true))
310 return false;
311 }
312 else if (!g_passwordManager.IsProfileLockUnlocked())
313 return false;
314
315 return CGUIDialogMediaSource::ShowAndEditMediaSource(type, *share);
316
317 case CONTEXT_BUTTON_REMOVE_SOURCE:
318 {
319 if (CServiceBroker::GetSettingsComponent()->GetProfileManager()->IsMasterProfile())
320 {
321 if (!g_passwordManager.IsMasterLockUnlocked(true))
322 return false;
323 }
324 else
325 {
326 if (!CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetCurrentProfile().canWriteSources() && !g_passwordManager.IsMasterLockUnlocked(false))
327 return false;
328 if (CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked())
329 return false;
330 }
331 // prompt user if they want to really delete the source
332 if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{751}, CVariant{750}))
333 return false;
334
335 // check default before we delete, as deletion will kill the share object
336 std::string defaultSource(GetDefaultShareNameByType(type));
337 if (!defaultSource.empty())
338 {
339 if (share->strName == defaultSource)
340 ClearDefault(type);
341 }
342 CMediaSourceSettings::GetInstance().DeleteSource(type, share->strName, share->strPath);
343 return true;
344 }
345 case CONTEXT_BUTTON_SET_DEFAULT:
346 if (CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked())
347 return false;
348 else if (!g_passwordManager.IsMasterLockUnlocked(true))
349 return false;
350
351 // make share default
352 SetDefault(type, share->strName);
353 return true;
354
355 case CONTEXT_BUTTON_CLEAR_DEFAULT:
356 if (CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked())
357 return false;
358 else if (!g_passwordManager.IsMasterLockUnlocked(true))
359 return false;
360 // remove share default
361 ClearDefault(type);
362 return true;
363
364 case CONTEXT_BUTTON_SET_THUMB:
365 {
366 if (CServiceBroker::GetSettingsComponent()->GetProfileManager()->GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked())
367 return false;
368 else if (!g_passwordManager.IsMasterLockUnlocked(true))
369 return false;
370
371 // setup our thumb list
372 CFileItemList items;
373
374 // add the current thumb, if available
375 if (!share->m_strThumbnailImage.empty())
376 {
377 CFileItemPtr current(new CFileItem("thumb://Current", false));
378 current->SetArt("thumb", share->m_strThumbnailImage);
379 current->SetLabel(g_localizeStrings.Get(20016));
380 items.Add(current);
381 }
382 else if (item->HasArt("thumb"))
383 { // already have a thumb that the share doesn't know about - must be a local one, so we mayaswell reuse it.
384 CFileItemPtr current(new CFileItem("thumb://Current", false));
385 current->SetArt("thumb", item->GetArt("thumb"));
386 current->SetLabel(g_localizeStrings.Get(20016));
387 items.Add(current);
388 }
389 // see if there's a local thumb for this item
390 std::string folderThumb = item->GetFolderThumb();
391 if (XFILE::CFile::Exists(folderThumb))
392 {
393 CFileItemPtr local(new CFileItem("thumb://Local", false));
394 local->SetArt("thumb", folderThumb);
395 local->SetLabel(g_localizeStrings.Get(20017));
396 items.Add(local);
397 }
398 // and add a "no thumb" entry as well
399 CFileItemPtr nothumb(new CFileItem("thumb://None", false));
400 nothumb->SetArt("icon", item->GetArt("icon"));
401 nothumb->SetLabel(g_localizeStrings.Get(20018));
402 items.Add(nothumb);
403
404 std::string strThumb;
405 VECSOURCES shares;
406 CServiceBroker::GetMediaManager().GetLocalDrives(shares);
407 if (!CGUIDialogFileBrowser::ShowAndGetImage(items, shares, g_localizeStrings.Get(1030), strThumb))
408 return false;
409
410 if (strThumb == "thumb://Current")
411 return true;
412
413 if (strThumb == "thumb://Local")
414 strThumb = folderThumb;
415
416 if (strThumb == "thumb://None")
417 strThumb = "";
418
419 if (!share->m_ignore)
420 {
421 CMediaSourceSettings::GetInstance().UpdateSource(type,share->strName,"thumbnail",strThumb);
422 CMediaSourceSettings::GetInstance().Save();
423 }
424 else if (!strThumb.empty())
425 { // this is some sort of an auto-share, so store in the texture database
426 CTextureDatabase db;
427 if (db.Open())
428 db.SetTextureForPath(item->GetPath(), "thumb", strThumb);
429 }
430
431 CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES);
432 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
433 return true;
434 }
435
436 case CONTEXT_BUTTON_ADD_LOCK:
437 {
438 // prompt user for mastercode when changing lock settings) only for default user
439 if (!g_passwordManager.IsMasterLockUnlocked(true))
440 return false;
441
442 std::string strNewPassword = "";
443 if (!CGUIDialogLockSettings::ShowAndGetLock(share->m_iLockMode,strNewPassword))
444 return false;
445 // password entry and re-entry succeeded, write out the lock data
446 share->m_iHasLock = LOCK_STATE_LOCKED;
447 CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockcode", strNewPassword);
448 strNewPassword = StringUtils::Format("%i", share->m_iLockMode);
449 CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockmode", strNewPassword);
450 CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "badpwdcount", "0");
451 CMediaSourceSettings::GetInstance().Save();
452
453 CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES);
454 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
455 return true;
456 }
457 case CONTEXT_BUTTON_RESET_LOCK:
458 {
459 // prompt user for profile lock when changing lock settings
460 if (!g_passwordManager.IsMasterLockUnlocked(true))
461 return false;
462
463 CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "badpwdcount", "0");
464 CMediaSourceSettings::GetInstance().Save();
465 CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES);
466 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
467 return true;
468 }
469 case CONTEXT_BUTTON_REMOVE_LOCK:
470 {
471 if (!g_passwordManager.IsMasterLockUnlocked(true))
472 return false;
473
474 // prompt user if they want to really remove the lock
475 if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{12335}, CVariant{750}))
476 return false;
477
478 share->m_iHasLock = LOCK_STATE_NO_LOCK;
479 CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockmode", "0");
480 CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockcode", "0");
481 CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "badpwdcount", "0");
482 CMediaSourceSettings::GetInstance().Save();
483 CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES);
484 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
485 return true;
486 }
487 case CONTEXT_BUTTON_REACTIVATE_LOCK:
488 {
489 bool maxRetryExceeded = false;
490 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_MASTERLOCK_MAXRETRIES) != 0)
491 maxRetryExceeded = (share->m_iBadPwdCount >= CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_MASTERLOCK_MAXRETRIES));
492 if (!maxRetryExceeded)
493 {
494 // don't prompt user for mastercode when reactivating a lock
495 g_passwordManager.LockSource(type, share->strName, true);
496 return true;
497 }
498 return false;
499 }
500 case CONTEXT_BUTTON_CHANGE_LOCK:
501 {
502 if (!g_passwordManager.IsMasterLockUnlocked(true))
503 return false;
504
505 std::string strNewPW;
506 std::string strNewLockMode;
507 if (CGUIDialogLockSettings::ShowAndGetLock(share->m_iLockMode,strNewPW))
508 strNewLockMode = StringUtils::Format("%i",share->m_iLockMode);
509 else
510 return false;
511 // password ReSet and re-entry succeeded, write out the lock data
512 CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockcode", strNewPW);
513 CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockmode", strNewLockMode);
514 CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "badpwdcount", "0");
515 CMediaSourceSettings::GetInstance().Save();
516 CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES);
517 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
518 return true;
519 }
520 default:
521 break;
522 }
523 return false;
524 }
525
GetShare(const std::string & type,const CFileItem * item)526 CMediaSource *CGUIDialogContextMenu::GetShare(const std::string &type, const CFileItem *item)
527 {
528 VECSOURCES *shares = CMediaSourceSettings::GetInstance().GetSources(type);
529 if (!shares || !item)
530 return nullptr;
531 for (unsigned int i = 0; i < shares->size(); i++)
532 {
533 CMediaSource &testShare = shares->at(i);
534 if (URIUtils::IsDVD(testShare.strPath))
535 {
536 if (!item->IsDVD())
537 continue;
538 }
539 else
540 {
541 if (!URIUtils::CompareWithoutSlashAtEnd(testShare.strPath, item->GetPath()))
542 continue;
543 }
544 // paths match, what about share name - only match the leftmost
545 // characters as the label may contain other info (status for instance)
546 if (StringUtils::StartsWithNoCase(item->GetLabel(), testShare.strName))
547 {
548 return &testShare;
549 }
550 }
551 return nullptr;
552 }
553
OnWindowLoaded()554 void CGUIDialogContextMenu::OnWindowLoaded()
555 {
556 m_coordX = m_posX;
557 m_coordY = m_posY;
558
559 const CGUIControlGroupList* pGroupList = dynamic_cast<const CGUIControlGroupList *>(GetControl(GROUP_LIST));
560 m_backgroundImage = GetControl(BACKGROUND_IMAGE);
561 if (m_backgroundImage && pGroupList)
562 {
563 if (pGroupList->GetOrientation() == VERTICAL)
564 m_backgroundImageSize = m_backgroundImage->GetHeight();
565 else
566 m_backgroundImageSize = m_backgroundImage->GetWidth();
567 }
568
569 CGUIDialog::OnWindowLoaded();
570 }
571
OnDeinitWindow(int nextWindowID)572 void CGUIDialogContextMenu::OnDeinitWindow(int nextWindowID)
573 {
574 //we can't be sure that controls are removed on window unload
575 //we have to remove them to be sure that they won't stay for next use of context menu
576 for (unsigned int i = 0; i < m_buttons.size(); i++)
577 {
578 const CGUIControl *control = GetControl(BUTTON_START + i);
579 if (control)
580 RemoveControl(control);
581 }
582
583 m_buttons.clear();
584 m_initiallyFocusedButtonIdx = 0;
585 CGUIDialog::OnDeinitWindow(nextWindowID);
586 }
587
GetDefaultShareNameByType(const std::string & strType)588 std::string CGUIDialogContextMenu::GetDefaultShareNameByType(const std::string &strType)
589 {
590 VECSOURCES *pShares = CMediaSourceSettings::GetInstance().GetSources(strType);
591 std::string strDefault = CMediaSourceSettings::GetInstance().GetDefaultSource(strType);
592
593 if (!pShares) return "";
594
595 bool bIsSourceName(false);
596 int iIndex = CUtil::GetMatchingSource(strDefault, *pShares, bIsSourceName);
597 if (iIndex < 0 || iIndex >= (int)pShares->size())
598 return "";
599
600 return pShares->at(iIndex).strName;
601 }
602
SetDefault(const std::string & strType,const std::string & strDefault)603 void CGUIDialogContextMenu::SetDefault(const std::string &strType, const std::string &strDefault)
604 {
605 CMediaSourceSettings::GetInstance().SetDefaultSource(strType, strDefault);
606 CMediaSourceSettings::GetInstance().Save();
607 }
608
ClearDefault(const std::string & strType)609 void CGUIDialogContextMenu::ClearDefault(const std::string &strType)
610 {
611 SetDefault(strType, "");
612 }
613
SwitchMedia(const std::string & strType,const std::string & strPath)614 void CGUIDialogContextMenu::SwitchMedia(const std::string& strType, const std::string& strPath)
615 {
616 // create menu
617 CContextButtons choices;
618 if (strType != "music")
619 choices.Add(WINDOW_MUSIC_NAV, 2);
620 if (strType != "video")
621 choices.Add(WINDOW_VIDEO_NAV, 3);
622 if (strType != "pictures")
623 choices.Add(WINDOW_PICTURES, 1);
624 if (strType != "files")
625 choices.Add(WINDOW_FILES, 7);
626
627 int window = ShowAndGetChoice(choices);
628 if (window >= 0)
629 {
630 CUtil::DeleteDirectoryCache();
631 CServiceBroker::GetGUI()->GetWindowManager().ChangeActiveWindow(window, strPath);
632 }
633 }
634
Show(const CContextButtons & choices,int focusedButtonIdx)635 int CGUIDialogContextMenu::Show(const CContextButtons& choices, int focusedButtonIdx /* = 0 */)
636 {
637 auto dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogContextMenu>(WINDOW_DIALOG_CONTEXT_MENU);
638 if (!dialog)
639 return -1;
640
641 dialog->m_buttons = choices;
642 dialog->Initialize();
643 dialog->SetInitialVisibility();
644 dialog->SetupButtons();
645 dialog->PositionAtCurrentFocus();
646 dialog->m_initiallyFocusedButtonIdx = focusedButtonIdx;
647 dialog->Open();
648 return dialog->m_clickedButton;
649 }
650
ShowAndGetChoice(const CContextButtons & choices)651 int CGUIDialogContextMenu::ShowAndGetChoice(const CContextButtons &choices)
652 {
653 if (choices.empty())
654 return -1;
655
656 CGUIDialogContextMenu *pMenu = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogContextMenu>(WINDOW_DIALOG_CONTEXT_MENU);
657 if (pMenu)
658 {
659 pMenu->m_buttons = choices;
660 pMenu->Initialize();
661 pMenu->SetInitialVisibility();
662 pMenu->SetupButtons();
663 pMenu->PositionAtCurrentFocus();
664 pMenu->Open();
665
666 int idx = pMenu->m_clickedButton;
667 if (idx != -1)
668 return choices[idx].first;
669 }
670 return -1;
671 }
672
PositionAtCurrentFocus()673 void CGUIDialogContextMenu::PositionAtCurrentFocus()
674 {
675 CGUIWindow *window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog());
676 if (window)
677 {
678 const CGUIControl *focusedControl = window->GetFocusedControl();
679 if (focusedControl)
680 {
681 CPoint pos = focusedControl->GetRenderPosition() + CPoint(focusedControl->GetWidth() * 0.5f, focusedControl->GetHeight() * 0.5f);
682 SetPosition(m_coordX + pos.x - GetWidth() * 0.5f, m_coordY + pos.y - GetHeight() * 0.5f);
683 return;
684 }
685 }
686 // no control to center at, so just center the window
687 CenterWindow();
688 }
689