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 
36  m_baseTitle.remove(QRegExp(" \\(.*\\)$"));
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 
75  connect(m_ruleList, SIGNAL(itemSelected(MythUIButtonListItem *)),
77  connect(m_titleEdit, SIGNAL(valueChanged(void)), this,
78  SLOT(textChanged(void)));
81  connect(m_descriptionEdit, SIGNAL(valueChanged(void)), this,
82  SLOT(textChanged(void)));
84 
85  connect(m_clauseList, SIGNAL(itemSelected(MythUIButtonListItem *)),
87  connect(m_clauseList, SIGNAL(itemClicked(MythUIButtonListItem *)),
89 
90  connect(m_testButton, SIGNAL(Clicked()), SLOT(testClicked()));
91  connect(m_recordButton, SIGNAL(Clicked()), SLOT(recordClicked()));
92  connect(m_storeButton, SIGNAL(Clicked()), SLOT(storeClicked()));
93  connect(m_cancelButton, SIGNAL(Clicked()), SLOT(Close()));
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(QRegExp(" \\(.*\\)$"));
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  int e0=0;
158  while (true) {
159  int s0 = clause.indexOf (QRegExp("\\{[A-Z]+\\}"), e0);
160 
161  if (s0 < 0)
162  break;
163 
164  e0 = clause.indexOf ("}", s0);
165 
166  QString mid = clause.mid(s0 + 1, e0 - s0 - 1);
167  QString repl = "";
168 
169  if (mid.compare("TITLE") == 0) {
170  repl = m_pginfo->GetTitle();
171  repl.replace("\'","\'\'");
172  } else if (mid.compare("SUBTITLE") == 0) {
173  repl = m_pginfo->GetSubtitle();
174  repl.replace("\'","\'\'");
175  } else if (mid.compare("DESCR") == 0) {
176  repl = m_pginfo->GetDescription();
177  repl.replace("\'","\'\'");
178  } else if (mid.compare("SERIESID") == 0) {
179  repl = QString("%1").arg(m_pginfo->GetSeriesID());
180  } else if (mid.compare("PROGID") == 0) {
181  repl = m_pginfo->GetProgramID();
182  } else if (mid.compare("SEASON") == 0) {
183  repl = QString::number(m_pginfo->GetSeason());
184  } else if (mid.compare("EPISODE") == 0) {
185  repl = QString::number(m_pginfo->GetEpisode());
186  } else if (mid.compare("CATEGORY") == 0) {
187  repl = m_pginfo->GetCategory();
188  } else if (mid.compare("CHANID") == 0) {
189  repl = QString("%1").arg(m_pginfo->GetChanID());
190  } else if (mid.compare("CHANNUM") == 0) {
191  repl = m_pginfo->GetChanNum();
192  } else if (mid.compare("SCHEDID") == 0) {
194  } else if (mid.compare("CHANNAME") == 0) {
195  repl = m_pginfo->GetChannelName();
196  } else if (mid.compare("DAYNAME") == 0) {
197  repl = m_pginfo->GetScheduledStartTime().toString("dddd");
198  } else if (mid.compare("STARTDATE") == 0) {
199  repl = m_pginfo->GetScheduledStartTime().toString("yyyy-mm-dd hh:mm:ss");
200  } else if (mid.compare("ENDDATE") == 0) {
201  repl = m_pginfo->GetScheduledEndTime().toString("yyyy-mm-dd hh:mm:ss");
202  } else if (mid.compare("STARTTIME") == 0) {
203  repl = m_pginfo->GetScheduledStartTime().toString("hh:mm");
204  } else if (mid.compare("ENDTIME") == 0) {
205  repl = m_pginfo->GetScheduledEndTime().toString("hh:mm");
206  } else if (mid.compare("STARTSEC") == 0) {
207  QDateTime date = m_pginfo->GetScheduledStartTime();
208 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
209  QDateTime midnight = QDateTime(date.date());
210 #else
211  QDateTime midnight = date.date().startOfDay();
212 #endif
213  repl = QString("%1").arg(midnight.secsTo(date));
214  } else if (mid.compare("ENDSEC") == 0) {
215  QDateTime date = m_pginfo->GetScheduledEndTime();
216 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
217  QDateTime midnight = QDateTime(date.date());
218 #else
219  QDateTime midnight = date.date().startOfDay();
220 #endif
221  repl = QString("%1").arg(midnight.secsTo(date));
222  }
223  // unknown tags are simply removed
224  clause.replace(s0, e0 - s0 + 1, repl);
225  }
226  return clause;
227 }
228 
230 {
231  CustomRuleInfo rule;
232 
233  rule.title = tr("Match an exact title");
234  if (!m_baseTitle.isEmpty())
235  rule.description = QString("program.title = '{TITLE}' ");
236  else
237  rule.description = "program.title = 'Nova' ";
239  QVariant::fromValue(rule));
240 
241  if (!m_pginfo->GetSeriesID().isEmpty())
242  {
243  rule.title = tr("Match this series");
244  rule.subtitle.clear();
245  rule.description = QString("program.seriesid = '{SERIESID}' ");
247  QVariant::fromValue(rule));
248  }
249 
250  rule.title = tr("Match words in the title");
251  rule.subtitle.clear();
252  if (!m_pginfo->GetTitle().isEmpty())
253  rule.description = QString("program.title LIKE '%{TITLE}%' ");
254  else
255  rule.description = "program.title LIKE 'CSI: %' ";
257  QVariant::fromValue(rule));
258 
259  rule.title = tr("Match words in the subtitle");
260  rule.subtitle.clear();
261  if (!m_pginfo->GetSubtitle().isEmpty())
262  rule.description = QString("program.subtitle LIKE '%{SUBTITLE}%' ");
263  else
264  rule.description = "program.subtitle LIKE '%Las Vegas%' ";
266  QVariant::fromValue(rule));
267 
268  if (!m_pginfo->GetProgramID().isEmpty())
269  {
270  rule.title = tr("Match this episode");
271  rule.subtitle.clear();
272  rule.description = QString("program.programid = '{PROGID}' ");
273  }
274  else if (!m_pginfo->GetSubtitle().isEmpty())
275  {
276  rule.title = tr("Match this episode");
277  rule.subtitle.clear();
278  rule.description = QString("program.subtitle = '{SUBTITLE}' \n"
279  "AND program.description = '{DESCR}' ");
280  }
281  else
282  {
283  rule.title = tr("Match an exact episode");
284  rule.subtitle.clear();
285  rule.description = QString("program.title = 'Seinfeld' \n"
286  "AND program.subtitle = 'The Soup' ");
287  }
289  QVariant::fromValue(rule));
290 
291  rule.title = tr("Match in any descriptive field");
292  rule.subtitle.clear();
293  rule.description = QString("(program.title LIKE '%Japan%' \n"
294  " OR program.subtitle LIKE '%Japan%' \n"
295  " OR program.description LIKE '%Japan%') ");
297  QVariant::fromValue(rule));
298 
299  rule.title = tr("New episodes only");
300  rule.subtitle.clear();
301  rule.description = "program.previouslyshown = 0 ";
303  QVariant::fromValue(rule));
304 
305  rule.title = tr("Exclude unidentified episodes");
306  rule.subtitle.clear();
307  rule.description = "program.generic = 0 ";
309  QVariant::fromValue(rule));
310 
311  rule.title = tr("First showing of each episode");
312  rule.subtitle.clear();
313  rule.description = "program.first > 0 ";
315  QVariant::fromValue(rule));
316 
317  rule.title = tr("Last showing of each episode");
318  rule.subtitle.clear();
319  rule.description = "program.last > 0 ";
321  QVariant::fromValue(rule));
322 
323  rule.title = tr("Anytime on a specific day of the week");
324  rule.subtitle.clear();
325  rule.description =
326  "DAYNAME(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) = '{DAYNAME}' ";
328  QVariant::fromValue(rule));
329 
330  rule.title = tr("Only on weekdays (Monday through Friday)");
331  rule.subtitle.clear();
332  rule.description =
333  "WEEKDAY(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) < 5 ";
335  QVariant::fromValue(rule));
336 
337  rule.title = tr("Only on weekends");
338  rule.subtitle.clear();
339  rule.description =
340  "WEEKDAY(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) >= 5 ";
342  QVariant::fromValue(rule));
343 
344  rule.title = tr("Only in prime time");
345  rule.subtitle.clear();
346  rule.description =
347  "HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) >= 19 "
348  "AND HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) < 23 ";
350  QVariant::fromValue(rule));
351 
352  rule.title = tr("Not in prime time");
353  rule.subtitle.clear();
354  rule.description =
355  "(HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) < 19 "
356  " OR HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) >= 23) ";
358  QVariant::fromValue(rule));
359 
360  rule.title = tr("Only on a specific station");
361  rule.subtitle.clear();
362  if (!m_pginfo->GetChannelSchedulingID().isEmpty())
363  rule.description = QString("channel.callsign = '{SCHEDID}' ");
364  else
365  rule.description = "channel.callsign = 'ESPN' ";
367  QVariant::fromValue(rule));
368 
369  rule.title = tr("Exclude one station");
370  rule.subtitle.clear();
371  rule.description = "channel.callsign != 'GOLF' ";
373  QVariant::fromValue(rule));
374 
375  rule.title = tr("Match related callsigns");
376  rule.subtitle.clear();
377  rule.description = "channel.callsign LIKE 'HBO%' ";
379  QVariant::fromValue(rule));
380 
381  rule.title = tr("Only channels from the Favorites group");
382  rule.subtitle = ", channelgroup cg, channelgroupnames cgn";
383  rule.description = "cgn.name = 'Favorites' \n"
384  "AND cg.grpid = cgn.grpid \n"
385  "AND program.chanid = cg.chanid ";
387  QVariant::fromValue(rule));
388 
389  rule.title = tr("Only channels from a specific video source");
390  rule.subtitle.clear();
391  rule.description = "channel.sourceid = 2 ";
393  QVariant::fromValue(rule));
394 
395  rule.title = tr("Only channels marked as commercial free");
396  rule.subtitle.clear();
397  rule.description = QString("channel.commmethod = %1 ")
398  .arg(COMM_DETECT_COMMFREE);
400  QVariant::fromValue(rule));
401 
402  rule.title = tr("Only shows marked as HDTV");
403  rule.subtitle.clear();
404  rule.description = "program.hdtv > 0 ";
406  QVariant::fromValue(rule));
407 
408  rule.title = tr("Only shows marked as widescreen");
409  rule.subtitle.clear();
410  rule.description = "FIND_IN_SET('WIDESCREEN', program.videoprop) > 0 ";
412  QVariant::fromValue(rule));
413 
414  rule.title = tr("Exclude H.264 encoded streams (EIT only)");
415  rule.subtitle.clear();
416  rule.description = "FIND_IN_SET('AVC', program.videoprop) = 0 ";
418  QVariant::fromValue(rule));
419 
420  rule.title = tr("Only shows with in-vision signing");
421  rule.subtitle.clear();
422  rule.description = "FIND_IN_SET('SIGNED', program.subtitletypes) > 0 ";
424  QVariant::fromValue(rule));
425 
426  rule.title = tr("Only shows with in-vision subtitles");
427  rule.subtitle.clear();
428  rule.description = "FIND_IN_SET('ONSCREEN', program.subtitletypes) > 0 ";
430  QVariant::fromValue(rule));
431 
432  rule.title = tr("Limit by category");
433  rule.subtitle.clear();
434  if (!m_pginfo->GetCategory().isEmpty())
435  rule.description = QString("program.category = '{CATEGORY}' ");
436  else
437  rule.description = "program.category = 'Reality' ";
439  QVariant::fromValue(rule));
440 
441  rule.title = tr("All matches for a genre (Schedules Direct)");
442  rule.subtitle = "LEFT JOIN programgenres ON "
443  "program.chanid = programgenres.chanid AND "
444  "program.starttime = programgenres.starttime ";
445  if (!m_pginfo->GetCategory().isEmpty())
446  rule.description = QString("programgenres.genre = '{CATEGORY}' ");
447  else
448  rule.description = "programgenres.genre = 'Reality' ";
450  QVariant::fromValue(rule));
451 
452  rule.title = tr("Limit by MPAA or VCHIP rating (Schedules Direct)");
453  rule.subtitle = "LEFT JOIN programrating ON "
454  "program.chanid = programrating.chanid AND "
455  "program.starttime = programrating.starttime ";
456  rule.description = "(programrating.rating = 'G' OR programrating.rating "
457  "LIKE 'TV-Y%') ";
459  QVariant::fromValue(rule));
460 
461  rule.title = tr("Category type (%1)", "List of hardcoded category types")
462  .arg("'movie', 'series', 'sports', 'tvshow'");
463  rule.subtitle.clear();
464  rule.description = "program.category_type = 'sports' ";
466  QVariant::fromValue(rule));
467 
468  rule.title = tr("Limit movies by the year of release");
469  rule.subtitle.clear();
470  rule.description = "program.category_type = 'movie' AND "
471  "program.airdate >= 2000 ";
473  QVariant::fromValue(rule));
474 
475  rule.title = tr("Minimum star rating (0.0 to 1.0 for movies only)");
476  rule.subtitle.clear();
477  rule.description = "program.stars >= 0.75 ";
479  QVariant::fromValue(rule));
480 
481  rule.title = tr("Person named in the credits (Schedules Direct)");
482  rule.subtitle = ", people, credits";
483  rule.description = "people.name = 'Tom Hanks' \n"
484  "AND credits.person = people.person \n"
485  "AND program.chanid = credits.chanid \n"
486  "AND program.starttime = credits.starttime ";
488  QVariant::fromValue(rule));
489 
490 /* This shows how to use oldprogram but is a bad idea in practice.
491  This would match all future showings until the day after the first
492  showing when all future showing are no longer 'new' titles.
493 
494  rule.title = tr("Only titles from the New Titles list");
495  rule.subtitle = "LEFT JOIN oldprogram"
496  " ON oldprogram.oldtitle = program.title ";
497  rule.description = "oldprogram.oldtitle IS NULL ";
498  new MythUIButtonListItem(m_clauseList, rule.title,
499  QVariant::fromValue(rule));
500 */
501 
502  rule.title = tr("Re-record SDTV in HDTV (disable duplicate matching)");
503  rule.subtitle = ", recordedprogram rp1 LEFT OUTER JOIN recordedprogram rp2"
504  " ON rp1.programid = rp2.programid AND rp2.hdtv = 1";
505  rule.description = "program.programid = rp1.programid \n"
506  "AND rp1.hdtv = 0 \n"
507  "AND program.hdtv = 1 \n"
508  "AND rp2.starttime IS NULL ";
510  QVariant::fromValue(rule));
511 
512  rule.title = tr("Multiple sports teams (complete example)");
513  rule.subtitle.clear();
514  rule.description = "program.title = 'NBA Basketball' \n"
515  "AND program.subtitle REGEXP '(Miami|Cavaliers|Lakers)' \n"
516  "AND program.first > 0 \n";
518  QVariant::fromValue(rule));
519 
520  rule.title = tr("Sci-fi B-movies (complete example)");
521  rule.subtitle.clear();
522  rule.description = "program.category_type='movie' \n"
523  "AND program.category='Science fiction' \n"
524  "AND program.stars <= 0.5 AND program.airdate < 1970 ";
526  QVariant::fromValue(rule));
527 
528  rule.title =
529  tr("SportsCenter Overnight (complete example - use FindDaily)");
530  rule.subtitle.clear();
531  rule.description =
532  "program.title = 'SportsCenter' \n"
533  "AND HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) >= 2 \n"
534  "AND HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) <= 6 ";
536  QVariant::fromValue(rule));
537 
538  rule.title = tr("Movie of the Week (complete example - use FindWeekly)");
539  rule.subtitle.clear();
540  rule.description =
541  "program.category_type='movie' \n"
542  "AND program.stars >= 1.0 AND program.airdate >= 1965 \n"
543  "AND DAYNAME(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) = 'Friday' \n"
544  "AND HOUR(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) >= 12 ";
546  QVariant::fromValue(rule));
547 
548  rule.title = tr("First Episodes (complete example for Schedules Direct)");
549  rule.subtitle.clear();
550  rule.description = "program.first > 0 \n"
551  "AND program.programid LIKE 'EP%0001' \n"
552  "AND program.originalairdate = "
553  "DATE(CONVERT_TZ(program.starttime, 'Etc/UTC', 'SYSTEM')) ";
555  QVariant::fromValue(rule));
556 
558 
559  MSqlQuery result(MSqlQuery::InitCon());
560  result.prepare("SELECT rulename,fromclause,whereclause,search "
561  "FROM customexample;");
562 
563  if (result.exec())
564  {
565  while (result.next())
566  {
567  QString str = result.value(0).toString();
568 
569  if (result.value(3).toInt() > 0)
570  str += m_seSuffix;
571  else
572  str += m_exSuffix;
573 
574  rule.title = str;
575  rule.subtitle = result.value(1).toString();
576  rule.description = result.value(2).toString();
578  QVariant::fromValue(rule));
579  }
580  }
581 }
582 
584 {
585  if (!item || item == m_currentRuleItem)
586  return;
587 
588  m_currentRuleItem = item;
589 
590  auto rule = item->GetData().value<CustomRuleInfo>();
591 
592  m_titleEdit->SetText(rule.title);
593  m_descriptionEdit->SetText(rule.description);
594  m_subtitleEdit->SetText(rule.subtitle);
595 
596  textChanged();
597 }
598 
600 {
601  bool hastitle = !m_titleEdit->GetText().isEmpty();
602  bool hasdesc = !m_descriptionEdit->GetText().isEmpty();
603 
604  m_testButton->SetEnabled(hasdesc);
605  m_recordButton->SetEnabled(hastitle && hasdesc);
607  (hastitle && hasdesc));
608 }
609 
611 {
612  if (!item)
613  return;
614 
615  auto rule = item->GetData().value<CustomRuleInfo>();
616 
617  QString msg = (m_evaluate) ? evaluate(rule.description) : rule.description;
618  msg.replace('\n', ' ');
619  msg.replace(QRegExp(" [ ]*"), " ");
620  msg = QString("\"%1\"").arg(msg);
621  m_clauseText->SetText(msg);
622 
623  bool hastitle = !m_titleEdit->GetText().isEmpty();
624  bool hasdesc = !m_descriptionEdit->GetText().isEmpty();
625 
627  (hastitle && hasdesc));
628 }
629 
631 {
632  if (!item)
633  return;
634 
635  auto rule = item->GetData().value<CustomRuleInfo>();
636 
637  QString clause;
638  QString desc = m_descriptionEdit->GetText();
639 
640  if (desc.contains(QRegExp("\\S")))
641  clause = "AND ";
642  clause += (m_evaluate) ? evaluate(rule.description) : rule.description;
643 
644  m_descriptionEdit->SetText(desc.append(clause));
645 
646  QString sub = m_subtitleEdit->GetText();
647  m_subtitleEdit->SetText(sub.append(rule.subtitle));
648 }
649 
651 {
652  if (!checkSyntax())
653  {
654  return;
655  }
656 
658  auto *pl = new ProgLister(mainStack, plSQLSearch,
661  if (pl->Create())
662  {
663  mainStack->AddScreen(pl);
664  }
665  else
666  delete pl;
667 }
668 
683 {
684  if (!checkSyntax())
685  return;
686 
687  auto *record = new RecordingRule();
688 
690  auto rule = item->GetData().value<CustomRuleInfo>();
691 
692  int cur_recid = rule.recordid.toInt();
693  if (cur_recid > 0)
694  {
695  record->ModifyPowerSearchByID(cur_recid, m_titleEdit->GetText(),
698  }
699  else
700  {
701  record->LoadBySearch(kPowerSearch, m_titleEdit->GetText(),
704  m_pginfo->GetTitle().isEmpty() ? nullptr : m_pginfo);
705  }
706 
708  auto *schededit = new ScheduleEditor(mainStack, record);
709  if (schededit->Create())
710  {
711  mainStack->AddScreen(schededit);
712  connect(schededit, SIGNAL(ruleSaved(int)), SLOT(scheduleCreated(int)));
713  }
714  else
715  delete schededit;
716 }
717 
719 {
720  if (ruleID > 0)
721  Close();
722 }
723 
725 {
726  bool exampleExists = false;
727 
729  query.prepare("SELECT rulename,whereclause FROM customexample "
730  "WHERE rulename = :RULE;");
731  query.bindValue(":RULE", m_titleEdit->GetText());
732 
733  if (query.exec() && query.next())
734  exampleExists = true;
735 
736  QString msg = QString("%1: %2\n\n").arg(tr("Current Example"))
737  .arg(m_titleEdit->GetText());
738 
739  if (!m_subtitleEdit->GetText().isEmpty())
740  msg += m_subtitleEdit->GetText() + "\n\n";
741 
742  msg += m_descriptionEdit->GetText();
743 
745  auto *storediag = new MythDialogBox(msg, mainStack, "storePopup", true);
746 
747  storediag->SetReturnEvent(this, "storeruledialog");
748  if (storediag->Create())
749  {
750  if (!m_titleEdit->GetText().isEmpty())
751  {
752  QString str;
753  // Keep strings whole for translation!
754  if (exampleExists)
755  str = tr("Replace as a search");
756  else
757  str = tr("Store as a search");
758  storediag->AddButton(str);
759 
760  if (exampleExists)
761  str = tr("Replace as an example");
762  else
763  str = tr("Store as an example");
764  storediag->AddButton(str);
765  }
766 
768  {
770  QString str = QString("%1 \"%2\"").arg(tr("Delete"))
771  .arg(item->GetText());
772  storediag->AddButton(str);
773  }
774  mainStack->AddScreen(storediag);
775  }
776  else
777  delete storediag;
778 }
779 
780 
782 {
783  bool ret = false;
784  QString msg;
785 
786  QString desc = evaluate(m_descriptionEdit->GetText());
787  QString from = m_subtitleEdit->GetText();
788  if (desc.contains(QRegExp("^\\s*AND\\s", Qt::CaseInsensitive)))
789  {
790  msg = tr("Power Search rules no longer require a leading \"AND\".");
791  }
792  else if (desc.contains(';'))
793  {
794  msg = tr("Power Search rules cannot include semicolon ( ; ) ");
795  msg += tr("statement terminators.");
796  }
797  else
798  {
800  query.prepare(QString("SELECT NULL FROM (program,channel) "
801  "%1 WHERE\n%2").arg(from).arg(desc));
802 
803  if (query.exec())
804  {
805  ret = true;
806  }
807  else
808  {
809  msg = tr("An error was found when checking") + ":\n\n";
810  msg += query.executedQuery();
811  msg += "\n\n" + tr("The database error was") + ":\n";
812  msg += query.lastError().databaseText();
813  }
814  }
815 
816  if (!msg.isEmpty())
817  {
818  MythScreenStack *popupStack = GetMythMainWindow()->
819  GetStack("popup stack");
820  auto *checkSyntaxPopup =
821  new MythConfirmationDialog(popupStack, msg, false);
822 
823  if (checkSyntaxPopup->Create())
824  {
825  checkSyntaxPopup->SetReturnEvent(this, "checkSyntaxPopup");
826  popupStack->AddScreen(checkSyntaxPopup);
827  }
828  else
829  {
830  delete checkSyntaxPopup;
831  }
832  ret = false;
833  }
834  return ret;
835 }
836 
837 void CustomEdit::storeRule(bool is_search, bool is_new)
838 {
839  CustomRuleInfo rule;
840  rule.recordid = '0';
841  rule.title = m_titleEdit->GetText();
842  rule.subtitle = m_subtitleEdit->GetText();
844 
846  query.prepare("REPLACE INTO customexample "
847  "(rulename,fromclause,whereclause,search) "
848  "VALUES(:RULE,:FROMC,:WHEREC,:SEARCH);");
849  query.bindValue(":RULE", rule.title);
850  query.bindValue(":FROMC", rule.subtitle);
851  query.bindValue(":WHEREC", rule.description);
852  query.bindValue(":SEARCH", is_search);
853 
854  if (is_search)
855  rule.title += m_seSuffix;
856  else
857  rule.title += m_exSuffix;
858 
859  if (!query.exec())
860  MythDB::DBError("Store custom example", query);
861  else if (is_new)
862  {
864  QVariant::fromValue(rule));
865  }
866  else
867  {
868  /* Modify the existing entry. We know one exists from the database
869  search but do not know its position in the clause list. It may
870  or may not be the current item. */
871  for (int i = m_maxex; i < m_clauseList->GetCount(); i++)
872  {
874  QString removedStr = item->GetText().remove(m_seSuffix)
875  .remove(m_exSuffix);
876  if (m_titleEdit->GetText() == removedStr)
877  {
878  item->SetData(QVariant::fromValue(rule));
879  clauseChanged(item);
880  break;
881  }
882  }
883  }
884 
885 
886 }
887 
889 {
891  if (!item || m_clauseList->GetCurrentPos() < m_maxex)
892  return;
893 
895  query.prepare("DELETE FROM customexample "
896  "WHERE rulename = :RULE;");
897  query.bindValue(":RULE", item->GetText().remove(m_seSuffix)
898  .remove(m_exSuffix));
899 
900  if (!query.exec())
901  MythDB::DBError("Delete custom example", query);
902  else
903  {
904  m_clauseList->RemoveItem(item);
905  }
906 }
907 
908 void CustomEdit::customEvent(QEvent *event)
909 {
910  if (event->type() == DialogCompletionEvent::kEventType)
911  {
912  auto *dce = (DialogCompletionEvent*)(event);
913 
914  QString resultid = dce->GetId();
915  QString resulttext = dce->GetResultText();
916 
917  if (resultid == "storeruledialog")
918  {
919  if (resulttext.startsWith(tr("Delete")))
920  {
921  deleteRule();
922  }
923  else if (!resulttext.isEmpty())
924  {
925  storeRule(resulttext.contains(tr("as a search")),
926  !resulttext.startsWith(tr("Replace")));
927  }
928  }
929  }
930 }
931 
932 bool CustomEdit::keyPressEvent(QKeyEvent *event)
933 {
934  if (GetFocusWidget()->keyPressEvent(event))
935  return true;
936 
937  QStringList actions;
938  bool handled = GetMythMainWindow()->TranslateKeyPress("TV Frontend", event, actions);
939 
940  for (int i = 0; i < actions.size() && !handled; i++)
941  {
942  QString action = actions[i];
943  handled = true;
944 
945  if (action == "DELETE")
946  {
947  if (GetFocusWidget() == m_clauseList)
948  deleteRule();
949  // else if (GetFocusWidget() == m_ruleList)
950  // deleteRecordingRule();
951  }
952  else if (action == "EDIT")
953  {
954  // toggle evaluated/unevaluated sample view
957  clauseChanged(item);
958  } else
959  handled = false;
960  }
961 
962  if (!handled && MythScreenType::keyPressEvent(event))
963  handled = true;
964 
965  return handled;
966 }
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:783
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:126
MythMainWindow::GetMainStack
MythScreenStack * GetMainStack()
Definition: mythmainwindow.cpp:304
CustomEdit::m_seSuffix
QString m_seSuffix
Definition: customedit.h:53
MythUITextEdit::SetMaxLength
void SetMaxLength(int length)
Definition: mythuitextedit.cpp:211
MythUIButtonList::GetItemCurrent
MythUIButtonListItem * GetItemCurrent() const
Definition: mythuibuttonlist.cpp:1590
CustomEdit::m_ruleList
MythUIButtonList * m_ruleList
Definition: customedit.h:56
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:837
mythdb.h
CustomEdit::ruleChanged
void ruleChanged(MythUIButtonListItem *item)
Definition: customedit.cpp:583
MythScreenType::Close
virtual void Close()
Definition: mythscreentype.cpp:402
CustomEdit::m_currentRuleItem
const MythUIButtonListItem * m_currentRuleItem
Definition: customedit.h:73
CustomEdit::clauseChanged
void clauseChanged(MythUIButtonListItem *item)
Definition: customedit.cpp:610
MythMainWindow::TranslateKeyPress
bool TranslateKeyPress(const QString &context, QKeyEvent *e, QStringList &actions, bool allowJumps=true)
Get a list of actions for a keypress in the given context.
Definition: mythmainwindow.cpp:1106
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:381
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:371
CustomEdit::m_maxex
int m_maxex
Definition: customedit.h:50
CustomEdit::checkSyntax
bool checkSyntax(void)
Definition: customedit.cpp:781
mythdialogbox.h
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:198
MythScreenStack
Definition: mythscreenstack.h:16
arg
arg(title).arg(filename).arg(doDelete))
ProgramInfo::GetChannelSchedulingID
QString GetChannelSchedulingID(void) const
This is the unique programming identifier of a channel.
Definition: programinfo.h:378
ProgramInfo::GetCategory
QString GetCategory(void) const
Definition: programinfo.h:364
ProgLister
Definition: proglist.h:32
RecordingRule
Internal representation of a recording rule, mirrors the record table.
Definition: recordingrule.h:33
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:392
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
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:888
MythUITextEdit::GetText
QString GetText(void) const
Definition: mythuitextedit.h:47
CustomEdit::customEvent
void customEvent(QEvent *event) override
Definition: customedit.cpp:908
mythuibuttonlist.h
MythUIButtonList::GetCount
int GetCount() const
Definition: mythuibuttonlist.cpp:1655
CustomRuleInfo
Definition: customedit.h:76
CustomEdit::loadClauses
void loadClauses(void)
Definition: customedit.cpp:229
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:599
MythObservable::addListener
void addListener(QObject *listener)
Add a listener to the observable.
Definition: mythobservable.cpp:38
MythUIButtonListItem
Definition: mythuibuttonlist.h:28
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:216
COMM_DETECT_COMMFREE
@ COMM_DETECT_COMMFREE
Definition: programtypes.h:93
ProgramInfo::GetScheduledStartTime
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:385
kPowerSearch
@ kPowerSearch
Definition: recordingtypes.h:72
MythUIButtonList::GetCurrentPos
int GetCurrentPos() const
Definition: mythuibuttonlist.h:190
CustomRuleInfo::recordid
QString recordid
Definition: customedit.h:77
MythDialogBox
Basic menu dialog, message and a list of options.
Definition: mythdialogbox.h:138
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:356
ProgramInfo::GetDescription
QString GetDescription(void) const
Definition: programinfo.h:360
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
MythScreenType::BuildFocusList
void BuildFocusList(void)
Definition: mythscreentype.cpp:222
CustomEdit::m_testButton
MythUIButton * m_testButton
Definition: customedit.h:68
MythUIType::SetEnabled
void SetEnabled(bool enable)
Definition: mythuitype.cpp:1107
CustomEdit::m_cancelButton
MythUIButton * m_cancelButton
Definition: customedit.h:71
MythUIButtonListItem::GetData
QVariant GetData()
Definition: mythuibuttonlist.cpp:3588
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:932
ProgramInfo::GetSeriesID
QString GetSeriesID(void) const
Definition: programinfo.h:430
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:202
MythUIButtonListItem::GetText
QString GetText(const QString &name="") const
Definition: mythuibuttonlist.cpp:3313
CustomEdit::testClicked
void testClicked(void)
Definition: customedit.cpp:650
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:367
ProgramInfo::GetEpisode
uint GetEpisode(void) const
Definition: programinfo.h:362
ProgramInfo
Holds information on recordings and videos.
Definition: programinfo.h:68
MythScreenType::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: mythscreentype.cpp:414
MythConfirmationDialog
Dialog asking for user confirmation. Ok and optional Cancel button.
Definition: mythdialogbox.h:222
CustomEdit::m_pginfo
ProgramInfo * m_pginfo
Definition: customedit.h:47
ProgramInfo::GetSeason
uint GetSeason(void) const
Definition: programinfo.h:361
mythcorecontext.h
MSqlQuery::executedQuery
QString executedQuery(void) const
Definition: mythdbcon.h:199
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:864
DialogCompletionEvent
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:42
CustomEdit::clauseClicked
void clauseClicked(MythUIButtonListItem *item)
Definition: customedit.cpp:630
MythUIText::SetText
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:134
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:107
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:682
mythuibutton.h
plSQLSearch
@ plSQLSearch
Definition: proglist.h:20
CustomRuleInfo::subtitle
QString subtitle
Definition: customedit.h:79
ProgramInfo::GetProgramID
QString GetProgramID(void) const
Definition: programinfo.h:431
ProgramInfo::GetRecordingRuleID
uint GetRecordingRuleID(void) const
Definition: programinfo.h:444
CustomEdit::m_evaluate
bool m_evaluate
Definition: customedit.h:51
recordingrule.h
MythUIButtonListItem::SetData
void SetData(QVariant data)
Definition: mythuibuttonlist.cpp:3583
MythScreenStack::AddScreen
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Definition: mythscreenstack.cpp:49
query
MSqlQuery query(MSqlQuery::InitCon())
ScheduleEditor
Construct a recording schedule.
Definition: scheduleeditor.h:150
CustomEdit::storeClicked
void storeClicked(void)
Definition: customedit.cpp:724
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:718
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808
ProgramInfo::GetSubtitle
QString GetSubtitle(void) const
Definition: programinfo.h:358