1 /*
2 * Copyright (C) 2012-2018 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 "GUIDialogPVRTimerSettings.h"
10
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/PVRManager.h"
17 #include "pvr/addons/PVRClient.h"
18 #include "pvr/addons/PVRClients.h"
19 #include "pvr/channels/PVRChannel.h"
20 #include "pvr/channels/PVRChannelGroup.h"
21 #include "pvr/channels/PVRChannelGroupsContainer.h"
22 #include "pvr/epg/EpgInfoTag.h"
23 #include "pvr/timers/PVRTimerInfoTag.h"
24 #include "pvr/timers/PVRTimerType.h"
25 #include "settings/SettingUtils.h"
26 #include "settings/dialogs/GUIDialogSettingsBase.h"
27 #include "settings/lib/Setting.h"
28 #include "settings/lib/SettingsManager.h"
29 #include "settings/windows/GUIControlSettings.h"
30 #include "utils/StringUtils.h"
31 #include "utils/Variant.h"
32 #include "utils/log.h"
33
34 #include <memory>
35 #include <string>
36 #include <utility>
37 #include <vector>
38
39 using namespace PVR;
40 using namespace KODI::MESSAGING;
41
42 #define SETTING_TMR_TYPE "timer.type"
43 #define SETTING_TMR_ACTIVE "timer.active"
44 #define SETTING_TMR_NAME "timer.name"
45 #define SETTING_TMR_EPGSEARCH "timer.epgsearch"
46 #define SETTING_TMR_FULLTEXT "timer.fulltext"
47 #define SETTING_TMR_CHANNEL "timer.channel"
48 #define SETTING_TMR_START_ANYTIME "timer.startanytime"
49 #define SETTING_TMR_END_ANYTIME "timer.endanytime"
50 #define SETTING_TMR_START_DAY "timer.startday"
51 #define SETTING_TMR_END_DAY "timer.endday"
52 #define SETTING_TMR_BEGIN "timer.begin"
53 #define SETTING_TMR_END "timer.end"
54 #define SETTING_TMR_WEEKDAYS "timer.weekdays"
55 #define SETTING_TMR_FIRST_DAY "timer.firstday"
56 #define SETTING_TMR_NEW_EPISODES "timer.newepisodes"
57 #define SETTING_TMR_BEGIN_PRE "timer.startmargin"
58 #define SETTING_TMR_END_POST "timer.endmargin"
59 #define SETTING_TMR_PRIORITY "timer.priority"
60 #define SETTING_TMR_LIFETIME "timer.lifetime"
61 #define SETTING_TMR_MAX_REC "timer.maxrecordings"
62 #define SETTING_TMR_DIR "timer.directory"
63 #define SETTING_TMR_REC_GROUP "timer.recgroup"
64
65 #define TYPE_DEP_VISIBI_COND_ID_POSTFIX "visibi.typedep"
66 #define TYPE_DEP_ENABLE_COND_ID_POSTFIX "enable.typedep"
67 #define CHANNEL_DEP_VISIBI_COND_ID_POSTFIX "visibi.channeldep"
68 #define START_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX "visibi.startanytimedep"
69 #define END_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX "visibi.endanytimedep"
70
CGUIDialogPVRTimerSettings()71 CGUIDialogPVRTimerSettings::CGUIDialogPVRTimerSettings() :
72 CGUIDialogSettingsManualBase(WINDOW_DIALOG_PVR_TIMER_SETTING, "DialogSettings.xml"),
73 m_iWeekdays(PVR_WEEKDAY_NONE)
74 {
75 m_loadType = LOAD_EVERY_TIME;
76 }
77
78 CGUIDialogPVRTimerSettings::~CGUIDialogPVRTimerSettings() = default;
79
CanBeActivated() const80 bool CGUIDialogPVRTimerSettings::CanBeActivated() const
81 {
82 if (!m_timerInfoTag)
83 {
84 CLog::LogF(LOGERROR, "No timer info tag");
85 return false;
86 }
87 return true;
88 }
89
SetTimer(const std::shared_ptr<CPVRTimerInfoTag> & timer)90 void CGUIDialogPVRTimerSettings::SetTimer(const std::shared_ptr<CPVRTimerInfoTag>& timer)
91 {
92 if (!timer)
93 {
94 CLog::LogF(LOGERROR, "No timer given");
95 return;
96 }
97
98 m_timerInfoTag = timer;
99
100 // Copy data we need from tag. Do not modify the tag itself until Save()!
101 m_timerType = m_timerInfoTag->GetTimerType();
102 m_bIsRadio = m_timerInfoTag->m_bIsRadio;
103 m_bIsNewTimer = m_timerInfoTag->m_iClientIndex == PVR_TIMER_NO_CLIENT_INDEX;
104 m_bTimerActive = m_bIsNewTimer || !m_timerType->SupportsEnableDisable() || !(m_timerInfoTag->m_state == PVR_TIMER_STATE_DISABLED);
105 m_bStartAnyTime = m_bIsNewTimer || !m_timerType->SupportsStartAnyTime() || m_timerInfoTag->m_bStartAnyTime;
106 m_bEndAnyTime = m_bIsNewTimer || !m_timerType->SupportsEndAnyTime() || m_timerInfoTag->m_bEndAnyTime;
107 m_strTitle = m_timerInfoTag->m_strTitle;
108
109 m_startLocalTime = m_timerInfoTag->StartAsLocalTime();
110 m_endLocalTime = m_timerInfoTag->EndAsLocalTime();
111
112 m_timerStartTimeStr = m_startLocalTime.GetAsLocalizedTime("", false);
113 m_timerEndTimeStr = m_endLocalTime.GetAsLocalizedTime("", false);
114 m_firstDayLocalTime = m_timerInfoTag->FirstDayAsLocalTime();
115
116 m_strEpgSearchString = m_timerInfoTag->m_strEpgSearchString;
117 if ((m_bIsNewTimer || !m_timerType->SupportsEpgTitleMatch()) && m_strEpgSearchString.empty())
118 m_strEpgSearchString = m_strTitle;
119
120 m_bFullTextEpgSearch = m_timerInfoTag->m_bFullTextEpgSearch;
121
122 m_iWeekdays = m_timerInfoTag->m_iWeekdays;
123 if ((m_bIsNewTimer || !m_timerType->SupportsWeekdays()) && m_iWeekdays == PVR_WEEKDAY_NONE)
124 m_iWeekdays = PVR_WEEKDAY_ALLDAYS;
125
126 m_iPreventDupEpisodes = m_timerInfoTag->m_iPreventDupEpisodes;
127 m_iMarginStart = m_timerInfoTag->m_iMarginStart;
128 m_iMarginEnd = m_timerInfoTag->m_iMarginEnd;
129 m_iPriority = m_timerInfoTag->m_iPriority;
130 m_iLifetime = m_timerInfoTag->m_iLifetime;
131 m_iMaxRecordings = m_timerInfoTag->m_iMaxRecordings;
132
133 if (m_bIsNewTimer && m_timerInfoTag->m_strDirectory.empty() && m_timerType->SupportsRecordingFolders())
134 m_strDirectory = m_strTitle;
135 else
136 m_strDirectory = m_timerInfoTag->m_strDirectory;
137
138 m_iRecordingGroup = m_timerInfoTag->m_iRecordingGroup;
139
140 InitializeChannelsList();
141 InitializeTypesList();
142
143 // Channel
144 m_channel = ChannelDescriptor();
145
146 if (m_timerInfoTag->m_iClientChannelUid == PVR_CHANNEL_INVALID_UID)
147 {
148 bool bChannelSet(false);
149 if (m_timerType->SupportsAnyChannel())
150 {
151 // Select first matching "Any channel" entry.
152 for (const auto& channel : m_channelEntries)
153 {
154 if (channel.second.channelUid == PVR_CHANNEL_INVALID_UID &&
155 channel.second.clientId == m_timerInfoTag->m_iClientId)
156 {
157 m_channel = channel.second;
158 bChannelSet = true;
159 }
160 }
161 }
162 else if (m_bIsNewTimer)
163 {
164 // Select first matching regular (not "Any channel") entry.
165 for (const auto& channel : m_channelEntries)
166 {
167 if (channel.second.channelUid != PVR_CHANNEL_INVALID_UID &&
168 channel.second.clientId == m_timerInfoTag->m_iClientId)
169 {
170 m_channel = channel.second;
171 bChannelSet = true;
172 break;
173 }
174 }
175 }
176
177 if (!bChannelSet)
178 CLog::LogF(LOGERROR, "Unable to map PVR_CHANNEL_INVALID_UID to channel entry!");
179 }
180 else
181 {
182 // Find matching channel entry
183 bool bChannelSet(false);
184 for (const auto& channel : m_channelEntries)
185 {
186 if ((channel.second.channelUid == m_timerInfoTag->m_iClientChannelUid) &&
187 (channel.second.clientId == m_timerInfoTag->m_iClientId))
188 {
189 m_channel = channel.second;
190 bChannelSet = true;
191 break;
192 }
193 }
194
195 if (!bChannelSet)
196 CLog::LogF(LOGERROR, "Unable to map channel uid to channel entry!");
197 }
198 }
199
SetupView()200 void CGUIDialogPVRTimerSettings::SetupView()
201 {
202 CGUIDialogSettingsManualBase::SetupView();
203 SetHeading(19065);
204 SET_CONTROL_HIDDEN(CONTROL_SETTINGS_CUSTOM_BUTTON);
205 SET_CONTROL_LABEL(CONTROL_SETTINGS_OKAY_BUTTON, 186);
206 SET_CONTROL_LABEL(CONTROL_SETTINGS_CANCEL_BUTTON, 222);
207 SetButtonLabels();
208 }
209
InitializeSettings()210 void CGUIDialogPVRTimerSettings::InitializeSettings()
211 {
212 CGUIDialogSettingsManualBase::InitializeSettings();
213
214 const std::shared_ptr<CSettingCategory> category = AddCategory("pvrtimersettings", -1);
215 if (category == NULL)
216 {
217 CLog::LogF(LOGERROR, "Unable to add settings category");
218 return;
219 }
220
221 const std::shared_ptr<CSettingGroup> group = AddGroup(category);
222 if (group == NULL)
223 {
224 CLog::LogF(LOGERROR, "Unable to add settings group");
225 return;
226 }
227
228 std::shared_ptr<CSetting> setting = NULL;
229
230 // Timer type
231 setting = AddList(group, SETTING_TMR_TYPE, 803, SettingLevel::Basic, 0, TypesFiller, 803);
232 AddTypeDependentEnableCondition(setting, SETTING_TMR_TYPE);
233
234 // Timer enabled/disabled
235 setting = AddToggle(group, SETTING_TMR_ACTIVE, 305, SettingLevel::Basic, m_bTimerActive);
236 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_ACTIVE);
237 AddTypeDependentEnableCondition(setting, SETTING_TMR_ACTIVE);
238
239 // Name
240 setting = AddEdit(group, SETTING_TMR_NAME, 19075, SettingLevel::Basic, m_strTitle, true, false, 19097);
241 AddTypeDependentEnableCondition(setting, SETTING_TMR_NAME);
242
243 // epg search string (only for epg-based timer rules)
244 setting = AddEdit(group, SETTING_TMR_EPGSEARCH, 804, SettingLevel::Basic, m_strEpgSearchString, true, false, 805);
245 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_EPGSEARCH);
246 AddTypeDependentEnableCondition(setting, SETTING_TMR_EPGSEARCH);
247
248 // epg fulltext search (only for epg-based timer rules)
249 setting = AddToggle(group, SETTING_TMR_FULLTEXT, 806, SettingLevel::Basic, m_bFullTextEpgSearch);
250 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_FULLTEXT);
251 AddTypeDependentEnableCondition(setting, SETTING_TMR_FULLTEXT);
252
253 // Channel
254 setting = AddList(group, SETTING_TMR_CHANNEL, 19078, SettingLevel::Basic, 0, ChannelsFiller, 19078);
255 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_CHANNEL);
256 AddTypeDependentEnableCondition(setting, SETTING_TMR_CHANNEL);
257
258 // Days of week (only for timer rules)
259 std::vector<int> weekdaysPreselect;
260 if (m_iWeekdays & PVR_WEEKDAY_MONDAY)
261 weekdaysPreselect.push_back(PVR_WEEKDAY_MONDAY);
262 if (m_iWeekdays & PVR_WEEKDAY_TUESDAY)
263 weekdaysPreselect.push_back(PVR_WEEKDAY_TUESDAY);
264 if (m_iWeekdays & PVR_WEEKDAY_WEDNESDAY)
265 weekdaysPreselect.push_back(PVR_WEEKDAY_WEDNESDAY);
266 if (m_iWeekdays & PVR_WEEKDAY_THURSDAY)
267 weekdaysPreselect.push_back(PVR_WEEKDAY_THURSDAY);
268 if (m_iWeekdays & PVR_WEEKDAY_FRIDAY)
269 weekdaysPreselect.push_back(PVR_WEEKDAY_FRIDAY);
270 if (m_iWeekdays & PVR_WEEKDAY_SATURDAY)
271 weekdaysPreselect.push_back(PVR_WEEKDAY_SATURDAY);
272 if (m_iWeekdays & PVR_WEEKDAY_SUNDAY)
273 weekdaysPreselect.push_back(PVR_WEEKDAY_SUNDAY);
274
275 setting = AddList(group, SETTING_TMR_WEEKDAYS, 19079, SettingLevel::Basic, weekdaysPreselect, WeekdaysFiller, 19079, 1, -1, true, -1, WeekdaysValueFormatter);
276 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_WEEKDAYS);
277 AddTypeDependentEnableCondition(setting, SETTING_TMR_WEEKDAYS);
278
279 // "Start any time" (only for timer rules)
280 setting = AddToggle(group, SETTING_TMR_START_ANYTIME, 810, SettingLevel::Basic, m_bStartAnyTime);
281 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_START_ANYTIME);
282 AddTypeDependentEnableCondition(setting, SETTING_TMR_START_ANYTIME);
283
284 // Start day (day + month + year only, no hours, minutes)
285 setting = AddSpinner(group, SETTING_TMR_START_DAY, 19128, SettingLevel::Basic, GetDateAsIndex(m_startLocalTime), DaysFiller);
286 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_START_DAY);
287 AddTypeDependentEnableCondition(setting, SETTING_TMR_START_DAY);
288 AddStartAnytimeDependentVisibilityCondition(setting, SETTING_TMR_START_DAY);
289
290 // Start time (hours + minutes only, no day, month, year)
291 setting = AddButton(group, SETTING_TMR_BEGIN, 19126, SettingLevel::Basic);
292 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_BEGIN);
293 AddTypeDependentEnableCondition(setting, SETTING_TMR_BEGIN);
294 AddStartAnytimeDependentVisibilityCondition(setting, SETTING_TMR_BEGIN);
295
296 // "End any time" (only for timer rules)
297 setting = AddToggle(group, SETTING_TMR_END_ANYTIME, 817, SettingLevel::Basic, m_bEndAnyTime);
298 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_END_ANYTIME);
299 AddTypeDependentEnableCondition(setting, SETTING_TMR_END_ANYTIME);
300
301 // End day (day + month + year only, no hours, minutes)
302 setting = AddSpinner(group, SETTING_TMR_END_DAY, 19129, SettingLevel::Basic, GetDateAsIndex(m_endLocalTime), DaysFiller);
303 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_END_DAY);
304 AddTypeDependentEnableCondition(setting, SETTING_TMR_END_DAY);
305 AddEndAnytimeDependentVisibilityCondition(setting, SETTING_TMR_END_DAY);
306
307 // End time (hours + minutes only, no day, month, year)
308 setting = AddButton(group, SETTING_TMR_END, 19127, SettingLevel::Basic);
309 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_END);
310 AddTypeDependentEnableCondition(setting, SETTING_TMR_END);
311 AddEndAnytimeDependentVisibilityCondition(setting, SETTING_TMR_END);
312
313 // First day (only for timer rules)
314 setting = AddSpinner(group, SETTING_TMR_FIRST_DAY, 19084, SettingLevel::Basic, GetDateAsIndex(m_firstDayLocalTime), DaysFiller);
315 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_FIRST_DAY);
316 AddTypeDependentEnableCondition(setting, SETTING_TMR_FIRST_DAY);
317
318 // "Prevent duplicate episodes" (only for timer rules)
319 setting = AddList(group, SETTING_TMR_NEW_EPISODES, 812, SettingLevel::Basic, m_iPreventDupEpisodes, DupEpisodesFiller, 812);
320 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_NEW_EPISODES);
321 AddTypeDependentEnableCondition(setting, SETTING_TMR_NEW_EPISODES);
322
323 // Pre and post record time
324 setting = AddList(group, SETTING_TMR_BEGIN_PRE, 813, SettingLevel::Basic, m_iMarginStart, MarginTimeFiller, 813);
325 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_BEGIN_PRE);
326 AddTypeDependentEnableCondition(setting, SETTING_TMR_BEGIN_PRE);
327
328 setting = AddList(group, SETTING_TMR_END_POST, 814, SettingLevel::Basic, m_iMarginEnd, MarginTimeFiller, 814);
329 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_END_POST);
330 AddTypeDependentEnableCondition(setting, SETTING_TMR_END_POST);
331
332 // Priority
333 setting = AddList(group, SETTING_TMR_PRIORITY, 19082, SettingLevel::Basic, m_iPriority, PrioritiesFiller, 19082);
334 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_PRIORITY);
335 AddTypeDependentEnableCondition(setting, SETTING_TMR_PRIORITY);
336
337 // Lifetime
338 setting = AddList(group, SETTING_TMR_LIFETIME, 19083, SettingLevel::Basic, m_iLifetime, LifetimesFiller, 19083);
339 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_LIFETIME);
340 AddTypeDependentEnableCondition(setting, SETTING_TMR_LIFETIME);
341
342 // MaxRecordings
343 setting = AddList(group, SETTING_TMR_MAX_REC, 818, SettingLevel::Basic, m_iMaxRecordings, MaxRecordingsFiller, 818);
344 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_MAX_REC);
345 AddTypeDependentEnableCondition(setting, SETTING_TMR_MAX_REC);
346
347 // Recording folder
348 setting = AddEdit(group, SETTING_TMR_DIR, 19076, SettingLevel::Basic, m_strDirectory, true, false, 19104);
349 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_DIR);
350 AddTypeDependentEnableCondition(setting, SETTING_TMR_DIR);
351
352 // Recording group
353 setting = AddList(group, SETTING_TMR_REC_GROUP, 811, SettingLevel::Basic, m_iRecordingGroup, RecordingGroupFiller, 811);
354 AddTypeDependentVisibilityCondition(setting, SETTING_TMR_REC_GROUP);
355 AddTypeDependentEnableCondition(setting, SETTING_TMR_REC_GROUP);
356 }
357
GetWeekdaysFromSetting(const SettingConstPtr & setting)358 int CGUIDialogPVRTimerSettings::GetWeekdaysFromSetting(const SettingConstPtr& setting)
359 {
360 std::shared_ptr<const CSettingList> settingList = std::static_pointer_cast<const CSettingList>(setting);
361 if (settingList->GetElementType() != SettingType::Integer)
362 {
363 CLog::LogF(LOGERROR, "Wrong weekdays element type");
364 return 0;
365 }
366 int weekdays = 0;
367 std::vector<CVariant> list = CSettingUtils::GetList(settingList);
368 for (const auto& value : list)
369 {
370 if (!value.isInteger())
371 {
372 CLog::LogF(LOGERROR, "Wrong weekdays value type");
373 return 0;
374 }
375 weekdays += static_cast<int>(value.asInteger());
376 }
377
378 return weekdays;
379 }
380
OnSettingChanged(const std::shared_ptr<const CSetting> & setting)381 void CGUIDialogPVRTimerSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
382 {
383 if (setting == NULL)
384 {
385 CLog::LogF(LOGERROR, "No setting");
386 return;
387 }
388
389 CGUIDialogSettingsManualBase::OnSettingChanged(setting);
390
391 const std::string& settingId = setting->GetId();
392
393 if (settingId == SETTING_TMR_TYPE)
394 {
395 int idx = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
396 const auto it = m_typeEntries.find(idx);
397 if (it != m_typeEntries.end())
398 {
399 m_timerType = it->second;
400
401 if (m_timerType->IsTimerRule() && (m_iWeekdays == PVR_WEEKDAY_ALLDAYS))
402 SetButtonLabels(); // update "Any day" vs. "Every day"
403 }
404 else
405 {
406 CLog::LogF(LOGERROR, "Unable to get 'type' value");
407 }
408 }
409 else if (settingId == SETTING_TMR_ACTIVE)
410 {
411 m_bTimerActive = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
412 }
413 else if (settingId == SETTING_TMR_NAME)
414 {
415 m_strTitle = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
416 }
417 else if (settingId == SETTING_TMR_EPGSEARCH)
418 {
419 m_strEpgSearchString = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
420 }
421 else if (settingId == SETTING_TMR_FULLTEXT)
422 {
423 m_bFullTextEpgSearch = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
424 }
425 else if (settingId == SETTING_TMR_CHANNEL)
426 {
427 int idx = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
428 const auto it = m_channelEntries.find(idx);
429 if (it != m_channelEntries.end())
430 {
431 m_channel = it->second;
432 }
433 else
434 {
435 CLog::LogF(LOGERROR, "Unable to get 'type' value");
436 }
437 }
438 else if (settingId == SETTING_TMR_WEEKDAYS)
439 {
440 m_iWeekdays = GetWeekdaysFromSetting(setting);
441 }
442 else if (settingId == SETTING_TMR_START_ANYTIME)
443 {
444 m_bStartAnyTime = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
445 }
446 else if (settingId == SETTING_TMR_END_ANYTIME)
447 {
448 m_bEndAnyTime = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
449 }
450 else if (settingId == SETTING_TMR_START_DAY)
451 {
452 SetDateFromIndex(m_startLocalTime, std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
453 }
454 else if (settingId == SETTING_TMR_END_DAY)
455 {
456 SetDateFromIndex(m_endLocalTime, std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
457 }
458 else if (settingId == SETTING_TMR_FIRST_DAY)
459 {
460 SetDateFromIndex(m_firstDayLocalTime, std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
461 }
462 else if (settingId == SETTING_TMR_NEW_EPISODES)
463 {
464 m_iPreventDupEpisodes = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
465 }
466 else if (settingId == SETTING_TMR_BEGIN_PRE)
467 {
468 m_iMarginStart = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
469 }
470 else if (settingId == SETTING_TMR_END_POST)
471 {
472 m_iMarginEnd = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
473 }
474 else if (settingId == SETTING_TMR_PRIORITY)
475 {
476 m_iPriority = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
477 }
478 else if (settingId == SETTING_TMR_LIFETIME)
479 {
480 m_iLifetime = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
481 }
482 else if (settingId == SETTING_TMR_MAX_REC)
483 {
484 m_iMaxRecordings = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
485 }
486 else if (settingId == SETTING_TMR_DIR)
487 {
488 m_strDirectory = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
489 }
490 else if (settingId == SETTING_TMR_REC_GROUP)
491 {
492 m_iRecordingGroup = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
493 }
494 }
495
OnSettingAction(const std::shared_ptr<const CSetting> & setting)496 void CGUIDialogPVRTimerSettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
497 {
498 if (setting == NULL)
499 {
500 CLog::LogF(LOGERROR, "No setting");
501 return;
502 }
503
504 CGUIDialogSettingsManualBase::OnSettingAction(setting);
505
506 const std::string& settingId = setting->GetId();
507 if (settingId == SETTING_TMR_BEGIN)
508 {
509 KODI::TIME::SystemTime timerStartTime;
510 m_startLocalTime.GetAsSystemTime(timerStartTime);
511 if (CGUIDialogNumeric::ShowAndGetTime(timerStartTime, g_localizeStrings.Get(14066)))
512 {
513 SetTimeFromSystemTime(m_startLocalTime, timerStartTime);
514 m_timerStartTimeStr = m_startLocalTime.GetAsLocalizedTime("", false);
515 SetButtonLabels();
516 }
517 }
518 else if (settingId == SETTING_TMR_END)
519 {
520 KODI::TIME::SystemTime timerEndTime;
521 m_endLocalTime.GetAsSystemTime(timerEndTime);
522 if (CGUIDialogNumeric::ShowAndGetTime(timerEndTime, g_localizeStrings.Get(14066)))
523 {
524 SetTimeFromSystemTime(m_endLocalTime, timerEndTime);
525 m_timerEndTimeStr = m_endLocalTime.GetAsLocalizedTime("", false);
526 SetButtonLabels();
527 }
528 }
529 }
530
Validate()531 bool CGUIDialogPVRTimerSettings::Validate()
532 {
533 bool bStartAnyTime = m_bStartAnyTime;
534 bool bEndAnyTime = m_bEndAnyTime;
535
536 if (!m_timerType->SupportsStartAnyTime() ||
537 !m_timerType->IsEpgBased()) // Start anytime toggle is not displayed
538 bStartAnyTime = false; // Assume start time change needs checking for
539
540 if (!m_timerType->SupportsEndAnyTime() ||
541 !m_timerType->IsEpgBased()) // End anytime toggle is not displayed
542 bEndAnyTime = false; // Assume end time change needs checking for
543
544 // Begin and end time
545 if (!bStartAnyTime && !bEndAnyTime)
546 {
547 // Not in the set of having neither or both of start clock entry and
548 // end clock entry while also being a timer rule
549 if (!(m_timerType->SupportsStartTime() == m_timerType->SupportsEndTime() &&
550 m_timerType->IsTimerRule()) &&
551 m_endLocalTime < m_startLocalTime)
552 {
553 HELPERS::ShowOKDialogText(CVariant{19065}, // "Timer settings"
554 CVariant{19072}); // In order to add/update a timer
555 return false;
556 }
557 }
558
559 return true;
560 }
561
Save()562 bool CGUIDialogPVRTimerSettings::Save()
563 {
564 if (!Validate())
565 return false;
566
567 // Timer type
568 m_timerInfoTag->SetTimerType(m_timerType);
569
570 // Timer active/inactive
571 m_timerInfoTag->m_state = m_bTimerActive ? PVR_TIMER_STATE_SCHEDULED : PVR_TIMER_STATE_DISABLED;
572
573 // Name
574 m_timerInfoTag->m_strTitle = m_strTitle;
575
576 // epg search string (only for epg-based timer rules)
577 m_timerInfoTag->m_strEpgSearchString = m_strEpgSearchString;
578
579 // epg fulltext search, instead of just title match. (only for epg-based timer rules)
580 m_timerInfoTag->m_bFullTextEpgSearch = m_bFullTextEpgSearch;
581
582 // Channel
583 m_timerInfoTag->m_iClientChannelUid = m_channel.channelUid;
584 m_timerInfoTag->m_iClientId = m_channel.clientId;
585 m_timerInfoTag->m_bIsRadio = m_bIsRadio;
586 m_timerInfoTag->UpdateChannel();
587
588 if (!m_timerType->SupportsStartAnyTime() || !m_timerType->IsEpgBased()) // Start anytime toggle is not displayed
589 m_bStartAnyTime = false; // Assume start time change needs checking for
590 m_timerInfoTag->m_bStartAnyTime = m_bStartAnyTime;
591
592 if (!m_timerType->SupportsEndAnyTime() || !m_timerType->IsEpgBased()) // End anytime toggle is not displayed
593 m_bEndAnyTime = false; // Assume end time change needs checking for
594 m_timerInfoTag->m_bEndAnyTime = m_bEndAnyTime;
595
596 // Begin and end time
597 if (!m_bStartAnyTime && !m_bEndAnyTime)
598 {
599 if (m_timerType->SupportsStartTime() && // has start clock entry
600 m_timerType->SupportsEndTime() && // and end clock entry
601 m_timerType->IsTimerRule()) // but no associated start/end day spinners
602 {
603 if (m_endLocalTime < m_startLocalTime) // And the end clock is earlier than the start clock
604 {
605 CLog::LogFC(LOGDEBUG, LOGPVR, "End before start, adding a day.");
606 m_endLocalTime += CDateTimeSpan(1, 0, 0, 0);
607 if (m_endLocalTime < m_startLocalTime)
608 {
609 CLog::Log(LOGWARNING, "Timer settings dialog: End before start. Setting end time to start time.");
610 m_endLocalTime = m_startLocalTime;
611 }
612 }
613 else if (m_endLocalTime > (m_startLocalTime + CDateTimeSpan(1, 0, 0, 0))) // Or the duration is more than a day
614 {
615 CLog::LogFC(LOGDEBUG, LOGPVR, "End > 1 day after start, removing a day.");
616 m_endLocalTime -= CDateTimeSpan(1, 0, 0, 0);
617 if (m_endLocalTime > (m_startLocalTime + CDateTimeSpan(1, 0, 0, 0)))
618 {
619 CLog::Log(LOGWARNING, "Timer settings dialog: End > 1 day after start. Setting end time to start time.");
620 m_endLocalTime = m_startLocalTime;
621 }
622 }
623 }
624 else if (m_endLocalTime < m_startLocalTime)
625 {
626 // this case will fail validation so this can't be reached.
627 }
628 m_timerInfoTag->SetStartFromLocalTime(m_startLocalTime);
629 m_timerInfoTag->SetEndFromLocalTime(m_endLocalTime);
630 }
631 else if (!m_bStartAnyTime)
632 m_timerInfoTag->SetStartFromLocalTime(m_startLocalTime);
633 else if (!m_bEndAnyTime)
634 m_timerInfoTag->SetEndFromLocalTime(m_endLocalTime);
635
636 // Days of week (only for timer rules)
637 if (m_timerType->IsTimerRule())
638 m_timerInfoTag->m_iWeekdays = m_iWeekdays;
639 else
640 m_timerInfoTag->m_iWeekdays = PVR_WEEKDAY_NONE;
641
642 // First day (only for timer rules)
643 m_timerInfoTag->SetFirstDayFromLocalTime(m_firstDayLocalTime);
644
645 // "New episodes only" (only for timer rules)
646 m_timerInfoTag->m_iPreventDupEpisodes = m_iPreventDupEpisodes;
647
648 // Pre and post record time
649 m_timerInfoTag->m_iMarginStart = m_iMarginStart;
650 m_timerInfoTag->m_iMarginEnd = m_iMarginEnd;
651
652 // Priority
653 m_timerInfoTag->m_iPriority = m_iPriority;
654
655 // Lifetime
656 m_timerInfoTag->m_iLifetime = m_iLifetime;
657
658 // MaxRecordings
659 m_timerInfoTag->m_iMaxRecordings = m_iMaxRecordings;
660
661 // Recording folder
662 m_timerInfoTag->m_strDirectory = m_strDirectory;
663
664 // Recording group
665 m_timerInfoTag->m_iRecordingGroup = m_iRecordingGroup;
666
667 // Set the timer's title to the channel name if it's empty or 'New Timer'
668 if (m_strTitle.empty() || m_strTitle == g_localizeStrings.Get(19056))
669 {
670 const std::string channelName = m_timerInfoTag->ChannelName();
671 if (!channelName.empty())
672 m_timerInfoTag->m_strTitle = channelName;
673 }
674
675 // Update summary
676 m_timerInfoTag->UpdateSummary();
677
678 return true;
679 }
680
SetButtonLabels()681 void CGUIDialogPVRTimerSettings::SetButtonLabels()
682 {
683 // timer start time
684 BaseSettingControlPtr settingControl = GetSettingControl(SETTING_TMR_BEGIN);
685 if (settingControl != NULL && settingControl->GetControl() != NULL)
686 {
687 SET_CONTROL_LABEL2(settingControl->GetID(), m_timerStartTimeStr);
688 }
689
690 // timer end time
691 settingControl = GetSettingControl(SETTING_TMR_END);
692 if (settingControl != NULL && settingControl->GetControl() != NULL)
693 {
694 SET_CONTROL_LABEL2(settingControl->GetID(), m_timerEndTimeStr);
695 }
696 }
697
AddCondition(const std::shared_ptr<CSetting> & setting,const std::string & identifier,SettingConditionCheck condition,SettingDependencyType depType,const std::string & settingId)698 void CGUIDialogPVRTimerSettings::AddCondition(const std::shared_ptr<CSetting>& setting,
699 const std::string& identifier,
700 SettingConditionCheck condition,
701 SettingDependencyType depType,
702 const std::string& settingId)
703 {
704 GetSettingsManager()->AddDynamicCondition(identifier, condition, this);
705 CSettingDependency dep(depType, GetSettingsManager());
706 dep.And()->Add(
707 CSettingDependencyConditionPtr(
708 new CSettingDependencyCondition(identifier, "true", settingId, false, GetSettingsManager())));
709 SettingDependencies deps(setting->GetDependencies());
710 deps.push_back(dep);
711 setting->SetDependencies(deps);
712 }
713
GetDateAsIndex(const CDateTime & datetime)714 int CGUIDialogPVRTimerSettings::GetDateAsIndex(const CDateTime& datetime)
715 {
716 const CDateTime date(datetime.GetYear(), datetime.GetMonth(), datetime.GetDay(), 0, 0, 0);
717 time_t t(0);
718 date.GetAsTime(t);
719 return static_cast<int>(t);
720 }
721
SetDateFromIndex(CDateTime & datetime,int date)722 void CGUIDialogPVRTimerSettings::SetDateFromIndex(CDateTime& datetime, int date)
723 {
724 const CDateTime newDate(static_cast<time_t>(date));
725 datetime.SetDateTime(
726 newDate.GetYear(), newDate.GetMonth(), newDate.GetDay(),
727 datetime.GetHour(), datetime.GetMinute(), datetime.GetSecond());
728 }
729
SetTimeFromSystemTime(CDateTime & datetime,const KODI::TIME::SystemTime & time)730 void CGUIDialogPVRTimerSettings::SetTimeFromSystemTime(CDateTime& datetime,
731 const KODI::TIME::SystemTime& time)
732 {
733 const CDateTime newTime(time);
734 datetime.SetDateTime(
735 datetime.GetYear(), datetime.GetMonth(), datetime.GetDay(),
736 newTime.GetHour(), newTime.GetMinute(), newTime.GetSecond());
737 }
738
InitializeTypesList()739 void CGUIDialogPVRTimerSettings::InitializeTypesList()
740 {
741 m_typeEntries.clear();
742
743 // If timer is read-only or was created by a timer rule, only add current type, for information. Type can't be changed.
744 if (m_timerType->IsReadOnly() || m_timerInfoTag->GetTimerRuleId() != PVR_TIMER_NO_PARENT)
745 {
746 m_typeEntries.insert(std::make_pair(0, m_timerType));
747 return;
748 }
749
750 bool bFoundThisType(false);
751 int idx(0);
752 const std::vector<std::shared_ptr<CPVRTimerType>> types(CPVRTimerType::GetAllTypes());
753 for (const auto& type : types)
754 {
755 // Type definition prohibits created of new instances.
756 // But the dialog can act as a viewer for these types.
757 if (type->ForbidsNewInstances())
758 continue;
759
760 // Read-only timers cannot be created using this dialog.
761 // But the dialog can act as a viewer for read-only types.
762 if (type->IsReadOnly())
763 continue;
764
765 // Drop TimerTypes that require EPGInfo, if none is populated
766 if (type->RequiresEpgTagOnCreate() && !m_timerInfoTag->GetEpgInfoTag())
767 continue;
768
769 // Drop TimerTypes without 'Series' EPG attributes if none are set
770 if (type->RequiresEpgSeriesOnCreate())
771 {
772 const std::shared_ptr<CPVREpgInfoTag> epgTag(m_timerInfoTag->GetEpgInfoTag());
773 if (epgTag && !epgTag->IsSeries())
774 continue;
775 }
776
777 // Drop TimerTypes which need series link if none is set
778 if (type->RequiresEpgSeriesLinkOnCreate())
779 {
780 const std::shared_ptr<CPVREpgInfoTag> epgTag(m_timerInfoTag->GetEpgInfoTag());
781 if (!epgTag || epgTag->SeriesLink().empty())
782 continue;
783 }
784
785 // Drop TimerTypes that forbid EPGInfo, if it is populated
786 if (type->ForbidsEpgTagOnCreate() && m_timerInfoTag->GetEpgInfoTag())
787 continue;
788
789 // Drop TimerTypes that aren't rules and cannot be recorded
790 if (!type->IsTimerRule())
791 {
792 const std::shared_ptr<CPVREpgInfoTag> epgTag(m_timerInfoTag->GetEpgInfoTag());
793 bool bCanRecord = epgTag ? epgTag->IsRecordable() : m_timerInfoTag->EndAsLocalTime() > CDateTime::GetCurrentDateTime();
794 if (!bCanRecord)
795 continue;
796 }
797
798 if (!bFoundThisType && *type == *m_timerType)
799 bFoundThisType = true;
800
801 m_typeEntries.insert(std::make_pair(idx++, type));
802 }
803
804 if (!bFoundThisType)
805 m_typeEntries.insert(std::make_pair(idx++, m_timerType));
806 }
807
InitializeChannelsList()808 void CGUIDialogPVRTimerSettings::InitializeChannelsList()
809 {
810 m_channelEntries.clear();
811
812 int index = 0;
813
814 // Add special "any channel" entries - one for every client (used for epg-based timer rules).
815 CPVRClientMap clients;
816 CServiceBroker::GetPVRManager().Clients()->GetCreatedClients(clients);
817 for (const auto& client : clients)
818 {
819 m_channelEntries.insert({index, ChannelDescriptor(PVR_CHANNEL_INVALID_UID,
820 client.second->GetID(),
821 g_localizeStrings.Get(809))}); // "Any channel"
822 ++index;
823 }
824
825 // Add regular channels
826 const std::shared_ptr<CPVRChannelGroup> allGroup = CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAll(m_bIsRadio);
827 const std::vector<std::shared_ptr<PVRChannelGroupMember>> groupMembers = allGroup->GetMembers(CPVRChannelGroup::Include::ONLY_VISIBLE);
828 for (const auto& groupMember : groupMembers)
829 {
830 const std::shared_ptr<CPVRChannel> channel = groupMember->channel;
831 const std::string channelDescription
832 = StringUtils::Format("%s %s", channel->ChannelNumber().FormattedChannelNumber().c_str(), channel->ChannelName().c_str());
833 m_channelEntries.insert({index, ChannelDescriptor(channel->UniqueID(), channel->ClientID(), channelDescription)});
834 ++index;
835 }
836 }
837
TypesFiller(const SettingConstPtr & setting,std::vector<IntegerSettingOption> & list,int & current,void * data)838 void CGUIDialogPVRTimerSettings::TypesFiller(const SettingConstPtr& setting,
839 std::vector<IntegerSettingOption>& list,
840 int& current,
841 void* data)
842 {
843 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
844 if (pThis)
845 {
846 list.clear();
847 current = 0;
848
849 static const std::vector<std::pair<std::string, CVariant>> reminderTimerProps{std::make_pair("PVR.IsRemindingTimer", CVariant{true})};
850 static const std::vector<std::pair<std::string, CVariant>> recordingTimerProps{std::make_pair("PVR.IsRecordingTimer", CVariant{true})};
851
852 bool foundCurrent(false);
853 for (const auto& typeEntry : pThis->m_typeEntries)
854 {
855 list.emplace_back(typeEntry.second->GetDescription(), typeEntry.first,
856 typeEntry.second->IsReminder() ? reminderTimerProps : recordingTimerProps);
857
858 if (!foundCurrent && (*(pThis->m_timerType) == *(typeEntry.second)))
859 {
860 current = typeEntry.first;
861 foundCurrent = true;
862 }
863 }
864 }
865 else
866 CLog::LogF(LOGERROR, "No dialog");
867 }
868
ChannelsFiller(const SettingConstPtr & setting,std::vector<IntegerSettingOption> & list,int & current,void * data)869 void CGUIDialogPVRTimerSettings::ChannelsFiller(const SettingConstPtr& setting,
870 std::vector<IntegerSettingOption>& list,
871 int& current,
872 void* data)
873 {
874 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
875 if (pThis)
876 {
877 list.clear();
878 current = 0;
879
880 bool foundCurrent(false);
881 for (const auto& channelEntry : pThis->m_channelEntries)
882 {
883 // Only include channels for the currently selected timer type or all channels if type is client-independent.
884 if (pThis->m_timerType->GetClientId() == -1 || // client-independent
885 pThis->m_timerType->GetClientId() == channelEntry.second.clientId)
886 {
887 // Do not add "any channel" entry if not supported by selected timer type.
888 if (channelEntry.second.channelUid == PVR_CHANNEL_INVALID_UID &&
889 !pThis->m_timerType->SupportsAnyChannel())
890 continue;
891
892 list.emplace_back(IntegerSettingOption(channelEntry.second.description, channelEntry.first));
893 }
894
895 if (!foundCurrent && (pThis->m_channel == channelEntry.second))
896 {
897 current = channelEntry.first;
898 foundCurrent = true;
899 }
900 }
901 }
902 else
903 CLog::LogF(LOGERROR, "No dialog");
904 }
905
DaysFiller(const SettingConstPtr & setting,std::vector<IntegerSettingOption> & list,int & current,void * data)906 void CGUIDialogPVRTimerSettings::DaysFiller(const SettingConstPtr& setting,
907 std::vector<IntegerSettingOption>& list,
908 int& current,
909 void* data)
910 {
911 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
912 if (pThis)
913 {
914 list.clear();
915 current = 0;
916
917 // Data range: "today" until "yesterday next year"
918 const CDateTime now(CDateTime::GetCurrentDateTime());
919 CDateTime time(now.GetYear(), now.GetMonth(), now.GetDay(), 0, 0, 0);
920 const CDateTime yesterdayPlusOneYear(CDateTime(time.GetYear() + 1, time.GetMonth(), time.GetDay(), time.GetHour(), time.GetMinute(), time.GetSecond())
921 - CDateTimeSpan(1, 0, 0, 0));
922
923 CDateTime oldCDateTime;
924 if (setting->GetId() == SETTING_TMR_FIRST_DAY)
925 oldCDateTime = pThis->m_timerInfoTag->FirstDayAsLocalTime();
926 else if (setting->GetId() == SETTING_TMR_START_DAY)
927 oldCDateTime = pThis->m_timerInfoTag->StartAsLocalTime();
928 else
929 oldCDateTime = pThis->m_timerInfoTag->EndAsLocalTime();
930 const CDateTime oldCDate(oldCDateTime.GetYear(), oldCDateTime.GetMonth(), oldCDateTime.GetDay(), 0, 0, 0);
931
932 if ((oldCDate < time) || (oldCDate > yesterdayPlusOneYear))
933 list.emplace_back(oldCDate.GetAsLocalizedDate(true /*long date*/), GetDateAsIndex(oldCDate));
934
935 while (time <= yesterdayPlusOneYear)
936 {
937 list.emplace_back(time.GetAsLocalizedDate(true /*long date*/), GetDateAsIndex(time));
938 time += CDateTimeSpan(1, 0, 0, 0);
939 }
940
941 if (setting->GetId() == SETTING_TMR_FIRST_DAY)
942 current = GetDateAsIndex(pThis->m_firstDayLocalTime);
943 else if (setting->GetId() == SETTING_TMR_START_DAY)
944 current = GetDateAsIndex(pThis->m_startLocalTime);
945 else
946 current = GetDateAsIndex(pThis->m_endLocalTime);
947 }
948 else
949 CLog::LogF(LOGERROR, "No dialog");
950 }
951
DupEpisodesFiller(const SettingConstPtr & setting,std::vector<IntegerSettingOption> & list,int & current,void * data)952 void CGUIDialogPVRTimerSettings::DupEpisodesFiller(const SettingConstPtr& setting,
953 std::vector<IntegerSettingOption>& list,
954 int& current,
955 void* data)
956 {
957 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
958 if (pThis)
959 {
960 list.clear();
961
962 std::vector<std::pair<std::string,int>> values;
963 pThis->m_timerType->GetPreventDuplicateEpisodesValues(values);
964 for (const auto& value : values)
965 list.emplace_back(IntegerSettingOption(value.first, value.second));
966
967 current = pThis->m_iPreventDupEpisodes;
968 }
969 else
970 CLog::LogF(LOGERROR, "No dialog");
971 }
972
WeekdaysFiller(const SettingConstPtr & setting,std::vector<IntegerSettingOption> & list,int & current,void * data)973 void CGUIDialogPVRTimerSettings::WeekdaysFiller(const SettingConstPtr& setting,
974 std::vector<IntegerSettingOption>& list,
975 int& current,
976 void* data)
977 {
978 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
979 if (pThis)
980 {
981 list.clear();
982 list.emplace_back(g_localizeStrings.Get(831), PVR_WEEKDAY_MONDAY); // "Mondays"
983 list.emplace_back(g_localizeStrings.Get(832), PVR_WEEKDAY_TUESDAY); // "Tuesdays"
984 list.emplace_back(g_localizeStrings.Get(833), PVR_WEEKDAY_WEDNESDAY); // "Wednesdays"
985 list.emplace_back(g_localizeStrings.Get(834), PVR_WEEKDAY_THURSDAY); // "Thursdays"
986 list.emplace_back(g_localizeStrings.Get(835), PVR_WEEKDAY_FRIDAY); // "Fridays"
987 list.emplace_back(g_localizeStrings.Get(836), PVR_WEEKDAY_SATURDAY); // "Saturdays"
988 list.emplace_back(g_localizeStrings.Get(837), PVR_WEEKDAY_SUNDAY); // "Sundays"
989
990 current = pThis->m_iWeekdays;
991 }
992 else
993 CLog::LogF(LOGERROR, "No dialog");
994 }
995
PrioritiesFiller(const SettingConstPtr & setting,std::vector<IntegerSettingOption> & list,int & current,void * data)996 void CGUIDialogPVRTimerSettings::PrioritiesFiller(const SettingConstPtr& setting,
997 std::vector<IntegerSettingOption>& list,
998 int& current,
999 void* data)
1000 {
1001 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1002 if (pThis)
1003 {
1004 list.clear();
1005
1006 std::vector<std::pair<std::string,int>> values;
1007 pThis->m_timerType->GetPriorityValues(values);
1008 for (const auto& value : values)
1009 list.emplace_back(IntegerSettingOption(value.first, value.second));
1010
1011 current = pThis->m_iPriority;
1012
1013 auto it = list.begin();
1014 while (it != list.end())
1015 {
1016 if (it->value == current)
1017 break; // value already in list
1018
1019 ++it;
1020 }
1021
1022 if (it == list.end())
1023 {
1024 // PVR backend supplied value is not in the list of predefined values. Insert it.
1025 list.insert(it, IntegerSettingOption(StringUtils::Format("%d", current), current));
1026 }
1027 }
1028 else
1029 CLog::LogF(LOGERROR, "No dialog");
1030 }
1031
LifetimesFiller(const SettingConstPtr & setting,std::vector<IntegerSettingOption> & list,int & current,void * data)1032 void CGUIDialogPVRTimerSettings::LifetimesFiller(const SettingConstPtr& setting,
1033 std::vector<IntegerSettingOption>& list,
1034 int& current,
1035 void* data)
1036 {
1037 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1038 if (pThis)
1039 {
1040 list.clear();
1041
1042 std::vector<std::pair<std::string,int>> values;
1043 pThis->m_timerType->GetLifetimeValues(values);
1044 for (const auto& value : values)
1045 list.emplace_back(IntegerSettingOption(value.first, value.second));
1046
1047 current = pThis->m_iLifetime;
1048
1049 auto it = list.begin();
1050 while (it != list.end())
1051 {
1052 if (it->value == current)
1053 break; // value already in list
1054
1055 ++it;
1056 }
1057
1058 if (it == list.end())
1059 {
1060 // PVR backend supplied value is not in the list of predefined values. Insert it.
1061 list.insert(it, IntegerSettingOption(StringUtils::Format(g_localizeStrings.Get(17999).c_str(), current) /* %i days */, current));
1062 }
1063 }
1064 else
1065 CLog::LogF(LOGERROR, "No dialog");
1066 }
1067
MaxRecordingsFiller(const SettingConstPtr & setting,std::vector<IntegerSettingOption> & list,int & current,void * data)1068 void CGUIDialogPVRTimerSettings::MaxRecordingsFiller(const SettingConstPtr& setting,
1069 std::vector<IntegerSettingOption>& list,
1070 int& current,
1071 void* data)
1072 {
1073 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1074 if (pThis)
1075 {
1076 list.clear();
1077
1078 std::vector<std::pair<std::string,int>> values;
1079 pThis->m_timerType->GetMaxRecordingsValues(values);
1080 for (const auto& value : values)
1081 list.emplace_back(IntegerSettingOption(value.first, value.second));
1082
1083 current = pThis->m_iMaxRecordings;
1084
1085 auto it = list.begin();
1086 while (it != list.end())
1087 {
1088 if (it->value == current)
1089 break; // value already in list
1090
1091 ++it;
1092 }
1093
1094 if (it == list.end())
1095 {
1096 // PVR backend supplied value is not in the list of predefined values. Insert it.
1097 list.insert(it, IntegerSettingOption(StringUtils::Format("%d", current), current));
1098 }
1099 }
1100 else
1101 CLog::LogF(LOGERROR, "No dialog");
1102 }
1103
RecordingGroupFiller(const SettingConstPtr & setting,std::vector<IntegerSettingOption> & list,int & current,void * data)1104 void CGUIDialogPVRTimerSettings::RecordingGroupFiller(const SettingConstPtr& setting,
1105 std::vector<IntegerSettingOption>& list,
1106 int& current,
1107 void* data)
1108 {
1109 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1110 if (pThis)
1111 {
1112 list.clear();
1113
1114 std::vector<std::pair<std::string,int>> values;
1115 pThis->m_timerType->GetRecordingGroupValues(values);
1116 for (const auto& value : values)
1117 list.emplace_back(IntegerSettingOption(value.first, value.second));
1118
1119 current = pThis->m_iRecordingGroup;
1120 }
1121 else
1122 CLog::LogF(LOGERROR, "No dialog");
1123 }
1124
MarginTimeFiller(const SettingConstPtr & setting,std::vector<IntegerSettingOption> & list,int & current,void * data)1125 void CGUIDialogPVRTimerSettings::MarginTimeFiller(const SettingConstPtr& setting,
1126 std::vector<IntegerSettingOption>& list,
1127 int& current,
1128 void* data)
1129 {
1130 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1131 if (pThis)
1132 {
1133 list.clear();
1134
1135 // Get global settings values
1136 CPVRSettings::MarginTimeFiller(setting, list, current, data);
1137
1138 if (setting->GetId() == SETTING_TMR_BEGIN_PRE)
1139 current = pThis->m_iMarginStart;
1140 else
1141 current = pThis->m_iMarginEnd;
1142
1143 bool bInsertValue = true;
1144 auto it = list.begin();
1145 while (it != list.end())
1146 {
1147 if (it->value == current)
1148 {
1149 bInsertValue = false;
1150 break; // value already in list
1151 }
1152
1153 if (it->value > current)
1154 break;
1155
1156 ++it;
1157 }
1158
1159 if (bInsertValue)
1160 {
1161 // PVR backend supplied value is not in the list of predefined values. Insert it.
1162 list.insert(it, IntegerSettingOption(StringUtils::Format(g_localizeStrings.Get(14044).c_str(), current) /* %i min */, current));
1163 }
1164 }
1165 else
1166 CLog::LogF(LOGERROR, "No dialog");
1167 }
1168
WeekdaysValueFormatter(const SettingConstPtr & setting)1169 std::string CGUIDialogPVRTimerSettings::WeekdaysValueFormatter(const SettingConstPtr& setting)
1170 {
1171 return CPVRTimerInfoTag::GetWeekdaysString(GetWeekdaysFromSetting(setting), true, true);
1172 }
1173
AddTypeDependentEnableCondition(const std::shared_ptr<CSetting> & setting,const std::string & identifier)1174 void CGUIDialogPVRTimerSettings::AddTypeDependentEnableCondition(
1175 const std::shared_ptr<CSetting>& setting, const std::string& identifier)
1176 {
1177 // Enable setting depending on read-only attribute of the selected timer type
1178 std::string id(identifier);
1179 id.append(TYPE_DEP_ENABLE_COND_ID_POSTFIX);
1180 AddCondition(setting, id, TypeReadOnlyCondition, SettingDependencyType::Enable, SETTING_TMR_TYPE);
1181 }
1182
TypeReadOnlyCondition(const std::string & condition,const std::string & value,const SettingConstPtr & setting,void * data)1183 bool CGUIDialogPVRTimerSettings::TypeReadOnlyCondition(const std::string& condition,
1184 const std::string& value,
1185 const SettingConstPtr& setting,
1186 void* data)
1187 {
1188 if (setting == NULL)
1189 return false;
1190
1191 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1192 if (pThis == NULL)
1193 {
1194 CLog::LogF(LOGERROR, "No dialog");
1195 return false;
1196 }
1197
1198 if (!StringUtils::EqualsNoCase(value, "true"))
1199 return false;
1200
1201 std::string cond(condition);
1202 cond.erase(cond.find(TYPE_DEP_ENABLE_COND_ID_POSTFIX));
1203
1204 // If only one type is available, disable type selector.
1205 if (pThis->m_typeEntries.size() == 1)
1206 {
1207 if (cond == SETTING_TMR_TYPE)
1208 return false;
1209 }
1210
1211 // For existing one time epg-based timers, disable editing of epg-filled data.
1212 if (!pThis->m_bIsNewTimer && pThis->m_timerType->IsEpgBasedOnetime())
1213 {
1214 if ((cond == SETTING_TMR_NAME) ||
1215 (cond == SETTING_TMR_CHANNEL) ||
1216 (cond == SETTING_TMR_START_DAY) ||
1217 (cond == SETTING_TMR_END_DAY) ||
1218 (cond == SETTING_TMR_BEGIN) ||
1219 (cond == SETTING_TMR_END))
1220 return false;
1221 }
1222
1223 /* Always enable enable/disable, if supported by the timer type. */
1224 if (pThis->m_timerType->SupportsEnableDisable() && !pThis->m_timerInfoTag->IsBroken())
1225 {
1226 if (cond == SETTING_TMR_ACTIVE)
1227 return true;
1228 }
1229
1230 // Let the PVR client decide...
1231 int idx = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
1232 const auto entry = pThis->m_typeEntries.find(idx);
1233 if (entry != pThis->m_typeEntries.end())
1234 return !entry->second->IsReadOnly();
1235 else
1236 CLog::LogF(LOGERROR, "No type entry");
1237
1238 return false;
1239 }
1240
AddTypeDependentVisibilityCondition(const std::shared_ptr<CSetting> & setting,const std::string & identifier)1241 void CGUIDialogPVRTimerSettings::AddTypeDependentVisibilityCondition(
1242 const std::shared_ptr<CSetting>& setting, const std::string& identifier)
1243 {
1244 // Show or hide setting depending on attributes of the selected timer type
1245 std::string id(identifier);
1246 id.append(TYPE_DEP_VISIBI_COND_ID_POSTFIX);
1247 AddCondition(setting, id, TypeSupportsCondition, SettingDependencyType::Visible, SETTING_TMR_TYPE);
1248 }
1249
TypeSupportsCondition(const std::string & condition,const std::string & value,const SettingConstPtr & setting,void * data)1250 bool CGUIDialogPVRTimerSettings::TypeSupportsCondition(const std::string& condition,
1251 const std::string& value,
1252 const SettingConstPtr& setting,
1253 void* data)
1254 {
1255 if (setting == NULL)
1256 return false;
1257
1258 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1259 if (pThis == NULL)
1260 {
1261 CLog::LogF(LOGERROR, "No dialog");
1262 return false;
1263 }
1264
1265 if (!StringUtils::EqualsNoCase(value, "true"))
1266 return false;
1267
1268 int idx = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
1269 const auto entry = pThis->m_typeEntries.find(idx);
1270 if (entry != pThis->m_typeEntries.end())
1271 {
1272 std::string cond(condition);
1273 cond.erase(cond.find(TYPE_DEP_VISIBI_COND_ID_POSTFIX));
1274
1275 if (cond == SETTING_TMR_EPGSEARCH)
1276 return entry->second->SupportsEpgTitleMatch() || entry->second->SupportsEpgFulltextMatch();
1277 else if (cond == SETTING_TMR_FULLTEXT)
1278 return entry->second->SupportsEpgFulltextMatch();
1279 else if (cond == SETTING_TMR_ACTIVE)
1280 return entry->second->SupportsEnableDisable();
1281 else if (cond == SETTING_TMR_CHANNEL)
1282 return entry->second->SupportsChannels();
1283 else if (cond == SETTING_TMR_START_ANYTIME)
1284 return entry->second->SupportsStartAnyTime() && entry->second->IsEpgBased();
1285 else if (cond == SETTING_TMR_END_ANYTIME)
1286 return entry->second->SupportsEndAnyTime() && entry->second->IsEpgBased();
1287 else if (cond == SETTING_TMR_START_DAY)
1288 return entry->second->SupportsStartTime() && entry->second->IsOnetime();
1289 else if (cond == SETTING_TMR_END_DAY)
1290 return entry->second->SupportsEndTime() && entry->second->IsOnetime();
1291 else if (cond == SETTING_TMR_BEGIN)
1292 return entry->second->SupportsStartTime();
1293 else if (cond == SETTING_TMR_END)
1294 return entry->second->SupportsEndTime();
1295 else if (cond == SETTING_TMR_WEEKDAYS)
1296 return entry->second->SupportsWeekdays();
1297 else if (cond == SETTING_TMR_FIRST_DAY)
1298 return entry->second->SupportsFirstDay();
1299 else if (cond == SETTING_TMR_NEW_EPISODES)
1300 return entry->second->SupportsRecordOnlyNewEpisodes();
1301 else if (cond == SETTING_TMR_BEGIN_PRE)
1302 return entry->second->SupportsStartMargin();
1303 else if (cond == SETTING_TMR_END_POST)
1304 return entry->second->SupportsEndMargin();
1305 else if (cond == SETTING_TMR_PRIORITY)
1306 return entry->second->SupportsPriority();
1307 else if (cond == SETTING_TMR_LIFETIME)
1308 return entry->second->SupportsLifetime();
1309 else if (cond == SETTING_TMR_MAX_REC)
1310 return entry->second->SupportsMaxRecordings();
1311 else if (cond == SETTING_TMR_DIR)
1312 return entry->second->SupportsRecordingFolders();
1313 else if (cond == SETTING_TMR_REC_GROUP)
1314 return entry->second->SupportsRecordingGroup();
1315 else
1316 CLog::LogF(LOGERROR, "Unknown condition");
1317 }
1318 else
1319 {
1320 CLog::LogF(LOGERROR, "No type entry");
1321 }
1322 return false;
1323 }
1324
AddStartAnytimeDependentVisibilityCondition(const std::shared_ptr<CSetting> & setting,const std::string & identifier)1325 void CGUIDialogPVRTimerSettings::AddStartAnytimeDependentVisibilityCondition(
1326 const std::shared_ptr<CSetting>& setting, const std::string& identifier)
1327 {
1328 // Show or hide setting depending on value of setting "any time"
1329 std::string id(identifier);
1330 id.append(START_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX);
1331 AddCondition(setting, id, StartAnytimeSetCondition, SettingDependencyType::Visible, SETTING_TMR_START_ANYTIME);
1332 }
1333
StartAnytimeSetCondition(const std::string & condition,const std::string & value,const SettingConstPtr & setting,void * data)1334 bool CGUIDialogPVRTimerSettings::StartAnytimeSetCondition(const std::string& condition,
1335 const std::string& value,
1336 const SettingConstPtr& setting,
1337 void* data)
1338 {
1339 if (setting == NULL)
1340 return false;
1341
1342 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1343 if (pThis == NULL)
1344 {
1345 CLog::LogF(LOGERROR, "No dialog");
1346 return false;
1347 }
1348
1349 if (!StringUtils::EqualsNoCase(value, "true"))
1350 return false;
1351
1352 // "any time" setting is only relevant for epg-based timers.
1353 if (!pThis->m_timerType->IsEpgBased())
1354 return true;
1355
1356 // If 'Start anytime' option isn't supported, don't hide start time
1357 if (!pThis->m_timerType->SupportsStartAnyTime())
1358 return true;
1359
1360 std::string cond(condition);
1361 cond.erase(cond.find(START_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX));
1362
1363 if ((cond == SETTING_TMR_START_DAY) ||
1364 (cond == SETTING_TMR_BEGIN))
1365 {
1366 bool bAnytime = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
1367 return !bAnytime;
1368 }
1369 return false;
1370 }
1371
AddEndAnytimeDependentVisibilityCondition(const std::shared_ptr<CSetting> & setting,const std::string & identifier)1372 void CGUIDialogPVRTimerSettings::AddEndAnytimeDependentVisibilityCondition(
1373 const std::shared_ptr<CSetting>& setting, const std::string& identifier)
1374 {
1375 // Show or hide setting depending on value of setting "any time"
1376 std::string id(identifier);
1377 id.append(END_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX);
1378 AddCondition(setting, id, EndAnytimeSetCondition, SettingDependencyType::Visible, SETTING_TMR_END_ANYTIME);
1379 }
1380
EndAnytimeSetCondition(const std::string & condition,const std::string & value,const SettingConstPtr & setting,void * data)1381 bool CGUIDialogPVRTimerSettings::EndAnytimeSetCondition(const std::string& condition,
1382 const std::string& value,
1383 const SettingConstPtr& setting,
1384 void* data)
1385 {
1386 if (setting == NULL)
1387 return false;
1388
1389 CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data);
1390 if (pThis == NULL)
1391 {
1392 CLog::LogF(LOGERROR, "No dialog");
1393 return false;
1394 }
1395
1396 if (!StringUtils::EqualsNoCase(value, "true"))
1397 return false;
1398
1399 // "any time" setting is only relevant for epg-based timers.
1400 if (!pThis->m_timerType->IsEpgBased())
1401 return true;
1402
1403 // If 'End anytime' option isn't supported, don't hide end time
1404 if (!pThis->m_timerType->SupportsEndAnyTime())
1405 return true;
1406
1407 std::string cond(condition);
1408 cond.erase(cond.find(END_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX));
1409
1410 if ((cond == SETTING_TMR_END_DAY) ||
1411 (cond == SETTING_TMR_END))
1412 {
1413 bool bAnytime = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
1414 return !bAnytime;
1415 }
1416 return false;
1417 }
1418