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