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