MythTV  master
custompriority.cpp
Go to the documentation of this file.
1 
2 #include "custompriority.h"
3 
4 // qt
5 #include <QSqlError>
6 
7 // libmythbase
8 #include "mythdb.h"
9 #include "mythlogging.h"
10 
11 // libmyth
12 #include "mythcorecontext.h"
13 
14 // libmythtv
15 #include "scheduledrecording.h"
16 #include "channelutil.h"
17 
18 // libmythui
19 #include "mythuibuttonlist.h"
20 #include "mythuispinbox.h"
21 #include "mythuitextedit.h"
22 #include "mythuibutton.h"
23 #include "mythdialogbox.h"
24 #include "mythmainwindow.h"
25 
26 //mythfrontend
27 #include "viewschedulediff.h"
28 
30  : MythScreenType(parent, "CustomPriority")
31 {
32  if (proginfo)
33  m_pginfo = new ProgramInfo(*proginfo);
34  else
35  m_pginfo = new ProgramInfo();
36 
38 }
39 
41 {
42  delete m_pginfo;
43 
45 }
46 
48 {
49  if (!LoadWindowFromXML("schedule-ui.xml", "custompriority", this))
50  return false;
51 
52  m_ruleList = dynamic_cast<MythUIButtonList *>(GetChild("rules"));
53  m_clauseList = dynamic_cast<MythUIButtonList *>(GetChild("clauses"));
54 
55  m_prioritySpin = dynamic_cast<MythUISpinBox *>(GetChild("priority"));
56 
57  m_titleEdit = dynamic_cast<MythUITextEdit *>(GetChild("title"));
58  m_descriptionEdit = dynamic_cast<MythUITextEdit *>(GetChild("description"));
59 
60  m_addButton = dynamic_cast<MythUIButton *>(GetChild("add"));
61  m_installButton = dynamic_cast<MythUIButton *>(GetChild("install"));
62  m_testButton = dynamic_cast<MythUIButton *>(GetChild("test"));
63  m_deleteButton = dynamic_cast<MythUIButton *>(GetChild("delete"));
64  m_cancelButton = dynamic_cast<MythUIButton *>(GetChild("cancel"));
65 
69  {
70  LOG(VB_GENERAL, LOG_ERR,
71  "CustomPriority, theme is missing required elements");
72  return false;
73  }
74 
75  connect(m_ruleList, SIGNAL(itemSelected(MythUIButtonListItem *)),
77 
78  connect(m_titleEdit, SIGNAL(valueChanged()), SLOT(textChanged()));
80  connect(m_descriptionEdit, SIGNAL(valueChanged()), SLOT(textChanged()));
82 
83  connect(m_addButton, SIGNAL(Clicked()), SLOT(addClicked()));
84  connect(m_testButton, SIGNAL(Clicked()), SLOT(testClicked()));
85  connect(m_installButton, SIGNAL(Clicked()), SLOT(installClicked()));
86  connect(m_deleteButton, SIGNAL(Clicked()), SLOT(deleteClicked()));
87  connect(m_cancelButton, SIGNAL(Clicked()), SLOT(Close()));
88 
89  loadData();
90 
92 
93  return true;
94 }
95 
97 {
98  QString baseTitle = m_pginfo->GetTitle();
99  baseTitle.remove(QRegExp(" \\(.*\\)$"));
100 
101  QString quoteTitle = baseTitle;
102  quoteTitle.replace("\'","\'\'");
103 
104  m_prioritySpin->SetRange(-99,99,1);
106 
107  RuleInfo rule;
108  rule.priority = QString().setNum(1);
109 
110  new MythUIButtonListItem(m_ruleList, tr("<New priority rule>"),
111  qVariantFromValue(rule));
112 
113  MSqlQuery result(MSqlQuery::InitCon());
114  result.prepare("SELECT priorityname, recpriority, selectclause "
115  "FROM powerpriority ORDER BY priorityname;");
116 
117  if (result.exec())
118  {
119  MythUIButtonListItem *item = nullptr;
120  while (result.next())
121  {
122  QString trimTitle = result.value(0).toString();
123  trimTitle.remove(QRegExp(" \\(.*\\)$"));
124 
125  rule.title = trimTitle;
126  rule.priority = result.value(1).toString();
127  rule.description = result.value(2).toString();
128 
129  item = new MythUIButtonListItem(m_ruleList, rule.title,
130  qVariantFromValue(rule));
131 
132  if (trimTitle == baseTitle)
133  m_ruleList->SetItemCurrent(item);
134  }
135  }
136  else
137  MythDB::DBError("Get power search rules query", result);
138 
140 
141  if (!m_pginfo->GetTitle().isEmpty())
142  {
143  m_titleEdit->SetText(baseTitle);
144  m_descriptionEdit->SetText("program.title = '" + quoteTitle + "' ");
145  textChanged();
146  }
147 
148  if (m_titleEdit->GetText().isEmpty())
150  else
152 }
153 
155 {
156  QMap<QString, QString> examples;
157  examples.insert(tr("Modify priority for an input (Input priority)"),
158  "capturecard.cardid = 1");
159  examples.insert(tr("Modify priority for every card on a host"),
160  "capturecard.hostname = 'mythbox'");
161  examples.insert(tr("Only one specific channel ID (Channel priority)"),
162  "channel.chanid = '1003' ");
163  examples.insert(tr("Only a certain channel number"),
164  "channel.channum = '3' ");
165  examples.insert(tr("Only channels that carry a specific station"),
166  "channel.callsign = 'ESPN' ");
167  examples.insert(tr("Match related callsigns"),
168  "channel.callsign LIKE 'HBO%' ");
169  examples.insert(tr("Only channels marked as commercial free"),
170  QString("channel.commmethod = %1 ")
171  .arg(COMM_DETECT_COMMFREE));
172  examples.insert(tr("Modify priority for a station on an input"),
173  "channel.callsign = 'ESPN' AND capturecard.cardid = 2");
174  examples.insert(tr("Priority for all matching titles"),
175  "program.title LIKE 'CSI: %' ");
176  examples.insert(tr("Only shows marked as HDTV"),
177  "program.hdtv > 0 ");
178  examples.insert(tr("Close Captioned priority"),
179  "program.closecaptioned > 0 ");
180  examples.insert(tr("New episodes only"),
181  "program.previouslyshown = 0 ");
182  examples.insert(tr("Modify unidentified episodes"),
183  "program.generic = 0 ");
184  examples.insert(tr("First showing of each episode"),
185  "program.first > 0 ");
186  examples.insert(tr("Last showing of each episode"),
187  "program.last > 0 ");
188  examples.insert(tr("Priority for any show with End Late time"),
189  "RECTABLE.endoffset > 0 ");
190  examples.insert(tr("Priority for a category"),
191  "program.category = 'Reality' ");
192  examples.insert(QString("%1 ('movie', 'series', 'sports', 'tvshow')")
193  .arg(tr("Priority for a category type")),
194  "program.category_type = 'sports' ");
195  examples.insert(tr("Modify priority by star rating (0.0 to 1.0 for "
196  "movies only)"),
197  "program.stars >= 0.75 ");
198  examples.insert(tr("Priority when shown once (complete example)"),
199  "program.first > 0 AND program.last > 0");
200  examples.insert(tr("Prefer a host for a storage group (complete example)"),
201  QString("RECTABLE.storagegroup = 'Archive' "
202  "AND capturecard.hostname = 'mythbox' "));
203  examples.insert(tr("Priority for HD shows under two hours (complete "
204  "example)"),
205  "program.hdtv > 0 AND program.starttime > "
206  "DATE_SUB(program.endtime, INTERVAL 2 HOUR) ");
207  examples.insert(tr("Priority for movies by the year of release (complete "
208  "example)"),
209  "program.category_type = 'movie' "
210  "AND program.airdate >= 2006 ");
211  examples.insert(tr("Prefer movies when shown at night (complete example)"),
212  "program.category_type = 'movie' "
213  "AND HOUR(program.starttime) < 6 ");
214  examples.insert(tr("Prefer a host for live sports with overtime (complete "
215  "example)"),
216  "RECTABLE.endoffset > 0 "
217  "AND program.category = 'Sports event' "
218  "AND capturecard.hostname = 'mythbox' ");
219  examples.insert(tr("Avoid poor signal quality (complete example)"),
220  "capturecard.cardid = 1 AND "
221  "channel.channum IN (3, 5, 39, 66) ");
222 
223  QMapIterator<QString, QString> it(examples);
224  while (it.hasNext())
225  {
226  it.next();
227  new MythUIButtonListItem(m_clauseList, it.key(),
228  qVariantFromValue(it.value()));
229  }
230 }
231 
233 {
234  if (!item)
235  return;
236 
237  RuleInfo rule = item->GetData().value<RuleInfo>();
238 
239  m_titleEdit->SetText(rule.title);
240 
244  textChanged();
245 }
246 
248 {
249  bool hastitle = !m_titleEdit->GetText().isEmpty();
250  bool hasdesc = !m_descriptionEdit->GetText().isEmpty();
251 
252  m_testButton->SetEnabled(hasdesc);
253  m_installButton->SetEnabled(hastitle && hasdesc);
254 }
255 
257 {
259 
260  if (!item)
261  return;
262 
263  QString clause;
264 
265  QString desc = m_descriptionEdit->GetText();
266 
267  if (desc.contains(QRegExp("\\S")))
268  clause = "AND ";
269  clause = item->GetData().toString();
270  m_descriptionEdit->SetText(desc.append(clause));
271 }
272 
274 {
275  if (!checkSyntax())
276  return;
277 
278  testSchedule();
279 }
280 
282 {
283  if (!checkSyntax())
284  return;
285 
287  if (!item)
288  return;
289 
290  MSqlQuery query(MSqlQuery::InitCon());
291  query.prepare("DELETE FROM powerpriority WHERE priorityname = :NAME;");
292  query.bindValue(":NAME", m_titleEdit->GetText());
293 
294  if (!query.exec())
295  MythDB::DBError("Install power search delete", query);
296 
297  query.prepare("INSERT INTO powerpriority "
298  "(priorityname, recpriority, selectclause) "
299  "VALUES(:NAME,:VALUE,:CLAUSE);");
300  query.bindValue(":NAME", m_titleEdit->GetText());
301  query.bindValue(":VALUE", item->GetText());
302  query.bindValue(":CLAUSE", m_descriptionEdit->GetText());
303 
304  if (!query.exec())
305  MythDB::DBError("Install power search insert", query);
306  else
307  ScheduledRecording::ReschedulePlace("InstallCustomPriority");
308 
309  Close();
310 }
311 
313 {
314  if (!checkSyntax())
315  return;
316 
317  MSqlQuery query(MSqlQuery::InitCon());
318  query.prepare("DELETE FROM powerpriority "
319  "WHERE priorityname=:NAME;");
320  query.bindValue(":NAME", m_titleEdit->GetText());
321 
322  if (!query.exec())
323  MythDB::DBError("Delete power search query", query);
324  else
325  ScheduledRecording::ReschedulePlace("DeleteCustomPriority");
326 
327  Close();
328 }
329 
331 {
332  bool ret = false;
333  QString msg;
334 
335  QString desc = m_descriptionEdit->GetText();
336 
337  if (desc.contains(QRegExp("^\\s*AND\\s", Qt::CaseInsensitive)))
338  {
339  msg = "Power Priority rules do not reqiure a leading \"AND\"";
340  }
341  else if (desc.contains(';'))
342  {
343  msg = "Power Priority rules cannot include semicolon ( ; ) ";
344  msg += "statement terminators.";
345  }
346  else
347  {
348  QString qstr = QString("SELECT (%1) FROM (recordmatch, record, "
349  "program, channel, capturecard, "
350  "oldrecorded) WHERE NULL").arg(desc);
351  while (true)
352  {
353  int i = qstr.indexOf("RECTABLE");
354  if (i == -1) break;
355  qstr = qstr.replace(i, strlen("RECTABLE"), "record");
356  }
357 
358  MSqlQuery query(MSqlQuery::InitCon());
359  query.prepare(qstr);
360 
361  if (query.exec())
362  {
363  ret = true;
364  }
365  else
366  {
367  msg = tr("An error was found when checking") + ":\n\n";
368  msg += query.executedQuery();
369  msg += "\n\n" + tr("The database error was") + ":\n";
370  msg += query.lastError().databaseText();
371  ret = false;
372  }
373  }
374 
375  if (!msg.isEmpty())
376  ShowOkPopup(msg);
377 
378  return ret;
379 }
380 
382 {
384  if (!item)
385  return;
386 
387  QString ttable = "powerpriority_tmp";
388 
390  MSqlQuery query(dbcon);
391  QString thequery;
392 
393  thequery = "SELECT GET_LOCK(:LOCK, 2);";
394  query.prepare(thequery);
395  query.bindValue(":LOCK", "DiffSchedule");
396  if (!query.exec())
397  {
398  QString msg =
399  QString("DB Error (Obtaining lock in testRecording): \n"
400  "Query was: %1 \nError was: %2 \n")
401  .arg(thequery)
402  .arg(MythDB::DBErrorMessage(query.lastError()));
403  LOG(VB_GENERAL, LOG_ERR, msg);
404  return;
405  }
406 
407  thequery = QString("DROP TABLE IF EXISTS %1;").arg(ttable);
408  query.prepare(thequery);
409  if (!query.exec())
410  {
411  QString msg =
412  QString("DB Error (deleting old table in testRecording): \n"
413  "Query was: %1 \nError was: %2 \n")
414  .arg(thequery)
415  .arg(MythDB::DBErrorMessage(query.lastError()));
416  LOG(VB_GENERAL, LOG_ERR, msg);
417  return;
418  }
419 
420  thequery = QString("CREATE TABLE %1 SELECT * FROM powerpriority;")
421  .arg(ttable);
422  query.prepare(thequery);
423  if (!query.exec())
424  {
425  QString msg =
426  QString("DB Error (create new table): \n"
427  "Query was: %1 \nError was: %2 \n")
428  .arg(thequery)
429  .arg(MythDB::DBErrorMessage(query.lastError()));
430  LOG(VB_GENERAL, LOG_ERR, msg);
431  return;
432  }
433 
434  query.prepare(QString("DELETE FROM %1 WHERE priorityname = :NAME;")
435  .arg(ttable));
436  query.bindValue(":NAME", m_titleEdit->GetText());
437 
438  if (!query.exec())
439  MythDB::DBError("Test power search delete", query);
440 
441  thequery = QString("INSERT INTO %1 "
442  "(priorityname, recpriority, selectclause) "
443  "VALUES(:NAME,:VALUE,:CLAUSE);").arg(ttable);
444  query.prepare(thequery);
445  query.bindValue(":NAME", m_titleEdit->GetText());
446  query.bindValue(":VALUE", item->GetText());
447  query.bindValue(":CLAUSE", m_descriptionEdit->GetText());
448 
449  if (!query.exec())
450  MythDB::DBError("Test power search insert", query);
451 
452  QString ltitle = tr("Power Priority");
453  if (!m_titleEdit->GetText().isEmpty())
454  ltitle = m_titleEdit->GetText();
455 
457  auto *vsd = new ViewScheduleDiff(mainStack, ttable, 0, ltitle);
458 
459  if (vsd->Create())
460  mainStack->AddScreen(vsd);
461  else
462  delete vsd;
463 
464  thequery = "SELECT RELEASE_LOCK(:LOCK);";
465  query.prepare(thequery);
466  query.bindValue(":LOCK", "DiffSchedule");
467  if (!query.exec())
468  {
469  QString msg =
470  QString("DB Error (free lock): \n"
471  "Query was: %1 \nError was: %2 \n")
472  .arg(thequery)
473  .arg(MythDB::DBErrorMessage(query.lastError()));
474  LOG(VB_GENERAL, LOG_ERR, msg);
475  }
476 }
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:782
void loadData(void)
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:863
void SetEnabled(bool enable)
MythConfirmationDialog * ShowOkPopup(const QString &message, QObject *parent, const char *slot, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
MythUITextEdit * m_descriptionEdit
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
QString priority
QString GetTitle(void) const
Definition: programinfo.h:353
MythUIButton * m_testButton
void addClicked(void)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
void SetValue(int val) override
Definition: mythuispinbox.h:26
MythScreenStack * GetMainStack()
void testSchedule(void)
void addListener(QObject *listener)
Add a listener to the observable.
bool checkSyntax(void)
void BuildFocusList(void)
QVariant value(int i) const
Definition: mythdbcon.h:198
virtual void Close()
Holds information on recordings and videos.
Definition: programinfo.h:66
void SetRange(int low, int high, int step, uint pageMultiple=5)
Set the lower and upper bounds of the spinbox, the interval and page amount.
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
void SetMaxLength(const int length)
void installClicked(void)
QSqlError lastError(void) const
Definition: mythdbcon.h:202
QString title
MythUITextEdit * m_titleEdit
QString description
static void ReschedulePlace(const QString &why)
static MSqlQueryInfo SchedCon()
Returns dedicated connection. (Required for using temporary SQL tables.)
Definition: mythdbcon.cpp:565
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
bool Create() override
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
void SetText(const QString &text, bool moveCursor=true)
MythMainWindow * GetMythMainWindow(void)
QString GetText(const QString &name="") const
void loadExampleRules(void)
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:807
MythUIButton * m_cancelButton
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void deleteClicked(void)
MythUIButton * m_installButton
MythUIButtonList * m_ruleList
void testClicked(void)
MythUIButton * m_addButton
void ruleChanged(MythUIButtonListItem *item)
CustomPriority(MythScreenStack *parent, ProgramInfo *proginfo=nullptr)
MythUIButtonList * m_clauseList
static QString DBErrorMessage(const QSqlError &err)
Definition: mythdb.cpp:184
void SetItemCurrent(MythUIButtonListItem *item)
QString executedQuery(void) const
Definition: mythdbcon.h:199
bool SetFocusWidget(MythUIType *widget=nullptr)
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
ProgramInfo * m_pginfo
MythUIButton * m_deleteButton
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
Screen in which all other widgets are contained and rendered.
MythUISpinBox * m_prioritySpin
int GetCurrentPos() const
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:132
MSqlDatabase Info, used by MSqlQuery. Do not use directly.
Definition: mythdbcon.h:90
QString GetText(void) const
MythUIButtonListItem * GetItemCurrent() const