00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <qapplication.h>
00023 #include <iostream>
00024 #include <cstdlib>
00025
00026 #include <qptrlist.h>
00027 #include <qstring.h>
00028 #include <qfile.h>
00029 #include <qdom.h>
00030 #include <qtimer.h>
00031
00032 #include "mythtv/mythcontext.h"
00033 #include "mythtv/mythdbcon.h"
00034
00035 #include "mythflixconfig.h"
00036
00037 using namespace std;
00038
00039
00040
00041 class NewsSiteItem
00042 {
00043 public:
00044
00045 typedef QPtrList<NewsSiteItem> List;
00046
00047 QString name;
00048 QString category;
00049 QString url;
00050 QString ico;
00051 bool inDB;
00052 };
00053
00054
00055
00056 class NewsCategory
00057 {
00058 public:
00059
00060 typedef QPtrList<NewsCategory> List;
00061
00062 QString name;
00063 NewsSiteItem::List siteList;
00064
00065 NewsCategory() {
00066 siteList.setAutoDelete(true);
00067 }
00068
00069 ~NewsCategory() {
00070 siteList.clear();
00071 }
00072
00073 void clear() {
00074 siteList.clear();
00075 };
00076 };
00077
00078
00079
00080 class MythFlixConfigPriv
00081 {
00082 public:
00083
00084 NewsCategory::List categoryList;
00085 QStringList selectedSitesList;
00086
00087 MythFlixConfigPriv() {
00088 categoryList.setAutoDelete(true);
00089 }
00090
00091 ~MythFlixConfigPriv() {
00092 categoryList.clear();
00093 }
00094
00095 };
00096
00097
00098
00099 MythFlixConfig::MythFlixConfig(MythMainWindow *parent,
00100 const char *name)
00101 : MythDialog(parent, name)
00102 {
00103 m_priv = new MythFlixConfigPriv;
00104 m_updateFreqTimer = new QTimer(this);
00105 m_updateFreq = gContext->GetNumSetting("NewsUpdateFrequency", 30);
00106
00107 connect(m_updateFreqTimer, SIGNAL(timeout()),
00108 this, SLOT(slotUpdateFreqTimerTimeout()));
00109
00110 m_Theme = 0;
00111 m_UICategory = 0;
00112 m_UISite = 0;
00113 m_SpinBox = 0;
00114
00115 m_Context = 0;
00116 m_InColumn = 1;
00117
00118 populateSites();
00119
00120 setNoErase();
00121 loadTheme();
00122
00123 updateBackground();
00124 }
00125
00126 MythFlixConfig::~MythFlixConfig()
00127 {
00128 delete m_priv;
00129 delete m_Theme;
00130 }
00131
00132 void MythFlixConfig::populateSites()
00133 {
00134 QString filename = gContext->GetShareDir()
00135 + "mythflix/netflix-rss.xml";
00136 QFile xmlFile(filename);
00137
00138 if (!xmlFile.exists() || !xmlFile.open(IO_ReadOnly)) {
00139 VERBOSE(VB_IMPORTANT, QString("MythFlix: Cannot open netflix-rss.xml"));
00140 return;
00141 }
00142
00143 QString errorMsg;
00144 int errorLine = 0;
00145 int errorColumn = 0;
00146
00147 QDomDocument domDoc;
00148
00149 if (!domDoc.setContent(&xmlFile, false, &errorMsg, &errorLine, &errorColumn))
00150 {
00151 VERBOSE(VB_IMPORTANT, QString("MythFlix: Error in reading content of netflix-rss.xml"));
00152 VERBOSE(VB_IMPORTANT, QString("MythFlix: Error, parsing %1\n"
00153 "at line: %2 column: %3 msg: %4").
00154 arg(filename).arg(errorLine).arg(errorColumn).arg(errorMsg));
00155 return;
00156 }
00157
00158 m_priv->categoryList.clear();
00159
00160 QDomNodeList catList =
00161 domDoc.elementsByTagName(QString::fromLatin1("category"));
00162
00163 QDomNode catNode;
00164 QDomNode siteNode;
00165 for (unsigned int i = 0; i < catList.count(); i++) {
00166 catNode = catList.item(i);
00167
00168 NewsCategory *cat = new NewsCategory();
00169 cat->name = catNode.toElement().attribute("name");
00170
00171 m_priv->categoryList.append(cat);
00172
00173 QDomNodeList siteList = catNode.childNodes();
00174
00175 for (unsigned int j = 0; j < siteList.count(); j++) {
00176 siteNode = siteList.item(j);
00177
00178 NewsSiteItem *site = new NewsSiteItem();
00179 site->name =
00180 siteNode.namedItem(QString("title")).toElement().text();
00181 site->category =
00182 cat->name;
00183 site->url =
00184 siteNode.namedItem(QString("url")).toElement().text();
00185 site->ico =
00186 siteNode.namedItem(QString("ico")).toElement().text();
00187
00188 site->inDB = findInDB(site->name);
00189
00190 cat->siteList.append(site);
00191 }
00192
00193 }
00194
00195 xmlFile.close();
00196 }
00197
00198 void MythFlixConfig::loadTheme()
00199 {
00200 m_Theme = new XMLParse();
00201 m_Theme->SetWMult(wmult);
00202 m_Theme->SetHMult(hmult);
00203
00204 QDomElement xmldata;
00205 m_Theme->LoadTheme(xmldata, "config", "netflix-");
00206
00207 for (QDomNode child = xmldata.firstChild(); !child.isNull();
00208 child = child.nextSibling()) {
00209
00210 QDomElement e = child.toElement();
00211 if (!e.isNull()) {
00212
00213 if (e.tagName() == "font") {
00214 m_Theme->parseFont(e);
00215 }
00216 else if (e.tagName() == "container") {
00217
00218 QRect area;
00219 QString name;
00220 int context;
00221 m_Theme->parseContainer(e, name, context, area);
00222
00223 if (name.lower() == "config-sites")
00224 m_SiteRect = area;
00225 else if (name.lower() == "config-freq")
00226 m_FreqRect = area;
00227 }
00228 else {
00229 VERBOSE(VB_IMPORTANT, QString("MythFlix: Unknown element: %1").arg(e.tagName()));
00230 exit(-1);
00231 }
00232 }
00233 }
00234
00235
00236 LayerSet *container = m_Theme->GetSet("config-sites");
00237 if (!container) {
00238 VERBOSE(VB_IMPORTANT, QString("MythFlix: Failed to get sites container."));
00239 exit(-1);
00240 }
00241
00242 UITextType* ttype = (UITextType*)container->GetType("context_switch");
00243 if (ttype) {
00244 ttype->SetText(tr("Press MENU to set the update frequency."));
00245 }
00246
00247
00248 m_UICategory = (UIListBtnType*)container->GetType("category");
00249 if (!m_UICategory) {
00250 VERBOSE(VB_IMPORTANT, QString("MythFlix: Failed to get category list area."));
00251 exit(-1);
00252 }
00253
00254 m_UISite = (UIListBtnType*)container->GetType("sites");
00255 if (!m_UISite) {
00256 VERBOSE(VB_IMPORTANT, QString("MythFlix: Failed to get site list area."));
00257 exit(-1);
00258 }
00259
00260 for (NewsCategory* cat = m_priv->categoryList.first();
00261 cat; cat = m_priv->categoryList.next() ) {
00262 UIListBtnTypeItem* item =
00263 new UIListBtnTypeItem(m_UICategory, cat->name);
00264 item->setData(cat);
00265 }
00266 slotCategoryChanged(m_UICategory->GetItemFirst());
00267
00268 container = m_Theme->GetSet("config-freq");
00269 if (!container) {
00270 VERBOSE(VB_IMPORTANT, QString("MythFlix: Failed to get frequency container."));
00271 exit(-1);
00272 }
00273
00274 UIBlackHoleType* spinboxHolder =
00275 (UIBlackHoleType*)container->GetType("spinbox_holder");
00276 if (spinboxHolder) {
00277 m_SpinBox = new MythFlixSpinBox(this);
00278 m_SpinBox->setRange(30,1000);
00279 m_SpinBox->setLineStep(10);
00280 m_SpinBox->setValue(m_updateFreq);
00281 QFont f = gContext->GetMediumFont();
00282 m_SpinBox->setFont(f);
00283 m_SpinBox->setFocusPolicy(QWidget::NoFocus);
00284 m_SpinBox->setGeometry(spinboxHolder->getScreenArea());
00285 m_SpinBox->hide();
00286 connect(m_SpinBox, SIGNAL(valueChanged(int)),
00287 SLOT(slotUpdateFreqChanged()));
00288 }
00289
00290 ttype = (UITextType*)container->GetType("help");
00291 if (ttype) {
00292 ttype->SetText(tr("Set update frequency by using the up/down arrows."
00293 "The minimum allowed value is 30 Minutes."));
00294 }
00295
00296 ttype = (UITextType*)container->GetType("context_switch");
00297 if (ttype) {
00298 ttype->SetText(tr("Press Escape or Menu to exit."));
00299 }
00300
00301 connect(m_UICategory, SIGNAL(itemSelected(UIListBtnTypeItem*)),
00302 SLOT(slotCategoryChanged(UIListBtnTypeItem*)));
00303
00304 m_UICategory->SetActive(true);
00305 }
00306
00307
00308 void MythFlixConfig::paintEvent(QPaintEvent *e)
00309 {
00310 QRect r = e->rect();
00311
00312 if (m_Context == 0) {
00313 if (r.intersects(m_SiteRect)) {
00314 updateSites();
00315 }
00316 }
00317 else {
00318 if (r.intersects(m_FreqRect)) {
00319 updateFreq();
00320 }
00321 }
00322 }
00323
00324 void MythFlixConfig::updateBackground(void)
00325 {
00326 QPixmap bground(size());
00327 bground.fill(this, 0, 0);
00328
00329 QPainter tmp(&bground);
00330
00331 LayerSet *container = m_Theme->GetSet("background");
00332 if (container)
00333 {
00334 container->Draw(&tmp, 0, 0);
00335 }
00336
00337 tmp.end();
00338 m_background = bground;
00339
00340 setPaletteBackgroundPixmap(m_background);
00341 }
00342
00343 void MythFlixConfig::updateSites()
00344 {
00345 QPixmap pix(m_SiteRect.size());
00346 pix.fill(this, m_SiteRect.topLeft());
00347 QPainter p(&pix);
00348
00349 LayerSet* container = m_Theme->GetSet("config-sites");
00350 if (container) {
00351 container->Draw(&p, 0, 0);
00352 container->Draw(&p, 1, 0);
00353 container->Draw(&p, 2, 0);
00354 container->Draw(&p, 3, 0);
00355 container->Draw(&p, 4, 0);
00356 container->Draw(&p, 5, 0);
00357 container->Draw(&p, 6, 0);
00358 container->Draw(&p, 7, 0);
00359 container->Draw(&p, 8, 0);
00360 }
00361 p.end();
00362
00363 bitBlt(this, m_SiteRect.left(), m_SiteRect.top(),
00364 &pix, 0, 0, -1, -1, Qt::CopyROP);
00365 }
00366
00367 void MythFlixConfig::updateFreq()
00368 {
00369 QPixmap pix(m_FreqRect.size());
00370 pix.fill(this, m_FreqRect.topLeft());
00371 QPainter p(&pix);
00372
00373 LayerSet* container = m_Theme->GetSet("config-freq");
00374 if (container) {
00375 container->Draw(&p, 0, 0);
00376 container->Draw(&p, 1, 0);
00377 container->Draw(&p, 2, 0);
00378 container->Draw(&p, 3, 0);
00379 container->Draw(&p, 4, 0);
00380 container->Draw(&p, 5, 0);
00381 container->Draw(&p, 6, 0);
00382 container->Draw(&p, 7, 0);
00383 container->Draw(&p, 8, 0);
00384 }
00385 p.end();
00386
00387 bitBlt(this, m_FreqRect.left(), m_FreqRect.top(),
00388 &pix, 0, 0, -1, -1, Qt::CopyROP);
00389 }
00390
00391 void MythFlixConfig::toggleItem(UIListBtnTypeItem *item)
00392 {
00393 if (!item || !item->getData())
00394 return;
00395
00396 NewsSiteItem* site = (NewsSiteItem*) item->getData();
00397
00398 bool checked = (item->state() == UIListBtnTypeItem::FullChecked);
00399
00400 if (!checked) {
00401 if (insertInDB(site)) {
00402 site->inDB = true;
00403 item->setChecked(UIListBtnTypeItem::FullChecked);
00404 }
00405 else {
00406 site->inDB = false;
00407 item->setChecked(UIListBtnTypeItem::NotChecked);
00408 }
00409 }
00410 else {
00411 if (removeFromDB(site)) {
00412 site->inDB = false;
00413 item->setChecked(UIListBtnTypeItem::NotChecked);
00414 }
00415 else {
00416 site->inDB = true;
00417 item->setChecked(UIListBtnTypeItem::FullChecked);
00418 }
00419 }
00420
00421 updateSites();
00422 }
00423
00424 bool MythFlixConfig::findInDB(const QString& name)
00425 {
00426 bool val = false;
00427
00428 MSqlQuery query(MSqlQuery::InitCon());
00429 query.prepare("SELECT name FROM netflix WHERE name = :NAME ;");
00430 query.bindValue(":NAME", name.utf8());
00431 if (!query.exec() || !query.isActive()) {
00432 MythContext::DBError("new find in db", query);
00433 return val;
00434 }
00435
00436 val = query.numRowsAffected() > 0;
00437
00438 return val;
00439 }
00440
00441 bool MythFlixConfig::insertInDB(NewsSiteItem* site)
00442 {
00443 if (!site) return false;
00444
00445 if (findInDB(site->name))
00446 return false;
00447
00448 MSqlQuery query(MSqlQuery::InitCon());
00449 query.prepare("INSERT INTO netflix (name,category,url,ico, is_queue) "
00450 " VALUES( :NAME, :CATEGORY, :URL, :ICON, 0);");
00451 query.bindValue(":NAME", site->name.utf8());
00452 query.bindValue(":CATEGORY", site->category.utf8());
00453 query.bindValue(":URL", site->url.utf8());
00454 query.bindValue(":ICON", site->ico.utf8());
00455 if (!query.exec() || !query.isActive()) {
00456 MythContext::DBError("netlix: inserting in DB", query);
00457 return false;
00458 }
00459
00460 return (query.numRowsAffected() > 0);
00461 }
00462
00463 bool MythFlixConfig::removeFromDB(NewsSiteItem* site)
00464 {
00465 if (!site) return false;
00466
00467 MSqlQuery query(MSqlQuery::InitCon());
00468 query.prepare("DELETE FROM netflix WHERE name = :NAME ;");
00469 query.bindValue(":NAME", site->name.utf8());
00470 if (!query.exec() || !query.isActive()) {
00471 MythContext::DBError("netflix: delete from db", query);
00472 return false;
00473 }
00474
00475 return (query.numRowsAffected() > 0);
00476 }
00477
00478
00479 void MythFlixConfig::slotCategoryChanged(UIListBtnTypeItem *item)
00480 {
00481 if (!item)
00482 return;
00483
00484 m_UISite->Reset();
00485
00486 NewsCategory* cat = (NewsCategory*) item->getData();
00487 if (cat) {
00488
00489 for (NewsSiteItem* site = cat->siteList.first();
00490 site; site = cat->siteList.next() ) {
00491 UIListBtnTypeItem* item =
00492 new UIListBtnTypeItem(m_UISite, site->name, 0, true,
00493 site->inDB ?
00494 UIListBtnTypeItem::FullChecked :
00495 UIListBtnTypeItem::NotChecked);
00496 item->setData(site);
00497 }
00498 }
00499 }
00500
00501 void MythFlixConfig::slotUpdateFreqChanged()
00502 {
00503 m_updateFreqTimer->stop();
00504 m_updateFreqTimer->start(500, true);
00505 }
00506
00507 void MythFlixConfig::slotUpdateFreqTimerTimeout()
00508 {
00509 if (m_updateFreqTimer->isActive()) return;
00510
00511 if (m_SpinBox) {
00512 gContext->SaveSetting("NewsUpdateFrequency",
00513 m_SpinBox->value());
00514 }
00515 }
00516
00517
00518 void MythFlixConfig::keyPressEvent(QKeyEvent *e)
00519 {
00520 if (!e) return;
00521
00522 bool handled = false;
00523 QStringList actions;
00524 gContext->GetMainWindow()->TranslateKeyPress("NetFlix", e, actions);
00525
00526 for (unsigned int i = 0; i < actions.size() && !handled; i++)
00527 {
00528 QString action = actions[i];
00529 handled = true;
00530
00531 if (action == "UP") {
00532 cursorUp();
00533 }
00534 else if (action == "PAGEUP") {
00535 cursorUp(true);
00536 }
00537 else if (action == "DOWN") {
00538 cursorDown();
00539 }
00540 else if (action == "PAGEDOWN") {
00541 cursorDown(true);
00542 }
00543 else if (action == "LEFT") {
00544 cursorLeft();
00545 }
00546 else if (action == "RIGHT") {
00547 cursorRight();
00548 }
00549 else if (action == "MENU") {
00550 changeContext();
00551 }
00552 else if (action == "ESCAPE" && m_Context == 1)
00553 {
00554 changeContext();
00555 }
00556 else if (action == "SELECT") {
00557 if (m_InColumn == 2 && m_Context == 0) {
00558 UIListBtnTypeItem *item = m_UISite->GetItemCurrent();
00559 if (item)
00560 toggleItem(item);
00561 }
00562 }
00563 else
00564 handled = false;
00565 }
00566
00567 if (!handled)
00568 MythDialog::keyPressEvent(e);
00569 else
00570 update();
00571 }
00572
00573 void MythFlixConfig::cursorUp(bool page)
00574 {
00575 UIListBtnType::MovementUnit unit = page ? UIListBtnType::MovePage : UIListBtnType::MoveItem;
00576
00577 if (m_Context == 0) {
00578 if (m_InColumn == 1)
00579 m_UICategory->MoveUp(unit);
00580 else
00581 m_UISite->MoveUp(unit);
00582 }
00583
00584 update();
00585 }
00586
00587 void MythFlixConfig::cursorDown(bool page)
00588 {
00589 UIListBtnType::MovementUnit unit = page ? UIListBtnType::MovePage : UIListBtnType::MoveItem;
00590
00591 if (m_Context == 0) {
00592 if (m_InColumn == 1)
00593 m_UICategory->MoveDown(unit);
00594 else
00595 m_UISite->MoveDown(unit);
00596 }
00597
00598 update();
00599 }
00600
00601 void MythFlixConfig::cursorLeft()
00602 {
00603 if (m_InColumn == 1)
00604 return;
00605
00606 m_InColumn--;
00607
00608 if (m_Context == 0) {
00609 if (m_InColumn == 1) {
00610 m_UICategory->SetActive(true);
00611 m_UISite->SetActive(false);
00612 }
00613 }
00614 update();
00615 }
00616
00617
00618 void MythFlixConfig::cursorRight()
00619 {
00620 if (m_InColumn == 2 || (m_InColumn == 1 && m_Context == 1 ))
00621 return;
00622
00623 m_InColumn++;
00624
00625
00626 if (m_Context == 0) {
00627 if (m_InColumn == 1) {
00628 m_UICategory->SetActive(true);
00629 }
00630 else {
00631 if (m_UISite->GetCount() == 0)
00632 m_InColumn--;
00633 else {
00634 m_UICategory->SetActive(false);
00635 m_UISite->SetActive(true);
00636 }
00637 }
00638 }
00639 update();
00640 }
00641
00642
00643
00644 MythFlixSpinBox::MythFlixSpinBox(QWidget* parent, const char* widgetName )
00645 : MythSpinBox(parent,widgetName)
00646 {
00647
00648 }
00649
00650 bool MythFlixSpinBox::eventFilter(QObject* o, QEvent* e)
00651 {
00652 (void)o;
00653
00654 if (e->type() == QEvent::FocusIn)
00655 {
00656 QColor highlight = colorGroup().highlight();
00657 editor()->setPaletteBackgroundColor(highlight);
00658 }
00659 else if (e->type() == QEvent::FocusOut)
00660 {
00661 editor()->unsetPalette();
00662 }
00663
00664 if (e->type() != QEvent::KeyPress)
00665 return FALSE;
00666
00667 bool handled = false;
00668 QStringList actions;
00669 if (gContext->GetMainWindow()->TranslateKeyPress("qt", (QKeyEvent *)e,
00670 actions))
00671 {
00672 for (unsigned int i = 0; i < actions.size() && !handled; i++)
00673 {
00674 QString action = actions[i];
00675 handled = true;
00676
00677 if (action == "UP")
00678 stepUp();
00679 else if (action == "DOWN")
00680 stepDown();
00681 else if (action == "PAGEUP")
00682 stepUp();
00683 else if (action == "PAGEDOWN")
00684 stepDown();
00685 else if (action == "SELECT" || action == "LEFT" || action == "MENU") {
00686 QKeyEvent *ev = (QKeyEvent*)e;
00687 QApplication::postEvent(parentWidget(),
00688 new QKeyEvent(ev->type(),ev->key(),
00689 ev->ascii(),
00690 ev->state()));
00691 }
00692 else if (action == "ESCAPE")
00693 return false;
00694 else
00695 handled = false;
00696 }
00697 }
00698
00699 return true;
00700 }
00701
00702
00703 void MythFlixConfig::changeContext()
00704 {
00705 if(m_Context == 1)
00706 {
00707 m_Context = 0;
00708 m_SpinBox->hide();
00709 m_SpinBox->clearFocus();
00710 }
00711 else
00712 {
00713 m_Context = 1;
00714 m_SpinBox->show();
00715 m_SpinBox->setFocus();
00716 }
00717
00718 update();
00719 }
00720