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