32 m_seSuffix = QString(
" (%1)").arg(tr(
"stored search"));
33 m_exSuffix = QString(
" (%1)").arg(tr(
"stored example"));
65 LOG(VB_GENERAL, LOG_ERR,
"Cannot load screen 'customedit'");
106 quoteTitle.replace(
"\'",
"\'\'");
107 rule.
description = QString(
"program.title = '%1' ").arg(quoteTitle);
111 QVariant::fromValue(rule));
114 result.
prepare(
"SELECT recordid, title, subtitle, description "
115 "FROM record WHERE search = :SEARCH ORDER BY title;");
121 while (result.
next())
123 QString trimTitle = result.
value(1).toString();
127 rule.
title = trimTitle;
134 QVariant::fromValue(rule));
153 static const QRegularExpression replacement {
"\\{([A-Z]+)\\}" };
156 QRegularExpressionMatch match;
157 if (!clause.contains(replacement, &match))
160 QString mid = match.captured(1);
163 if (mid.compare(
"TITLE") == 0) {
165 repl.replace(
"\'",
"\'\'");
166 }
else if (mid.compare(
"SUBTITLE") == 0) {
168 repl.replace(
"\'",
"\'\'");
169 }
else if (mid.compare(
"DESCR") == 0) {
171 repl.replace(
"\'",
"\'\'");
172 }
else if (mid.compare(
"SERIESID") == 0) {
174 }
else if (mid.compare(
"PROGID") == 0) {
176 }
else if (mid.compare(
"SEASON") == 0) {
178 }
else if (mid.compare(
"EPISODE") == 0) {
180 }
else if (mid.compare(
"CATEGORY") == 0) {
182 }
else if (mid.compare(
"CHANID") == 0) {
184 }
else if (mid.compare(
"CHANNUM") == 0) {
186 }
else if (mid.compare(
"SCHEDID") == 0) {
188 }
else if (mid.compare(
"CHANNAME") == 0) {
190 }
else if (mid.compare(
"DAYNAME") == 0) {
192 }
else if (mid.compare(
"STARTDATE") == 0) {
194 }
else if (mid.compare(
"ENDDATE") == 0) {
196 }
else if (mid.compare(
"STARTTIME") == 0) {
198 }
else if (mid.compare(
"ENDTIME") == 0) {
200 }
else if (mid.compare(
"STARTSEC") == 0) {
202 QDateTime midnight = date.date().startOfDay();
203 repl = QString(
"%1").arg(midnight.secsTo(date));
204 }
else if (mid.compare(
"ENDSEC") == 0) {
206 QDateTime midnight = date.date().startOfDay();
207 repl = QString(
"%1").arg(midnight.secsTo(date));
210 clause.replace(match.capturedStart(), match.capturedLength(), repl);
219 rule.
title = tr(
"Match an exact title");
221 rule.
description = QString(
"program.title = '{TITLE}' ");
225 QVariant::fromValue(rule));
229 rule.
title = tr(
"Match this series");
231 rule.
description = QString(
"program.seriesid = '{SERIESID}' ");
233 QVariant::fromValue(rule));
236 rule.
title = tr(
"Match words in the title");
239 rule.
description = QString(
"program.title LIKE '%{TITLE}%' ");
243 QVariant::fromValue(rule));
245 rule.
title = tr(
"Match words in the subtitle");
248 rule.
description = QString(
"program.subtitle LIKE '%{SUBTITLE}%' ");
250 rule.
description =
"program.subtitle LIKE '%Las Vegas%' ";
252 QVariant::fromValue(rule));
256 rule.
title = tr(
"Match this episode");
258 rule.
description = QString(
"program.programid = '{PROGID}' ");
262 rule.
title = tr(
"Match this episode");
264 rule.
description = QString(
"program.subtitle = '{SUBTITLE}' \n"
265 "AND program.description = '{DESCR}' ");
269 rule.
title = tr(
"Match an exact episode");
271 rule.
description = QString(
"program.title = 'Seinfeld' \n"
272 "AND program.subtitle = 'The Soup' ");
275 QVariant::fromValue(rule));
277 rule.
title = tr(
"Match in any descriptive field");
279 rule.
description = QString(
"(program.title LIKE '%Japan%' \n"
280 " OR program.subtitle LIKE '%Japan%' \n"
281 " OR program.description LIKE '%Japan%') ");
283 QVariant::fromValue(rule));
285 rule.
title = tr(
"New episodes only");
289 QVariant::fromValue(rule));
291 rule.
title = tr(
"Exclude unidentified episodes");
295 QVariant::fromValue(rule));
297 rule.
title = tr(
"First showing of each episode");
301 QVariant::fromValue(rule));
303 rule.
title = tr(
"Last showing of each episode");
307 QVariant::fromValue(rule));
309 rule.
title = tr(
"Anytime on a specific day of the week");
312 "DAYNAME(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) = '{DAYNAME}' ";
314 QVariant::fromValue(rule));
316 rule.
title = tr(
"Only on weekdays (Monday through Friday)");
319 "WEEKDAY(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) < 5 ";
321 QVariant::fromValue(rule));
323 rule.
title = tr(
"Only on weekends");
326 "WEEKDAY(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) >= 5 ";
328 QVariant::fromValue(rule));
330 rule.
title = tr(
"Only in prime time");
333 "HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) >= 19 "
334 "AND HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) < 23 ";
336 QVariant::fromValue(rule));
338 rule.
title = tr(
"Not in prime time");
341 "(HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) < 19 "
342 " OR HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) >= 23) ";
344 QVariant::fromValue(rule));
346 rule.
title = tr(
"Only on a specific station");
349 rule.
description = QString(
"channel.callsign = '{SCHEDID}' ");
353 QVariant::fromValue(rule));
355 rule.
title = tr(
"Exclude one station");
359 QVariant::fromValue(rule));
361 rule.
title = tr(
"Match related callsigns");
363 rule.
description =
"channel.callsign LIKE 'HBO%' ";
365 QVariant::fromValue(rule));
367 rule.
title = tr(
"Only channels from the Favorites group");
368 rule.
subtitle =
", channelgroup cg, channelgroupnames cgn";
370 "AND cg.grpid = cgn.grpid \n"
371 "AND program.chanid = cg.chanid ";
373 QVariant::fromValue(rule));
375 rule.
title = tr(
"Only channels from a specific video source");
379 QVariant::fromValue(rule));
381 rule.
title = tr(
"Only channels marked as commercial free");
383 rule.
description = QString(
"channel.commmethod = %1 ")
386 QVariant::fromValue(rule));
388 rule.
title = tr(
"Only shows marked as HDTV");
392 QVariant::fromValue(rule));
394 rule.
title = tr(
"Only shows marked as widescreen");
396 rule.
description =
"FIND_IN_SET('WIDESCREEN', program.videoprop) > 0 ";
398 QVariant::fromValue(rule));
400 rule.
title = tr(
"Exclude H.264 encoded streams (EIT only)");
402 rule.
description =
"FIND_IN_SET('AVC', program.videoprop) = 0 ";
404 QVariant::fromValue(rule));
406 rule.
title = tr(
"Only shows with in-vision signing");
408 rule.
description =
"FIND_IN_SET('SIGNED', program.subtitletypes) > 0 ";
410 QVariant::fromValue(rule));
412 rule.
title = tr(
"Only shows with in-vision subtitles");
414 rule.
description =
"FIND_IN_SET('ONSCREEN', program.subtitletypes) > 0 ";
416 QVariant::fromValue(rule));
418 rule.
title = tr(
"Limit by category");
421 rule.
description = QString(
"program.category = '{CATEGORY}' ");
423 rule.
description =
"program.category = 'Reality' ";
425 QVariant::fromValue(rule));
427 rule.
title = tr(
"All matches for a genre (Schedules Direct)");
428 rule.
subtitle =
"LEFT JOIN programgenres ON "
429 "program.chanid = programgenres.chanid AND "
430 "program.starttime = programgenres.starttime ";
432 rule.
description = QString(
"programgenres.genre = '{CATEGORY}' ");
434 rule.
description =
"programgenres.genre = 'Reality' ";
436 QVariant::fromValue(rule));
438 rule.
title = tr(
"Limit by MPAA or VCHIP rating (Schedules Direct)");
439 rule.
subtitle =
"LEFT JOIN programrating ON "
440 "program.chanid = programrating.chanid AND "
441 "program.starttime = programrating.starttime ";
442 rule.
description =
"(programrating.rating = 'G' OR programrating.rating "
445 QVariant::fromValue(rule));
447 rule.
title = tr(
"Category type (%1)",
"List of hardcoded category types")
448 .arg(
"'movie', 'series', 'sports', 'tvshow'");
450 rule.
description =
"program.category_type = 'sports' ";
452 QVariant::fromValue(rule));
454 rule.
title = tr(
"Limit movies by the year of release");
456 rule.
description =
"program.category_type = 'movie' AND "
457 "program.airdate >= 2000 ";
459 QVariant::fromValue(rule));
461 rule.
title = tr(
"Minimum star rating (0.0 to 1.0 for movies only)");
465 QVariant::fromValue(rule));
467 rule.
title = tr(
"Person named in the credits (Schedules Direct)");
468 rule.
subtitle =
", people, credits";
470 "AND credits.person = people.person \n"
471 "AND program.chanid = credits.chanid \n"
472 "AND program.starttime = credits.starttime ";
474 QVariant::fromValue(rule));
488 rule.
title = tr(
"Re-record SDTV in HDTV (disable duplicate matching)");
489 rule.
subtitle =
", recordedprogram rp1 LEFT OUTER JOIN recordedprogram rp2"
490 " ON rp1.programid = rp2.programid AND rp2.hdtv = 1";
491 rule.
description =
"program.programid = rp1.programid \n"
492 "AND rp1.hdtv = 0 \n"
493 "AND program.hdtv = 1 \n"
494 "AND rp2.starttime IS NULL ";
496 QVariant::fromValue(rule));
498 rule.
title = tr(
"Multiple sports teams (complete example)");
500 rule.
description =
"program.title = 'NBA Basketball' \n"
501 "AND program.subtitle REGEXP '(Miami|Cavaliers|Lakers)' \n"
502 "AND program.first > 0 \n";
504 QVariant::fromValue(rule));
506 rule.
title = tr(
"Sci-fi B-movies (complete example)");
508 rule.
description =
"program.category_type='movie' \n"
509 "AND program.category='Science fiction' \n"
510 "AND program.stars <= 0.5 AND program.airdate < 1970 ";
512 QVariant::fromValue(rule));
515 tr(
"SportsCenter Overnight (complete example - use FindDaily)");
518 "program.title = 'SportsCenter' \n"
519 "AND HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) >= 2 \n"
520 "AND HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) <= 6 ";
522 QVariant::fromValue(rule));
524 rule.
title = tr(
"Movie of the Week (complete example - use FindWeekly)");
527 "program.category_type='movie' \n"
528 "AND program.stars >= 1.0 AND program.airdate >= 1965 \n"
529 "AND DAYNAME(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) = 'Friday' \n"
530 "AND HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) >= 12 ";
532 QVariant::fromValue(rule));
534 rule.
title = tr(
"First Episodes (complete example for Schedules Direct)");
537 "AND program.programid LIKE 'EP%0001' \n"
538 "AND program.originalairdate = "
539 "DATE(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) ";
541 QVariant::fromValue(rule));
546 result.
prepare(
"SELECT rulename,fromclause,whereclause,search "
547 "FROM customexample;");
551 while (result.
next())
553 QString str = result.
value(0).toString();
555 if (result.
value(3).toInt() > 0)
564 QVariant::fromValue(rule));
593 (hastitle && hasdesc));
604 msg = QString(
"\"%1\"").arg(msg.simplified());
611 (hastitle && hasdesc));
624 static const QRegularExpression kNonWhitespaceRE {
"\\S" };
625 if (desc.contains(kNonWhitespaceRE))
679 int cur_recid = rule.
recordid.toInt();
696 if (schededit->Create())
715 bool exampleExists =
false;
718 query.
prepare(
"SELECT rulename,whereclause FROM customexample "
719 "WHERE rulename = :RULE;");
723 exampleExists =
true;
725 QString msg = QString(
"%1: %2\n\n").arg(tr(
"Current Example"),
734 auto *storediag =
new MythDialogBox(msg, mainStack,
"storePopup",
true);
736 storediag->SetReturnEvent(
this,
"storeruledialog");
737 if (storediag->Create())
744 str = tr(
"Replace as a search");
746 str = tr(
"Store as a search");
747 storediag->AddButton(str);
750 str = tr(
"Replace as an example");
752 str = tr(
"Store as an example");
753 storediag->AddButton(str);
759 QString str = QString(
"%1 \"%2\"").arg(tr(
"Delete"),
761 storediag->AddButton(str);
781 msg = tr(
"Power Search rules no longer require a leading \"AND\".");
783 else if (desc.contains(
';'))
785 msg = tr(
"Power Search rules cannot include semicolon ( ; ) ");
786 msg += tr(
"statement terminators.");
791 query.
prepare(QString(
"SELECT NULL FROM (program, channel, oldrecorded AS oldrecstatus) "
792 "%1 WHERE %2 LIMIT 5").arg(from, desc));
800 msg = tr(
"An error was found when checking") +
":\n\n";
802 msg +=
"\n\n" + tr(
"The database error was") +
":\n";
810 GetStack(
"popup stack");
811 auto *checkSyntaxPopup =
814 if (checkSyntaxPopup->Create())
816 checkSyntaxPopup->SetReturnEvent(
this,
"checkSyntaxPopup");
821 delete checkSyntaxPopup;
837 query.
prepare(
"REPLACE INTO customexample "
838 "(rulename,fromclause,whereclause,search) "
839 "VALUES(:RULE,:FROMC,:WHEREC,:SEARCH);");
855 QVariant::fromValue(rule));
869 item->
SetData(QVariant::fromValue(rule));
886 query.
prepare(
"DELETE FROM customexample "
887 "WHERE rulename = :RULE;");
905 QString resultid = dce->GetId();
906 QString resulttext = dce->GetResultText();
908 if (resultid ==
"storeruledialog")
910 if (resulttext.startsWith(tr(
"Delete")))
914 else if (!resulttext.isEmpty())
916 storeRule(resulttext.contains(tr(
"as a search")),
917 !resulttext.startsWith(tr(
"Replace")));
931 for (
int i = 0; i < actions.size() && !handled; i++)
933 const QString&
action = actions[i];
943 else if (
action ==
"EDIT")
void recordClicked(void)
The user clicked on the 'Record' button in the 'Custom Edit' window.
~CustomEdit(void) override
MythUIButton * m_recordButton
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
MythUIButton * m_cancelButton
void customEvent(QEvent *event) override
void scheduleCreated(int ruleID)
CustomEdit(MythScreenStack *parent, ProgramInfo *m_pginfo=nullptr)
void clauseChanged(MythUIButtonListItem *item)
MythUIButton * m_testButton
MythUIButtonList * m_ruleList
MythUIText * m_clauseText
void clauseClicked(MythUIButtonListItem *item)
void ruleChanged(MythUIButtonListItem *item)
MythUITextEdit * m_titleEdit
MythUIButton * m_storeButton
MythUIButtonList * m_clauseList
void storeRule(bool is_search, bool is_new)
MythUITextEdit * m_subtitleEdit
const MythUIButtonListItem * m_currentRuleItem
MythUITextEdit * m_descriptionEdit
QString evaluate(QString clause)
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
static const Type kEventType
QSqlQuery wrapper that fetches a DB connection from the connection pool.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
QString executedQuery(void) const
QSqlError lastError(void) const
QVariant value(int i) const
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Dialog asking for user confirmation.
static void DBError(const QString &where, const MSqlQuery &query)
Basic menu dialog, message and a list of options.
MythScreenStack * GetMainStack()
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
void addListener(QObject *listener)
Add a listener to the observable.
void removeListener(QObject *listener)
Remove a listener to the observable.
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Screen in which all other widgets are contained and rendered.
void BuildFocusList(void)
MythUIType * GetFocusWidget(void) const
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
QString GetText(void) const
void SetText(const QString &text, bool moveCursor=true)
void SetMaxLength(int length)
virtual void SetText(const QString &text)
void SetEnabled(bool enable)
Holds information on recordings and videos.
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
uint GetRecordingRuleID(void) const
QString GetSeriesID(void) const
uint GetEpisode(void) const
QString GetProgramID(void) const
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
QString GetDescription(void) const
QString GetChannelName(void) const
This is the channel name in the local market, i.e.
QString GetTitle(void) const
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
QString GetChanNum(void) const
This is the channel "number", in the form 1, 1_2, 1-2, 1#1, etc.
QString GetSubtitle(void) const
QString GetCategory(void) const
uint GetSeason(void) const
QString GetChannelSchedulingID(void) const
This is the unique programming identifier of a channel.
static const QRegularExpression kReLeadingAnd
static const QRegularExpression kReSearchTypeName
Internal representation of a recording rule, mirrors the record table.
Construct a recording schedule.
void ruleSaved(int ruleId)
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
MythMainWindow * GetMythMainWindow(void)
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)