MythTV  master
customedit.cpp
Go to the documentation of this file.
1 
2 #include "customedit.h"
3 
4 // QT
5 #include <QSqlError>
6 
7 // libmythbase
8 #include "mythdb.h"
9 
10 // libmyth
11 #include "mythcorecontext.h"
12 
13 // libmythui
14 #include "mythuibuttonlist.h"
15 #include "mythuitextedit.h"
16 #include "mythuibutton.h"
17 #include "mythdialogbox.h"
18 #include "mythuitext.h"
19 
20 // libmythtv
21 #include "recordingrule.h"
22 
23 // mythfrontend
24 #include "scheduleeditor.h"
25 #include "proglist.h"
26 
28  : MythScreenType(parent, "CustomEdit")
29 {
30  if (pginfo)
31  m_pginfo = new ProgramInfo(*pginfo);
32  else
33  m_pginfo = new ProgramInfo();
34 
37 
38  m_seSuffix = QString(" (%1)").arg(tr("stored search"));
39  m_exSuffix = QString(" (%1)").arg(tr("stored example"));
40 
42 }
43 
45 {
46  delete m_pginfo;
47 
49 }
50 
52 {
53  if (!LoadWindowFromXML("schedule-ui.xml", "customedit", this))
54  return false;
55 
56  bool err = false;
57  UIUtilE::Assign(this, m_ruleList, "rules", &err);
58  UIUtilE::Assign(this, m_clauseList, "clauses", &err);
59 
60  UIUtilE::Assign(this, m_titleEdit, "title", &err);
61  UIUtilE::Assign(this, m_subtitleEdit, "subtitle", &err);
62  UIUtilE::Assign(this, m_descriptionEdit, "description", &err);
63  UIUtilE::Assign(this, m_clauseText, "clausetext", &err);
64  UIUtilE::Assign(this, m_testButton, "test", &err);
65  UIUtilE::Assign(this, m_recordButton, "record", &err);
66  UIUtilE::Assign(this, m_storeButton, "store", &err);
67  UIUtilE::Assign(this, m_cancelButton, "cancel", &err);
68 
69  if (err)
70  {
71  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'customedit'");
72  return false;
73  }
74 
84 
89 
94 
95  loadData();
97 
98  return true;
99 }
100 
101 
103 {
104  CustomRuleInfo rule;
105 
106  // New Rule defaults
107  rule.recordid = '0';
108  rule.title = m_baseTitle;
109  if (!m_baseTitle.isEmpty())
110  {
111  QString quoteTitle = m_baseTitle;
112  quoteTitle.replace("\'","\'\'");
113  rule.description = QString("program.title = '%1' ").arg(quoteTitle);
114  }
115 
116  new MythUIButtonListItem(m_ruleList, tr("<New rule>"),
117  QVariant::fromValue(rule));
118 
119  MSqlQuery result(MSqlQuery::InitCon());
120  result.prepare("SELECT recordid, title, subtitle, description "
121  "FROM record WHERE search = :SEARCH ORDER BY title;");
122  result.bindValue(":SEARCH", kPowerSearch);
123 
124  if (result.exec())
125  {
126  // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
127  while (result.next())
128  {
129  QString trimTitle = result.value(1).toString();
130  trimTitle.remove(RecordingInfo::kReSearchTypeName);
131 
132  rule.recordid = result.value(0).toString();
133  rule.title = trimTitle;
134  rule.subtitle = result.value(2).toString();
135  rule.description = result.value(3).toString();
136 
137  // No memory leak. MythUIButtonListItem adds the new item
138  // into m_ruleList.
139  auto *item = new MythUIButtonListItem(m_ruleList, rule.title,
140  QVariant::fromValue(rule));
141 
142  if (trimTitle == m_baseTitle ||
143  result.value(0).toUInt() == m_pginfo->GetRecordingRuleID())
144  m_ruleList->SetItemCurrent(item);
145  }
146  }
147  else
148  MythDB::DBError("Get power search rules query", result);
149 
150  loadClauses();
151 
152  textChanged();
153 }
154 
155 QString CustomEdit::evaluate(QString clause)
156 {
157  static const QRegularExpression replacement { "\\{([A-Z]+)\\}" };
158 
159  while (true) {
160  QRegularExpressionMatch match;
161  if (!clause.contains(replacement, &match))
162  break;
163 
164  QString mid = match.captured(1);
165  QString repl = "";
166 
167  if (mid.compare("TITLE") == 0) {
168  repl = m_pginfo->GetTitle();
169  repl.replace("\'","\'\'");
170  } else if (mid.compare("SUBTITLE") == 0) {
171  repl = m_pginfo->GetSubtitle();
172  repl.replace("\'","\'\'");
173  } else if (mid.compare("DESCR") == 0) {
174  repl = m_pginfo->GetDescription();
175  repl.replace("\'","\'\'");
176  } else if (mid.compare("SERIESID") == 0) {
177  repl = QString("%1").arg(m_pginfo->GetSeriesID());
178  } else if (mid.compare("PROGID") == 0) {
179  repl = m_pginfo->GetProgramID();
180  } else if (mid.compare("SEASON") == 0) {
181  repl = QString::number(m_pginfo->GetSeason());
182  } else if (mid.compare("EPISODE") == 0) {
183  repl = QString::number(m_pginfo->GetEpisode());
184  } else if (mid.compare("CATEGORY") == 0) {
185  repl = m_pginfo->GetCategory();
186  } else if (mid.compare("CHANID") == 0) {
187  repl = QString("%1").arg(m_pginfo->GetChanID());
188  } else if (mid.compare("CHANNUM") == 0) {
189  repl = m_pginfo->GetChanNum();
190  } else if (mid.compare("SCHEDID") == 0) {
192  } else if (mid.compare("CHANNAME") == 0) {
193  repl = m_pginfo->GetChannelName();
194  } else if (mid.compare("DAYNAME") == 0) {
195  repl = m_pginfo->GetScheduledStartTime().toString("dddd");
196  } else if (mid.compare("STARTDATE") == 0) {
197  repl = m_pginfo->GetScheduledStartTime().toString("yyyy-mm-dd hh:mm:ss");
198  } else if (mid.compare("ENDDATE") == 0) {
199  repl = m_pginfo->GetScheduledEndTime().toString("yyyy-mm-dd hh:mm:ss");
200  } else if (mid.compare("STARTTIME") == 0) {
201  repl = m_pginfo->GetScheduledStartTime().toString("hh:mm");
202  } else if (mid.compare("ENDTIME") == 0) {
203  repl = m_pginfo->GetScheduledEndTime().toString("hh:mm");
204  } else if (mid.compare("STARTSEC") == 0) {
205  QDateTime date = m_pginfo->GetScheduledStartTime();
206 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
207  QDateTime midnight = QDateTime(date.date());
208 #else
209  QDateTime midnight = date.date().startOfDay();
210 #endif
211  repl = QString("%1").arg(midnight.secsTo(date));
212  } else if (mid.compare("ENDSEC") == 0) {
213  QDateTime date = m_pginfo->GetScheduledEndTime();
214 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
215  QDateTime midnight = QDateTime(date.date());
216 #else
217  QDateTime midnight = date.date().startOfDay();
218 #endif
219  repl = QString("%1").arg(midnight.secsTo(date));
220  }
221  // unknown tags are simply removed
222  clause.replace(match.capturedStart(), match.capturedLength(), repl);
223  }
224  return clause;
225 }
226 
228 {
229  CustomRuleInfo rule;
230 
231  rule.title = tr("Match an exact title");
232  if (!m_baseTitle.isEmpty())
233  rule.description = QString("program.title = '{TITLE}' ");
234  else
235  rule.description = "program.title = 'Nova' ";
237  QVariant::fromValue(rule));
238 
239  if (!m_pginfo->GetSeriesID().isEmpty())
240  {
241  rule.title = tr("Match this series");
242  rule.subtitle.clear();
243  rule.description = QString("program.seriesid = '{SERIESID}' ");
245  QVariant::fromValue(rule));
246  }
247 
248  rule.title = tr("Match words in the title");
249  rule.subtitle.clear();
250  if (!m_pginfo->GetTitle().isEmpty())
251  rule.description = QString("program.title LIKE '%{TITLE}%' ");
252  else
253  rule.description = "program.title LIKE 'CSI: %' ";
255  QVariant::fromValue(rule));
256 
257  rule.title = tr("Match words in the subtitle");
258  rule.subtitle.clear();
259  if (!m_pginfo->GetSubtitle().isEmpty())
260  rule.description = QString("program.subtitle LIKE '%{SUBTITLE}%' ");
261  else
262  rule.description = "program.subtitle LIKE '%Las Vegas%' ";
264  QVariant::fromValue(rule));
265 
266  if (!m_pginfo->GetProgramID().isEmpty())
267  {
268  rule.title = tr("Match this episode");
269  rule.subtitle.clear();
270  rule.description = QString("program.programid = '{PROGID}' ");
271  }
272  else if (!m_pginfo->GetSubtitle().isEmpty())
273  {
274  rule.title = tr("Match this episode");
275  rule.subtitle.clear();
276  rule.description = QString("program.subtitle = '{SUBTITLE}' \n"
277  "AND program.description = '{DESCR}' ");
278  }
279  else
280  {
281  rule.title = tr("Match an exact episode");
282  rule.subtitle.clear();
283  rule.description = QString("program.title = 'Seinfeld' \n"
284  "AND program.subtitle = 'The Soup' ");
285  }
287  QVariant::fromValue(rule));
288 
289  rule.title = tr("Match in any descriptive field");
290  rule.subtitle.clear();
291  rule.description = QString("(program.title LIKE '%Japan%' \n"
292  " OR program.subtitle LIKE '%Japan%' \n"
293  " OR program.description LIKE '%Japan%') ");
295  QVariant::fromValue(rule));
296 
297  rule.title = tr("New episodes only");
298  rule.subtitle.clear();
299  rule.description = "program.previouslyshown = 0 ";
301  QVariant::fromValue(rule));
302 
303  rule.title = tr("Exclude unidentified episodes");
304  rule.subtitle.clear();
305  rule.description = "program.generic = 0 ";
307  QVariant::fromValue(rule));
308 
309  rule.title = tr("First showing of each episode");
310  rule.subtitle.clear();
311  rule.description = "program.first > 0 ";
313  QVariant::fromValue(rule));
314 
315  rule.title = tr("Last showing of each episode");
316  rule.subtitle.clear();
317  rule.description = "program.last > 0 ";
319  QVariant::fromValue(rule));
320 
321  rule.title = tr("Anytime on a specific day of the week");
322  rule.subtitle.clear();
323  rule.description =
324  "DAYNAME(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) = '{DAYNAME}' ";
326  QVariant::fromValue(rule));
327 
328  rule.title = tr("Only on weekdays (Monday through Friday)");
329  rule.subtitle.clear();
330  rule.description =
331  "WEEKDAY(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) < 5 ";
333  QVariant::fromValue(rule));
334 
335  rule.title = tr("Only on weekends");
336  rule.subtitle.clear();
337  rule.description =
338  "WEEKDAY(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) >= 5 ";
340  QVariant::fromValue(rule));
341 
342  rule.title = tr("Only in prime time");
343  rule.subtitle.clear();
344  rule.description =
345  "HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) >= 19 "
346  "AND HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) < 23 ";
348  QVariant::fromValue(rule));
349 
350  rule.title = tr("Not in prime time");
351  rule.subtitle.clear();
352  rule.description =
353  "(HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) < 19 "
354  " OR HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) >= 23) ";
356  QVariant::fromValue(rule));
357 
358  rule.title = tr("Only on a specific station");
359  rule.subtitle.clear();
360  if (!m_pginfo->GetChannelSchedulingID().isEmpty())
361  rule.description = QString("channel.callsign = '{SCHEDID}' ");
362  else
363  rule.description = "channel.callsign = 'ESPN' ";
365  QVariant::fromValue(rule));
366 
367  rule.title = tr("Exclude one station");
368  rule.subtitle.clear();
369  rule.description = "channel.callsign != 'GOLF' ";
371  QVariant::fromValue(rule));
372 
373  rule.title = tr("Match related callsigns");
374  rule.subtitle.clear();
375  rule.description = "channel.callsign LIKE 'HBO%' ";
377  QVariant::fromValue(rule));
378 
379  rule.title = tr("Only channels from the Favorites group");
380  rule.subtitle = ", channelgroup cg, channelgroupnames cgn";
381  rule.description = "cgn.name = 'Favorites' \n"
382  "AND cg.grpid = cgn.grpid \n"
383  "AND program.chanid = cg.chanid ";
385  QVariant::fromValue(rule));
386 
387  rule.title = tr("Only channels from a specific video source");
388  rule.subtitle.clear();
389  rule.description = "channel.sourceid = 2 ";
391  QVariant::fromValue(rule));
392 
393  rule.title = tr("Only channels marked as commercial free");
394  rule.subtitle.clear();
395  rule.description = QString("channel.commmethod = %1 ")
396  .arg(COMM_DETECT_COMMFREE);
398  QVariant::fromValue(rule));
399 
400  rule.title = tr("Only shows marked as HDTV");
401  rule.subtitle.clear();
402  rule.description = "program.hdtv > 0 ";
404  QVariant::fromValue(rule));
405 
406  rule.title = tr("Only shows marked as widescreen");
407  rule.subtitle.clear();
408  rule.description = "FIND_IN_SET('WIDESCREEN', program.videoprop) > 0 ";
410  QVariant::fromValue(rule));
411 
412  rule.title = tr("Exclude H.264 encoded streams (EIT only)");
413  rule.subtitle.clear();
414  rule.description = "FIND_IN_SET('AVC', program.videoprop) = 0 ";
416  QVariant::fromValue(rule));
417 
418  rule.title = tr("Only shows with in-vision signing");
419  rule.subtitle.clear();
420  rule.description = "FIND_IN_SET('SIGNED', program.subtitletypes) > 0 ";
422  QVariant::fromValue(rule));
423 
424  rule.title = tr("Only shows with in-vision subtitles");
425  rule.subtitle.clear();
426  rule.description = "FIND_IN_SET('ONSCREEN', program.subtitletypes) > 0 ";
428  QVariant::fromValue(rule));
429 
430  rule.title = tr("Limit by category");
431  rule.subtitle.clear();
432  if (!m_pginfo->GetCategory().isEmpty())
433  rule.description = QString("program.category = '{CATEGORY}' ");
434  else
435  rule.description = "program.category = 'Reality' ";
437  QVariant::fromValue(rule));
438 
439  rule.title = tr("All matches for a genre (Schedules Direct)");
440  rule.subtitle = "LEFT JOIN programgenres ON "
441  "program.chanid = programgenres.chanid AND "
442  "program.starttime = programgenres.starttime ";
443  if (!m_pginfo->GetCategory().isEmpty())
444  rule.description = QString("programgenres.genre = '{CATEGORY}' ");
445  else
446  rule.description = "programgenres.genre = 'Reality' ";
448  QVariant::fromValue(rule));
449 
450  rule.title = tr("Limit by MPAA or VCHIP rating (Schedules Direct)");
451  rule.subtitle = "LEFT JOIN programrating ON "
452  "program.chanid = programrating.chanid AND "
453  "program.starttime = programrating.starttime ";
454  rule.description = "(programrating.rating = 'G' OR programrating.rating "
455  "LIKE 'TV-Y%') ";
457  QVariant::fromValue(rule));
458 
459  rule.title = tr("Category type (%1)", "List of hardcoded category types")
460  .arg("'movie', 'series', 'sports', 'tvshow'");
461  rule.subtitle.clear();
462  rule.description = "program.category_type = 'sports' ";
464  QVariant::fromValue(rule));
465 
466  rule.title = tr("Limit movies by the year of release");
467  rule.subtitle.clear();
468  rule.description = "program.category_type = 'movie' AND "
469  "program.airdate >= 2000 ";
471  QVariant::fromValue(rule));
472 
473  rule.title = tr("Minimum star rating (0.0 to 1.0 for movies only)");
474  rule.subtitle.clear();
475  rule.description = "program.stars >= 0.75 ";
477  QVariant::fromValue(rule));
478 
479  rule.title = tr("Person named in the credits (Schedules Direct)");
480  rule.subtitle = ", people, credits";
481  rule.description = "people.name = 'Tom Hanks' \n"
482  "AND credits.person = people.person \n"
483  "AND program.chanid = credits.chanid \n"
484  "AND program.starttime = credits.starttime ";
486  QVariant::fromValue(rule));
487 
488 /* This shows how to use oldprogram but is a bad idea in practice.
489  This would match all future showings until the day after the first
490  showing when all future showing are no longer 'new' titles.
491 
492  rule.title = tr("Only titles from the New Titles list");
493  rule.subtitle = "LEFT JOIN oldprogram"
494  " ON oldprogram.oldtitle = program.title ";
495  rule.description = "oldprogram.oldtitle IS NULL ";
496  new MythUIButtonListItem(m_clauseList, rule.title,
497  QVariant::fromValue(rule));
498 */
499 
500  rule.title = tr("Re-record SDTV in HDTV (disable duplicate matching)");
501  rule.subtitle = ", recordedprogram rp1 LEFT OUTER JOIN recordedprogram rp2"
502  " ON rp1.programid = rp2.programid AND rp2.hdtv = 1";
503  rule.description = "program.programid = rp1.programid \n"
504  "AND rp1.hdtv = 0 \n"
505  "AND program.hdtv = 1 \n"
506  "AND rp2.starttime IS NULL ";
508  QVariant::fromValue(rule));
509 
510  rule.title = tr("Multiple sports teams (complete example)");
511  rule.subtitle.clear();
512  rule.description = "program.title = 'NBA Basketball' \n"
513  "AND program.subtitle REGEXP '(Miami|Cavaliers|Lakers)' \n"
514  "AND program.first > 0 \n";
516  QVariant::fromValue(rule));
517 
518  rule.title = tr("Sci-fi B-movies (complete example)");
519  rule.subtitle.clear();
520  rule.description = "program.category_type='movie' \n"
521  "AND program.category='Science fiction' \n"
522  "AND program.stars <= 0.5 AND program.airdate < 1970 ";
524  QVariant::fromValue(rule));
525 
526  rule.title =
527  tr("SportsCenter Overnight (complete example - use FindDaily)");
528  rule.subtitle.clear();
529  rule.description =
530  "program.title = 'SportsCenter' \n"
531  "AND HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) >= 2 \n"
532  "AND HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) <= 6 ";
534  QVariant::fromValue(rule));
535 
536  rule.title = tr("Movie of the Week (complete example - use FindWeekly)");
537  rule.subtitle.clear();
538  rule.description =
539  "program.category_type='movie' \n"
540  "AND program.stars >= 1.0 AND program.airdate >= 1965 \n"
541  "AND DAYNAME(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) = 'Friday' \n"
542  "AND HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) >= 12 ";
544  QVariant::fromValue(rule));
545 
546  rule.title = tr("First Episodes (complete example for Schedules Direct)");
547  rule.subtitle.clear();
548  rule.description = "program.first > 0 \n"
549  "AND program.programid LIKE 'EP%0001' \n"
550  "AND program.originalairdate = "
551  "DATE(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) ";
553  QVariant::fromValue(rule));
554 
556 
557  MSqlQuery result(MSqlQuery::InitCon());
558  result.prepare("SELECT rulename,fromclause,whereclause,search "
559  "FROM customexample;");
560 
561  if (result.exec())
562  {
563  while (result.next())
564  {
565  QString str = result.value(0).toString();
566 
567  if (result.value(3).toInt() > 0)
568  str += m_seSuffix;
569  else
570  str += m_exSuffix;
571 
572  rule.title = str;
573  rule.subtitle = result.value(1).toString();
574  rule.description = result.value(2).toString();
576  QVariant::fromValue(rule));
577  }
578  }
579 }
580 
582 {
583  if (!item || item == m_currentRuleItem)
584  return;
585 
586  m_currentRuleItem = item;
587 
588  auto rule = item->GetData().value<CustomRuleInfo>();
589 
590  m_titleEdit->SetText(rule.title);
591  m_descriptionEdit->SetText(rule.description);
592  m_subtitleEdit->SetText(rule.subtitle);
593 
594  textChanged();
595 }
596 
598 {
599  bool hastitle = !m_titleEdit->GetText().isEmpty();
600  bool hasdesc = !m_descriptionEdit->GetText().isEmpty();
601 
602  m_testButton->SetEnabled(hasdesc);
603  m_recordButton->SetEnabled(hastitle && hasdesc);
605  (hastitle && hasdesc));
606 }
607 
609 {
610  if (!item)
611  return;
612 
613  auto rule = item->GetData().value<CustomRuleInfo>();
614 
615  QString msg = (m_evaluate) ? evaluate(rule.description) : rule.description;
616  msg = QString("\"%1\"").arg(msg.simplified());
617  m_clauseText->SetText(msg);
618 
619  bool hastitle = !m_titleEdit->GetText().isEmpty();
620  bool hasdesc = !m_descriptionEdit->GetText().isEmpty();
621 
623  (hastitle && hasdesc));
624 }
625 
627 {
628  if (!item)
629  return;
630 
631  auto rule = item->GetData().value<CustomRuleInfo>();
632 
633  QString clause;
634  QString desc = m_descriptionEdit->GetText();
635 
636  if (desc.contains(QRegularExpression("\\S")))
637  clause = "AND ";
638  clause += (m_evaluate) ? evaluate(rule.description) : rule.description;
639 
640  m_descriptionEdit->SetText(desc.append(clause));
641 
642  QString sub = m_subtitleEdit->GetText();
643  m_subtitleEdit->SetText(sub.append(rule.subtitle));
644 }
645 
647 {
648  if (!checkSyntax())
649  {
650  return;
651  }
652 
654  auto *pl = new ProgLister(mainStack, plSQLSearch,
657  if (pl->Create())
658  {
659  mainStack->AddScreen(pl);
660  }
661  else
662  delete pl;
663 }
664 
679 {
680  if (!checkSyntax())
681  return;
682 
683  auto *record = new RecordingRule();
684 
686  auto rule = item->GetData().value<CustomRuleInfo>();
687 
688  int cur_recid = rule.recordid.toInt();
689  if (cur_recid > 0)
690  {
691  record->ModifyPowerSearchByID(cur_recid, m_titleEdit->GetText(),
694  }
695  else
696  {
697  record->LoadBySearch(kPowerSearch, m_titleEdit->GetText(),
700  m_pginfo->GetTitle().isEmpty() ? nullptr : m_pginfo);
701  }
702 
704  auto *schededit = new ScheduleEditor(mainStack, record);
705  if (schededit->Create())
706  {
707  mainStack->AddScreen(schededit);
708  connect(schededit, &ScheduleEditor::ruleSaved, this, &CustomEdit::scheduleCreated);
709  }
710  else
711  delete schededit;
712 }
713 
715 {
716  if (ruleID > 0)
717  Close();
718 }
719 
721 {
722  bool exampleExists = false;
723 
724  MSqlQuery query(MSqlQuery::InitCon());
725  query.prepare("SELECT rulename,whereclause FROM customexample "
726  "WHERE rulename = :RULE;");
727  query.bindValue(":RULE", m_titleEdit->GetText());
728 
729  if (query.exec() && query.next())
730  exampleExists = true;
731 
732  QString msg = QString("%1: %2\n\n").arg(tr("Current Example"),
733  m_titleEdit->GetText());
734 
735  if (!m_subtitleEdit->GetText().isEmpty())
736  msg += m_subtitleEdit->GetText() + "\n\n";
737 
738  msg += m_descriptionEdit->GetText();
739 
741  auto *storediag = new MythDialogBox(msg, mainStack, "storePopup", true);
742 
743  storediag->SetReturnEvent(this, "storeruledialog");
744  if (storediag->Create())
745  {
746  if (!m_titleEdit->GetText().isEmpty())
747  {
748  QString str;
749  // Keep strings whole for translation!
750  if (exampleExists)
751  str = tr("Replace as a search");
752  else
753  str = tr("Store as a search");
754  storediag->AddButton(str);
755 
756  if (exampleExists)
757  str = tr("Replace as an example");
758  else
759  str = tr("Store as an example");
760  storediag->AddButton(str);
761  }
762 
764  {
766  QString str = QString("%1 \"%2\"").arg(tr("Delete"),
767  item->GetText());
768  storediag->AddButton(str);
769  }
770  mainStack->AddScreen(storediag);
771  }
772  else
773  delete storediag;
774 }
775 
776 
778 {
779  bool ret = false;
780  QString msg;
781 
782  QString desc = evaluate(m_descriptionEdit->GetText());
783  QString from = m_subtitleEdit->GetText();
784  if (desc.contains(RecordingInfo::kReLeadingAnd))
785  {
786  msg = tr("Power Search rules no longer require a leading \"AND\".");
787  }
788  else if (desc.contains(';'))
789  {
790  msg = tr("Power Search rules cannot include semicolon ( ; ) ");
791  msg += tr("statement terminators.");
792  }
793  else
794  {
795  MSqlQuery query(MSqlQuery::InitCon());
796  query.prepare(QString("SELECT NULL FROM (program,channel) "
797  "%1 WHERE\n%2").arg(from, desc));
798 
799  if (query.exec())
800  {
801  ret = true;
802  }
803  else
804  {
805  msg = tr("An error was found when checking") + ":\n\n";
806  msg += query.executedQuery();
807  msg += "\n\n" + tr("The database error was") + ":\n";
808  msg += query.lastError().databaseText();
809  }
810  }
811 
812  if (!msg.isEmpty())
813  {
814  MythScreenStack *popupStack = GetMythMainWindow()->
815  GetStack("popup stack");
816  auto *checkSyntaxPopup =
817  new MythConfirmationDialog(popupStack, msg, false);
818 
819  if (checkSyntaxPopup->Create())
820  {
821  checkSyntaxPopup->SetReturnEvent(this, "checkSyntaxPopup");
822  popupStack->AddScreen(checkSyntaxPopup);
823  }
824  else
825  {
826  delete checkSyntaxPopup;
827  }
828  ret = false;
829  }
830  return ret;
831 }
832 
833 void CustomEdit::storeRule(bool is_search, bool is_new)
834 {
835  CustomRuleInfo rule;
836  rule.recordid = '0';
837  rule.title = m_titleEdit->GetText();
838  rule.subtitle = m_subtitleEdit->GetText();
840 
841  MSqlQuery query(MSqlQuery::InitCon());
842  query.prepare("REPLACE INTO customexample "
843  "(rulename,fromclause,whereclause,search) "
844  "VALUES(:RULE,:FROMC,:WHEREC,:SEARCH);");
845  query.bindValue(":RULE", rule.title);
846  query.bindValue(":FROMC", rule.subtitle);
847  query.bindValue(":WHEREC", rule.description);
848  query.bindValue(":SEARCH", is_search);
849 
850  if (is_search)
851  rule.title += m_seSuffix;
852  else
853  rule.title += m_exSuffix;
854 
855  if (!query.exec())
856  MythDB::DBError("Store custom example", query);
857  else if (is_new)
858  {
860  QVariant::fromValue(rule));
861  }
862  else
863  {
864  /* Modify the existing entry. We know one exists from the database
865  search but do not know its position in the clause list. It may
866  or may not be the current item. */
867  for (int i = m_maxex; i < m_clauseList->GetCount(); i++)
868  {
870  QString removedStr = item->GetText().remove(m_seSuffix)
871  .remove(m_exSuffix);
872  if (m_titleEdit->GetText() == removedStr)
873  {
874  item->SetData(QVariant::fromValue(rule));
875  clauseChanged(item);
876  break;
877  }
878  }
879  }
880 
881 
882 }
883 
885 {
887  if (!item || m_clauseList->GetCurrentPos() < m_maxex)
888  return;
889 
890  MSqlQuery query(MSqlQuery::InitCon());
891  query.prepare("DELETE FROM customexample "
892  "WHERE rulename = :RULE;");
893  query.bindValue(":RULE", item->GetText().remove(m_seSuffix)
894  .remove(m_exSuffix));
895 
896  if (!query.exec())
897  MythDB::DBError("Delete custom example", query);
898  else
899  {
900  m_clauseList->RemoveItem(item);
901  }
902 }
903 
904 void CustomEdit::customEvent(QEvent *event)
905 {
906  if (event->type() == DialogCompletionEvent::kEventType)
907  {
908  auto *dce = (DialogCompletionEvent*)(event);
909 
910  QString resultid = dce->GetId();
911  QString resulttext = dce->GetResultText();
912 
913  if (resultid == "storeruledialog")
914  {
915  if (resulttext.startsWith(tr("Delete")))
916  {
917  deleteRule();
918  }
919  else if (!resulttext.isEmpty())
920  {
921  storeRule(resulttext.contains(tr("as a search")),
922  !resulttext.startsWith(tr("Replace")));
923  }
924  }
925  }
926 }
927 
928 bool CustomEdit::keyPressEvent(QKeyEvent *event)
929 {
930  if (GetFocusWidget()->keyPressEvent(event))
931  return true;
932 
933  QStringList actions;
934  bool handled = GetMythMainWindow()->TranslateKeyPress("TV Frontend", event, actions);
935 
936  for (int i = 0; i < actions.size() && !handled; i++)
937  {
938  QString action = actions[i];
939  handled = true;
940 
941  if (action == "DELETE")
942  {
943  if (GetFocusWidget() == m_clauseList)
944  deleteRule();
945  // else if (GetFocusWidget() == m_ruleList)
946  // deleteRecordingRule();
947  }
948  else if (action == "EDIT")
949  {
950  // toggle evaluated/unevaluated sample view
953  clauseChanged(item);
954  } else
955  handled = false;
956  }
957 
958  if (!handled && MythScreenType::keyPressEvent(event))
959  handled = true;
960 
961  return handled;
962 }
MythUIButton::Clicked
void Clicked()
scheduleeditor.h
MythUIButtonList::GetItemAt
MythUIButtonListItem * GetItemAt(int pos) const
Definition: mythuibuttonlist.cpp:1676
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:802
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:124
MythMainWindow::GetMainStack
MythScreenStack * GetMainStack()
Definition: mythmainwindow.cpp:317
CustomEdit::m_seSuffix
QString m_seSuffix
Definition: customedit.h:53
MythUITextEdit::SetMaxLength
void SetMaxLength(int length)
Definition: mythuitextedit.cpp:210
MythUIButtonList::GetItemCurrent
MythUIButtonListItem * GetItemCurrent() const
Definition: mythuibuttonlist.cpp:1590
CustomEdit::m_ruleList
MythUIButtonList * m_ruleList
Definition: customedit.h:56
RecordingInfo::kReSearchTypeName
static const QRegularExpression kReSearchTypeName
Definition: recordinginfo.h:197
mythuitext.h
CustomRuleInfo::title
QString title
Definition: customedit.h:78
customedit.h
CustomEdit::storeRule
void storeRule(bool is_search, bool is_new)
Definition: customedit.cpp:833
mythdb.h
CustomEdit::ruleChanged
void ruleChanged(MythUIButtonListItem *item)
Definition: customedit.cpp:581
MythScreenType::Close
virtual void Close()
Definition: mythscreentype.cpp:385
CustomEdit::m_currentRuleItem
const MythUIButtonListItem * m_currentRuleItem
Definition: customedit.h:73
CustomEdit::clauseChanged
void clauseChanged(MythUIButtonListItem *item)
Definition: customedit.cpp:608
MythUIButtonList::RemoveItem
void RemoveItem(MythUIButtonListItem *item)
Definition: mythuibuttonlist.cpp:1488
ProgramInfo::GetChannelName
QString GetChannelName(void) const
This is the channel name in the local market, i.e.
Definition: programinfo.h:385
DialogCompletionEvent::kEventType
static Type kEventType
Definition: mythdialogbox.h:57
ProgramInfo::GetChanNum
QString GetChanNum(void) const
This is the channel "number", in the form 1, 1_2, 1-2, 1#1, etc.
Definition: programinfo.h:375
CustomEdit::m_maxex
int m_maxex
Definition: customedit.h:50
MythUIButtonList::itemSelected
void itemSelected(MythUIButtonListItem *item)
CustomEdit::checkSyntax
bool checkSyntax(void)
Definition: customedit.cpp:777
mythdialogbox.h
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:201
MythScreenStack
Definition: mythscreenstack.h:16
ProgramInfo::GetChannelSchedulingID
QString GetChannelSchedulingID(void) const
This is the unique programming identifier of a channel.
Definition: programinfo.h:382
ProgramInfo::GetCategory
QString GetCategory(void) const
Definition: programinfo.h:368
ProgLister
Definition: proglist.h:31
RecordingInfo::kReLeadingAnd
static const QRegularExpression kReLeadingAnd
Definition: recordinginfo.h:200
RecordingRule
Internal representation of a recording rule, mirrors the record table.
Definition: recordingrule.h:32
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
CustomEdit::~CustomEdit
~CustomEdit(void) override
Definition: customedit.cpp:44
ProgramInfo::GetScheduledEndTime
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:396
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
MythScreenType
Screen in which all other widgets are contained and rendered.
Definition: mythscreentype.h:44
CustomEdit::m_descriptionEdit
MythUITextEdit * m_descriptionEdit
Definition: customedit.h:62
CustomEdit::m_exSuffix
QString m_exSuffix
Definition: customedit.h:54
CustomEdit::deleteRule
void deleteRule(void)
Definition: customedit.cpp:884
ScheduleEditor::ruleSaved
void ruleSaved(int ruleId)
MythUITextEdit::GetText
QString GetText(void) const
Definition: mythuitextedit.h:47
CustomEdit::customEvent
void customEvent(QEvent *event) override
Definition: customedit.cpp:904
mythuibuttonlist.h
MythUIButtonList::GetCount
int GetCount() const
Definition: mythuibuttonlist.cpp:1655
CustomRuleInfo
Definition: customedit.h:76
CustomEdit::loadClauses
void loadClauses(void)
Definition: customedit.cpp:227
CustomRuleInfo::description
QString description
Definition: customedit.h:80
proglist.h
CustomEdit::m_clauseText
MythUIText * m_clauseText
Definition: customedit.h:67
MythScreenType::GetFocusWidget
MythUIType * GetFocusWidget(void) const
Definition: mythscreentype.cpp:112
CustomEdit::m_titleEdit
MythUITextEdit * m_titleEdit
Definition: customedit.h:59
CustomEdit::textChanged
void textChanged(void)
Definition: customedit.cpp:597
MythObservable::addListener
void addListener(QObject *listener)
Add a listener to the observable.
Definition: mythobservable.cpp:38
MythUIButtonListItem
Definition: mythuibuttonlist.h:27
CustomEdit::CustomEdit
CustomEdit(MythScreenStack *parent, ProgramInfo *m_pginfo=nullptr)
Definition: customedit.cpp:27
MythUITextEdit::SetText
void SetText(const QString &text, bool moveCursor=true)
Definition: mythuitextedit.cpp:215
COMM_DETECT_COMMFREE
@ COMM_DETECT_COMMFREE
Definition: programtypes.h:130
ProgramInfo::GetScheduledStartTime
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:389
MythUIButtonList::itemClicked
void itemClicked(MythUIButtonListItem *item)
MythMainWindow::TranslateKeyPress
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
Definition: mythmainwindow.cpp:1116
kPowerSearch
@ kPowerSearch
Definition: recordingtypes.h:74
MythUIButtonList::GetCurrentPos
int GetCurrentPos() const
Definition: mythuibuttonlist.h:198
CustomRuleInfo::recordid
QString recordid
Definition: customedit.h:77
MythDialogBox
Basic menu dialog, message and a list of options.
Definition: mythdialogbox.h:166
CustomEdit::m_baseTitle
QString m_baseTitle
Definition: customedit.h:48
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
ProgramInfo::GetTitle
QString GetTitle(void) const
Definition: programinfo.h:360
ProgramInfo::GetDescription
QString GetDescription(void) const
Definition: programinfo.h:364
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:200
MythScreenType::BuildFocusList
void BuildFocusList(void)
Definition: mythscreentype.cpp:205
CustomEdit::m_testButton
MythUIButton * m_testButton
Definition: customedit.h:68
MythUIType::SetEnabled
void SetEnabled(bool enable)
Definition: mythuitype.cpp:1109
CustomEdit::m_cancelButton
MythUIButton * m_cancelButton
Definition: customedit.h:71
MythUIButtonListItem::GetData
QVariant GetData()
Definition: mythuibuttonlist.cpp:3587
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:60
CustomEdit::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: customedit.cpp:928
ProgramInfo::GetSeriesID
QString GetSeriesID(void) const
Definition: programinfo.h:434
UIUtilDisp::Assign
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
MSqlQuery::lastError
QSqlError lastError(void) const
Definition: mythdbcon.h:210
MythUIButtonListItem::GetText
QString GetText(const QString &name="") const
Definition: mythuibuttonlist.cpp:3312
CustomEdit::testClicked
void testClicked(void)
Definition: customedit.cpp:646
CustomEdit::Create
bool Create() override
Definition: customedit.cpp:51
ProgramInfo::GetChanID
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:371
ProgramInfo::GetEpisode
uint GetEpisode(void) const
Definition: programinfo.h:366
ProgramInfo
Holds information on recordings and videos.
Definition: programinfo.h:67
MythScreenType::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: mythscreentype.cpp:397
MythConfirmationDialog
Dialog asking for user confirmation. Ok and optional Cancel button.
Definition: mythdialogbox.h:271
CustomEdit::m_pginfo
ProgramInfo * m_pginfo
Definition: customedit.h:47
ProgramInfo::GetSeason
uint GetSeason(void) const
Definition: programinfo.h:365
mythcorecontext.h
MSqlQuery::executedQuery
QString executedQuery(void) const
Definition: mythdbcon.h:202
mythuitextedit.h
XMLParseBase::LoadWindowFromXML
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
Definition: xmlparsebase.cpp:692
CustomEdit::m_storeButton
MythUIButton * m_storeButton
Definition: customedit.h:70
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:878
DialogCompletionEvent
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:41
CustomEdit::clauseClicked
void clauseClicked(MythUIButtonListItem *item)
Definition: customedit.cpp:626
MythUIText::SetText
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:133
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:104
MythUIButtonList::SetItemCurrent
void SetItemCurrent(MythUIButtonListItem *item)
Definition: mythuibuttonlist.cpp:1557
build_compdb.action
action
Definition: build_compdb.py:9
CustomEdit::recordClicked
void recordClicked(void)
The user clicked on the 'Record' button in the 'Custom Edit' window.
Definition: customedit.cpp:678
mythuibutton.h
MythUITextEdit::valueChanged
void valueChanged()
plSQLSearch
@ plSQLSearch
Definition: proglist.h:20
CustomRuleInfo::subtitle
QString subtitle
Definition: customedit.h:79
ProgramInfo::GetProgramID
QString GetProgramID(void) const
Definition: programinfo.h:435
ProgramInfo::GetRecordingRuleID
uint GetRecordingRuleID(void) const
Definition: programinfo.h:448
CustomEdit::m_evaluate
bool m_evaluate
Definition: customedit.h:51
recordingrule.h
MythUIButtonListItem::SetData
void SetData(QVariant data)
Definition: mythuibuttonlist.cpp:3582
MythScreenStack::AddScreen
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Definition: mythscreenstack.cpp:50
ScheduleEditor
Construct a recording schedule.
Definition: scheduleeditor.h:147
CustomEdit::storeClicked
void storeClicked(void)
Definition: customedit.cpp:720
CustomEdit::evaluate
QString evaluate(QString clause)
Definition: customedit.cpp:155
MythObservable::removeListener
void removeListener(QObject *listener)
Remove a listener to the observable.
Definition: mythobservable.cpp:55
CustomEdit::m_clauseList
MythUIButtonList * m_clauseList
Definition: customedit.h:57
CustomEdit::m_subtitleEdit
MythUITextEdit * m_subtitleEdit
Definition: customedit.h:65
CustomEdit::loadData
void loadData(void)
Definition: customedit.cpp:102
CustomEdit::m_recordButton
MythUIButton * m_recordButton
Definition: customedit.h:69
CustomEdit::scheduleCreated
void scheduleCreated(int ruleID)
Definition: customedit.cpp:714
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:827
ProgramInfo::GetSubtitle
QString GetSubtitle(void) const
Definition: programinfo.h:362