00001 #include <qlayout.h>
00002 #include <qpushbutton.h>
00003 #include <qbuttongroup.h>
00004 #include <qlabel.h>
00005 #include <qcursor.h>
00006 #include <qsqldatabase.h>
00007 #include <qdatetime.h>
00008 #include <qapplication.h>
00009 #include <qregexp.h>
00010 #include <qheader.h>
00011
00012 #include <iostream>
00013 #include <cstdlib>
00014 using namespace std;
00015
00016 #include "viewschdiff.h"
00017 #include "scheduledrecording.h"
00018 #include "proglist.h"
00019 #include "tv.h"
00020
00021 #include "exitcodes.h"
00022 #include "dialogbox.h"
00023 #include "mythcontext.h"
00024 #include "remoteutil.h"
00025
00026 ViewScheduleDiff::ViewScheduleDiff(MythMainWindow *parent, const char *name, QString altTbl, int recordidDiff, QString ltitle)
00027 : MythDialog(parent, name)
00028 {
00029 dateformat = gContext->GetSetting("ShortDateFormat", "M/d");
00030 timeformat = gContext->GetSetting("TimeFormat", "h:mm AP");
00031 channelFormat = gContext->GetSetting("ChannelFormat", "<num> <sign>");
00032
00033 altTable = altTbl;
00034 recordid = recordidDiff;
00035 m_title = ltitle;
00036
00037 fullRect = QRect(0, 0, size().width(), size().height());
00038 listRect = QRect(0, 0, 0, 0);
00039 infoRect = QRect(0, 0, 0, 0);
00040 showLevelRect = QRect(0, 0, 0, 0);
00041 recStatusRect = QRect(0, 0, 0, 0);
00042
00043 theme = new XMLParse();
00044 theme->SetWMult(wmult);
00045 theme->SetHMult(hmult);
00046 if (!theme->LoadTheme(xmldata, "schdiff"))
00047 {
00048 DialogBox *dlg = new DialogBox(
00049 gContext->GetMainWindow(),
00050 QObject::tr(
00051 "The theme you are using does not contain the "
00052 "%1 element. Please contact the theme creator "
00053 "and ask if they could please update it.<br><br>"
00054 "The next screen will be empty. "
00055 "Escape out of it to return to the menu.")
00056 .arg("'schdiff'"));
00057
00058 dlg->AddButton(tr("OK"));
00059 dlg->exec();
00060 dlg->deleteLater();
00061
00062 return;
00063 }
00064
00065 LoadWindow(xmldata);
00066
00067 LayerSet *container = theme->GetSet("selector");
00068 if (container)
00069 {
00070 UIListType *ltype = (UIListType *)container->GetType("conflictlist");
00071 if (ltype)
00072 listsize = ltype->GetItems();
00073 }
00074 else
00075 {
00076 VERBOSE(VB_IMPORTANT, "ViewScheduleDiff::ViewScheduleDiff(): "
00077 "Failed to get selector object.");
00078 exit(FRONTEND_BUGGY_EXIT_NO_SELECTOR);
00079 }
00080 container = theme->GetSet("background");
00081 if (container)
00082 {
00083 UITextType *type = (UITextType *)container->GetType("view");
00084 if (type)
00085 type->SetText(m_title);
00086 }
00087
00088 updateBackground();
00089
00090 inEvent = false;
00091 inFill = false;
00092 needFill = false;
00093
00094 listPos = 0;
00095 FillList();
00096
00097 setNoErase();
00098
00099 gContext->addListener(this);
00100
00101 }
00102
00103 ViewScheduleDiff::~ViewScheduleDiff()
00104 {
00105 gContext->removeListener(this);
00106 delete theme;
00107 }
00108
00109 void ViewScheduleDiff::keyPressEvent(QKeyEvent *e)
00110 {
00111 if (inEvent)
00112 return;
00113
00114 inEvent = true;
00115
00116 bool handled = false;
00117 QStringList actions;
00118 if (gContext->GetMainWindow()->TranslateKeyPress("TV Frontend", e, actions))
00119 {
00120 for (unsigned int i = 0; i < actions.size() && !handled; i++)
00121 {
00122 QString action = actions[i];
00123 handled = true;
00124
00125 if (action == "ESCAPE" || action == "LEFT")
00126 done(MythDialog::Accepted);
00127 else if (action == "UP")
00128 cursorUp();
00129 else if (action == "DOWN")
00130 cursorDown();
00131 else if (action == "PAGEUP")
00132 pageUp();
00133 else if (action == "PAGEDOWN")
00134 pageDown();
00135 else if (action == "INFO")
00136 edit();
00137 else if (action == "UPCOMING")
00138 upcoming();
00139 else if (action == "DETAILS")
00140 details();
00141 else if (action == "SELECT")
00142 statusDialog();
00143 else
00144 handled = false;
00145 }
00146 }
00147
00148 if (!handled)
00149 MythDialog::keyPressEvent(e);
00150
00151 inEvent = false;
00152 }
00153
00154 void ViewScheduleDiff::LoadWindow(QDomElement &element)
00155 {
00156 for (QDomNode child = element.firstChild(); !child.isNull();
00157 child = child.nextSibling())
00158 {
00159 QDomElement e = child.toElement();
00160 if (!e.isNull())
00161 {
00162 if (e.tagName() == "font")
00163 theme->parseFont(e);
00164 else if (e.tagName() == "container")
00165 parseContainer(e);
00166 else
00167 {
00168 VERBOSE(VB_IMPORTANT,
00169 QString("ViewScheduleDiff: Unknown child element: %1. "
00170 "Ignoring.").arg(e.tagName()));
00171 }
00172 }
00173 }
00174 }
00175
00176 void ViewScheduleDiff::parseContainer(QDomElement &element)
00177 {
00178 QRect area;
00179 QString name;
00180 int context;
00181 theme->parseContainer(element, name, context, area);
00182
00183 if (name.lower() == "selector")
00184 listRect = area;
00185 if (name.lower() == "program_info")
00186 infoRect = area;
00187 if (name.lower() == "showlevel_info")
00188 showLevelRect = area;
00189 if (name.lower() == "status_info")
00190 recStatusRect = area;
00191 }
00192
00193 void ViewScheduleDiff::updateBackground(void)
00194 {
00195 QPixmap bground(size());
00196 bground.fill(this, 0, 0);
00197
00198 QPainter tmp(&bground);
00199
00200 LayerSet *container = theme->GetSet("background");
00201 container->Draw(&tmp, 0, 0);
00202
00203 tmp.end();
00204 myBackground = bground;
00205
00206 setPaletteBackgroundPixmap(myBackground);
00207 }
00208
00209 void ViewScheduleDiff::paintEvent(QPaintEvent *e)
00210 {
00211 if (inFill)
00212 return;
00213
00214 QRect r = e->rect();
00215 QPainter p(this);
00216
00217 if (r.intersects(listRect))
00218 updateList(&p);
00219 if (r.intersects(infoRect))
00220 updateInfo(&p);
00221 if (r.intersects(showLevelRect))
00222 updateShowLevel(&p);
00223 if (r.intersects(recStatusRect))
00224 updateRecStatus(&p);
00225 }
00226
00227 void ViewScheduleDiff::cursorDown(bool page)
00228 {
00229 if (recList.count() == 0) return;
00230 if (listPos < recList.count() - 1)
00231 {
00232 listPos += (page ? listsize : 1);
00233 if (listPos > recList.count() - 1)
00234 listPos = recList.count() - 1;
00235 update(fullRect);
00236 }
00237 }
00238
00239 void ViewScheduleDiff::cursorUp(bool page)
00240 {
00241 if (listPos > 0)
00242 {
00243 unsigned int move = (page ? listsize : 1);
00244 if (move > listPos) listPos = 0;
00245 else listPos -= move;
00246 update(fullRect);
00247 }
00248 }
00249
00250 void ViewScheduleDiff::edit()
00251 {
00252 ProgramInfo *pi = CurrentProgram();;
00253
00254 if (!pi)
00255 return;
00256
00257 pi->EditScheduled();
00258 }
00259
00260 void ViewScheduleDiff::upcoming()
00261 {
00262 ProgramInfo *pi = CurrentProgram();
00263
00264 ProgLister *pl = new ProgLister(plTitle, pi->title, "",
00265 gContext->GetMainWindow(), "proglist");
00266 pl->exec();
00267 delete pl;
00268 }
00269
00270 void ViewScheduleDiff::details()
00271 {
00272 ProgramInfo *pi = CurrentProgram();
00273
00274 if (pi)
00275 pi->showDetails();
00276 }
00277
00278 void ViewScheduleDiff::statusDialog()
00279 {
00280 ProgramInfo *pi = CurrentProgram();
00281 if (!pi)
00282 return;
00283
00284 QString timeFormat = gContext->GetSetting("TimeFormat", "h:mm AP");
00285
00286 QString message = pi->title;
00287
00288 if (pi->subtitle != "")
00289 message += QString(" - \"%1\"").arg(pi->subtitle);
00290
00291 message += "\n\n";
00292 message += pi->RecStatusDesc();
00293
00294 if (pi->recstatus == rsConflict || pi->recstatus == rsLaterShowing)
00295 {
00296 message += " " + QObject::tr("The following programs will be recorded "
00297 "instead:") + "\n\n";
00298 ProgramInfo *pa = recListAfter.first();
00299 while (pa)
00300 {
00301 if (pa->recstartts >= pi->recendts)
00302 break;
00303 if (pa->recendts > pi->recstartts &&
00304 (pa->recstatus == rsWillRecord ||
00305 pa->recstatus == rsRecording))
00306 {
00307 message += QString("%1 - %2 %3")
00308 .arg(pa->recstartts.toString(timeFormat))
00309 .arg(pa->recendts.toString(timeFormat)).arg(pa->title);
00310 if (pa->subtitle != "")
00311 message += QString(" - \"%1\"").arg(pa->subtitle);
00312 message += "\n";
00313 }
00314 pa = recListAfter.next();
00315 }
00316 }
00317
00318 DialogBox *dlg = new DialogBox(gContext->GetMainWindow(), message);
00319 dlg->AddButton(QObject::tr("OK"));
00320 dlg->exec();
00321 dlg->deleteLater();
00322
00323 return;
00324 }
00325
00326 static int comp_recstart(ProgramInfo *a, ProgramInfo *b)
00327 {
00328 if (a->recstartts != b->recstartts)
00329 {
00330 if (a->recstartts > b->recstartts)
00331 return 1;
00332 else
00333 return -1;
00334 }
00335 if (a->recendts != b->recendts)
00336 {
00337 if (a->recendts > b->recendts)
00338 return 1;
00339 else
00340 return -1;
00341 }
00342 if (a->chansign != b->chansign)
00343 {
00344 if (a->chansign < b->chansign)
00345 return 1;
00346 else
00347 return -1;
00348 }
00349 if (a->recpriority != b->recpriority &&
00350 (a->recstatus == rsWillRecord || b->recstatus == rsWillRecord))
00351 {
00352 if (a->recpriority < b->recpriority)
00353 return 1;
00354 else
00355 return -1;
00356 }
00357 return 0;
00358 }
00359
00360 void ViewScheduleDiff::FillList(void)
00361 {
00362 inFill = true;
00363
00364 QString callsign;
00365 QDateTime startts, recstartts;
00366
00367 if (listPos < recList.count())
00368 {
00369 struct ProgramStruct s = recList[listPos];
00370 ProgramInfo *p = s.after;
00371 if (!p) p = s.before;
00372
00373 if (p) {
00374 callsign = p->chansign;
00375 startts = p->startts;
00376 recstartts = p->recstartts;
00377 }
00378 }
00379
00380 recListBefore.FromScheduler(conflictBool);
00381 recListAfter.FromScheduler(conflictBool, altTable, recordid);
00382
00383 recListBefore.Sort(comp_recstart);
00384 recListAfter.Sort(comp_recstart);
00385
00386 QDateTime now = QDateTime::currentDateTime();
00387
00388 ProgramInfo *p = recListBefore.first();
00389 while (p)
00390 {
00391 if (p->recendts >= now || p->endts >= now)
00392 p = recListBefore.next();
00393 else
00394 {
00395 recListBefore.remove();
00396 p = recListBefore.current();
00397 }
00398 }
00399
00400 p = recListAfter.first();
00401 while (p)
00402 {
00403 if (p->recendts >= now || p->endts >= now)
00404 p = recListAfter.next();
00405 else
00406 {
00407 recListAfter.remove();
00408 p = recListAfter.current();
00409 }
00410 }
00411
00412 ProgramInfo *pb = recListBefore.first();
00413 ProgramInfo *pa = recListAfter.first();
00414 ProgramStruct s;
00415
00416 recList.clear();
00417 while (pa || pb) {
00418 s.before = pb;
00419 s.after = pa;
00420
00421 if (!pa) {
00422 pb = recListBefore.next();
00423 } else if (!pb) {
00424 pa = recListAfter.next();
00425 } else switch (comp_recstart(pb, pa)) {
00426 case 0:
00427 pb = recListBefore.next();
00428 pa = recListAfter.next();
00429 break;
00430 case -1:
00431 pb = recListBefore.next();
00432 s.after = NULL;
00433 break;
00434 case 1:
00435 s.before = NULL;
00436 pa = recListAfter.next();
00437 break;
00438 }
00439 if (s.before && s.after && (s.before->cardid == s.after->cardid) &&
00440 (s.before->recstatus == s.after->recstatus))
00441 {
00442 continue;
00443 }
00444 recList.push_back(s);
00445 }
00446
00447 if (!callsign.isNull())
00448 {
00449 listPos = recList.count() - 1;
00450 int i;
00451 for (i = listPos; i >= 0; i--)
00452 {
00453 struct ProgramStruct s = recList[listPos];
00454 ProgramInfo *p = s.after;
00455 if (!p) p = s.before;
00456
00457 if (callsign == p->chansign &&
00458 startts == p->startts)
00459 {
00460 listPos = i;
00461 break;
00462 }
00463 else if (recstartts <= p->recstartts)
00464 listPos = i;
00465 }
00466 }
00467
00468 inFill = false;
00469 }
00470
00471 void ViewScheduleDiff::updateList(QPainter *p)
00472 {
00473 QRect pr = listRect;
00474 QPixmap pix(pr.size());
00475 pix.fill(this, pr.topLeft());
00476 QPainter tmp(&pix);
00477
00478 LayerSet *container = theme->GetSet("selector");
00479 if (container)
00480 {
00481 UIListType *ltype = (UIListType *)container->GetType("conflictlist");
00482 if (ltype)
00483 {
00484 ltype->ResetList();
00485 ltype->SetActive(true);
00486
00487 int listCount = (int)recList.count();
00488
00489 int skip;
00490 if (listCount <= listsize || (int)listPos <= listsize / 2)
00491 skip = 0;
00492 else if ((int)listPos >= listCount - listsize + listsize / 2)
00493 skip = listCount - listsize;
00494 else
00495 skip = (int)listPos - listsize / 2;
00496
00497 ltype->SetUpArrow(skip > 0);
00498 ltype->SetDownArrow(skip + listsize < listCount);
00499
00500 int i;
00501 for (i = 0; i < listsize; i++)
00502 {
00503 if (i + skip >= listCount)
00504 break;
00505
00506 struct ProgramStruct s = recList[skip+i];
00507 struct ProgramInfo *p = s.after;
00508 if (!p) p = s.before;
00509
00510 QString temp;
00511
00512 temp = (p->recstartts).toString(dateformat);
00513 temp += " " + (p->recstartts).toString(timeformat);
00514 ltype->SetItemText(i, 1, temp);
00515
00516 ltype->SetItemText(i, 2, p->ChannelText(channelFormat));
00517
00518 temp = p->title;
00519 if ((p->subtitle).stripWhiteSpace().length() > 0)
00520 temp += " - \"" + p->subtitle + "\"";
00521 ltype->SetItemText(i, 3, temp);
00522
00523 if (s.before) temp = s.before->RecStatusChar();
00524 else temp = "-";
00525 ltype->SetItemText(i, 4, temp);
00526
00527 if (s.after) temp = s.after->RecStatusChar();
00528 else temp = "-";
00529 ltype->SetItemText(i, 5, temp);
00530
00531 if (i + skip == (int)listPos)
00532 ltype->SetItemCurrent(i);
00533
00534 if (!s.after)
00535 ltype->EnableForcedFont(i, "disabledrecording");
00536 else if (p->recstatus == rsRecording)
00537 ltype->EnableForcedFont(i, "recording");
00538 else if (p->recstatus == rsConflict ||
00539 p->recstatus == rsOffLine ||
00540 p->recstatus == rsAborted)
00541 ltype->EnableForcedFont(i, "conflictingrecording");
00542 else if (p->recstatus == rsWillRecord)
00543 ltype->EnableForcedFont(i, "record");
00544 else if (p->recstatus == rsRepeat ||
00545 p->recstatus == rsOtherShowing ||
00546 (p->recstatus != rsDontRecord &&
00547 p->recstatus <= rsEarlierShowing))
00548 ltype->EnableForcedFont(i, "disabledrecording");
00549 }
00550 }
00551 }
00552
00553 if (recList.count() == 0)
00554 container = theme->GetSet("norecordings_list");
00555
00556 if (container)
00557 {
00558 container->Draw(&tmp, 0, 0);
00559 container->Draw(&tmp, 1, 0);
00560 container->Draw(&tmp, 2, 0);
00561 container->Draw(&tmp, 3, 0);
00562 container->Draw(&tmp, 4, 0);
00563 container->Draw(&tmp, 5, 0);
00564 container->Draw(&tmp, 6, 0);
00565 container->Draw(&tmp, 7, 0);
00566 container->Draw(&tmp, 8, 0);
00567 }
00568
00569 tmp.end();
00570 p->drawPixmap(pr.topLeft(), pix);
00571 }
00572
00573 void ViewScheduleDiff::updateShowLevel(QPainter *p)
00574 {
00575 QRect pr = showLevelRect;
00576 QPixmap pix(pr.size());
00577 pix.fill(this, pr.topLeft());
00578 QPainter tmp(&pix);
00579
00580 LayerSet *container = theme->GetSet("showlevel_info");
00581 if (container)
00582 {
00583 UITextType *type = (UITextType *)container->GetType("showlevel");
00584 if (type)
00585 {
00586 type->SetText(tr("All"));
00587 }
00588 }
00589
00590 if (container)
00591 {
00592 container->Draw(&tmp, 4, 0);
00593 container->Draw(&tmp, 5, 0);
00594 container->Draw(&tmp, 6, 0);
00595 container->Draw(&tmp, 7, 0);
00596 container->Draw(&tmp, 8, 0);
00597 }
00598
00599 tmp.end();
00600 p->drawPixmap(pr.topLeft(), pix);
00601 }
00602
00603 void ViewScheduleDiff::updateInfo(QPainter *p)
00604 {
00605 QRect pr = infoRect;
00606 QPixmap pix(pr.size());
00607 pix.fill(this, pr.topLeft());
00608 QPainter tmp(&pix);
00609 QMap<QString, QString> infoMap;
00610
00611 LayerSet *container = theme->GetSet("program_info");
00612 if (container)
00613 {
00614 ProgramInfo *p = CurrentProgram();
00615
00616 if (p)
00617 {
00618 p->ToMap(infoMap);
00619 container->ClearAllText();
00620 container->SetText(infoMap);
00621 }
00622 }
00623 if (recList.count() == 0)
00624 container = theme->GetSet("norecordings_info");
00625
00626 if (container)
00627 {
00628 container->Draw(&tmp, 4, 0);
00629 container->Draw(&tmp, 5, 0);
00630 container->Draw(&tmp, 6, 0);
00631 container->Draw(&tmp, 7, 0);
00632 container->Draw(&tmp, 8, 0);
00633 }
00634
00635 tmp.end();
00636 p->drawPixmap(pr.topLeft(), pix);
00637 }
00638
00639 void ViewScheduleDiff::updateRecStatus(QPainter *p)
00640 {
00641 QRect pr = recStatusRect;
00642 QPixmap pix(pr.size());
00643 pix.fill(this, pr.topLeft());
00644 QPainter tmp(&pix);
00645 QMap<QString, QString> infoMap;
00646
00647 LayerSet *container = theme->GetSet("status_info");
00648 if (container)
00649 {
00650 ProgramInfo *p = CurrentProgram();
00651 if (p)
00652 {
00653 p->ToMap(infoMap);
00654 container->ClearAllText();
00655 container->SetText(infoMap);
00656 }
00657 }
00658
00659 if (container)
00660 {
00661 container->Draw(&tmp, 4, 0);
00662 container->Draw(&tmp, 5, 0);
00663 container->Draw(&tmp, 6, 0);
00664 container->Draw(&tmp, 7, 0);
00665 container->Draw(&tmp, 8, 0);
00666 }
00667
00668 tmp.end();
00669 p->drawPixmap(pr.topLeft(), pix);
00670 }
00671
00672 ProgramInfo *ViewScheduleDiff::CurrentProgram() {
00673 if (listPos >= recList.count())
00674 return NULL;
00675 ProgramStruct s = recList[listPos];
00676 if (s.after) return s.after;
00677 else return s.before;
00678 }