00001 #include <qlayout.h>
00002 #include <qpushbutton.h>
00003 #include <qbuttongroup.h>
00004 #include <qlabel.h>
00005 #include <qcursor.h>
00006 #include <qlistview.h>
00007 #include <qdatetime.h>
00008 #include <qapplication.h>
00009 #include <qimage.h>
00010 #include <qpainter.h>
00011 #include <qheader.h>
00012 #include <qsqldatabase.h>
00013 #include <qhbox.h>
00014
00015 #include <unistd.h>
00016
00017 #include <iostream>
00018 using namespace std;
00019
00020 #include "custompriority.h"
00021
00022 #include "mythcontext.h"
00023 #include "dialogbox.h"
00024 #include "programinfo.h"
00025 #include "proglist.h"
00026 #include "scheduledrecording.h"
00027 #include "recordingtypes.h"
00028 #include "viewschdiff.h"
00029 #include "mythdbcon.h"
00030
00031 CustomPriority::CustomPriority(MythMainWindow *parent, const char *name,
00032 ProgramInfo *pginfo)
00033 : MythDialog(parent, name)
00034 {
00035 ProgramInfo *p = new ProgramInfo();
00036
00037 if (pginfo)
00038 {
00039 delete p;
00040 p = pginfo;
00041 }
00042
00043 QString baseTitle = p->title;
00044 baseTitle.remove(QRegExp(" \\(.*\\)$"));
00045
00046 QString quoteTitle = baseTitle;
00047 quoteTitle.replace("\'","\'\'");
00048
00049 prevItem = 0;
00050 addString = tr("Add");
00051
00052 QVBoxLayout *vbox = new QVBoxLayout(this, (int)(20 * wmult));
00053
00054 QVBoxLayout *vkbox = new QVBoxLayout(vbox, (int)(1 * wmult));
00055 QHBoxLayout *hbox = new QHBoxLayout(vkbox, (int)(1 * wmult));
00056
00057
00058 hbox = new QHBoxLayout(vbox, (int)(10 * wmult));
00059
00060 QString message = tr("Edit Priority Rule") + ": ";
00061 QLabel *label = new QLabel(message, this);
00062 label->setBackgroundOrigin(WindowOrigin);
00063 label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
00064 hbox->addWidget(label);
00065
00066 m_rule = new MythComboBox( false, this, "rule");
00067 m_rule->setBackgroundOrigin(WindowOrigin);
00068
00069 m_rule->insertItem(tr("<New priority rule>"));
00070 m_recpri << "1";
00071 m_recdesc << "";
00072
00073 MSqlQuery result(MSqlQuery::InitCon());
00074 result.prepare("SELECT priorityname, recpriority, selectclause "
00075 "FROM powerpriority ORDER BY priorityname;");
00076
00077 int titlematch = -1;
00078 if (result.exec() && result.isActive())
00079 {
00080 while (result.next())
00081 {
00082 QString trimTitle = QString::fromUtf8(result.value(0).toString());
00083 trimTitle.remove(QRegExp(" \\(.*\\)$"));
00084
00085 m_rule->insertItem(trimTitle);
00086 m_recpri << result.value(1).toString();
00087 m_recdesc << QString::fromUtf8(result.value(2).toString());
00088
00089 if (trimTitle == baseTitle)
00090 titlematch = m_rule->count() - 1;
00091 }
00092 }
00093 else
00094 MythContext::DBError("Get power search rules query", result);
00095
00096 hbox->addWidget(m_rule);
00097
00098
00099 hbox = new QHBoxLayout(vbox, (int)(10 * wmult));
00100
00101 message = tr("Priority Rule Name") + ": ";
00102 label = new QLabel(message, this);
00103 label->setBackgroundOrigin(WindowOrigin);
00104 label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
00105 hbox->addWidget(label);
00106
00107 m_title = new MythRemoteLineEdit( this, "title" );
00108 m_title->setBackgroundOrigin(WindowOrigin);
00109 hbox->addWidget(m_title);
00110
00111
00112 hbox = new QHBoxLayout(vbox, (int)(10 * wmult));
00113
00114 message = tr("Priority Value") + ": ";
00115 label = new QLabel(message, this);
00116 label->setBackgroundOrigin(WindowOrigin);
00117 label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
00118 hbox->addWidget(label);
00119
00120 m_value = new MythSpinBox( this, "value" );
00121 m_value->setMinValue(-99);
00122 m_value->setMaxValue(99);
00123 m_value->setValue(1);
00124 m_value->setBackgroundOrigin(WindowOrigin);
00125 hbox->addWidget(m_value);
00126
00127
00128 m_clause = new MythComboBox( false, this, "clause");
00129 m_clause->setBackgroundOrigin(WindowOrigin);
00130
00131 m_clause->insertItem(tr("Modify priority for an input (Input priority)"));
00132 m_csql << "cardinput.cardinputid = 1";
00133
00134 m_clause->insertItem(tr("Modify priority for all inputs on a card"));
00135 m_csql << "cardinput.cardid = 2";
00136
00137 m_clause->insertItem(tr("Modify priority for every card on a host"));
00138 m_csql << "capturecard.hostname = 'mythbox'";
00139
00140 m_clause->insertItem(tr("Only one specific channel ID (Channel priority)"));
00141 m_csql << "channel.chanid = '1003' ";
00142
00143 m_clause->insertItem(tr("Only a certian channel number"));
00144 m_csql << "channel.channum = '3' ";
00145
00146 m_clause->insertItem(tr("Only channels that carry a specific station"));
00147 m_csql << "channel.callsign = 'ESPN' ";
00148
00149 m_clause->insertItem(tr("Match related callsigns"));
00150 m_csql << "channel.callsign LIKE 'HBO%' ";
00151
00152 m_clause->insertItem(tr("Only channels marked as commercial free"));
00153 m_csql << "channel.commmethod = -2 ";
00154
00155 m_clause->insertItem(tr("Modify priority for a station on an input"));
00156 m_csql << "channel.callsign = 'ESPN' AND cardinput.cardinputid = 2";
00157
00158 m_clause->insertItem(tr("Priority for all matching titles"));
00159 m_csql << "program.title LIKE 'CSI: %' ";
00160
00161 m_clause->insertItem(tr("Only shows marked as HDTV"));
00162 m_csql << "program.hdtv > 0 ";
00163
00164 m_clause->insertItem(tr("Close Captioned priority"));
00165 m_csql << "program.closecaptioned > 0 ";
00166
00167 m_clause->insertItem(tr("New episodes only"));
00168 m_csql << "program.previouslyshown = 0 ";
00169
00170 m_clause->insertItem(tr("Modify unidentified episodes"));
00171 m_csql << "program.generic = 0 ";
00172
00173 m_clause->insertItem(tr("First showing of each episode"));
00174 m_csql << "program.first > 0 ";
00175
00176 m_clause->insertItem(tr("Last showing of each episode"));
00177 m_csql << "program.last > 0 ";
00178
00179 m_clause->insertItem(tr("Priority for any show with End Late time"));
00180 m_csql << "RECTABLE.endoffset > 0 ";
00181
00182 m_clause->insertItem(tr("Priority for a category"));
00183 m_csql << "program.category = 'Reality' ";
00184
00185 m_clause->insertItem(QString(tr("Priority for a category type") +
00186 " ('movie', 'series', 'sports' " + tr("or") + " 'tvshow')"));
00187 m_csql << "program.category_type = 'sports' ";
00188
00189 m_clause->insertItem(tr("Modify priority by star rating (0.0 to 1.0 for movies only)"));
00190 m_csql << "program.stars >= 0.75 ";
00191
00192 m_clause->insertItem(tr("Priority when shown once (complete example)"));
00193 m_csql << "program.first > 0 AND program.last > 0";
00194
00195 m_clause->insertItem(tr("Prefer a host for a storage group (complete example)"));
00196 m_csql << QString("RECTABLE.storagegroup = 'Archive' \n"
00197 "AND capturecard.hostname = 'mythbox' ");
00198
00199 m_clause->insertItem(tr("Priority for HD shows under two hours (complete example)"));
00200 m_csql << QString("program.hdtv > 0 AND \nprogram.starttime > "
00201 "DATE_SUB(program.endtime, INTERVAL 2 HOUR) ");
00202
00203 m_clause->insertItem(tr("Priority for movies by the year of release (complete example)"));
00204 m_csql << "program.category_type = 'movie' AND program.airdate >= 2006 ";
00205
00206 m_clause->insertItem(tr("Prefer movies when shown at night (complete example)"));
00207 m_csql << QString("program.category_type = 'movie' \n"
00208 "AND HOUR(program.starttime) < 6 ");
00209
00210 m_clause->insertItem(tr("Prefer a host for live sports with overtime (complete example)"));
00211 m_csql << QString("RECTABLE.endoffset > 0 \n"
00212 "AND program.category = 'Sports event' \n"
00213 "AND capturecard.hostname = 'mythbox' ");
00214
00215 m_clause->insertItem(tr("Avoid poor signal quality (complete example)"));
00216 m_csql << QString("cardinput.cardinputid = 1 AND \n"
00217 "channel.channum IN (3, 5, 39, 66) ");
00218
00219 vbox->addWidget(m_clause);
00220
00221
00222 m_addButton = new MythPushButton( this, "add" );
00223 m_addButton->setBackgroundOrigin(WindowOrigin);
00224 m_addButton->setText(addString);
00225 m_addButton->setEnabled(true);
00226
00227 vbox->addWidget(m_addButton);
00228
00229
00230 m_description = new MythRemoteLineEdit(5, this, "description" );
00231 m_description->setBackgroundOrigin(WindowOrigin);
00232 vbox->addWidget(m_description);
00233
00234
00235 hbox = new QHBoxLayout(vbox, (int)(10 * wmult));
00236
00237 m_testButton = new MythPushButton( this, "test" );
00238 m_testButton->setBackgroundOrigin(WindowOrigin);
00239 m_testButton->setText( tr( "Test" ) );
00240 m_testButton->setEnabled(false);
00241
00242 hbox->addWidget(m_testButton);
00243
00244
00245 m_installButton = new MythPushButton( this, "install" );
00246 m_installButton->setBackgroundOrigin(WindowOrigin);
00247 m_installButton->setText( tr( "Install" ) );
00248 m_installButton->setEnabled(false);
00249
00250 hbox->addWidget(m_installButton);
00251
00252
00253 m_deleteButton = new MythPushButton( this, "delete" );
00254 m_deleteButton->setBackgroundOrigin(WindowOrigin);
00255 m_deleteButton->setText( tr( "Delete" ) );
00256 m_deleteButton->setEnabled(false);
00257
00258 hbox->addWidget(m_deleteButton);
00259
00260
00261 m_cancelButton = new MythPushButton( this, "cancel" );
00262 m_cancelButton->setBackgroundOrigin(WindowOrigin);
00263 m_cancelButton->setText( tr( "Cancel" ) );
00264 m_cancelButton->setEnabled(true);
00265
00266 hbox->addWidget(m_cancelButton);
00267
00268 connect(this, SIGNAL(dismissWindow()), this, SLOT(accept()));
00269
00270 connect(m_rule, SIGNAL(activated(int)), this, SLOT(ruleChanged(void)));
00271 connect(m_rule, SIGNAL(highlighted(int)), this, SLOT(ruleChanged(void)));
00272 connect(m_title, SIGNAL(textChanged(void)), this, SLOT(textChanged(void)));
00273 connect(m_addButton, SIGNAL(clicked()), this, SLOT(addClicked()));
00274 connect(m_clause, SIGNAL(activated(int)), this, SLOT(clauseChanged(void)));
00275 connect(m_clause, SIGNAL(highlighted(int)), this, SLOT(clauseChanged(void)));
00276 connect(m_description, SIGNAL(textChanged(void)), this,
00277 SLOT(textChanged(void)));
00278 connect(m_testButton, SIGNAL(clicked()), this, SLOT(testClicked()));
00279 connect(m_installButton, SIGNAL(clicked()), this, SLOT(installClicked()));
00280 connect(m_deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
00281 connect(m_cancelButton, SIGNAL(clicked()), this, SLOT(cancelClicked()));
00282
00283 gContext->addListener(this);
00284 gContext->addCurrentLocation("CustomPriority");
00285
00286 if (titlematch >= 0)
00287 {
00288 m_rule->setCurrentItem(titlematch);
00289 ruleChanged();
00290 }
00291 else if (p->title > "")
00292 {
00293 m_title->setText(baseTitle);
00294
00295 m_description->setText("program.title = '" + quoteTitle + "' ");
00296 textChanged();
00297 }
00298
00299 if (m_title->text().isEmpty())
00300 m_rule->setFocus();
00301 else
00302 m_clause->setFocus();
00303
00304 clauseChanged();
00305 }
00306
00307 CustomPriority::~CustomPriority(void)
00308 {
00309 gContext->removeListener(this);
00310 gContext->removeCurrentLocation();
00311 }
00312
00313 void CustomPriority::ruleChanged(void)
00314 {
00315 int curItem = m_rule->currentItem();
00316 if (curItem == prevItem)
00317 return;
00318
00319 prevItem = curItem;
00320
00321 if (curItem > 0)
00322 m_title->setText(m_rule->currentText());
00323 else
00324 m_title->setText("");
00325
00326 m_description->setText(m_recdesc[curItem]);
00327 m_value->setValue(m_recpri[curItem].toInt());
00328 m_deleteButton->setEnabled((bool) curItem);
00329 textChanged();
00330 }
00331
00332 void CustomPriority::textChanged(void)
00333 {
00334 bool hastitle = !m_title->text().isEmpty();
00335 bool hasdesc = !m_description->text().isEmpty();
00336
00337 m_testButton->setEnabled(hasdesc);
00338 m_installButton->setEnabled(hastitle && hasdesc);
00339 }
00340
00341 void CustomPriority::clauseChanged(void)
00342 {
00343 QString msg = m_csql[m_clause->currentItem()];
00344 msg.replace("\n", " ");
00345 msg.replace(QRegExp(" [ ]*"), " ");
00346 msg = QString("%1: \"%2\"").arg(addString).arg(msg);
00347 if (msg.length() > 50)
00348 {
00349 msg.truncate(48);
00350 msg += "...\"";
00351 }
00352 m_addButton->setText(msg);
00353 }
00354
00355 void CustomPriority::addClicked(void)
00356 {
00357 QString clause = "";
00358
00359 if (m_description->text().contains(QRegExp("\\S")))
00360 clause = "AND ";
00361
00362 clause += m_csql[m_clause->currentItem()];
00363 m_description->append(clause);
00364 }
00365
00366 void CustomPriority::testClicked(void)
00367 {
00368 if (!checkSyntax())
00369 {
00370 m_testButton->setFocus();
00371 return;
00372 }
00373 testSchedule();
00374 m_testButton->setFocus();
00375 }
00376
00377 void CustomPriority::installClicked(void)
00378 {
00379 if (!checkSyntax())
00380 {
00381 m_installButton->setFocus();
00382 return;
00383 }
00384
00385 MSqlQuery query(MSqlQuery::InitCon());
00386 query.prepare("DELETE FROM powerpriority WHERE priorityname = :NAME;");
00387 query.bindValue(":NAME", m_title->text());
00388
00389 if (!query.exec())
00390 MythContext::DBError("Install power search delete", query);
00391
00392 query.prepare("INSERT INTO powerpriority "
00393 "(priorityname, recpriority, selectclause) "
00394 "VALUES(:NAME,:VALUE,:CLAUSE);");
00395 query.bindValue(":NAME", m_title->text());
00396 query.bindValue(":VALUE", m_value->value());
00397 query.bindValue(":CLAUSE", m_description->text());
00398
00399 if (!query.exec())
00400 MythContext::DBError("Install power search insert", query);
00401 else
00402 ScheduledRecording::signalChange(0);
00403
00404 accept();
00405 }
00406
00407 void CustomPriority::deleteClicked(void)
00408 {
00409 if (!checkSyntax())
00410 {
00411 m_deleteButton->setFocus();
00412 return;
00413 }
00414
00415 MSqlQuery query(MSqlQuery::InitCon());
00416 query.prepare("DELETE FROM powerpriority "
00417 "WHERE priorityname=:NAME;");
00418 query.bindValue(":NAME", m_title->text());
00419
00420 if (!query.exec())
00421 MythContext::DBError("Delete power search query", query);
00422 else
00423 ScheduledRecording::signalChange(0);
00424
00425 accept();
00426 }
00427
00428 void CustomPriority::cancelClicked(void)
00429 {
00430 accept();
00431 }
00432
00433 bool CustomPriority::checkSyntax(void)
00434 {
00435 bool ret = false;
00436 QString msg = "";
00437
00438 QString desc = m_description->text();
00439
00440 if (desc.contains(QRegExp("^\\s*AND\\s", false)))
00441 {
00442 msg = "Power Priority rules do not reqiure a leading \"AND\"";
00443 }
00444 else if (desc.contains(";", false))
00445 {
00446 msg = "Power Priority rules can not include semicolon ( ; ) ";
00447 msg += "statement terminators.";
00448 }
00449 else
00450 {
00451 QString qstr = QString("SELECT (%1)\nFROM (recordmatch, record, "
00452 "program, channel, cardinput, capturecard, "
00453 "oldrecorded) WHERE NULL").arg(desc);
00454 while (1)
00455 {
00456 int i = qstr.find("RECTABLE");
00457 if (i == -1) break;
00458 qstr = qstr.replace(i, strlen("RECTABLE"), "record");
00459 }
00460
00461 MSqlQuery query(MSqlQuery::InitCon());
00462 query.prepare(qstr);
00463
00464 if (query.exec() && query.isActive())
00465 {
00466 ret = true;
00467 }
00468 else
00469 {
00470 msg = tr("An error was found when checking") + ":\n\n";
00471 #if QT_VERSION >= 0x030200
00472 msg += query.executedQuery();
00473 #else
00474 msg += query.lastQuery();
00475 #endif
00476 msg += "\n\n" + tr("The database error was") + ":\n";
00477 msg += query.lastError().databaseText();
00478 ret = false;
00479 }
00480 }
00481
00482 if (!msg.isEmpty())
00483 {
00484 DialogBox *errdiag = new DialogBox(gContext->GetMainWindow(), msg);
00485 errdiag->AddButton(QObject::tr("OK"));
00486 errdiag->exec();
00487 errdiag->deleteLater();
00488 }
00489 return ret;
00490 }
00491
00492 void CustomPriority::testSchedule(void)
00493 {
00494
00495 QString ttable = "powerpriority_tmp";
00496
00497 MSqlQueryInfo dbcon = MSqlQuery::SchedCon();
00498 MSqlQuery query(dbcon);
00499 QString thequery;
00500
00501 thequery ="SELECT GET_LOCK(:LOCK, 2);";
00502 query.prepare(thequery);
00503 query.bindValue(":LOCK", "DiffSchedule");
00504 query.exec();
00505 if (query.lastError().type() != QSqlError::None)
00506 {
00507 QString msg =
00508 QString("DB Error (Obtaining lock in testRecording): \n"
00509 "Query was: %1 \nError was: %2 \n")
00510 .arg(thequery)
00511 .arg(MythContext::DBErrorMessage(query.lastError()));
00512 VERBOSE(VB_IMPORTANT, msg);
00513 return;
00514 }
00515
00516 thequery = QString("DROP TABLE IF EXISTS %1;").arg(ttable);
00517 query.prepare(thequery);
00518 query.exec();
00519 if (query.lastError().type() != QSqlError::None)
00520 {
00521 QString msg =
00522 QString("DB Error (deleting old table in testRecording): \n"
00523 "Query was: %1 \nError was: %2 \n")
00524 .arg(thequery)
00525 .arg(MythContext::DBErrorMessage(query.lastError()));
00526 VERBOSE(VB_IMPORTANT, msg);
00527 return;
00528 }
00529
00530 thequery = QString("CREATE TABLE %1 SELECT * FROM powerpriority;")
00531 .arg(ttable);
00532 query.prepare(thequery);
00533 query.exec();
00534 if (query.lastError().type() != QSqlError::None)
00535 {
00536 QString msg =
00537 QString("DB Error (create new table): \n"
00538 "Query was: %1 \nError was: %2 \n")
00539 .arg(thequery)
00540 .arg(MythContext::DBErrorMessage(query.lastError()));
00541 VERBOSE(VB_IMPORTANT, msg);
00542 return;
00543 }
00544
00545 query.prepare(QString("DELETE FROM %1 WHERE priorityname = :NAME;")
00546 .arg(ttable));
00547 query.bindValue(":NAME", m_title->text());
00548
00549 if (!query.exec())
00550 MythContext::DBError("Test power search delete", query);
00551
00552 thequery = QString("INSERT INTO %1 "
00553 "(priorityname, recpriority, selectclause) "
00554 "VALUES(:NAME,:VALUE,:CLAUSE);").arg(ttable);
00555 query.prepare(thequery);
00556 query.bindValue(":NAME", m_title->text());
00557 query.bindValue(":VALUE", m_value->value());
00558 query.bindValue(":CLAUSE", m_description->text());
00559
00560 if (!query.exec())
00561 MythContext::DBError("Test power search insert", query);
00562
00563 QString ltitle = tr("Power Priority");
00564 if (!m_title->text().isEmpty())
00565 ltitle = m_title->text();
00566
00567 ViewScheduleDiff vsd(gContext->GetMainWindow(), "Preview Schedule Changes",
00568 ttable, 0, ltitle);
00569
00570 thequery = "SELECT RELEASE_LOCK(:LOCK);";
00571 query.prepare(thequery);
00572 query.bindValue(":LOCK", "DiffSchedule");
00573 query.exec();
00574 if (query.lastError().type() != QSqlError::None)
00575 {
00576 QString msg =
00577 QString("DB Error (free lock): \n"
00578 "Query was: %1 \nError was: %2 \n")
00579 .arg(thequery)
00580 .arg(MythContext::DBErrorMessage(query.lastError()));
00581 VERBOSE(VB_IMPORTANT, msg);
00582 return;
00583 }
00584 vsd.exec();
00585 }