Merge pull request #25922 from sarbes/shader-cleanup
[xbmc.git] / xbmc / pvr / dialogs / GUIDialogPVRTimerSettings.cpp
blob33a9d832665e85d82740a394d3f9fc300424138e
1 /*
2 * Copyright (C) 2012-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 "GUIDialogPVRTimerSettings.h"
11 #include "ServiceBroker.h"
12 #include "dialogs/GUIDialogNumeric.h"
13 #include "guilib/GUIMessage.h"
14 #include "guilib/LocalizeStrings.h"
15 #include "messaging/helpers/DialogOKHelper.h"
16 #include "pvr/PVRConstants.h" // PVR_CLIENT_INVALID_UID
17 #include "pvr/PVRManager.h"
18 #include "pvr/addons/PVRClient.h"
19 #include "pvr/addons/PVRClients.h"
20 #include "pvr/channels/PVRChannel.h"
21 #include "pvr/channels/PVRChannelGroup.h"
22 #include "pvr/channels/PVRChannelGroupMember.h"
23 #include "pvr/channels/PVRChannelGroupsContainer.h"
24 #include "pvr/epg/EpgInfoTag.h"
25 #include "pvr/settings/PVRCustomTimerSettings.h"
26 #include "pvr/timers/PVRTimerInfoTag.h"
27 #include "pvr/timers/PVRTimerType.h"
28 #include "settings/SettingUtils.h"
29 #include "settings/dialogs/GUIDialogSettingsBase.h"
30 #include "settings/lib/Setting.h"
31 #include "settings/lib/SettingsManager.h"
32 #include "settings/windows/GUIControlSettings.h"
33 #include "utils/StringUtils.h"
34 #include "utils/Variant.h"
35 #include "utils/log.h"
37 #include <algorithm>
38 #include <iterator>
39 #include <memory>
40 #include <string>
41 #include <utility>
42 #include <vector>
44 using namespace PVR;
45 using namespace KODI::MESSAGING;
47 #define SETTING_TMR_TYPE "timer.type"
48 #define SETTING_TMR_ACTIVE "timer.active"
49 #define SETTING_TMR_NAME "timer.name"
50 #define SETTING_TMR_EPGSEARCH "timer.epgsearch"
51 #define SETTING_TMR_FULLTEXT "timer.fulltext"
52 #define SETTING_TMR_CHANNEL "timer.channel"
53 #define SETTING_TMR_START_ANYTIME "timer.startanytime"
54 #define SETTING_TMR_END_ANYTIME "timer.endanytime"
55 #define SETTING_TMR_START_DAY "timer.startday"
56 #define SETTING_TMR_END_DAY "timer.endday"
57 #define SETTING_TMR_BEGIN "timer.begin"
58 #define SETTING_TMR_END "timer.end"
59 #define SETTING_TMR_WEEKDAYS "timer.weekdays"
60 #define SETTING_TMR_FIRST_DAY "timer.firstday"
61 #define SETTING_TMR_NEW_EPISODES "timer.newepisodes"
62 #define SETTING_TMR_BEGIN_PRE "timer.startmargin"
63 #define SETTING_TMR_END_POST "timer.endmargin"
64 #define SETTING_TMR_PRIORITY "timer.priority"
65 #define SETTING_TMR_LIFETIME "timer.lifetime"
66 #define SETTING_TMR_MAX_REC "timer.maxrecordings"
67 #define SETTING_TMR_DIR "timer.directory"
68 #define SETTING_TMR_REC_GROUP "timer.recgroup"
70 #define TYPE_DEP_VISIBI_COND_ID_POSTFIX "visibi.typedep"
71 #define TYPE_DEP_ENABLE_COND_ID_POSTFIX "enable.typedep"
72 #define CHANNEL_DEP_VISIBI_COND_ID_POSTFIX "visibi.channeldep"
73 #define START_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX "visibi.startanytimedep"
74 #define END_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX "visibi.endanytimedep"
76 CGUIDialogPVRTimerSettings::CGUIDialogPVRTimerSettings()
77 : CGUIDialogSettingsManualBase(WINDOW_DIALOG_PVR_TIMER_SETTING, "DialogSettings.xml"),
78 m_iWeekdays(PVR_WEEKDAY_NONE)
80 m_loadType = LOAD_EVERY_TIME;
83 CGUIDialogPVRTimerSettings::~CGUIDialogPVRTimerSettings() = default;
85 bool CGUIDialogPVRTimerSettings::CanBeActivated() const
87 if (!m_timerInfoTag)
89 CLog::LogF(LOGERROR, "No timer info tag");
90 return false;
92 return true;
95 void CGUIDialogPVRTimerSettings::SetTimer(const std::shared_ptr<CPVRTimerInfoTag>& timer)
97 if (!timer)
99 CLog::LogF(LOGERROR, "No timer given");
100 return;
103 m_timerInfoTag = timer;
105 // Copy data we need from tag. Do not modify the tag itself until Save()!
106 m_timerType = m_timerInfoTag->GetTimerType();
107 m_bIsRadio = m_timerInfoTag->m_bIsRadio;
108 m_bIsNewTimer = m_timerInfoTag->m_iClientIndex == PVR_TIMER_NO_CLIENT_INDEX;
109 m_bTimerActive = m_bIsNewTimer || !m_timerType->SupportsEnableDisable() ||
110 !(m_timerInfoTag->m_state == PVR_TIMER_STATE_DISABLED);
111 m_bStartAnyTime =
112 m_bIsNewTimer || !m_timerType->SupportsStartAnyTime() || m_timerInfoTag->m_bStartAnyTime;
113 m_bEndAnyTime =
114 m_bIsNewTimer || !m_timerType->SupportsEndAnyTime() || m_timerInfoTag->m_bEndAnyTime;
115 m_strTitle = m_timerInfoTag->m_strTitle;
117 m_startLocalTime = m_timerInfoTag->StartAsLocalTime();
118 m_endLocalTime = m_timerInfoTag->EndAsLocalTime();
120 m_timerStartTimeStr = m_startLocalTime.GetAsLocalizedTime("", false);
121 m_timerEndTimeStr = m_endLocalTime.GetAsLocalizedTime("", false);
122 m_firstDayLocalTime = m_timerInfoTag->FirstDayAsLocalTime();
124 m_strEpgSearchString = m_timerInfoTag->m_strEpgSearchString;
125 if (!m_bIsNewTimer && m_strEpgSearchString.empty())
126 m_strEpgSearchString = m_strTitle;
128 m_bFullTextEpgSearch = m_timerInfoTag->m_bFullTextEpgSearch;
130 m_iWeekdays = m_timerInfoTag->m_iWeekdays;
131 if ((m_bIsNewTimer || !m_timerType->SupportsWeekdays()) && m_iWeekdays == PVR_WEEKDAY_NONE)
132 m_iWeekdays = PVR_WEEKDAY_ALLDAYS;
134 m_iPreventDupEpisodes = m_timerInfoTag->m_iPreventDupEpisodes;
135 m_iMarginStart = m_timerInfoTag->m_iMarginStart;
136 m_iMarginEnd = m_timerInfoTag->m_iMarginEnd;
137 m_iPriority = m_timerInfoTag->m_iPriority;
138 m_iLifetime = m_timerInfoTag->m_iLifetime;
139 m_iMaxRecordings = m_timerInfoTag->m_iMaxRecordings;
141 if (m_bIsNewTimer && m_timerInfoTag->m_strDirectory.empty() &&
142 m_timerType->SupportsRecordingFolders())
143 m_strDirectory = m_strTitle;
144 else
145 m_strDirectory = m_timerInfoTag->m_strDirectory;
147 m_iRecordingGroup = m_timerInfoTag->m_iRecordingGroup;
149 InitializeChannelsList();
150 InitializeTypesList();
152 m_customTimerSettings = std::make_unique<CPVRCustomTimerSettings>(
153 *m_timerType, m_timerInfoTag->m_customProps, m_typeEntries);
155 // Channel
156 m_channel = ChannelDescriptor();
158 if (m_timerInfoTag->m_iClientChannelUid == PVR_CHANNEL_INVALID_UID)
160 if (m_timerType->SupportsAnyChannel())
162 // Select first matching "Any channel" entry.
163 const auto it = std::find_if(m_channelEntries.cbegin(), m_channelEntries.cend(),
164 [this](const auto& channel) {
165 return channel.second.channelUid == PVR_CHANNEL_INVALID_UID &&
166 channel.second.clientId == m_timerInfoTag->m_iClientId;
169 if (it != m_channelEntries.cend())
171 m_channel = (*it).second;
173 else
175 CLog::LogF(LOGERROR, "Unable to map PVR_CHANNEL_INVALID_UID to channel entry!");
178 else if (m_bIsNewTimer)
180 // Select first matching regular (not "Any channel") entry.
181 const auto it = std::find_if(m_channelEntries.cbegin(), m_channelEntries.cend(),
182 [this](const auto& channel) {
183 return channel.second.channelUid != PVR_CHANNEL_INVALID_UID &&
184 channel.second.clientId == m_timerInfoTag->m_iClientId;
187 if (it != m_channelEntries.cend())
189 m_channel = (*it).second;
191 else
193 CLog::LogF(LOGERROR, "Unable to map PVR_CHANNEL_INVALID_UID to channel entry!");
197 else
199 // Find matching channel entry
200 const auto it = std::find_if(
201 m_channelEntries.cbegin(), m_channelEntries.cend(), [this](const auto& channel) {
202 return channel.second.channelUid == m_timerInfoTag->m_iClientChannelUid &&
203 channel.second.clientId == m_timerInfoTag->m_iClientId;
206 if (it != m_channelEntries.cend())
208 m_channel = (*it).second;
210 else
212 CLog::LogF(LOGERROR, "Unable to map channel uid to channel entry!");
217 void CGUIDialogPVRTimerSettings::SetupView()
219 CGUIDialogSettingsManualBase::SetupView();
220 SetHeading(19065);
221 SET_CONTROL_HIDDEN(CONTROL_SETTINGS_CUSTOM_BUTTON);
222 SET_CONTROL_LABEL(CONTROL_SETTINGS_OKAY_BUTTON, 186);
223 SET_CONTROL_LABEL(CONTROL_SETTINGS_CANCEL_BUTTON, 222);
224 SetButtonLabels();
227 void CGUIDialogPVRTimerSettings::InitializeSettings()
229 CGUIDialogSettingsManualBase::InitializeSettings();
231 const std::shared_ptr<CSettingCategory> category = AddCategory("pvrtimersettings", -1);
232 if (category == NULL)
234 CLog::LogF(LOGERROR, "Unable to add settings category");
235 return;
238 const std::shared_ptr<CSettingGroup> group = AddGroup(category);
239 if (group == NULL)
241 CLog::LogF(LOGERROR, "Unable to add settings group");
242 return;
245 std::shared_ptr<CSetting> setting = NULL;
247 // Timer type
248 bool useDetails = false;
249 bool foundClientSupportingTimers = false;
251 const CPVRClientMap clients = CServiceBroker::GetPVRManager().Clients()->GetCreatedClients();
252 for (const auto& client : clients)
254 if (client.second->GetClientCapabilities().SupportsTimers())
256 if (foundClientSupportingTimers)
258 // found second client supporting timers, use detailed timer type list layout
259 useDetails = true;
260 break;
262 foundClientSupportingTimers = true;
266 setting = AddList(group, SETTING_TMR_TYPE, 803, SettingLevel::Basic, 0, TypesFiller, 803, true,
267 -1, useDetails);
268 AddTypeDependentEnableCondition(setting, SETTING_TMR_TYPE);
270 // Timer enabled/disabled
271 setting = AddToggle(group, SETTING_TMR_ACTIVE, 305, SettingLevel::Basic, m_bTimerActive);
272 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_ACTIVE);
273 AddTypeDependentEnableCondition(setting, SETTING_TMR_ACTIVE);
275 // Name
276 setting =
277 AddEdit(group, SETTING_TMR_NAME, 19075, SettingLevel::Basic, m_strTitle, true, false, 19097);
278 AddTypeDependentEnableCondition(setting, SETTING_TMR_NAME);
280 // epg search string (only for epg-based timer rules)
281 setting = AddEdit(group, SETTING_TMR_EPGSEARCH, 804, SettingLevel::Basic, m_strEpgSearchString,
282 true, false, 805);
283 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_EPGSEARCH);
284 AddTypeDependentEnableCondition(setting, SETTING_TMR_EPGSEARCH);
286 // epg fulltext search (only for epg-based timer rules)
287 setting = AddToggle(group, SETTING_TMR_FULLTEXT, 806, SettingLevel::Basic, m_bFullTextEpgSearch);
288 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_FULLTEXT);
289 AddTypeDependentEnableCondition(setting, SETTING_TMR_FULLTEXT);
291 // Channel
292 setting =
293 AddList(group, SETTING_TMR_CHANNEL, 19078, SettingLevel::Basic, 0, ChannelsFiller, 19078);
294 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_CHANNEL);
295 AddTypeDependentEnableCondition(setting, SETTING_TMR_CHANNEL);
297 // Days of week (only for timer rules)
298 std::vector<int> weekdaysPreselect;
299 if (m_iWeekdays & PVR_WEEKDAY_MONDAY)
300 weekdaysPreselect.push_back(PVR_WEEKDAY_MONDAY);
301 if (m_iWeekdays & PVR_WEEKDAY_TUESDAY)
302 weekdaysPreselect.push_back(PVR_WEEKDAY_TUESDAY);
303 if (m_iWeekdays & PVR_WEEKDAY_WEDNESDAY)
304 weekdaysPreselect.push_back(PVR_WEEKDAY_WEDNESDAY);
305 if (m_iWeekdays & PVR_WEEKDAY_THURSDAY)
306 weekdaysPreselect.push_back(PVR_WEEKDAY_THURSDAY);
307 if (m_iWeekdays & PVR_WEEKDAY_FRIDAY)
308 weekdaysPreselect.push_back(PVR_WEEKDAY_FRIDAY);
309 if (m_iWeekdays & PVR_WEEKDAY_SATURDAY)
310 weekdaysPreselect.push_back(PVR_WEEKDAY_SATURDAY);
311 if (m_iWeekdays & PVR_WEEKDAY_SUNDAY)
312 weekdaysPreselect.push_back(PVR_WEEKDAY_SUNDAY);
314 setting = AddList(group, SETTING_TMR_WEEKDAYS, 19079, SettingLevel::Basic, weekdaysPreselect,
315 WeekdaysFiller, 19079, 1, -1, true, -1, WeekdaysValueFormatter);
316 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_WEEKDAYS);
317 AddTypeDependentEnableCondition(setting, SETTING_TMR_WEEKDAYS);
319 // "Start any time" (only for timer rules)
320 setting = AddToggle(group, SETTING_TMR_START_ANYTIME, 810, SettingLevel::Basic, m_bStartAnyTime);
321 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_START_ANYTIME);
322 AddTypeDependentEnableCondition(setting, SETTING_TMR_START_ANYTIME);
324 // Start day (day + month + year only, no hours, minutes)
325 setting = AddSpinner(group, SETTING_TMR_START_DAY, 19128, SettingLevel::Basic,
326 GetDateAsIndex(m_startLocalTime), DaysFiller);
327 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_START_DAY);
328 AddTypeDependentEnableCondition(setting, SETTING_TMR_START_DAY);
329 AddStartAnytimeDependentVisibilityCondition(setting, SETTING_TMR_START_DAY);
331 // Start time (hours + minutes only, no day, month, year)
332 setting = AddButton(group, SETTING_TMR_BEGIN, 19126, SettingLevel::Basic);
333 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_BEGIN);
334 AddTypeDependentEnableCondition(setting, SETTING_TMR_BEGIN);
335 AddStartAnytimeDependentVisibilityCondition(setting, SETTING_TMR_BEGIN);
337 // "End any time" (only for timer rules)
338 setting = AddToggle(group, SETTING_TMR_END_ANYTIME, 817, SettingLevel::Basic, m_bEndAnyTime);
339 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_END_ANYTIME);
340 AddTypeDependentEnableCondition(setting, SETTING_TMR_END_ANYTIME);
342 // End day (day + month + year only, no hours, minutes)
343 setting = AddSpinner(group, SETTING_TMR_END_DAY, 19129, SettingLevel::Basic,
344 GetDateAsIndex(m_endLocalTime), DaysFiller);
345 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_END_DAY);
346 AddTypeDependentEnableCondition(setting, SETTING_TMR_END_DAY);
347 AddEndAnytimeDependentVisibilityCondition(setting, SETTING_TMR_END_DAY);
349 // End time (hours + minutes only, no day, month, year)
350 setting = AddButton(group, SETTING_TMR_END, 19127, SettingLevel::Basic);
351 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_END);
352 AddTypeDependentEnableCondition(setting, SETTING_TMR_END);
353 AddEndAnytimeDependentVisibilityCondition(setting, SETTING_TMR_END);
355 // First day (only for timer rules)
356 setting = AddSpinner(group, SETTING_TMR_FIRST_DAY, 19084, SettingLevel::Basic,
357 GetDateAsIndex(m_firstDayLocalTime), DaysFiller);
358 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_FIRST_DAY);
359 AddTypeDependentEnableCondition(setting, SETTING_TMR_FIRST_DAY);
361 // "Prevent duplicate episodes" (only for timer rules)
362 setting = AddList(group, SETTING_TMR_NEW_EPISODES, 812, SettingLevel::Basic,
363 m_iPreventDupEpisodes, DupEpisodesFiller, 812);
364 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_NEW_EPISODES);
365 AddTypeDependentEnableCondition(setting, SETTING_TMR_NEW_EPISODES);
367 // Pre and post record time
368 setting = AddList(group, SETTING_TMR_BEGIN_PRE, 813, SettingLevel::Basic, m_iMarginStart,
369 MarginTimeFiller, 813);
370 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_BEGIN_PRE);
371 AddTypeDependentEnableCondition(setting, SETTING_TMR_BEGIN_PRE);
373 setting = AddList(group, SETTING_TMR_END_POST, 814, SettingLevel::Basic, m_iMarginEnd,
374 MarginTimeFiller, 814);
375 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_END_POST);
376 AddTypeDependentEnableCondition(setting, SETTING_TMR_END_POST);
378 // Priority
379 setting = AddList(group, SETTING_TMR_PRIORITY, 19082, SettingLevel::Basic, m_iPriority,
380 PrioritiesFiller, 19082);
381 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_PRIORITY);
382 AddTypeDependentEnableCondition(setting, SETTING_TMR_PRIORITY);
384 // Lifetime
385 setting = AddList(group, SETTING_TMR_LIFETIME, 19083, SettingLevel::Basic, m_iLifetime,
386 LifetimesFiller, 19083);
387 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_LIFETIME);
388 AddTypeDependentEnableCondition(setting, SETTING_TMR_LIFETIME);
390 // MaxRecordings
391 setting = AddList(group, SETTING_TMR_MAX_REC, 818, SettingLevel::Basic, m_iMaxRecordings,
392 MaxRecordingsFiller, 818);
393 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_MAX_REC);
394 AddTypeDependentEnableCondition(setting, SETTING_TMR_MAX_REC);
396 // Recording folder
397 setting = AddEdit(group, SETTING_TMR_DIR, 19076, SettingLevel::Basic, m_strDirectory, true, false,
398 19104);
399 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_DIR);
400 AddTypeDependentEnableCondition(setting, SETTING_TMR_DIR);
402 // Recording group
403 setting = AddList(group, SETTING_TMR_REC_GROUP, 811, SettingLevel::Basic, m_iRecordingGroup,
404 RecordingGroupFiller, 811);
405 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_REC_GROUP);
406 AddTypeDependentEnableCondition(setting, SETTING_TMR_REC_GROUP);
408 // Add-on supplied custom settings
409 m_customTimerSettings->AddSettings(*this, group);
412 void CGUIDialogPVRTimerSettings::AddMultiIntSetting(const std::shared_ptr<CSettingGroup>& group,
413 const std::string& settingName,
414 int settingValue)
416 const std::shared_ptr<CSetting> setting{AddList(group, settingName, 16028, SettingLevel::Basic,
417 settingValue, CustomIntSettingDefinitionsFiller,
418 16028)};
419 AddTypeDependentVisibilityCondition(setting, settingName);
420 AddTypeDependentEnableCondition(setting, settingName);
423 void CGUIDialogPVRTimerSettings::AddSingleIntSetting(const std::shared_ptr<CSettingGroup>& group,
424 const std::string& settingName,
425 int settingValue,
426 int minValue,
427 int step,
428 int maxValue)
430 const std::shared_ptr<CSetting> setting{AddEdit(group, settingName, 16028, SettingLevel::Basic,
431 settingValue, minValue, step, maxValue)};
432 AddTypeDependentVisibilityCondition(setting, settingName);
433 AddTypeDependentEnableCondition(setting, settingName);
436 void CGUIDialogPVRTimerSettings::AddMultiStringSetting(const std::shared_ptr<CSettingGroup>& group,
437 const std::string& settingName,
438 const std::string& settingValue)
440 const std::shared_ptr<CSetting> setting{AddList(group, settingName, 16028, SettingLevel::Basic,
441 settingValue,
442 CustomStringSettingDefinitionsFiller, 16028)};
443 AddTypeDependentVisibilityCondition(setting, settingName);
444 AddTypeDependentEnableCondition(setting, settingName);
447 void CGUIDialogPVRTimerSettings::AddSingleStringSetting(const std::shared_ptr<CSettingGroup>& group,
448 const std::string& settingName,
449 const std::string& settingValue,
450 bool allowEmptyValue)
452 const std::shared_ptr<CSetting> setting{
453 AddEdit(group, settingName, 16028, SettingLevel::Basic, settingValue, allowEmptyValue)};
454 AddTypeDependentVisibilityCondition(setting, settingName);
455 AddTypeDependentEnableCondition(setting, settingName);
458 int CGUIDialogPVRTimerSettings::GetWeekdaysFromSetting(const SettingConstPtr& setting)
460 std::shared_ptr<const CSettingList> settingList =
461 std::static_pointer_cast<const CSettingList>(setting);
462 if (settingList->GetElementType() != SettingType::Integer)
464 CLog::LogF(LOGERROR, "Wrong weekdays element type");
465 return 0;
467 int weekdays = 0;
468 std::vector<CVariant> list = CSettingUtils::GetList(settingList);
469 for (const auto& value : list)
471 if (!value.isInteger())
473 CLog::LogF(LOGERROR, "Wrong weekdays value type");
474 return 0;
476 weekdays += static_cast<int>(value.asInteger());
479 return weekdays;
482 void CGUIDialogPVRTimerSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
484 if (setting == NULL)
486 CLog::LogF(LOGERROR, "No setting");
487 return;
490 CGUIDialogSettingsManualBase::OnSettingChanged(setting);
492 const std::string& settingId = setting->GetId();
494 if (settingId == SETTING_TMR_TYPE)
496 int idx = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
497 const auto it = m_typeEntries.find(idx);
498 if (it != m_typeEntries.end())
500 m_timerType = it->second;
501 m_customTimerSettings->SetTimerType(*m_timerType);
503 // reset certain settings to the defaults of the new timer type
505 if (m_timerType->SupportsPriority())
506 m_iPriority = m_timerType->GetPriorityDefault();
508 if (m_timerType->SupportsLifetime())
509 m_iLifetime = m_timerType->GetLifetimeDefault();
511 if (m_timerType->SupportsMaxRecordings())
512 m_iMaxRecordings = m_timerType->GetMaxRecordingsDefault();
514 if (m_timerType->SupportsRecordingGroup())
515 m_iRecordingGroup = m_timerType->GetRecordingGroupDefault();
517 if (m_timerType->SupportsRecordOnlyNewEpisodes())
518 m_iPreventDupEpisodes = m_timerType->GetPreventDuplicateEpisodesDefault();
520 if (m_timerType->IsTimerRule() && (m_iWeekdays == PVR_WEEKDAY_ALLDAYS))
521 SetButtonLabels(); // update "Any day" vs. "Every day"
523 else
525 CLog::LogF(LOGERROR, "Unable to get 'type' value");
528 else if (settingId == SETTING_TMR_ACTIVE)
530 m_bTimerActive = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
532 else if (settingId == SETTING_TMR_NAME)
534 m_strTitle = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
536 else if (settingId == SETTING_TMR_EPGSEARCH)
538 m_strEpgSearchString = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
540 else if (settingId == SETTING_TMR_FULLTEXT)
542 m_bFullTextEpgSearch = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
544 else if (settingId == SETTING_TMR_CHANNEL)
546 int idx = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
547 const auto it = m_channelEntries.find(idx);
548 if (it != m_channelEntries.end())
550 m_channel = it->second;
552 else
554 CLog::LogF(LOGERROR, "Unable to get 'type' value");
557 else if (settingId == SETTING_TMR_WEEKDAYS)
559 m_iWeekdays = GetWeekdaysFromSetting(setting);
561 else if (settingId == SETTING_TMR_START_ANYTIME)
563 m_bStartAnyTime = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
565 else if (settingId == SETTING_TMR_END_ANYTIME)
567 m_bEndAnyTime = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
569 else if (settingId == SETTING_TMR_START_DAY)
571 SetDateFromIndex(m_startLocalTime,
572 std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
574 else if (settingId == SETTING_TMR_END_DAY)
576 SetDateFromIndex(m_endLocalTime,
577 std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
579 else if (settingId == SETTING_TMR_FIRST_DAY)
581 SetDateFromIndex(m_firstDayLocalTime,
582 std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
584 else if (settingId == SETTING_TMR_NEW_EPISODES)
586 m_iPreventDupEpisodes = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
588 else if (settingId == SETTING_TMR_BEGIN_PRE)
590 m_iMarginStart = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
592 else if (settingId == SETTING_TMR_END_POST)
594 m_iMarginEnd = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
596 else if (settingId == SETTING_TMR_PRIORITY)
598 m_iPriority = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
600 else if (settingId == SETTING_TMR_LIFETIME)
602 m_iLifetime = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
604 else if (settingId == SETTING_TMR_MAX_REC)
606 m_iMaxRecordings = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
608 else if (settingId == SETTING_TMR_DIR)
610 m_strDirectory = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
612 else if (settingId == SETTING_TMR_REC_GROUP)
614 m_iRecordingGroup = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
616 else if (m_customTimerSettings->IsCustomIntSetting(settingId))
618 m_customTimerSettings->UpdateIntProperty(setting);
620 else if (m_customTimerSettings->IsCustomStringSetting(settingId))
622 m_customTimerSettings->UpdateStringProperty(setting);
626 void CGUIDialogPVRTimerSettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
628 if (setting == NULL)
630 CLog::LogF(LOGERROR, "No setting");
631 return;
634 CGUIDialogSettingsManualBase::OnSettingAction(setting);
636 const std::string& settingId = setting->GetId();
637 if (settingId == SETTING_TMR_BEGIN)
639 KODI::TIME::SystemTime timerStartTime;
640 m_startLocalTime.GetAsSystemTime(timerStartTime);
641 if (CGUIDialogNumeric::ShowAndGetTime(timerStartTime, g_localizeStrings.Get(14066)))
643 SetTimeFromSystemTime(m_startLocalTime, timerStartTime);
644 m_timerStartTimeStr = m_startLocalTime.GetAsLocalizedTime("", false);
645 SetButtonLabels();
648 else if (settingId == SETTING_TMR_END)
650 KODI::TIME::SystemTime timerEndTime;
651 m_endLocalTime.GetAsSystemTime(timerEndTime);
652 if (CGUIDialogNumeric::ShowAndGetTime(timerEndTime, g_localizeStrings.Get(14066)))
654 SetTimeFromSystemTime(m_endLocalTime, timerEndTime);
655 m_timerEndTimeStr = m_endLocalTime.GetAsLocalizedTime("", false);
656 SetButtonLabels();
661 bool CGUIDialogPVRTimerSettings::Validate()
663 // @todo: Timer rules may have no date (time-only), so we can't check those for now.
664 // We need to extend the api with additional attributes to properly fix this
665 if (m_timerType->IsTimerRule())
666 return true;
668 bool bStartAnyTime = m_bStartAnyTime;
669 bool bEndAnyTime = m_bEndAnyTime;
671 if (!m_timerType->SupportsStartAnyTime() ||
672 !m_timerType->IsEpgBased()) // Start anytime toggle is not displayed
673 bStartAnyTime = false; // Assume start time change needs checking for
675 if (!m_timerType->SupportsEndAnyTime() ||
676 !m_timerType->IsEpgBased()) // End anytime toggle is not displayed
677 bEndAnyTime = false; // Assume end time change needs checking for
679 // Begin and end time
680 if (!bStartAnyTime && !bEndAnyTime)
682 if (m_timerType->SupportsStartTime() && m_timerType->SupportsEndTime() &&
683 m_endLocalTime < m_startLocalTime)
685 HELPERS::ShowOKDialogText(CVariant{19065}, // "Timer settings"
686 CVariant{19072}); // In order to add/update a timer
687 return false;
691 return true;
694 std::string CGUIDialogPVRTimerSettings::GetSettingsLabel(const std::shared_ptr<ISetting>& setting)
696 // Special handling for add-on supplied custom settings.
697 const std::string label{m_customTimerSettings->GetSettingsLabel(setting->GetId())};
698 if (!label.empty())
699 return label;
701 return CGUIDialogSettingsManualBase::GetSettingsLabel(setting);
704 bool CGUIDialogPVRTimerSettings::Save()
706 if (!Validate())
707 return false;
709 // Timer type
710 m_timerInfoTag->SetTimerType(m_timerType);
712 // Timer active/inactive
713 m_timerInfoTag->m_state = m_bTimerActive ? PVR_TIMER_STATE_SCHEDULED : PVR_TIMER_STATE_DISABLED;
715 // Name
716 m_timerInfoTag->m_strTitle = m_strTitle;
718 // epg search string (only for epg-based timer rules)
719 m_timerInfoTag->m_strEpgSearchString = m_strEpgSearchString;
721 // epg fulltext search, instead of just title match. (only for epg-based timer rules)
722 m_timerInfoTag->m_bFullTextEpgSearch = m_bFullTextEpgSearch;
724 // Channel
725 m_timerInfoTag->m_iClientChannelUid = m_channel.channelUid;
726 m_timerInfoTag->m_iClientId = m_channel.clientId;
727 m_timerInfoTag->m_bIsRadio = m_bIsRadio;
728 m_timerInfoTag->UpdateChannel();
730 if (!m_timerType->SupportsStartAnyTime() ||
731 !m_timerType->IsEpgBased()) // Start anytime toggle is not displayed
732 m_bStartAnyTime = false; // Assume start time change needs checking for
733 m_timerInfoTag->m_bStartAnyTime = m_bStartAnyTime;
735 if (!m_timerType->SupportsEndAnyTime() ||
736 !m_timerType->IsEpgBased()) // End anytime toggle is not displayed
737 m_bEndAnyTime = false; // Assume end time change needs checking for
738 m_timerInfoTag->m_bEndAnyTime = m_bEndAnyTime;
740 // Begin and end time
741 if (!m_bStartAnyTime && !m_bEndAnyTime)
743 if (m_timerType->SupportsStartTime() && // has start clock entry
744 m_timerType->SupportsEndTime() && // and end clock entry
745 m_timerType->IsTimerRule()) // but no associated start/end day spinners
747 if (m_endLocalTime < m_startLocalTime) // And the end clock is earlier than the start clock
749 CLog::LogFC(LOGDEBUG, LOGPVR, "End before start, adding a day.");
750 m_endLocalTime += CDateTimeSpan(1, 0, 0, 0);
751 if (m_endLocalTime < m_startLocalTime)
753 CLog::Log(LOGWARNING,
754 "Timer settings dialog: End before start. Setting end time to start time.");
755 m_endLocalTime = m_startLocalTime;
758 else if (m_endLocalTime >
759 (m_startLocalTime + CDateTimeSpan(1, 0, 0, 0))) // Or the duration is more than a day
761 CLog::LogFC(LOGDEBUG, LOGPVR, "End > 1 day after start, removing a day.");
762 m_endLocalTime -= CDateTimeSpan(1, 0, 0, 0);
763 if (m_endLocalTime > (m_startLocalTime + CDateTimeSpan(1, 0, 0, 0)))
765 CLog::Log(
766 LOGWARNING,
767 "Timer settings dialog: End > 1 day after start. Setting end time to start time.");
768 m_endLocalTime = m_startLocalTime;
772 else if (m_endLocalTime < m_startLocalTime)
774 // this case will fail validation so this can't be reached.
776 m_timerInfoTag->SetStartFromLocalTime(m_startLocalTime);
777 m_timerInfoTag->SetEndFromLocalTime(m_endLocalTime);
779 else if (!m_bStartAnyTime)
780 m_timerInfoTag->SetStartFromLocalTime(m_startLocalTime);
781 else if (!m_bEndAnyTime)
782 m_timerInfoTag->SetEndFromLocalTime(m_endLocalTime);
784 // Days of week (only for timer rules)
785 if (m_timerType->IsTimerRule())
786 m_timerInfoTag->m_iWeekdays = m_iWeekdays;
787 else
788 m_timerInfoTag->m_iWeekdays = PVR_WEEKDAY_NONE;
790 // First day (only for timer rules)
791 m_timerInfoTag->SetFirstDayFromLocalTime(m_firstDayLocalTime);
793 // "New episodes only" (only for timer rules)
794 m_timerInfoTag->m_iPreventDupEpisodes = m_iPreventDupEpisodes;
796 // Pre and post record time
797 m_timerInfoTag->m_iMarginStart = m_iMarginStart;
798 m_timerInfoTag->m_iMarginEnd = m_iMarginEnd;
800 // Priority
801 m_timerInfoTag->m_iPriority = m_iPriority;
803 // Lifetime
804 m_timerInfoTag->m_iLifetime = m_iLifetime;
806 // MaxRecordings
807 m_timerInfoTag->m_iMaxRecordings = m_iMaxRecordings;
809 // Recording folder
810 m_timerInfoTag->m_strDirectory = m_strDirectory;
812 // Recording group
813 m_timerInfoTag->m_iRecordingGroup = m_iRecordingGroup;
815 // Custom properties
816 m_timerInfoTag->m_customProps = m_customTimerSettings->GetProperties();
818 // Set the timer's title to the channel name if it's empty or 'New Timer'
819 if (m_strTitle.empty() || m_strTitle == g_localizeStrings.Get(19056))
821 const std::string channelName = m_timerInfoTag->ChannelName();
822 if (!channelName.empty())
823 m_timerInfoTag->m_strTitle = channelName;
826 // Update summary
827 m_timerInfoTag->UpdateSummary();
829 return true;
832 void CGUIDialogPVRTimerSettings::SetButtonLabels()
834 // timer start time
835 BaseSettingControlPtr settingControl = GetSettingControl(SETTING_TMR_BEGIN);
836 if (settingControl != NULL && settingControl->GetControl() != NULL)
838 SET_CONTROL_LABEL2(settingControl->GetID(), m_timerStartTimeStr);
841 // timer end time
842 settingControl = GetSettingControl(SETTING_TMR_END);
843 if (settingControl != NULL && settingControl->GetControl() != NULL)
845 SET_CONTROL_LABEL2(settingControl->GetID(), m_timerEndTimeStr);
849 void CGUIDialogPVRTimerSettings::AddCondition(const std::shared_ptr<CSetting>& setting,
850 const std::string& identifier,
851 SettingConditionCheck condition,
852 SettingDependencyType depType,
853 const std::string& settingId)
855 GetSettingsManager()->AddDynamicCondition(identifier, condition, this);
856 CSettingDependency dep(depType, GetSettingsManager());
857 dep.And()->Add(std::make_shared<CSettingDependencyCondition>(identifier, "true", settingId, false,
858 GetSettingsManager()));
859 SettingDependencies deps(setting->GetDependencies());
860 deps.push_back(dep);
861 setting->SetDependencies(deps);
864 int CGUIDialogPVRTimerSettings::GetDateAsIndex(const CDateTime& datetime)
866 const CDateTime date(datetime.GetYear(), datetime.GetMonth(), datetime.GetDay(), 0, 0, 0);
867 time_t t(0);
868 date.GetAsTime(t);
869 return static_cast<int>(t);
872 void CGUIDialogPVRTimerSettings::SetDateFromIndex(CDateTime& datetime, int date)
874 const CDateTime newDate(static_cast<time_t>(date));
875 datetime.SetDateTime(newDate.GetYear(), newDate.GetMonth(), newDate.GetDay(), datetime.GetHour(),
876 datetime.GetMinute(), datetime.GetSecond());
879 void CGUIDialogPVRTimerSettings::SetTimeFromSystemTime(CDateTime& datetime,
880 const KODI::TIME::SystemTime& time)
882 const CDateTime newTime(time);
883 datetime.SetDateTime(datetime.GetYear(), datetime.GetMonth(), datetime.GetDay(),
884 newTime.GetHour(), newTime.GetMinute(), newTime.GetSecond());
887 void CGUIDialogPVRTimerSettings::InitializeTypesList()
889 m_typeEntries.clear();
891 // If timer is read-only or was created by a timer rule, only add current type, for information. Type can't be changed.
892 if (m_timerType->IsReadOnly() || m_timerInfoTag->HasParent())
894 m_typeEntries.insert(std::make_pair(0, m_timerType));
895 return;
898 bool bFoundThisType(false);
899 int idx(0);
900 const std::vector<std::shared_ptr<CPVRTimerType>> types(CPVRTimerType::GetAllTypes());
901 for (const auto& type : types)
903 // Type definition prohibits created of new instances.
904 // But the dialog can act as a viewer for these types.
905 if (type->ForbidsNewInstances())
906 continue;
908 // Read-only timers cannot be created using this dialog.
909 // But the dialog can act as a viewer for read-only types.
910 if (type->IsReadOnly())
911 continue;
913 // Drop TimerTypes that require EPGInfo, if none is populated
914 if (type->RequiresEpgTagOnCreate() && !m_timerInfoTag->GetEpgInfoTag())
915 continue;
917 // Drop TimerTypes without 'Series' EPG attributes if none are set
918 if (type->RequiresEpgSeriesOnCreate())
920 const std::shared_ptr<const CPVREpgInfoTag> epgTag(m_timerInfoTag->GetEpgInfoTag());
921 if (epgTag && !epgTag->IsSeries())
922 continue;
925 // Drop TimerTypes which need series link if none is set
926 if (type->RequiresEpgSeriesLinkOnCreate())
928 const std::shared_ptr<const CPVREpgInfoTag> epgTag(m_timerInfoTag->GetEpgInfoTag());
929 if (!epgTag || epgTag->SeriesLink().empty())
930 continue;
933 // Drop TimerTypes that forbid EPGInfo, if it is populated
934 if (type->ForbidsEpgTagOnCreate() && m_timerInfoTag->GetEpgInfoTag())
935 continue;
937 // Drop TimerTypes that aren't rules and cannot be recorded
938 if (!type->IsTimerRule())
940 const std::shared_ptr<const CPVREpgInfoTag> epgTag(m_timerInfoTag->GetEpgInfoTag());
941 bool bCanRecord = epgTag ? epgTag->IsRecordable()
942 : m_timerInfoTag->EndAsLocalTime() > CDateTime::GetCurrentDateTime();
943 if (!bCanRecord)
944 continue;
947 if (!bFoundThisType && *type == *m_timerType)
948 bFoundThisType = true;
950 m_typeEntries.insert(std::make_pair(idx++, type));
953 if (!bFoundThisType)
954 m_typeEntries.insert(std::make_pair(idx++, m_timerType));
957 void CGUIDialogPVRTimerSettings::InitializeChannelsList()
959 m_channelEntries.clear();
961 int index = 0;
963 // Add special "any channel" entries - one for every client (used for epg-based timer rules),
964 // and for reminder rules another one representing any channel from any client.
965 const CPVRClientMap clients = CServiceBroker::GetPVRManager().Clients()->GetCreatedClients();
966 if (clients.size() > 1)
967 m_channelEntries.insert(
968 {index++, ChannelDescriptor(PVR_CHANNEL_INVALID_UID, PVR_CLIENT_INVALID_UID,
969 // Any channel from any client
970 g_localizeStrings.Get(854))});
972 for (const auto& client : clients)
974 m_channelEntries.insert(
975 {index, ChannelDescriptor(PVR_CHANNEL_INVALID_UID, client.second->GetID(),
976 clients.size() == 1
977 // Any channel
978 ? g_localizeStrings.Get(809)
979 // Any channel from client "X"
980 : StringUtils::Format(g_localizeStrings.Get(853),
981 client.second->GetFullClientName()))});
982 ++index;
985 // Add regular channels
986 const std::shared_ptr<const CPVRChannelGroup> allGroup =
987 CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAll(m_bIsRadio);
988 const std::vector<std::shared_ptr<CPVRChannelGroupMember>> groupMembers =
989 allGroup->GetMembers(CPVRChannelGroup::Include::ONLY_VISIBLE);
990 for (const auto& groupMember : groupMembers)
992 const std::shared_ptr<const CPVRChannel> channel = groupMember->Channel();
993 const std::string channelDescription = StringUtils::Format(
994 "{} {}", groupMember->ChannelNumber().FormattedChannelNumber(), channel->ChannelName());
995 m_channelEntries.insert(
996 {index, ChannelDescriptor(channel->UniqueID(), channel->ClientID(), channelDescription)});
997 ++index;
1001 void CGUIDialogPVRTimerSettings::TypesFiller(const SettingConstPtr& setting,
1002 std::vector<IntegerSettingOption>& list,
1003 int& current,
1004 void* data)
1006 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1007 if (pThis)
1009 list.clear();
1010 current = 0;
1012 static const std::vector<std::pair<std::string, CVariant>> reminderTimerProps{
1013 std::make_pair("PVR.IsRemindingTimer", CVariant{true})};
1014 static const std::vector<std::pair<std::string, CVariant>> recordingTimerProps{
1015 std::make_pair("PVR.IsRecordingTimer", CVariant{true})};
1017 const auto clients = CServiceBroker::GetPVRManager().Clients();
1019 bool foundCurrent(false);
1020 for (const auto& typeEntry : pThis->m_typeEntries)
1022 std::string clientName;
1024 const auto client = clients->GetCreatedClient(typeEntry.second->GetClientId());
1025 if (client)
1026 clientName = client->GetFullClientName();
1028 list.emplace_back(typeEntry.second->GetDescription(), clientName, typeEntry.first,
1029 typeEntry.second->IsReminder() ? reminderTimerProps : recordingTimerProps);
1031 if (!foundCurrent && (*(pThis->m_timerType) == *(typeEntry.second)))
1033 current = typeEntry.first;
1034 foundCurrent = true;
1038 else
1039 CLog::LogF(LOGERROR, "No dialog");
1042 void CGUIDialogPVRTimerSettings::ChannelsFiller(const SettingConstPtr& setting,
1043 std::vector<IntegerSettingOption>& list,
1044 int& current,
1045 void* data)
1047 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1048 if (pThis)
1050 list.clear();
1051 current = 0;
1053 bool foundCurrent(false);
1054 for (const auto& channelEntry : pThis->m_channelEntries)
1056 // Only include channels for the currently selected timer type or all channels if type is client-independent.
1057 if (pThis->m_timerType->GetClientId() == PVR_CLIENT_INVALID_UID || // client-independent
1058 pThis->m_timerType->GetClientId() == channelEntry.second.clientId)
1060 // Do not add "any channel" entry if not supported by selected timer type.
1061 if (channelEntry.second.channelUid == PVR_CHANNEL_INVALID_UID &&
1062 !pThis->m_timerType->SupportsAnyChannel())
1063 continue;
1065 // Do not add "any channel from any client" entry for reminder rules.
1066 if (channelEntry.second.channelUid == PVR_CHANNEL_INVALID_UID &&
1067 channelEntry.second.clientId == PVR_CLIENT_INVALID_UID &&
1068 !pThis->m_timerType->IsReminder() && !pThis->m_timerType->IsTimerRule())
1069 continue;
1071 list.emplace_back(channelEntry.second.description, channelEntry.first);
1074 if (!foundCurrent && (pThis->m_channel == channelEntry.second))
1076 current = channelEntry.first;
1077 foundCurrent = true;
1081 if (foundCurrent)
1083 // Verify m_channel is still valid. Update if not.
1084 if (std::find_if(list.cbegin(), list.cend(),
1085 [&current](const auto& channel)
1086 { return channel.value == current; }) == list.cend())
1088 // Set m_channel and current to first valid channel in list
1089 const int first{list.front().value};
1090 const auto it =
1091 std::find_if(pThis->m_channelEntries.cbegin(), pThis->m_channelEntries.cend(),
1092 [first](const auto& channel) { return channel.first == first; });
1094 if (it != pThis->m_channelEntries.cend())
1096 current = (*it).first;
1097 pThis->m_channel = (*it).second;
1099 else
1101 CLog::LogF(LOGERROR, "Unable to find channel to select");
1106 else
1107 CLog::LogF(LOGERROR, "No dialog");
1110 void CGUIDialogPVRTimerSettings::DaysFiller(const SettingConstPtr& setting,
1111 std::vector<IntegerSettingOption>& list,
1112 int& current,
1113 void* data)
1115 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1116 if (pThis)
1118 list.clear();
1119 current = 0;
1121 // Data range: "today" until "yesterday next year"
1122 const CDateTime now(CDateTime::GetCurrentDateTime());
1123 CDateTime time(now.GetYear(), now.GetMonth(), now.GetDay(), 0, 0, 0);
1124 const CDateTime yesterdayPlusOneYear(CDateTime(time.GetYear() + 1, time.GetMonth(),
1125 time.GetDay(), time.GetHour(), time.GetMinute(),
1126 time.GetSecond()) -
1127 CDateTimeSpan(1, 0, 0, 0));
1129 CDateTime oldCDateTime;
1130 if (setting->GetId() == SETTING_TMR_FIRST_DAY)
1131 oldCDateTime = pThis->m_timerInfoTag->FirstDayAsLocalTime();
1132 else if (setting->GetId() == SETTING_TMR_START_DAY)
1133 oldCDateTime = pThis->m_timerInfoTag->StartAsLocalTime();
1134 else
1135 oldCDateTime = pThis->m_timerInfoTag->EndAsLocalTime();
1136 const CDateTime oldCDate(oldCDateTime.GetYear(), oldCDateTime.GetMonth(), oldCDateTime.GetDay(),
1137 0, 0, 0);
1139 if ((oldCDate < time) || (oldCDate > yesterdayPlusOneYear))
1140 list.emplace_back(oldCDate.GetAsLocalizedDate(true /*long date*/), GetDateAsIndex(oldCDate));
1142 while (time <= yesterdayPlusOneYear)
1144 list.emplace_back(time.GetAsLocalizedDate(true /*long date*/), GetDateAsIndex(time));
1145 time += CDateTimeSpan(1, 0, 0, 0);
1148 if (setting->GetId() == SETTING_TMR_FIRST_DAY)
1149 current = GetDateAsIndex(pThis->m_firstDayLocalTime);
1150 else if (setting->GetId() == SETTING_TMR_START_DAY)
1151 current = GetDateAsIndex(pThis->m_startLocalTime);
1152 else
1153 current = GetDateAsIndex(pThis->m_endLocalTime);
1155 else
1156 CLog::LogF(LOGERROR, "No dialog");
1159 void CGUIDialogPVRTimerSettings::DupEpisodesFiller(const SettingConstPtr& setting,
1160 std::vector<IntegerSettingOption>& list,
1161 int& current,
1162 void* data)
1164 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1165 if (pThis)
1167 list.clear();
1169 const std::vector<SettingIntValue>& values{
1170 pThis->m_timerType->GetPreventDuplicateEpisodesValues()};
1171 std::transform(values.cbegin(), values.cend(), std::back_inserter(list), [](const auto& value) {
1172 return IntegerSettingOption(value.first, value.second);
1175 current = pThis->m_iPreventDupEpisodes;
1177 else
1178 CLog::LogF(LOGERROR, "No dialog");
1181 void CGUIDialogPVRTimerSettings::WeekdaysFiller(const SettingConstPtr& setting,
1182 std::vector<IntegerSettingOption>& list,
1183 int& current,
1184 void* data)
1186 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1187 if (pThis)
1189 list.clear();
1190 list.emplace_back(g_localizeStrings.Get(831), PVR_WEEKDAY_MONDAY); // "Mondays"
1191 list.emplace_back(g_localizeStrings.Get(832), PVR_WEEKDAY_TUESDAY); // "Tuesdays"
1192 list.emplace_back(g_localizeStrings.Get(833), PVR_WEEKDAY_WEDNESDAY); // "Wednesdays"
1193 list.emplace_back(g_localizeStrings.Get(834), PVR_WEEKDAY_THURSDAY); // "Thursdays"
1194 list.emplace_back(g_localizeStrings.Get(835), PVR_WEEKDAY_FRIDAY); // "Fridays"
1195 list.emplace_back(g_localizeStrings.Get(836), PVR_WEEKDAY_SATURDAY); // "Saturdays"
1196 list.emplace_back(g_localizeStrings.Get(837), PVR_WEEKDAY_SUNDAY); // "Sundays"
1198 current = pThis->m_iWeekdays;
1200 else
1201 CLog::LogF(LOGERROR, "No dialog");
1204 void CGUIDialogPVRTimerSettings::PrioritiesFiller(const SettingConstPtr& setting,
1205 std::vector<IntegerSettingOption>& list,
1206 int& current,
1207 void* data)
1209 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1210 if (pThis)
1212 list.clear();
1214 const std::vector<SettingIntValue>& values{pThis->m_timerType->GetPriorityValues()};
1215 std::transform(values.cbegin(), values.cend(), std::back_inserter(list), [](const auto& value) {
1216 return IntegerSettingOption(value.first, value.second);
1219 current = pThis->m_iPriority;
1221 auto it = list.begin();
1222 while (it != list.end())
1224 if (it->value == current)
1225 break; // value already in list
1227 ++it;
1230 if (it == list.end())
1232 // PVR backend supplied value is not in the list of predefined values. Insert it.
1233 list.insert(it, IntegerSettingOption(std::to_string(current), current));
1236 else
1237 CLog::LogF(LOGERROR, "No dialog");
1240 void CGUIDialogPVRTimerSettings::LifetimesFiller(const SettingConstPtr& setting,
1241 std::vector<IntegerSettingOption>& list,
1242 int& current,
1243 void* data)
1245 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1246 if (pThis)
1248 list.clear();
1250 const std::vector<SettingIntValue>& values{pThis->m_timerType->GetLifetimeValues()};
1251 std::transform(values.cbegin(), values.cend(), std::back_inserter(list), [](const auto& value) {
1252 return IntegerSettingOption(value.first, value.second);
1255 current = pThis->m_iLifetime;
1257 auto it = list.begin();
1258 while (it != list.end())
1260 if (it->value == current)
1261 break; // value already in list
1263 ++it;
1266 if (it == list.end())
1268 // PVR backend supplied value is not in the list of predefined values. Insert it.
1269 list.insert(it, IntegerSettingOption(
1270 StringUtils::Format(g_localizeStrings.Get(17999), current) /* {} days */,
1271 current));
1274 else
1275 CLog::LogF(LOGERROR, "No dialog");
1278 void CGUIDialogPVRTimerSettings::MaxRecordingsFiller(const SettingConstPtr& setting,
1279 std::vector<IntegerSettingOption>& list,
1280 int& current,
1281 void* data)
1283 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1284 if (pThis)
1286 list.clear();
1288 const std::vector<SettingIntValue>& values{pThis->m_timerType->GetMaxRecordingsValues()};
1289 std::transform(values.cbegin(), values.cend(), std::back_inserter(list), [](const auto& value) {
1290 return IntegerSettingOption(value.first, value.second);
1293 current = pThis->m_iMaxRecordings;
1295 auto it = list.begin();
1296 while (it != list.end())
1298 if (it->value == current)
1299 break; // value already in list
1301 ++it;
1304 if (it == list.end())
1306 // PVR backend supplied value is not in the list of predefined values. Insert it.
1307 list.insert(it, IntegerSettingOption(std::to_string(current), current));
1310 else
1311 CLog::LogF(LOGERROR, "No dialog");
1314 void CGUIDialogPVRTimerSettings::RecordingGroupFiller(const SettingConstPtr& setting,
1315 std::vector<IntegerSettingOption>& list,
1316 int& current,
1317 void* data)
1319 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1320 if (pThis)
1322 list.clear();
1324 const std::vector<SettingIntValue>& values{pThis->m_timerType->GetRecordingGroupValues()};
1325 std::transform(values.cbegin(), values.cend(), std::back_inserter(list), [](const auto& value) {
1326 return IntegerSettingOption(value.first, value.second);
1329 current = pThis->m_iRecordingGroup;
1331 else
1332 CLog::LogF(LOGERROR, "No dialog");
1335 void CGUIDialogPVRTimerSettings::MarginTimeFiller(const SettingConstPtr& setting,
1336 std::vector<IntegerSettingOption>& list,
1337 int& current,
1338 void* data)
1340 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1341 if (pThis)
1343 list.clear();
1345 // Get global settings values
1346 CPVRSettings::MarginTimeFiller(setting, list, current, data);
1348 if (setting->GetId() == SETTING_TMR_BEGIN_PRE)
1349 current = pThis->m_iMarginStart;
1350 else
1351 current = pThis->m_iMarginEnd;
1353 bool bInsertValue = true;
1354 auto it = list.begin();
1355 while (it != list.end())
1357 if (it->value == current)
1359 bInsertValue = false;
1360 break; // value already in list
1363 if (it->value > current)
1364 break;
1366 ++it;
1369 if (bInsertValue)
1371 // PVR backend supplied value is not in the list of predefined values. Insert it.
1372 list.insert(it, IntegerSettingOption(
1373 StringUtils::Format(g_localizeStrings.Get(14044), current) /* {} min */,
1374 current));
1377 else
1378 CLog::LogF(LOGERROR, "No dialog");
1381 void CGUIDialogPVRTimerSettings::CustomIntSettingDefinitionsFiller(
1382 const std::shared_ptr<const CSetting>& setting,
1383 std::vector<IntegerSettingOption>& list,
1384 int& current,
1385 void* data)
1387 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1388 if (pThis)
1390 list.clear();
1392 const std::string settingId{setting->GetId()};
1393 if (pThis->m_customTimerSettings->IsCustomIntSetting(settingId))
1394 pThis->m_customTimerSettings->IntSettingDefinitionsFiller(settingId, list, current);
1396 else
1397 CLog::LogF(LOGERROR, "No dialog");
1400 void CGUIDialogPVRTimerSettings::CustomStringSettingDefinitionsFiller(
1401 const std::shared_ptr<const CSetting>& setting,
1402 std::vector<StringSettingOption>& list,
1403 std::string& current,
1404 void* data)
1406 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1407 if (pThis)
1409 list.clear();
1411 const std::string settingId{setting->GetId()};
1412 if (pThis->m_customTimerSettings->IsCustomStringSetting(settingId))
1413 pThis->m_customTimerSettings->StringSettingDefinitionsFiller(settingId, list, current);
1415 else
1416 CLog::LogF(LOGERROR, "No dialog");
1419 std::string CGUIDialogPVRTimerSettings::WeekdaysValueFormatter(const SettingConstPtr& setting)
1421 return CPVRTimerInfoTag::GetWeekdaysString(GetWeekdaysFromSetting(setting), true, true);
1424 void CGUIDialogPVRTimerSettings::AddTypeDependentEnableCondition(
1425 const std::shared_ptr<CSetting>& setting, const std::string& identifier)
1427 // Enable setting depending on read-only attribute of the selected timer type
1428 std::string id(identifier);
1429 id.append(TYPE_DEP_ENABLE_COND_ID_POSTFIX);
1430 AddCondition(setting, id, TypeReadOnlyCondition, SettingDependencyType::Enable, SETTING_TMR_TYPE);
1433 bool CGUIDialogPVRTimerSettings::TypeReadOnlyCondition(const std::string& condition,
1434 const std::string& value,
1435 const SettingConstPtr& setting,
1436 void* data)
1438 if (setting == NULL)
1439 return false;
1441 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1442 if (pThis == NULL)
1444 CLog::LogF(LOGERROR, "No dialog");
1445 return false;
1448 if (!StringUtils::EqualsNoCase(value, "true"))
1449 return false;
1451 std::string cond(condition);
1452 cond.erase(cond.find(TYPE_DEP_ENABLE_COND_ID_POSTFIX));
1454 // If only one type is available, disable type selector.
1455 if (pThis->m_typeEntries.size() == 1)
1457 if (cond == SETTING_TMR_TYPE)
1458 return false;
1461 // For existing one time epg-based timers, disable editing of epg-filled data.
1462 if (!pThis->m_bIsNewTimer && pThis->m_timerType->IsEpgBasedOnetime())
1464 if ((cond == SETTING_TMR_NAME) || (cond == SETTING_TMR_CHANNEL) ||
1465 (cond == SETTING_TMR_START_DAY) || (cond == SETTING_TMR_END_DAY) ||
1466 (cond == SETTING_TMR_BEGIN) || (cond == SETTING_TMR_END))
1467 return false;
1470 /* Always enable enable/disable, if supported by the timer type. */
1471 if (pThis->m_timerType->SupportsEnableDisable() && !pThis->m_timerInfoTag->IsBroken())
1473 if (cond == SETTING_TMR_ACTIVE)
1474 return true;
1477 /* Handle recordings in progress. */
1478 if (pThis->m_timerInfoTag->State() == PVR_TIMER_STATE_RECORDING)
1480 if (cond == SETTING_TMR_TYPE || cond == SETTING_TMR_CHANNEL || cond == SETTING_TMR_BEGIN_PRE ||
1481 cond == SETTING_TMR_START_DAY || cond == SETTING_TMR_BEGIN ||
1482 cond == SETTING_TMR_PRIORITY || cond == SETTING_TMR_DIR)
1483 return false;
1486 if (pThis->m_customTimerSettings->IsCustomSetting(cond))
1488 return !pThis->m_customTimerSettings->IsSettingReadonlyForTimerState(
1489 cond, pThis->m_timerInfoTag->State());
1492 // Let the PVR client decide...
1493 int idx = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
1494 const auto entry = pThis->m_typeEntries.find(idx);
1495 if (entry != pThis->m_typeEntries.end())
1496 return !entry->second->IsReadOnly();
1497 else
1498 CLog::LogF(LOGERROR, "No type entry");
1500 return false;
1503 void CGUIDialogPVRTimerSettings::AddTypeDependentVisibilityCondition(
1504 const std::shared_ptr<CSetting>& setting, const std::string& identifier)
1506 // Show or hide setting depending on attributes of the selected timer type
1507 std::string id(identifier);
1508 id.append(TYPE_DEP_VISIBI_COND_ID_POSTFIX);
1509 AddCondition(setting, id, TypeSupportsCondition, SettingDependencyType::Visible,
1510 SETTING_TMR_TYPE);
1513 bool CGUIDialogPVRTimerSettings::TypeSupportsCondition(const std::string& condition,
1514 const std::string& value,
1515 const SettingConstPtr& setting,
1516 void* data)
1518 if (setting == NULL)
1519 return false;
1521 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1522 if (pThis == NULL)
1524 CLog::LogF(LOGERROR, "No dialog");
1525 return false;
1528 if (!StringUtils::EqualsNoCase(value, "true"))
1529 return false;
1531 int idx = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
1532 const auto entry = pThis->m_typeEntries.find(idx);
1533 if (entry != pThis->m_typeEntries.end())
1535 std::string cond(condition);
1536 cond.erase(cond.find(TYPE_DEP_VISIBI_COND_ID_POSTFIX));
1538 if (cond == SETTING_TMR_EPGSEARCH)
1539 return entry->second->SupportsEpgTitleMatch() || entry->second->SupportsEpgFulltextMatch();
1540 else if (cond == SETTING_TMR_FULLTEXT)
1541 return entry->second->SupportsEpgFulltextMatch();
1542 else if (cond == SETTING_TMR_ACTIVE)
1543 return entry->second->SupportsEnableDisable();
1544 else if (cond == SETTING_TMR_CHANNEL)
1545 return entry->second->SupportsChannels();
1546 else if (cond == SETTING_TMR_START_ANYTIME)
1547 return entry->second->SupportsStartAnyTime() && entry->second->IsEpgBased();
1548 else if (cond == SETTING_TMR_END_ANYTIME)
1549 return entry->second->SupportsEndAnyTime() && entry->second->IsEpgBased();
1550 else if (cond == SETTING_TMR_START_DAY)
1551 return entry->second->SupportsStartTime() && entry->second->IsOnetime();
1552 else if (cond == SETTING_TMR_END_DAY)
1553 return entry->second->SupportsEndTime() && entry->second->IsOnetime();
1554 else if (cond == SETTING_TMR_BEGIN)
1555 return entry->second->SupportsStartTime();
1556 else if (cond == SETTING_TMR_END)
1557 return entry->second->SupportsEndTime();
1558 else if (cond == SETTING_TMR_WEEKDAYS)
1559 return entry->second->SupportsWeekdays();
1560 else if (cond == SETTING_TMR_FIRST_DAY)
1561 return entry->second->SupportsFirstDay();
1562 else if (cond == SETTING_TMR_NEW_EPISODES)
1563 return entry->second->SupportsRecordOnlyNewEpisodes();
1564 else if (cond == SETTING_TMR_BEGIN_PRE)
1565 return entry->second->SupportsStartMargin();
1566 else if (cond == SETTING_TMR_END_POST)
1567 return entry->second->SupportsEndMargin();
1568 else if (cond == SETTING_TMR_PRIORITY)
1569 return entry->second->SupportsPriority();
1570 else if (cond == SETTING_TMR_LIFETIME)
1571 return entry->second->SupportsLifetime();
1572 else if (cond == SETTING_TMR_MAX_REC)
1573 return entry->second->SupportsMaxRecordings();
1574 else if (cond == SETTING_TMR_DIR)
1575 return entry->second->SupportsRecordingFolders();
1576 else if (cond == SETTING_TMR_REC_GROUP)
1577 return entry->second->SupportsRecordingGroup();
1578 else if (pThis->m_customTimerSettings->IsCustomSetting(cond))
1579 return pThis->m_customTimerSettings->IsSettingSupportedForTimerType(cond, *entry->second);
1580 else
1581 CLog::LogF(LOGERROR, "Unknown condition");
1583 else
1585 CLog::LogF(LOGERROR, "No type entry");
1587 return false;
1590 void CGUIDialogPVRTimerSettings::AddStartAnytimeDependentVisibilityCondition(
1591 const std::shared_ptr<CSetting>& setting, const std::string& identifier)
1593 // Show or hide setting depending on value of setting "any time"
1594 std::string id(identifier);
1595 id.append(START_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX);
1596 AddCondition(setting, id, StartAnytimeSetCondition, SettingDependencyType::Visible,
1597 SETTING_TMR_START_ANYTIME);
1600 bool CGUIDialogPVRTimerSettings::StartAnytimeSetCondition(const std::string& condition,
1601 const std::string& value,
1602 const SettingConstPtr& setting,
1603 void* data)
1605 if (setting == NULL)
1606 return false;
1608 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1609 if (pThis == NULL)
1611 CLog::LogF(LOGERROR, "No dialog");
1612 return false;
1615 if (!StringUtils::EqualsNoCase(value, "true"))
1616 return false;
1618 // "any time" setting is only relevant for epg-based timers.
1619 if (!pThis->m_timerType->IsEpgBased())
1620 return true;
1622 // If 'Start anytime' option isn't supported, don't hide start time
1623 if (!pThis->m_timerType->SupportsStartAnyTime())
1624 return true;
1626 std::string cond(condition);
1627 cond.erase(cond.find(START_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX));
1629 if ((cond == SETTING_TMR_START_DAY) || (cond == SETTING_TMR_BEGIN))
1631 bool bAnytime = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
1632 return !bAnytime;
1634 return false;
1637 void CGUIDialogPVRTimerSettings::AddEndAnytimeDependentVisibilityCondition(
1638 const std::shared_ptr<CSetting>& setting, const std::string& identifier)
1640 // Show or hide setting depending on value of setting "any time"
1641 std::string id(identifier);
1642 id.append(END_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX);
1643 AddCondition(setting, id, EndAnytimeSetCondition, SettingDependencyType::Visible,
1644 SETTING_TMR_END_ANYTIME);
1647 bool CGUIDialogPVRTimerSettings::EndAnytimeSetCondition(const std::string& condition,
1648 const std::string& value,
1649 const SettingConstPtr& setting,
1650 void* data)
1652 if (setting == NULL)
1653 return false;
1655 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1656 if (pThis == NULL)
1658 CLog::LogF(LOGERROR, "No dialog");
1659 return false;
1662 if (!StringUtils::EqualsNoCase(value, "true"))
1663 return false;
1665 // "any time" setting is only relevant for epg-based timers.
1666 if (!pThis->m_timerType->IsEpgBased())
1667 return true;
1669 // If 'End anytime' option isn't supported, don't hide end time
1670 if (!pThis->m_timerType->SupportsEndAnyTime())
1671 return true;
1673 std::string cond(condition);
1674 cond.erase(cond.find(END_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX));
1676 if ((cond == SETTING_TMR_END_DAY) || (cond == SETTING_TMR_END))
1678 bool bAnytime = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
1679 return !bAnytime;
1681 return false;