00001
00002 #include "mythuiguidegrid.h"
00003
00004
00005 #include <cmath>
00006
00007
00008 #include <algorithm>
00009 using namespace std;
00010
00011
00012 #include <QFile>
00013 #include <QDomElement>
00014
00015
00016 #include "mythfontproperties.h"
00017 #include "mythuihelper.h"
00018 #include "x11colors.h"
00019 #include "mythlogging.h"
00020 #include "mythimage.h"
00021 #include "mythuitype.h"
00022 #include "mythmainwindow.h"
00023 #include "mythdb.h"
00024
00025 #define LOC QString("MythUIGuideGrid: ")
00026
00027 MythUIGuideGrid::MythUIGuideGrid(MythUIType *parent, const QString &name)
00028 : MythUIType(parent, name)
00029 {
00030
00031 m_channelCount = 5;
00032 m_timeCount = 4;
00033 m_verticalLayout = false;
00034
00035 m_font = new MythFontProperties();
00036 m_justification = Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap;
00037 m_multilineText = true;
00038 m_cutdown = true;
00039
00040 m_selType = "box";
00041 m_drawSelLine = QPen(Qt::NoPen);
00042 m_drawSelFill = QBrush(Qt::NoBrush);
00043
00044 for (uint x = 0; x < RECSTATUSSIZE; x++)
00045 m_recImages[x] = NULL;
00046
00047 for (uint x = 0; x < ARROWIMAGESIZE; x++)
00048 m_arrowImages[x] = NULL;
00049
00050 m_fillType = Solid;
00051
00052 m_rowCount = 0;
00053 m_progPastCol = 0;
00054
00055 m_drawCategoryColors = true;
00056 m_drawCategoryText = true;
00057 m_categoryAlpha = 255;
00058
00059 QMap<QString, QString> catColors;
00060 parseDefaultCategoryColors(catColors);
00061 SetCategoryColors(catColors);
00062
00063 }
00064
00065 void MythUIGuideGrid::Finalize(void)
00066 {
00067 m_rowCount = m_channelCount;
00068
00069 allData = new QList<UIGTCon *>[m_rowCount];
00070
00071 MythUIType::Finalize();
00072 }
00073
00074 MythUIGuideGrid::~MythUIGuideGrid()
00075 {
00076 for (int i = 0; i < m_rowCount; i++)
00077 ResetRow(i);
00078
00079 delete [] allData;
00080
00081 delete m_font;
00082 m_font = NULL;
00083
00084 for (uint x = 0; x < RECSTATUSSIZE; x++)
00085 {
00086 if (m_recImages[x])
00087 m_recImages[x]->DownRef();
00088 }
00089
00090 for (uint x = 0; x < ARROWIMAGESIZE; x++)
00091 {
00092 if (m_arrowImages[x])
00093 m_arrowImages[x]->DownRef();
00094 }
00095 }
00096
00097 bool MythUIGuideGrid::ParseElement(
00098 const QString &filename, QDomElement &element, bool showWarnings)
00099 {
00100 if (element.tagName() == "layout")
00101 {
00102 QString layout = getFirstText(element).toLower();
00103 m_verticalLayout = (layout == "vertical");
00104 }
00105 else if (element.tagName() == "channels")
00106 {
00107 m_channelCount = getFirstText(element).toInt();
00108 m_channelCount = max(m_channelCount, 1);
00109 m_channelCount = min(m_channelCount, MAX_DISPLAY_CHANS);
00110 }
00111 else if (element.tagName() == "timeslots")
00112 {
00113 m_timeCount = getFirstText(element).toInt();
00114 m_timeCount = max(m_timeCount, 1);
00115 m_timeCount = min(m_timeCount, 8);
00116 }
00117 else if (element.tagName() == "solidcolor")
00118 {
00119 QString color = getFirstText(element);
00120 m_solidColor = QColor(color);
00121 }
00122 else if (element.tagName() == "selector")
00123 {
00124 m_selType = element.attribute("type");
00125 QString lineColor = element.attribute("linecolor", "");
00126 QString fillColor = element.attribute("fillcolor", "");
00127
00128 if (!lineColor.isEmpty())
00129 {
00130 m_drawSelLine = QPen(QColor(lineColor));
00131 m_drawSelLine.setWidth(2);
00132 }
00133 else
00134 {
00135 m_drawSelLine = QPen(Qt::NoPen);
00136 }
00137
00138 if (!fillColor.isEmpty())
00139 {
00140 m_drawSelFill = QBrush(QColor(fillColor));
00141 }
00142 else
00143 {
00144 m_drawSelFill = QBrush(Qt::NoBrush);
00145 }
00146 }
00147 else if (element.tagName() == "recordingcolor")
00148 {
00149 QString color = getFirstText(element);
00150 m_recordingColor = QColor(color);
00151 }
00152 else if (element.tagName() == "conflictingcolor")
00153 {
00154 QString color = getFirstText(element);
00155 m_conflictingColor = QColor(color);
00156 }
00157 else if (element.tagName() == "categoryalpha")
00158 {
00159 m_categoryAlpha = getFirstText(element).toInt();
00160 m_categoryAlpha = max(m_categoryAlpha, 1);
00161 m_categoryAlpha = min(m_categoryAlpha, 255);
00162 }
00163 else if (element.tagName() == "showcategories")
00164 {
00165 m_drawCategoryText = parseBool(element);
00166 }
00167 else if (element.tagName() == "showcategorycolors")
00168 {
00169 m_drawCategoryColors = parseBool(element);
00170 }
00171 else if (element.tagName() == "cutdown")
00172 {
00173 m_cutdown = parseBool(element);
00174 }
00175 else if (element.tagName() == "multiline")
00176 {
00177 SetMultiLine(parseBool(element));
00178 }
00179 else if (element.tagName() == "textoffset")
00180 {
00181 m_textOffset = parsePoint(element);
00182 }
00183 else if (element.tagName() == "font")
00184 {
00185 QString fontname = getFirstText(element);
00186 MythFontProperties *font = GetFont(fontname);
00187
00188 if (!font)
00189 font = GetGlobalFontMap()->GetFont(fontname);
00190
00191 if (font)
00192 {
00193 MythFontProperties fontcopy = *font;
00194 int screenHeight = GetMythMainWindow()->GetUIScreenRect().height();
00195 fontcopy.Rescale(screenHeight);
00196 int fontStretch = GetMythUI()->GetFontStretch();
00197 fontcopy.AdjustStretch(fontStretch);
00198 *m_font = fontcopy;
00199 }
00200 else
00201 LOG(VB_GENERAL, LOG_ERR, LOC + "Unknown font: " + fontname);
00202 }
00203 else if (element.tagName() == "recordstatus")
00204 {
00205 int inttype = 0;
00206 QString typ = element.attribute("type");
00207 QString img = element.attribute("image");
00208
00209 if (typ == "SingleRecord")
00210 inttype = 1;
00211 else if (typ == "TimeslotRecord")
00212 inttype = 2;
00213 else if (typ == "ChannelRecord")
00214 inttype = 3;
00215 else if (typ == "AllRecord")
00216 inttype = 4;
00217 else if (typ == "WeekslotRecord")
00218 inttype = 5;
00219 else if (typ == "FindOneRecord")
00220 inttype = 6;
00221 else if (typ == "OverrideRecord")
00222 inttype = 7;
00223
00224 LoadImage(inttype, img);
00225 }
00226 else if (element.tagName() == "arrow")
00227 {
00228 QString dir = element.attribute("direction");
00229 QString image = element.attribute("image");
00230
00231 if (dir == "left")
00232 SetArrow(0, image);
00233 else if (dir == "right")
00234 SetArrow(1, image);
00235 else if (dir == "up")
00236 SetArrow(2, image);
00237 else if (dir == "down")
00238 SetArrow(3, image);
00239 }
00240 else
00241 {
00242 return MythUIType::ParseElement(filename, element, showWarnings);
00243 }
00244
00245 return true;
00246 }
00247
00248 void MythUIGuideGrid::CopyFrom(MythUIType *base)
00249 {
00250 MythUIGuideGrid *gg = dynamic_cast<MythUIGuideGrid *>(base);
00251
00252 if (!gg)
00253 {
00254 LOG(VB_GENERAL, LOG_ERR, LOC + "bad parsing");
00255 return;
00256 }
00257
00258 m_channelCount = gg->m_channelCount;
00259 m_timeCount = gg->m_timeCount;
00260 m_verticalLayout = gg->m_verticalLayout;
00261 m_categoryAlpha = gg->m_categoryAlpha;
00262 m_textOffset = gg->m_textOffset;
00263 m_justification = gg->m_justification;
00264 m_multilineText = gg->m_multilineText;
00265 *m_font = *gg->m_font;
00266 m_solidColor = gg->m_solidColor;
00267
00268 m_selType = gg->m_selType;
00269 m_drawSelLine = gg->m_drawSelLine;
00270 m_drawSelFill = gg->m_drawSelFill;
00271
00272 m_recordingColor = gg->m_recordingColor;
00273 m_conflictingColor = gg->m_conflictingColor;
00274
00275 m_fillType = gg->m_fillType;
00276 m_cutdown = gg->m_cutdown;
00277 m_drawCategoryColors = gg->m_drawCategoryColors;
00278 m_drawCategoryText = gg->m_drawCategoryText;
00279
00280 MythUIType::CopyFrom(base);
00281 }
00282
00283 void MythUIGuideGrid::CreateCopy(MythUIType *parent)
00284 {
00285 MythUIGuideGrid *gg = new MythUIGuideGrid(parent, objectName());
00286 gg->CopyFrom(this);
00287 }
00288
00289 QColor MythUIGuideGrid::calcColor(const QColor &color, int alphaMod)
00290 {
00291 QColor newColor(color);
00292 newColor.setAlpha((int)(color.alpha() *(alphaMod / 255.0)));
00293 return newColor;
00294 }
00295
00296 void MythUIGuideGrid::DrawSelf(MythPainter *p, int xoffset, int yoffset,
00297 int alphaMod, QRect clipRect)
00298 {
00299 for (int i = 0; i < m_rowCount; i++)
00300 {
00301 QList<UIGTCon *>::iterator it = allData[i].begin();
00302
00303 for (; it != allData[i].end(); ++it)
00304 {
00305 UIGTCon *data = *it;
00306
00307 if (data->recStat == 0)
00308 drawBackground(p, data, alphaMod);
00309 else if (data->recStat == 1)
00310 drawBox(p, data, m_recordingColor, alphaMod);
00311 else
00312 drawBox(p, data, m_conflictingColor, alphaMod);
00313 }
00314 }
00315
00316 drawCurrent(p, &selectedItem, alphaMod);
00317
00318 for (int i = 0; i < m_rowCount; i++)
00319 {
00320 QList<UIGTCon *>::iterator it = allData[i].begin();
00321
00322 for (; it != allData[i].end(); ++it)
00323 {
00324 UIGTCon *data = *it;
00325 drawText(p, data, alphaMod);
00326
00327 if (data->recType != 0 || data->arrow != 0)
00328 drawRecType(p, data, alphaMod);
00329 }
00330 }
00331 }
00332
00333 void MythUIGuideGrid::drawCurrent(MythPainter *p, UIGTCon *data, int alphaMod)
00334 {
00335 int breakin = 2;
00336 QRect area = data->drawArea;
00337 area.translate(m_Area.x(), m_Area.y());
00338 area.adjust(breakin, breakin, -breakin, -breakin);
00339 int status = data->recStat;
00340
00341 if (m_selType == "roundbox")
00342 {
00343 QPen pen = m_drawSelLine;
00344
00345 if (status == 1)
00346 pen.setColor(m_recordingColor);
00347 else if (status == 2)
00348 pen.setColor(m_conflictingColor);
00349
00350 p->DrawRoundRect(area, 10, m_drawSelFill, pen, alphaMod);
00351 }
00352 else if (m_selType == "highlight")
00353 {
00354 QBrush brush = m_drawSelFill;
00355 QPen pen = m_drawSelLine;
00356
00357 if (m_drawCategoryColors && data->categoryColor.isValid())
00358 brush.setColor(calcColor(data->categoryColor, m_categoryAlpha));
00359 else
00360 brush.setColor(calcColor(m_solidColor, m_categoryAlpha));
00361
00362 if (status == 1)
00363 pen.setColor(m_recordingColor);
00364 else if (status == 2)
00365 pen.setColor(m_conflictingColor);
00366
00367 brush.setColor(brush.color().lighter());
00368 p->DrawRect(area, brush, pen, alphaMod);
00369 }
00370 else
00371 {
00372
00373 QPen pen = m_drawSelLine;
00374
00375 if (status == 1)
00376 pen.setColor(m_recordingColor);
00377 else if (status == 2)
00378 pen.setColor(m_conflictingColor);
00379
00380 p->DrawRect(area, m_drawSelFill, pen, alphaMod);
00381 }
00382 }
00383
00384 void MythUIGuideGrid::drawRecType(MythPainter *p, UIGTCon *data, int alphaMod)
00385 {
00386 int breakin = 1;
00387 QRect area = data->drawArea;
00388 area.translate(m_Area.x(), m_Area.y());
00389 area.adjust(breakin, breakin, -breakin, -breakin);
00390
00391
00392 if (data->arrow != 0)
00393 {
00394 if (data->arrow == 1 || data->arrow == 3)
00395 {
00396 if (m_verticalLayout)
00397 {
00398 if (m_arrowImages[2])
00399 p->DrawImage(area.left() + (area.width() / 2) - (m_arrowImages[2]->width() / 2),
00400 area.top() , m_arrowImages[2], alphaMod);
00401 }
00402 else
00403 {
00404 if (m_arrowImages[0])
00405 p->DrawImage(area.left(), area.top() + (area.height() / 2) -
00406 (m_arrowImages[0]->height() / 2), m_arrowImages[0], alphaMod);
00407 }
00408 }
00409
00410 if (data->arrow == 2 || data->arrow == 3)
00411 {
00412 if (m_verticalLayout)
00413 {
00414 if (m_arrowImages[3])
00415 p->DrawImage(area.left() + (area.width() / 2) - (m_arrowImages[3]->width() / 2),
00416 area.top() + area.height() - m_arrowImages[3]->height(), m_arrowImages[3], alphaMod);
00417 }
00418 else
00419 {
00420 if (m_arrowImages[1])
00421 p->DrawImage(area.right() - m_arrowImages[1]->width(),
00422 area.top() + (area.height() / 2) -
00423 (m_arrowImages[1]->height() / 2), m_arrowImages[1], alphaMod);
00424 }
00425 }
00426 }
00427
00428
00429 if (data->recType != 0 && m_recImages[data->recType])
00430 {
00431 p->DrawImage(area.right() - m_recImages[data->recType]->width(),
00432 area.bottom() - m_recImages[data->recType]->height(),
00433 m_recImages[data->recType], alphaMod);
00434 }
00435 }
00436
00437 void MythUIGuideGrid::drawBox(MythPainter *p, UIGTCon *data, const QColor &color, int alphaMod)
00438 {
00439 int breakin = 1;
00440 QRect area = data->drawArea;
00441 area.translate(m_Area.x(), m_Area.y());
00442 area.adjust(breakin, breakin, -breakin, -breakin);
00443
00444 static const QPen nopen(Qt::NoPen);
00445 p->DrawRect(area, QBrush(calcColor(color, m_categoryAlpha)), nopen, alphaMod);
00446 }
00447
00448 void MythUIGuideGrid::drawBackground(MythPainter *p, UIGTCon *data, int alphaMod)
00449 {
00450 QColor overColor;
00451 QRect overArea;
00452
00453 int breakin = 1;
00454 QRect area = data->drawArea;
00455 area.translate(m_Area.x(), m_Area.y());
00456 QColor fillColor;
00457
00458 if (m_drawCategoryColors && data->categoryColor.isValid())
00459 fillColor = calcColor(data->categoryColor, m_categoryAlpha);
00460 else
00461 fillColor = calcColor(m_solidColor, m_categoryAlpha);
00462
00463 if (m_verticalLayout)
00464 {
00465 if (m_progPastCol && area.top() < m_progPastCol)
00466 {
00467 if (area.bottom() < m_progPastCol)
00468 {
00469 fillColor = fillColor.dark();
00470 area.adjust(breakin, breakin, -breakin, -breakin);
00471 }
00472 else
00473 {
00474 overColor = fillColor.dark();
00475 int first = m_progPastCol - area.top();
00476 int second = area.height() - first;
00477 overArea = area;
00478 overArea.setHeight(first);
00479 area.translate(0, first);
00480 area.setHeight(second);
00481
00482 area.adjust(0, -breakin, -breakin, -breakin);
00483 overArea.adjust(0, breakin, -breakin, -breakin);
00484 }
00485 }
00486 else
00487 area.adjust(breakin, breakin, -breakin, -breakin);
00488 }
00489 else
00490 {
00491 if (m_progPastCol && area.left() < m_progPastCol)
00492 {
00493 if (area.right() < m_progPastCol)
00494 {
00495 fillColor = fillColor.dark();
00496 area.adjust(breakin, breakin, -breakin, -breakin);
00497 }
00498 else
00499 {
00500 overColor = fillColor.dark();
00501 int first = m_progPastCol - area.left();
00502 int second = area.width() - first;
00503 overArea = area;
00504 overArea.setWidth(first);
00505 area.translate(first, 0);
00506 area.setWidth(second);
00507
00508 area.adjust(0, breakin, -breakin, -breakin);
00509 overArea.adjust(breakin, breakin, 0, -breakin);
00510 }
00511 }
00512 else
00513 area.adjust(breakin, breakin, -breakin, -breakin);
00514 }
00515
00516 if (area.width() <= 1)
00517 area.setWidth(2);
00518
00519 if (area.height() <= 1)
00520 area.setHeight(2);
00521
00522 static const QPen nopen(Qt::NoPen);
00523 p->DrawRect(area, QBrush(fillColor), nopen, alphaMod);
00524
00525 if (overArea.width() > 0)
00526 p->DrawRect(overArea, QBrush(overColor), nopen, alphaMod);
00527 }
00528
00529 void MythUIGuideGrid::drawText(MythPainter *p, UIGTCon *data, int alphaMod)
00530 {
00531 QString msg = data->title;
00532
00533 if (m_drawCategoryText && !data->category.isEmpty())
00534 msg += QString(" (%1)").arg(data->category);
00535
00536 QRect area = data->drawArea;
00537 area.translate(m_Area.x(), m_Area.y());
00538 area.adjust(m_textOffset.x(), m_textOffset.y(),
00539 -m_textOffset.x(), -m_textOffset.y());
00540
00541 if (m_verticalLayout)
00542 {
00543 if ((data->arrow == 1 || data->arrow == 3) && m_arrowImages[2])
00544 area.setTop(area.top() + m_arrowImages[2]->height());
00545
00546 if ((data->arrow == 2 || data->arrow == 3) && m_arrowImages[3])
00547 area.setBottom(area.bottom() - m_arrowImages[3]->height());
00548 }
00549 else
00550 {
00551 if ((data->arrow == 1 || data->arrow == 3) && m_arrowImages[0])
00552 area.setLeft(area.left() + m_arrowImages[0]->width());
00553
00554 if ((data->arrow == 2 || data->arrow == 3) && m_arrowImages[1])
00555 area.setRight(area.right() - m_arrowImages[1]->width());
00556 }
00557
00558 if (area.width() <= 0 || area.height() <= 0)
00559 return;
00560
00561 p->DrawText(area, msg, m_justification, *m_font, alphaMod, area);
00562 }
00563
00564 void MythUIGuideGrid::SetProgramInfo(int row, int col, const QRect &area,
00565 const QString &title, const QString &genre,
00566 int arrow, int recType, int recStat,
00567 bool selected)
00568 {
00569 (void)col;
00570 UIGTCon *data = new UIGTCon(area, title, genre, arrow, recType, recStat);
00571 allData[row].append(data);
00572
00573 if (m_drawCategoryColors)
00574 {
00575 data->categoryColor = categoryColors[data->category.toLower()];
00576
00577 if (!data->categoryColor.isValid())
00578 data->categoryColor = categoryColors["none"];
00579 }
00580
00581 if (selected)
00582 selectedItem = *data;
00583 }
00584
00585 bool MythUIGuideGrid::parseDefaultCategoryColors(QMap<QString, QString> &catColors)
00586 {
00587 QFile f;
00588 QStringList searchpath = GetMythUI()->GetThemeSearchPath();
00589
00590 for (QStringList::const_iterator ii = searchpath.begin();
00591 ii != searchpath.end(); ++ii)
00592 {
00593 f.setFileName(*ii + "categories.xml");
00594
00595 if (f.open(QIODevice::ReadOnly))
00596 break;
00597 }
00598
00599 if (f.handle() == -1)
00600 {
00601 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unable to open '%1'")
00602 .arg(f.fileName()));
00603 return false;
00604 }
00605
00606 QDomDocument doc;
00607 QString errorMsg;
00608 int errorLine = 0;
00609 int errorColumn = 0;
00610
00611 if (!doc.setContent(&f, false, &errorMsg, &errorLine, &errorColumn))
00612 {
00613 LOG(VB_GENERAL, LOG_ERR, LOC +
00614 QString("Parsing colors: %1 at line: %2 column: %3")
00615 .arg(f.fileName()).arg(errorLine).arg(errorColumn) +
00616 QString("\n\t\t\t%1").arg(errorMsg));
00617 f.close();
00618 return false;
00619 }
00620
00621 f.close();
00622
00623 QDomElement element = doc.documentElement();
00624
00625 for (QDomNode child = element.firstChild(); !child.isNull();
00626 child = child.nextSibling())
00627 {
00628 QDomElement info = child.toElement();
00629
00630 if (!info.isNull() && info.tagName() == "catcolor")
00631 {
00632 QString cat = info.attribute("category");
00633 QString col = info.attribute("color");
00634
00635 catColors[cat.toLower()] = col;
00636 }
00637 }
00638
00639 return true;
00640 }
00641
00642 void MythUIGuideGrid::SetCategoryColors(const QMap<QString, QString> &catC)
00643 {
00644 for (QMap<QString, QString>::const_iterator it = catC.begin();
00645 it != catC.end(); ++it)
00646 {
00647 categoryColors[it.key()] = createColor(*it);
00648 }
00649 }
00650
00651 void MythUIGuideGrid::LoadImage(int recType, const QString &file)
00652 {
00653 QString themeDir = GetMythUI()->GetThemeDir();
00654 QString filename = themeDir + file;
00655
00656 QPixmap *pix = GetMythUI()->LoadScalePixmap(filename);
00657
00658 if (pix)
00659 {
00660 m_recImages[recType] = GetPainter()->GetFormatImage();
00661 m_recImages[recType]->Assign(*pix);
00662 delete pix;
00663 }
00664 }
00665
00666 void MythUIGuideGrid::SetArrow(int direction, const QString &file)
00667 {
00668 QString themeDir = GetMythUI()->GetThemeDir();
00669 QString filename = themeDir + file;
00670
00671 QPixmap *pix = GetMythUI()->LoadScalePixmap(filename);
00672
00673 if (pix)
00674 {
00675 m_arrowImages[direction] = GetPainter()->GetFormatImage();
00676 m_arrowImages[direction]->Assign(*pix);
00677 delete pix;
00678 }
00679 }
00680
00681 void MythUIGuideGrid::ResetData(void)
00682 {
00683 for (int i = 0; i < m_rowCount; i++)
00684 ResetRow(i);
00685 }
00686
00687 void MythUIGuideGrid::ResetRow(int row)
00688 {
00689 while (!allData[row].empty())
00690 {
00691 delete allData[row].back();
00692 allData[row].pop_back();
00693 }
00694 }
00695
00696 void MythUIGuideGrid::SetProgPast(int ppast)
00697 {
00698 if (m_verticalLayout)
00699 m_progPastCol = m_Area.y() + (m_Area.height() * ppast / 100);
00700 else
00701 m_progPastCol = m_Area.x() + (m_Area.width() * ppast / 100);
00702
00703 SetRedraw();
00704 }
00705
00706 void MythUIGuideGrid::SetMultiLine(bool multiline)
00707 {
00708 m_multilineText = multiline;
00709
00710 if (m_multilineText)
00711 m_justification |= Qt::TextWordWrap;
00712 else
00713 m_justification &= ~Qt::TextWordWrap;
00714 }