Merge pull request #24470 from fuzzard/release_20.3
[xbmc.git] / xbmc / interfaces / builtins / GUIBuiltins.cpp
blobf368e69a521026f68199f111cefba83a4afff55b
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "GUIBuiltins.h"
11 #include "ServiceBroker.h"
12 #include "Util.h"
13 #include "application/ApplicationComponents.h"
14 #include "application/ApplicationPowerHandling.h"
15 #include "dialogs/GUIDialogKaiToast.h"
16 #include "dialogs/GUIDialogNumeric.h"
17 #include "filesystem/Directory.h"
18 #include "guilib/GUIComponent.h"
19 #include "guilib/GUIWindowManager.h"
20 #include "guilib/LocalizeStrings.h"
21 #include "guilib/StereoscopicsManager.h"
22 #include "input/WindowTranslator.h"
23 #include "input/actions/Action.h"
24 #include "input/actions/ActionIDs.h"
25 #include "input/actions/ActionTranslator.h"
26 #include "messaging/ApplicationMessenger.h"
27 #include "settings/AdvancedSettings.h"
28 #include "settings/SettingsComponent.h"
29 #include "utils/AlarmClock.h"
30 #include "utils/RssManager.h"
31 #include "utils/Screenshot.h"
32 #include "utils/StringUtils.h"
33 #include "utils/URIUtils.h"
34 #include "utils/log.h"
35 #include "windows/GUIMediaWindow.h"
37 /*! \brief Execute a GUI action.
38 * \param params The parameters.
39 * \details params[0] = Action to execute.
40 * params[1] = Window to send action to (optional).
42 static int Action(const std::vector<std::string>& params)
44 // try translating the action from our ButtonTranslator
45 unsigned int actionID;
46 if (CActionTranslator::TranslateString(params[0], actionID))
48 int windowID = params.size() == 2 ? CWindowTranslator::TranslateWindow(params[1]) : WINDOW_INVALID;
49 CServiceBroker::GetAppMessenger()->SendMsg(TMSG_GUI_ACTION, windowID, -1,
50 static_cast<void*>(new CAction(actionID)));
53 return 0;
56 /*! \brief Activate a window.
57 * \param params The parameters.
58 * \details params[0] = The window name.
59 * params[1] = Window starting folder (optional).
61 * Set the Replace template parameter to true to replace current
62 * window in history.
64 template<bool Replace>
65 static int ActivateWindow(const std::vector<std::string>& params2)
67 std::vector<std::string> params(params2);
68 // get the parameters
69 std::string strWindow;
70 if (params.size())
72 strWindow = params[0];
73 params.erase(params.begin());
76 // confirm the window destination is valid prior to switching
77 int iWindow = CWindowTranslator::TranslateWindow(strWindow);
78 if (iWindow != WINDOW_INVALID)
80 // compare the given directory param with the current active directory
81 // if no directory is given, and you switch from a video window to another
82 // we retain history, so it makes sense to not switch to the same window in
83 // that case
84 bool bIsSameStartFolder = true;
85 if (!params.empty())
87 CGUIWindow *activeWindow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow());
88 if (activeWindow && activeWindow->IsMediaWindow())
89 bIsSameStartFolder = static_cast<CGUIMediaWindow*>(activeWindow)->IsSameStartFolder(params[0]);
92 // activate window only if window and path differ from the current active window
93 if (iWindow != CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() || !bIsSameStartFolder)
95 // if the window doesn't change, make sure it knows it's gonna be replaced
96 // this ensures setting the start directory if we switch paths
97 // if we change windows, that's done anyway
98 if (Replace && !params.empty() &&
99 iWindow == CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow())
100 params.emplace_back("replace");
102 auto& components = CServiceBroker::GetAppComponents();
103 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
104 appPower->WakeUpScreenSaverAndDPMS();
105 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(iWindow, params, Replace);
106 return 0;
109 else
111 CLog::Log(LOGERROR, "Activate/ReplaceWindow called with invalid destination window: {}",
112 strWindow);
113 return false;
116 return 1;
119 /*! \brief Activate a window and give given controls focus.
120 * \param params The parameters.
121 * \details params[0] = The window name.
122 * params[1,...] = Pair of (container ID, focus item).
124 * Set the Replace template parameter to true to replace current
125 * window in history.
127 template<bool Replace>
128 static int ActivateAndFocus(const std::vector<std::string>& params)
130 std::string strWindow = params[0];
132 // confirm the window destination is valid prior to switching
133 int iWindow = CWindowTranslator::TranslateWindow(strWindow);
134 if (iWindow != WINDOW_INVALID)
136 if (iWindow != CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow())
138 // disable the screensaver
139 auto& components = CServiceBroker::GetAppComponents();
140 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
141 appPower->WakeUpScreenSaverAndDPMS();
142 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(iWindow, {}, Replace);
144 unsigned int iPtr = 1;
145 while (params.size() > iPtr + 1)
147 CGUIMessage msg(GUI_MSG_SETFOCUS, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog(),
148 atol(params[iPtr].c_str()),
149 (params.size() >= iPtr + 2) ? atol(params[iPtr + 1].c_str())+1 : 0);
150 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
151 iPtr += 2;
153 return 0;
157 else
158 CLog::Log(LOGERROR, "Replace/ActivateWindowAndFocus called with invalid destination window: {}",
159 strWindow);
161 return 1;
164 /*! \brief Start an alarm clock
165 * \param params The parameters.
166 * \details param[0] = name
167 * param[1] = command
168 * param[2] = Length in seconds (optional).
169 * param[3] = "silent" to suppress notifications.
170 * param[3] = "loop" to loop the alarm.
172 static int AlarmClock(const std::vector<std::string>& params)
174 // format is alarmclock(name,command[,time,true,false]);
175 float seconds = 0;
176 if (params.size() > 2)
178 if (params[2].find(':') == std::string::npos)
179 seconds = static_cast<float>(atoi(params[2].c_str())*60);
180 else
181 seconds = (float)StringUtils::TimeStringToSeconds(params[2]);
183 else
184 { // check if shutdown is specified in particular, and get the time for it
185 std::string strHeading;
186 if (StringUtils::EqualsNoCase(params[0], "shutdowntimer"))
187 strHeading = g_localizeStrings.Get(20145);
188 else
189 strHeading = g_localizeStrings.Get(13209);
190 std::string strTime;
191 if( CGUIDialogNumeric::ShowAndGetNumber(strTime, strHeading) )
192 seconds = static_cast<float>(atoi(strTime.c_str())*60);
193 else
194 return false;
196 bool silent = false;
197 bool loop = false;
198 for (unsigned int i = 3; i < params.size() ; i++)
200 // check "true" for backward comp
201 if (StringUtils::EqualsNoCase(params[i], "true") || StringUtils::EqualsNoCase(params[i], "silent"))
202 silent = true;
203 else if (StringUtils::EqualsNoCase(params[i], "loop"))
204 loop = true;
207 if( g_alarmClock.IsRunning() )
208 g_alarmClock.Stop(params[0],silent);
209 // no negative times not allowed, loop must have a positive time
210 if (seconds < 0 || (seconds == 0 && loop))
211 return false;
212 g_alarmClock.Start(params[0], seconds, params[1], silent, loop);
214 return 0;
217 /*! \brief Cancel an alarm clock.
218 * \param params The parameters.
219 * \details params[0] = "true" to silently cancel alarm (optional).
221 static int CancelAlarm(const std::vector<std::string>& params)
223 bool silent = (params.size() > 1 &&
224 (StringUtils::EqualsNoCase(params[1], "true") ||
225 StringUtils::EqualsNoCase(params[1], "silent")));
226 g_alarmClock.Stop(params[0],silent);
228 return 0;
231 /*! \brief Clear a property in a window.
232 * \param params The parameters.
233 * \details params[0] = The property to clear.
234 * params[1] = The window to clear property in (optional).
236 static int ClearProperty(const std::vector<std::string>& params)
238 CGUIWindow *window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(params.size() > 1 ? CWindowTranslator::TranslateWindow(params[1]) : CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog());
239 if (window)
240 window->SetProperty(params[0],"");
242 return 0;
245 /*! \brief Close a dialog.
246 * \param params The parameters.
247 * \details params[0] = "all" to close all dialogs, or dialog name.
248 * params[1] = "true" to force close (skip animations) (optional).
250 static int CloseDialog(const std::vector<std::string>& params)
252 bool bForce = false;
253 if (params.size() > 1 && StringUtils::EqualsNoCase(params[1], "true"))
254 bForce = true;
255 if (StringUtils::EqualsNoCase(params[0], "all"))
257 CServiceBroker::GetGUI()->GetWindowManager().CloseDialogs(bForce);
259 else
261 int id = CWindowTranslator::TranslateWindow(params[0]);
262 CGUIWindow *window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(id);
263 if (window && window->IsDialog())
264 static_cast<CGUIDialog*>(window)->Close(bForce);
267 return 0;
270 /*! \brief Send a notification.
271 * \param params The parameters.
272 * \details params[0] = Notification title.
273 * params[1] = Notification text.
274 * params[2] = Display time in milliseconds (optional).
275 * params[3] = Notification icon (optional).
277 static int Notification(const std::vector<std::string>& params)
279 if (params.size() < 2)
280 return -1;
281 if (params.size() == 4)
282 CGUIDialogKaiToast::QueueNotification(params[3],params[0],params[1],atoi(params[2].c_str()));
283 else if (params.size() == 3)
284 CGUIDialogKaiToast::QueueNotification("",params[0],params[1],atoi(params[2].c_str()));
285 else
286 CGUIDialogKaiToast::QueueNotification(params[0],params[1]);
288 return 0;
291 /*! \brief Refresh RSS feed.
292 * \param params (ignored)
294 static int RefreshRSS(const std::vector<std::string>& params)
296 CRssManager::GetInstance().Reload();
298 return 0;
301 /*! \brief Take a screenshot.
302 * \param params The parameters.
303 * \details params[0] = URL to save file to. Blank to use default.
304 * params[1] = "sync" to run synchronously (optional).
306 static int Screenshot(const std::vector<std::string>& params)
308 if (!params.empty())
310 // get the parameters
311 std::string strSaveToPath = params[0];
312 bool sync = false;
313 if (params.size() >= 2)
314 sync = StringUtils::EqualsNoCase(params[1], "sync");
316 if (!strSaveToPath.empty())
318 if (XFILE::CDirectory::Exists(strSaveToPath))
320 std::string file = CUtil::GetNextFilename(
321 URIUtils::AddFileToFolder(strSaveToPath, "screenshot{:05}.png"), 65535);
323 if (!file.empty())
325 CScreenShot::TakeScreenshot(file, sync);
327 else
329 CLog::Log(LOGWARNING, "Too many screen shots or invalid folder {}", strSaveToPath);
332 else
333 CScreenShot::TakeScreenshot(strSaveToPath, sync);
336 else
337 CScreenShot::TakeScreenshot();
339 return 0;
342 /*! \brief Set GUI language.
343 * \param params The parameters.
344 * \details params[0] = The language to use.
346 static int SetLanguage(const std::vector<std::string>& params)
348 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_SETLANGUAGE, -1, -1, nullptr, params[0]);
350 return 0;
353 /*! \brief Set a property in a window.
354 * \param params The parameters.
355 * \details params[0] = The property to set.
356 * params[1] = The property value.
357 * params[2] = The window to set property in (optional).
359 static int SetProperty(const std::vector<std::string>& params)
361 CGUIWindow *window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(params.size() > 2 ? CWindowTranslator::TranslateWindow(params[2]) : CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog());
362 if (window)
363 window->SetProperty(params[0],params[1]);
365 return 0;
368 /*! \brief Set GUI stereo mode.
369 * \param params The parameters.
370 * \details param[0] = Stereo mode identifier.
372 static int SetStereoMode(const std::vector<std::string>& params)
374 CAction action = CStereoscopicsManager::ConvertActionCommandToAction("SetStereoMode", params[0]);
375 if (action.GetID() != ACTION_NONE)
376 CServiceBroker::GetAppMessenger()->SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1,
377 static_cast<void*>(new CAction(action)));
378 else
380 CLog::Log(LOGERROR, "Builtin 'SetStereoMode' called with unknown parameter: {}", params[0]);
381 return -2;
384 return 0;
387 /*! \brief Toggle visualization of dirty regions.
388 * \param params Ignored.
390 static int ToggleDirty(const std::vector<std::string>&)
392 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->ToggleDirtyRegionVisualization();
394 return 0;
397 // Note: For new Texts with comma add a "\" before!!! Is used for table text.
399 /// \page page_List_of_built_in_functions
400 /// \section built_in_functions_5 GUI built-in's
402 /// -----------------------------------------------------------------------------
404 /// \table_start
405 /// \table_h2_l{
406 /// Function,
407 /// Description }
408 /// \table_row2_l{
409 /// <b>`Action(action[\,window])`</b>
410 /// ,
411 /// Executes an action (same as in keymap) for the given window or the
412 /// active window if the parameter window is omitted. The parameter window
413 /// can either be the window's id\, or in the case of a standard window\, the
414 /// window's name. See here for a list of window names\, and their respective
415 /// ids.
416 /// @param[in] action Action to execute.
417 /// @param[in] window Window to send action to (optional).
418 /// }
419 /// \table_row2_l{
420 /// <b>`CancelAlarm(name[\,silent])`</b>
421 /// ,
422 /// Cancel a running alarm. Set silent to true to hide the alarm notification.
423 /// @param[in] silent Send "true" or "silent" to silently cancel alarm (optional).
424 /// }
425 /// \table_row2_l{
426 /// <b>`AlarmClock(name\,command[\,time\,silent\,loop])`</b>
427 /// ,
428 /// Pops up a dialog asking for the length of time for the alarm (unless the
429 /// parameter time is specified)\, and starts a timer. When the timer runs out\,
430 /// it'll execute the built-in command (the parameter command) if it is
431 /// specified\, otherwise it'll pop up an alarm notice. Add silent to hide the
432 /// alarm notification. Add loop for the alarm to execute the command each
433 /// time the specified time interval expires.
434 /// @note if using any of the last optional parameters (silent or loop)\, both must
435 /// be provided for any to take effect.
436 /// <p>
437 /// <b>Example:</b>
438 /// The following example will create an alarmclock named `mytimer` which will silently
439 /// fire (a single time) and set a property (timerelapsed) with value 1 in the window with
440 /// id 1109 after 5 seconds have passed.
441 /// ~~~~~~~~~~~~~
442 /// AlarmClock(mytimer\,SetProperty(timerelapsed\,1\,1109)\,00:00:05\,silent\,false])
443 /// ~~~~~~~~~~~~~
444 /// <p>
445 /// @param[in] name name
446 /// @param[in] command command
447 /// @param[in] time [opt] <b>(a)</b> Length in minutes or <b>(b)</b> a timestring in the format `hh:mm:ss` or `mm min`.
448 /// @param[in] silent [opt] Send "silent" to suppress notifications.
449 /// @param[in] loop [opt] Send "loop" to loop the alarm.
450 /// }
451 /// \table_row2_l{
452 /// <b>`ActivateWindow(window[\,dir\, return])`</b>
453 /// ,
454 /// Opens the given window. The parameter window can either be the window's id\,
455 /// or in the case of a standard window\, the window's name. See \ref window_ids "here" for a list
456 /// of window names\, and their respective ids.
457 /// If\, furthermore\, the window is
458 /// Music\, Video\, Pictures\, or Program files\, then the optional dir parameter
459 /// specifies which folder Kodi should default to once the window is opened.
460 /// This must be a source as specified in sources.xml\, or a subfolder of a
461 /// valid source. For some windows (MusicLibrary and VideoLibrary)\, a third
462 /// parameter (return) may be specified\, which indicates that Kodi should use this
463 /// folder as the "root" of the level\, and thus the "parent directory" action
464 /// from within this folder will return the user to where they were prior to
465 /// the window activating.
466 /// @param[in] window The window name.
467 /// @param[in] dir Window starting folder (optional).
468 /// @param[in] return if dir should be used as the rootfolder of the level
469 /// }
470 /// \table_row2_l{
471 /// <b>`ActivateWindowAndFocus(id1\, id2\,item1\, id3\,item2)`</b>
472 /// ,
473 /// Activate window with id1\, first focus control id2 and then focus control
474 /// id3. if either of the controls is a container\, you can specify which
475 /// item to focus (else\, set it to 0).
476 /// @param[in] id1 The window name.
477 /// @param[in] params[1\,...] Pair of (container ID\, focus item).
478 /// }
479 /// \table_row2_l{
480 /// <b>`ClearProperty(key[\,id])`</b>
481 /// ,
482 /// Clears a window property for the current focused window/dialog(key)\, or
483 /// the specified window (key\,id).
484 /// @param[in] key The property to clear.
485 /// @param[in] id The window to clear property in (optional).
486 /// }
487 /// \table_row2_l{
488 /// <b>`Dialog.Close(dialog[\,force])`</b>
489 /// ,
490 /// Close a dialog. Set force to true to bypass animations. Use (all\,true)
491 /// to close all opened dialogs at once.
492 /// @param[in] dialog Send "all" to close all dialogs\, or dialog name.
493 /// @param[in] force Send "true" to force close (skip animations) (optional).
494 /// }
495 /// \table_row2_l{
496 /// <b>`Notification(header\,message[\,time\,image])`</b>
497 /// ,
498 /// Will display a notification dialog with the specified header and message\,
499 /// in addition you can set the length of time it displays in milliseconds
500 /// and a icon image.
501 /// @param[in] header Notification title.
502 /// @param[in] message Notification text.
503 /// @param[in] time Display time in milliseconds (optional).
504 /// @param[in] image Notification icon (optional).
505 /// }
506 /// \table_row2_l{
507 /// <b>`RefreshRSS`</b>
508 /// ,
509 /// Reload RSS feeds from RSSFeeds.xml
510 /// }
511 /// \table_row2_l{
512 /// <b>`ReplaceWindow(window\,dir)`</b>
513 /// ,
514 /// Replaces the current window with the given window. This is the same as
515 /// ActivateWindow() but it doesn't update the window history list\, so when
516 /// you go back from the new window it will not return to the previous
517 /// window\, rather will return to the previous window's previous window.
518 /// @param[in] window The window name.
519 /// @param[in] dir Window starting folder (optional).
520 /// }
521 /// \table_row2_l{
522 /// <b>`ReplaceWindowAndFocus(id1\, id2\,item1\, id3\,item2)`</b>
523 /// ,
524 /// Replace window with id1\, first focus control id2 and then focus control
525 /// id3. if either of the controls is a container\, you can specify which
526 /// item to focus (else\, set it to 0).
527 /// @param[in] id1 The window name.
528 /// @param[in] params[1\,...] Pair of (container ID\, focus item).
529 /// }
530 /// \table_row2_l{
531 /// <b>`Resolution(resIdent)`</b>
532 /// ,
533 /// Change Kodi's Resolution (default is 4x3).
534 /// param[in] resIdent A resolution identifier.
535 /// | | Identifiers | |
536 /// |:--------:|:-----------:|:--------:|
537 /// | pal | pal16x9 | ntsc |
538 /// | ntsc16x9 | 720p | 720psbs |
539 /// | 720ptb | 1080psbs | 1080ptb |
540 /// | 1080i | | |
541 /// }
542 /// \table_row2_l{
543 /// <b>`SetGUILanguage(lang)`</b>
544 /// ,
545 /// Set GUI Language
546 /// @param[in] lang The language to use.
547 /// }
548 /// \table_row2_l{
549 /// <b>`SetProperty(key\,value[\,id])`</b>
550 /// ,
551 /// Sets a window property for the current window (key\,value)\, or the
552 /// specified window (key\,value\,id).
553 /// @param[in] key The property to set.
554 /// @param[in] value The property value.
555 /// @param[in] id The window to set property in (optional).
556 /// }
557 /// \table_row2_l{
558 /// <b>`SetStereoMode(ident)`</b>
559 /// ,
560 /// Changes the stereo mode of the GUI.
561 /// Params can be:
562 /// toggle\, next\, previous\, select\, tomono or any of the supported stereomodes (off\,
563 /// split_vertical\, split_horizontal\, row_interleaved\, hardware_based\, anaglyph_cyan_red\, anaglyph_green_magenta\, monoscopic)
564 /// @param[in] ident Stereo mode identifier.
565 /// }
566 /// \table_row2_l{
567 /// <b>`TakeScreenshot(url[\,sync)`</b>
568 /// ,
569 /// Takes a Screenshot
570 /// @param[in] url URL to save file to. Blank to use default.
571 /// @param[in] sync Add "sync" to run synchronously (optional).
572 /// }
573 /// \table_row2_l{
574 /// <b>`ToggleDirtyRegionVisualization`</b>
575 /// ,
576 /// makes dirty regions visible for debugging proposes.
577 /// }
578 /// \table_end
581 CBuiltins::CommandMap CGUIBuiltins::GetOperations() const
583 return {
584 {"action", {"Executes an action for the active window (same as in keymap)", 1, Action}},
585 {"cancelalarm", {"Cancels an alarm", 1, CancelAlarm}},
586 {"alarmclock", {"Prompt for a length of time and start an alarm clock", 2, AlarmClock}},
587 {"activatewindow", {"Activate the specified window", 1, ActivateWindow<false>}},
588 {"activatewindowandfocus", {"Activate the specified window and sets focus to the specified id", 1, ActivateAndFocus<false>}},
589 {"clearproperty", {"Clears a window property for the current focused window/dialog (key,value)", 1, ClearProperty}},
590 {"dialog.close", {"Close a dialog", 1, CloseDialog}},
591 {"notification", {"Shows a notification on screen, specify header, then message, and optionally time in milliseconds and a icon.", 2, Notification}},
592 {"refreshrss", {"Reload RSS feeds from RSSFeeds.xml", 0, RefreshRSS}},
593 {"replacewindow", {"Replaces the current window with the new one", 1, ActivateWindow<true>}},
594 {"replacewindowandfocus", {"Replaces the current window with the new one and sets focus to the specified id", 1, ActivateAndFocus<true>}},
595 {"setguilanguage", {"Set GUI Language", 1, SetLanguage}},
596 {"setproperty", {"Sets a window property for the current focused window/dialog (key,value)", 2, SetProperty}},
597 {"setstereomode", {"Changes the stereo mode of the GUI. Params can be: toggle, next, previous, select, tomono or any of the supported stereomodes (off, split_vertical, split_horizontal, row_interleaved, hardware_based, anaglyph_cyan_red, anaglyph_green_magenta, anaglyph_yellow_blue, monoscopic)", 1, SetStereoMode}},
598 {"takescreenshot", {"Takes a Screenshot", 0, Screenshot}},
599 {"toggledirtyregionvisualization", {"Enables/disables dirty-region visualization", 0, ToggleDirty}}