00001
00002 #include "custompriority.h"
00003
00004
00005 #include <QSqlError>
00006
00007
00008 #include "mythdb.h"
00009 #include "mythlogging.h"
00010
00011
00012 #include "mythcorecontext.h"
00013
00014
00015 #include "scheduledrecording.h"
00016 #include "channelutil.h"
00017
00018
00019 #include "mythuibuttonlist.h"
00020 #include "mythuispinbox.h"
00021 #include "mythuitextedit.h"
00022 #include "mythuibutton.h"
00023 #include "mythdialogbox.h"
00024 #include "mythmainwindow.h"
00025
00026
00027 #include "viewschedulediff.h"
00028
00029 CustomPriority::CustomPriority(MythScreenStack *parent, ProgramInfo *proginfo)
00030 : MythScreenType(parent, "CustomPriority")
00031 {
00032 if (proginfo)
00033 m_pginfo = new ProgramInfo(*proginfo);
00034 else
00035 m_pginfo = new ProgramInfo();
00036
00037 gCoreContext->addListener(this);
00038 }
00039
00040 CustomPriority::~CustomPriority(void)
00041 {
00042 delete m_pginfo;
00043
00044 gCoreContext->removeListener(this);
00045 }
00046
00047 bool CustomPriority::Create()
00048 {
00049 if (!LoadWindowFromXML("schedule-ui.xml", "custompriority", this))
00050 return false;
00051
00052 m_ruleList = dynamic_cast<MythUIButtonList *>(GetChild("rules"));
00053 m_clauseList = dynamic_cast<MythUIButtonList *>(GetChild("clauses"));
00054
00055 m_prioritySpin = dynamic_cast<MythUISpinBox *>(GetChild("priority"));
00056
00057 m_titleEdit = dynamic_cast<MythUITextEdit *>(GetChild("title"));
00058 m_descriptionEdit = dynamic_cast<MythUITextEdit *>(GetChild("description"));
00059
00060 m_addButton = dynamic_cast<MythUIButton *>(GetChild("add"));
00061 m_installButton = dynamic_cast<MythUIButton *>(GetChild("install"));
00062 m_testButton = dynamic_cast<MythUIButton *>(GetChild("test"));
00063 m_deleteButton = dynamic_cast<MythUIButton *>(GetChild("delete"));
00064 m_cancelButton = dynamic_cast<MythUIButton *>(GetChild("cancel"));
00065
00066 if (!m_ruleList || !m_clauseList || !m_prioritySpin || !m_titleEdit ||
00067 !m_descriptionEdit || !m_addButton || !m_installButton ||
00068 !m_testButton || !m_deleteButton || !m_cancelButton)
00069 {
00070 LOG(VB_GENERAL, LOG_ERR,
00071 "CustomPriority, theme is missing required elements");
00072 return false;
00073 }
00074
00075 connect(m_ruleList, SIGNAL(itemSelected(MythUIButtonListItem *)),
00076 SLOT(ruleChanged(MythUIButtonListItem *)));
00077
00078 connect(m_titleEdit, SIGNAL(valueChanged()), SLOT(textChanged()));
00079 m_titleEdit->SetMaxLength(128);
00080 connect(m_descriptionEdit, SIGNAL(valueChanged()), SLOT(textChanged()));
00081 m_descriptionEdit->SetMaxLength(0);
00082
00083 connect(m_addButton, SIGNAL(Clicked()), SLOT(addClicked()));
00084 connect(m_testButton, SIGNAL(Clicked()), SLOT(testClicked()));
00085 connect(m_installButton, SIGNAL(Clicked()), SLOT(installClicked()));
00086 connect(m_deleteButton, SIGNAL(Clicked()), SLOT(deleteClicked()));
00087 connect(m_cancelButton, SIGNAL(Clicked()), SLOT(Close()));
00088
00089 loadData();
00090
00091 BuildFocusList();
00092
00093 return true;
00094 }
00095
00096 void CustomPriority::loadData()
00097 {
00098 QString baseTitle = m_pginfo->GetTitle();
00099 baseTitle.remove(QRegExp(" \\(.*\\)$"));
00100
00101 QString quoteTitle = baseTitle;
00102 quoteTitle.replace("\'","\'\'");
00103
00104 m_prioritySpin->SetRange(-99,99,1);
00105 m_prioritySpin->SetValue(1);
00106
00107 RuleInfo rule;
00108 rule.priority = QString().setNum(1);
00109
00110 new MythUIButtonListItem(m_ruleList, tr("<New priority rule>"),
00111 qVariantFromValue(rule));
00112
00113 MSqlQuery result(MSqlQuery::InitCon());
00114 result.prepare("SELECT priorityname, recpriority, selectclause "
00115 "FROM powerpriority ORDER BY priorityname;");
00116
00117 if (result.exec())
00118 {
00119 MythUIButtonListItem *item = NULL;
00120 while (result.next())
00121 {
00122 QString trimTitle = result.value(0).toString();
00123 trimTitle.remove(QRegExp(" \\(.*\\)$"));
00124
00125 rule.title = trimTitle;
00126 rule.priority = result.value(1).toString();
00127 rule.description = result.value(2).toString();
00128
00129 item = new MythUIButtonListItem(m_ruleList, rule.title,
00130 qVariantFromValue(rule));
00131
00132 if (trimTitle == baseTitle)
00133 m_ruleList->SetItemCurrent(item);
00134 }
00135 }
00136 else
00137 MythDB::DBError("Get power search rules query", result);
00138
00139 loadExampleRules();
00140
00141 if (!m_pginfo->GetTitle().isEmpty())
00142 {
00143 m_titleEdit->SetText(baseTitle);
00144 m_descriptionEdit->SetText("program.title = '" + quoteTitle + "' ");
00145 textChanged();
00146 }
00147
00148 if (m_titleEdit->GetText().isEmpty())
00149 SetFocusWidget(m_ruleList);
00150 else
00151 SetFocusWidget(m_clauseList);
00152 }
00153
00154 void CustomPriority::loadExampleRules()
00155 {
00156 QMap<QString, QString> examples;
00157 examples.insert(tr("Modify priority for an input (Input priority)"),
00158 "cardinput.cardinputid = 1");
00159 examples.insert(tr("Modify priority for all inputs on a card"),
00160 "cardinput.cardid = 2");
00161 examples.insert(tr("Modify priority for every card on a host"),
00162 "capturecard.hostname = 'mythbox'");
00163 examples.insert(tr("Only one specific channel ID (Channel priority)"),
00164 "channel.chanid = '1003' ");
00165 examples.insert(tr("Only a certain channel number"),
00166 "channel.channum = '3' ");
00167 examples.insert(tr("Only channels that carry a specific station"),
00168 "channel.callsign = 'ESPN' ");
00169 examples.insert(tr("Match related callsigns"),
00170 "channel.callsign LIKE 'HBO%' ");
00171 examples.insert(tr("Only channels marked as commercial free"),
00172 QString("channel.commmethod = %1 ")
00173 .arg(COMM_DETECT_COMMFREE));
00174 examples.insert(tr("Modify priority for a station on an input"),
00175 "channel.callsign = 'ESPN' AND cardinput.cardinputid = 2");
00176 examples.insert(tr("Priority for all matching titles"),
00177 "program.title LIKE 'CSI: %' ");
00178 examples.insert(tr("Only shows marked as HDTV"),
00179 "program.hdtv > 0 ");
00180 examples.insert(tr("Close Captioned priority"),
00181 "program.closecaptioned > 0 ");
00182 examples.insert(tr("New episodes only"),
00183 "program.previouslyshown = 0 ");
00184 examples.insert(tr("Modify unidentified episodes"),
00185 "program.generic = 0 ");
00186 examples.insert(tr("First showing of each episode"),
00187 "program.first > 0 ");
00188 examples.insert(tr("Last showing of each episode"),
00189 "program.last > 0 ");
00190 examples.insert(tr("Priority for any show with End Late time"),
00191 "RECTABLE.endoffset > 0 ");
00192 examples.insert(tr("Priority for a category"),
00193 "program.category = 'Reality' ");
00194 examples.insert(QString("%1 ('movie', 'series', 'sports', 'tvshow')")
00195 .arg(tr("Priority for a category type")),
00196 "program.category_type = 'sports' ");
00197 examples.insert(tr("Modify priority by star rating (0.0 to 1.0 for "
00198 "movies only)"),
00199 "program.stars >= 0.75 ");
00200 examples.insert(tr("Priority when shown once (complete example)"),
00201 "program.first > 0 AND program.last > 0");
00202 examples.insert(tr("Prefer a host for a storage group (complete example)"),
00203 QString("RECTABLE.storagegroup = 'Archive' "
00204 "AND capturecard.hostname = 'mythbox' "));
00205 examples.insert(tr("Priority for HD shows under two hours (complete "
00206 "example)"),
00207 "program.hdtv > 0 AND program.starttime > "
00208 "DATE_SUB(program.endtime, INTERVAL 2 HOUR) ");
00209 examples.insert(tr("Priority for movies by the year of release (complete "
00210 "example)"),
00211 "program.category_type = 'movie' "
00212 "AND program.airdate >= 2006 ");
00213 examples.insert(tr("Prefer movies when shown at night (complete example)"),
00214 "program.category_type = 'movie' "
00215 "AND HOUR(program.starttime) < 6 ");
00216 examples.insert(tr("Prefer a host for live sports with overtime (complete "
00217 "example)"),
00218 "RECTABLE.endoffset > 0 "
00219 "AND program.category = 'Sports event' "
00220 "AND capturecard.hostname = 'mythbox' ");
00221 examples.insert(tr("Avoid poor signal quality (complete example)"),
00222 "cardinput.cardinputid = 1 AND "
00223 "channel.channum IN (3, 5, 39, 66) ");
00224
00225 QMapIterator<QString, QString> it(examples);
00226 while (it.hasNext())
00227 {
00228 it.next();
00229 new MythUIButtonListItem(m_clauseList, it.key(),
00230 qVariantFromValue(it.value()));
00231 }
00232 }
00233
00234 void CustomPriority::ruleChanged(MythUIButtonListItem *item)
00235 {
00236 if (!item)
00237 return;
00238
00239 RuleInfo rule = qVariantValue<RuleInfo>(item->GetData());
00240
00241 m_titleEdit->SetText(rule.title);
00242
00243 m_descriptionEdit->SetText(rule.description);
00244 m_prioritySpin->SetValue(rule.priority);
00245 m_deleteButton->SetEnabled((bool)m_ruleList->GetCurrentPos());
00246 textChanged();
00247 }
00248
00249 void CustomPriority::textChanged(void)
00250 {
00251 bool hastitle = !m_titleEdit->GetText().isEmpty();
00252 bool hasdesc = !m_descriptionEdit->GetText().isEmpty();
00253
00254 m_testButton->SetEnabled(hasdesc);
00255 m_installButton->SetEnabled(hastitle && hasdesc);
00256 }
00257
00258 void CustomPriority::addClicked(void)
00259 {
00260 MythUIButtonListItem *item = m_clauseList->GetItemCurrent();
00261
00262 if (!item)
00263 return;
00264
00265 QString clause;
00266
00267 QString desc = m_descriptionEdit->GetText();
00268
00269 if (desc.contains(QRegExp("\\S")))
00270 clause = "AND ";
00271 clause = item->GetData().toString();
00272 m_descriptionEdit->SetText(desc.append(clause));
00273 }
00274
00275 void CustomPriority::testClicked(void)
00276 {
00277 if (!checkSyntax())
00278 return;
00279
00280 testSchedule();
00281 }
00282
00283 void CustomPriority::installClicked(void)
00284 {
00285 if (!checkSyntax())
00286 return;
00287
00288 MythUIButtonListItem *item = m_prioritySpin->GetItemCurrent();
00289 if (!item)
00290 return;
00291
00292 MSqlQuery query(MSqlQuery::InitCon());
00293 query.prepare("DELETE FROM powerpriority WHERE priorityname = :NAME;");
00294 query.bindValue(":NAME", m_titleEdit->GetText());
00295
00296 if (!query.exec())
00297 MythDB::DBError("Install power search delete", query);
00298
00299 query.prepare("INSERT INTO powerpriority "
00300 "(priorityname, recpriority, selectclause) "
00301 "VALUES(:NAME,:VALUE,:CLAUSE);");
00302 query.bindValue(":NAME", m_titleEdit->GetText());
00303 query.bindValue(":VALUE", item->GetText());
00304 query.bindValue(":CLAUSE", m_descriptionEdit->GetText());
00305
00306 if (!query.exec())
00307 MythDB::DBError("Install power search insert", query);
00308 else
00309 ScheduledRecording::ReschedulePlace("InstallCustomPriority");
00310
00311 Close();
00312 }
00313
00314 void CustomPriority::deleteClicked(void)
00315 {
00316 if (!checkSyntax())
00317 return;
00318
00319 MSqlQuery query(MSqlQuery::InitCon());
00320 query.prepare("DELETE FROM powerpriority "
00321 "WHERE priorityname=:NAME;");
00322 query.bindValue(":NAME", m_titleEdit->GetText());
00323
00324 if (!query.exec())
00325 MythDB::DBError("Delete power search query", query);
00326 else
00327 ScheduledRecording::ReschedulePlace("DeleteCustomPriority");
00328
00329 Close();
00330 }
00331
00332 bool CustomPriority::checkSyntax(void)
00333 {
00334 bool ret = false;
00335 QString msg;
00336
00337 QString desc = m_descriptionEdit->GetText();
00338
00339 if (desc.contains(QRegExp("^\\s*AND\\s", Qt::CaseInsensitive)))
00340 {
00341 msg = "Power Priority rules do not reqiure a leading \"AND\"";
00342 }
00343 else if (desc.contains(';'))
00344 {
00345 msg = "Power Priority rules cannot include semicolon ( ; ) ";
00346 msg += "statement terminators.";
00347 }
00348 else
00349 {
00350 QString qstr = QString("SELECT (%1) FROM (recordmatch, record, "
00351 "program, channel, cardinput, capturecard, "
00352 "oldrecorded) WHERE NULL").arg(desc);
00353 while (1)
00354 {
00355 int i = qstr.indexOf("RECTABLE");
00356 if (i == -1) break;
00357 qstr = qstr.replace(i, strlen("RECTABLE"), "record");
00358 }
00359
00360 MSqlQuery query(MSqlQuery::InitCon());
00361 query.prepare(qstr);
00362
00363 if (query.exec())
00364 {
00365 ret = true;
00366 }
00367 else
00368 {
00369 msg = tr("An error was found when checking") + ":\n\n";
00370 msg += query.executedQuery();
00371 msg += "\n\n" + tr("The database error was") + ":\n";
00372 msg += query.lastError().databaseText();
00373 ret = false;
00374 }
00375 }
00376
00377 if (!msg.isEmpty())
00378 ShowOkPopup(msg);
00379
00380 return ret;
00381 }
00382
00383 void CustomPriority::testSchedule(void)
00384 {
00385 MythUIButtonListItem *item = m_prioritySpin->GetItemCurrent();
00386 if (!item)
00387 return;
00388
00389 QString ttable = "powerpriority_tmp";
00390
00391 MSqlQueryInfo dbcon = MSqlQuery::SchedCon();
00392 MSqlQuery query(dbcon);
00393 QString thequery;
00394
00395 thequery = "SELECT GET_LOCK(:LOCK, 2);";
00396 query.prepare(thequery);
00397 query.bindValue(":LOCK", "DiffSchedule");
00398 if (!query.exec())
00399 {
00400 QString msg =
00401 QString("DB Error (Obtaining lock in testRecording): \n"
00402 "Query was: %1 \nError was: %2 \n")
00403 .arg(thequery)
00404 .arg(MythDB::DBErrorMessage(query.lastError()));
00405 LOG(VB_GENERAL, LOG_ERR, msg);
00406 return;
00407 }
00408
00409 thequery = QString("DROP TABLE IF EXISTS %1;").arg(ttable);
00410 query.prepare(thequery);
00411 if (!query.exec())
00412 {
00413 QString msg =
00414 QString("DB Error (deleting old table in testRecording): \n"
00415 "Query was: %1 \nError was: %2 \n")
00416 .arg(thequery)
00417 .arg(MythDB::DBErrorMessage(query.lastError()));
00418 LOG(VB_GENERAL, LOG_ERR, msg);
00419 return;
00420 }
00421
00422 thequery = QString("CREATE TABLE %1 SELECT * FROM powerpriority;")
00423 .arg(ttable);
00424 query.prepare(thequery);
00425 if (!query.exec())
00426 {
00427 QString msg =
00428 QString("DB Error (create new table): \n"
00429 "Query was: %1 \nError was: %2 \n")
00430 .arg(thequery)
00431 .arg(MythDB::DBErrorMessage(query.lastError()));
00432 LOG(VB_GENERAL, LOG_ERR, msg);
00433 return;
00434 }
00435
00436 query.prepare(QString("DELETE FROM %1 WHERE priorityname = :NAME;")
00437 .arg(ttable));
00438 query.bindValue(":NAME", m_titleEdit->GetText());
00439
00440 if (!query.exec())
00441 MythDB::DBError("Test power search delete", query);
00442
00443 thequery = QString("INSERT INTO %1 "
00444 "(priorityname, recpriority, selectclause) "
00445 "VALUES(:NAME,:VALUE,:CLAUSE);").arg(ttable);
00446 query.prepare(thequery);
00447 query.bindValue(":NAME", m_titleEdit->GetText());
00448 query.bindValue(":VALUE", item->GetText());
00449 query.bindValue(":CLAUSE", m_descriptionEdit->GetText());
00450
00451 if (!query.exec())
00452 MythDB::DBError("Test power search insert", query);
00453
00454 QString ltitle = tr("Power Priority");
00455 if (!m_titleEdit->GetText().isEmpty())
00456 ltitle = m_titleEdit->GetText();
00457
00458 MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
00459 ViewScheduleDiff *vsd = new ViewScheduleDiff(mainStack, ttable, 0, ltitle);
00460
00461 if (vsd->Create())
00462 mainStack->AddScreen(vsd);
00463 else
00464 delete vsd;
00465
00466 thequery = "SELECT RELEASE_LOCK(:LOCK);";
00467 query.prepare(thequery);
00468 query.bindValue(":LOCK", "DiffSchedule");
00469 if (!query.exec())
00470 {
00471 QString msg =
00472 QString("DB Error (free lock): \n"
00473 "Query was: %1 \nError was: %2 \n")
00474 .arg(thequery)
00475 .arg(MythDB::DBErrorMessage(query.lastError()));
00476 LOG(VB_GENERAL, LOG_ERR, msg);
00477 }
00478 }