2 Copyright 2004-2020 Chris Tallon, 2014 Marten Richter
4 This file is part of VOMP.
6 VOMP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 VOMP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with VOMP. If not, see <https://www.gnu.org/licenses/>.
30 #include "messagequeue.h"
32 #include "movieinfo.h"
33 #include "seriesinfo.h"
36 #include "vepgsummary.h"
37 #include "vepgsettimer.h"
39 #include "staticartwork.h"
41 #include "imageloader.h"
43 #include "vepglistadvanced.h"
45 static const char* TAG = "VEpgListAdvanced";
47 VEpgListAdvanced::VEpgListAdvanced(MessageReceiver* tvideolive, std::shared_ptr<ChannelList> tchanList, u4 initialChannelNumber)
49 channelNumber = initialChannelNumber;
51 videolive = tvideolive;
52 boxstack = BoxStack::getInstance();
56 setSize(640 + 40, 500 + 40); //old setSize(570, 420);
62 setTitleBarColour(DrawStyle::TITLEBARBACKGROUND);
63 Image icon = ImageLoader::getInstance()->createChannelLogo(channelNumber, sa_tv);
64 setTitleBarIcon(icon);
66 sl.setPosition(10, 30 + 5);
67 sl.setSize(area.w * 42 / 100 - 20, area.h - 30 - 15 - 30);
68 sl.setLinesPerOption(2.4f);
71 Region slarea = sl.getRegionR();
73 epg.setParaMode(true);
74 epg.setPosition(slarea.x + slarea.w + 10, 30 + 5);
75 epg.setSize(area.w - slarea.x - slarea.w - 10, area.h - 30 - 15 - 30);
78 epg.setVideoBackground();
79 epg.setBackgroundColour(DrawStyle::VIEWTRANSPARENTBACKGROUND);
81 epgTVmedia.setPosition(epg.getRegionR().w - 100 - 10, 10);
82 epgTVmedia.setSize(100, static_cast<u4>(150 / Osd::getInstance()->getPixelAspect()));
85 boxRed.setBackgroundColour(DrawStyle::RED);
86 boxRed.setPosition(165 /*54*/, sl.getY2() + 8);
87 boxRed.setSize(18, 16);
90 textRed.setPosition(boxRed.getX2(), sl.getY2() + 4);
91 textRed.setSize(116, 30);
95 boxGreen.setBackgroundColour(DrawStyle::GREEN);
96 boxGreen.setPosition(165 + 1 * 110, sl.getY2() + 8);
97 boxGreen.setSize(18, 16);
100 textGreen.setPosition(boxGreen.getX2(), sl.getY2() + 4);
101 textGreen.setSize(116, 30);
104 boxYellow.setBackgroundColour(DrawStyle::YELLOW);
105 boxYellow.setPosition(165 + 2 * 110, sl.getY2() + 8);
106 boxYellow.setSize(18, 16);
109 textYellow.setPosition(boxYellow.getX2(), sl.getY2() + 4);
110 textYellow.setSize(116, 30);
113 boxBlue.setBackgroundColour(DrawStyle::BLUE);
114 boxBlue.setPosition(165 + 3 * 110, sl.getY2() + 8);
115 boxBlue.setSize(18, 16);
118 textBlue.setPosition(boxBlue.getX2(), sl.getY2() + 4);
119 textBlue.setSize(116, 30);
124 updateEpgDataChannel();
126 MessageQueue::getInstance()->addReceiver(this);
129 VEpgListAdvanced::~VEpgListAdvanced()
131 MessageQueue::getInstance()->removeReceiver(this);
135 void VEpgListAdvanced::setButtonText()
141 textRed.setText(tr("Record"));
142 textGreen.setText(tr("Now"));
143 textYellow.setText(tr("Next"));
144 textBlue.setText(tr("Guide"));
149 textRed.setText(tr("Record"));
150 textGreen.setText(tr("Next"));
151 textYellow.setText(tr("Schedule"));
152 textBlue.setText(tr("Switch"));
157 textRed.setText(tr("Record"));
158 textGreen.setText(tr("Now"));
159 textYellow.setText(tr("Schedule"));
160 textBlue.setText(tr("Switch"));
166 void VEpgListAdvanced::doRed()
171 void VEpgListAdvanced::doGreen()
189 void VEpgListAdvanced::doYellow()
207 void VEpgListAdvanced::doBlue()
225 void VEpgListAdvanced::doNext()
227 LogNT::getInstance()->debug(TAG, "doNext");
228 u4 slCurrentOption = reinterpret_cast<u4>(sl.getCurrentOptionData());
230 if (mode != OneChannel)
232 Channel* chan = (*chanList)[slCurrentOption];
233 channelNumber = chan->number;
237 updateEpgDataNowNext(true);
239 Image icon = ImageLoader::getInstance()->createStatic(sa_tv);
240 setTitleBarIcon(icon);
242 boxstack->update(this);
245 void VEpgListAdvanced::doNow()
247 LogNT::getInstance()->debug(TAG, "doNow");
248 u4 slCurrentOption = reinterpret_cast<u4>(sl.getCurrentOptionData());
250 if (mode != OneChannel)
252 Channel* chan = (*chanList)[slCurrentOption];
253 channelNumber = chan->number;
257 updateEpgDataNowNext(true);
259 Image icon = ImageLoader::getInstance()->createStatic(sa_tv);
260 setTitleBarIcon(icon);
262 boxstack->update(this);
265 void VEpgListAdvanced::doProgramm()
267 LogNT::getInstance()->debug(TAG, "doProgram");
269 u4 slCurrentOption = reinterpret_cast<u4>(sl.getCurrentOptionData());
270 Channel* chan = (*chanList)[slCurrentOption];
271 channelNumber = chan->number;
272 updateEpgDataChannel();
274 Image icon = ImageLoader::getInstance()->createChannelLogo(channelNumber, sa_tv);
275 setTitleBarIcon(icon);
277 boxstack->update(this);
280 void VEpgListAdvanced::doSwitch()
284 if (mode != OneChannel)
286 u4 slCurrentOption = reinterpret_cast<u4>(sl.getCurrentOptionData());
287 Channel* chan = (*chanList)[slCurrentOption];
288 channelNumber = chan->number;
291 LogNT::getInstance()->debug(TAG, "doSwitch {}", channelNumber);
292 Message* m = new Message(); // Must be done after this view deleted
295 m->message = Message::CHANNEL_CHANGE;
296 m->parameter = channelNumber;
298 MessageQueue::getInstance()->postMessage(m);
304 #pragma warning(disable : 4703)
307 void VEpgListAdvanced::doRecord()
310 Event* current = getCurrentOptionEvent(channel);
314 LogNT::getInstance()->debug(TAG, "Found the option you pointed at. {} {}", current->title, current->id);
315 unsigned int chanlistsize = chanList->size();
319 for (listIndex = 0; listIndex < chanlistsize; listIndex++)
321 chan = (*chanList)[listIndex];
323 if (chan->number == channel) break;
326 LogNT::getInstance()->debug(TAG, "ID {} TIME {} DURATION {} TITLE {}",
327 current->id, current->time, current->duration, current->title);
328 VEpgSetTimer* vs = new VEpgSetTimer(current, chan);
331 boxstack->update(vs);
335 void VEpgListAdvanced::doGrid()
337 if (mode != OneChannel)
339 u4 slCurrentOption = reinterpret_cast<u4>(sl.getCurrentOptionData());
340 Channel* chan = (*chanList)[slCurrentOption];
341 channelNumber = chan->number;
345 unsigned int chanlistsize = chanList->size();
348 for (listIndex = 0; listIndex < chanlistsize; listIndex++)
350 chan = (*chanList)[listIndex];
351 if (chan->number == channelNumber) break;
354 VEpg* vepg = new VEpg(videolive, listIndex, chanList);
357 boxstack->update(vepg);
360 void VEpgListAdvanced::clearEventList()
362 std::vector<EventList*>::iterator itty = eventLista.begin();
364 while (itty != eventLista.end())
380 * if (!chanList) return;
382 for(u4 listIndex = 0; listIndex < gridRows; listIndex++)
384 if(listTop + listIndex >= u4(chanListbox.getBottomOption()))
386 chan = (*chanList)[listTop + listIndex];
387 eventLista[listIndex] = VDR::getInstance()->getChannelSchedule(chan->number, ltime - 1, window_width * 60 + 2); // ltime - 1 to get prog before window (allows cursor left past ltime). + 2 to get prog after window
392 void VEpgListAdvanced::updateEpgData()
398 //updateEpgDataChannel();
404 updateEpgDataNowNext(false);
410 void VEpgListAdvanced::updateEpgDataNowNext(bool changeState)
415 u4 chanlistsize = chanList->size();
420 eventLista.resize(chanList->size());
423 for (u4 listIndex = 0; listIndex < chanlistsize; listIndex++)
425 if (listIndex >= chanlistsize) continue;
427 chan = (*chanList)[listIndex];
429 if (chan->number == channelNumber)
431 startupdate = listIndex - sl.getNumOptionsDisplayable() - 2;
432 endupdate = listIndex + sl.getNumOptionsDisplayable() + 2;
439 startupdate = sl.getTopOption() - 2;
440 endupdate = sl.getBottomOption() + 1;
448 for (int listIndex = startupdate; listIndex < endupdate; listIndex++)
450 if (listIndex < 0) continue;
451 if (listIndex >= toi4(chanlistsize)) continue;
453 chan = (*chanList)[listIndex];
455 if (!eventLista[listIndex]) eventLista[listIndex] = VDR::getInstance()->getChannelSchedule(chan->number, now, 4 * 60 * 60);
459 void VEpgListAdvanced::updateEpgDataChannel()
462 eventLista.resize(1);
465 eventLista[0] = VDR::getInstance()->getChannelSchedule(channelNumber, now, 24 * 60 * 60 * 30); // one month
466 LogNT::getInstance()->debug(TAG, "Eventlist {:#x} {}", static_cast<void*>(eventLista[0]), channelNumber);
469 void VEpgListAdvanced::drawData(bool doIndexPop)
471 if (mode == OneChannel) drawDataChannel(doIndexPop);
472 else if (mode == Next) drawDataNowNext(true, doIndexPop);
473 else if (mode == Now) drawDataNowNext(false, doIndexPop);
476 void VEpgListAdvanced::drawDataChannel(bool doIndexPop)
478 int saveIndex = sl.getCurrentOption();
479 int saveTop = sl.getTopOption();
483 sl.addColumn(25 + 7);
484 sl.addColumn(25 + 7 + 7);
491 char startTimeStr[16];
494 Event* currentEvent = NULL;
495 EventList::iterator j;
496 EventList* eventList = eventLista[0];
500 for (j = eventList->begin(); j != eventList->end(); j++)
503 time_t eventStartTime = static_cast<time_t>(currentEvent->time);
504 time_t eventEndTime = static_cast<time_t>(currentEvent->time + currentEvent->duration);
506 LOCALTIME_R(&eventStartTime, &btime);
507 strftime(startTimeStr, 16, "%d/%m/%y %H:%M", &btime);
508 LOCALTIME_R(&eventEndTime, &btime);
509 strftime(endTimeStr, 16, "%H:%M", &btime);
510 std::string eventText = fmt::format("\t {}\n \t \t{} - {}", currentEvent->title, startTimeStr, endTimeStr);
512 Image icon = ImageLoader::getInstance()->createEventThumb(channelNumber, currentEvent->id);
513 currentEvent->index = sl.addOption(eventText, reinterpret_cast<void*>(currentEvent->id), first, icon);
520 sl.hintSetCurrent(0);
524 sl.hintSetCurrent(saveIndex);
525 sl.hintSetTop(saveTop);
531 void VEpgListAdvanced::drawDataNowNext(bool next, bool doIndexPop)
533 int saveIndex = sl.getCurrentOption();
534 int saveTop = sl.getTopOption();
542 char startTimeStr[16];
546 Event* currentEvent = NULL;
547 EventList::iterator j;
550 if (next) minevents++;
552 int setcurrenthelper = 0;
554 unsigned int chanlistsize = chanList->size();
556 std::string eventText;
558 for (u4 listIndex = 0; listIndex < chanlistsize; listIndex++)
561 chan = (*chanList)[listIndex];
563 EventList* eventList = eventLista[listIndex];
565 if (eventList && (eventList->size() >= minevents))
567 j = eventList->begin();
569 currentEvent = j[minevents - 1];
570 time_t eventStartTime = static_cast<time_t>(currentEvent->time);
571 time_t eventEndTime = static_cast<time_t>(currentEvent->time + currentEvent->duration);
573 LOCALTIME_R(&eventStartTime, &btime);
574 strftime(startTimeStr, 16, "%H:%M", &btime);
575 LOCALTIME_R(&eventEndTime, &btime);
576 strftime(endTimeStr, 16, "%H:%M", &btime);
578 eventText = fmt::format("{}\n{}\t{} - {}", currentEvent->title, chan->name, startTimeStr, endTimeStr);
582 eventText = fmt::format("\n{}", chan->name);
586 if ((*chanList)[listIndex]->number == channelNumber)
589 setcurrenthelper = listIndex;
592 Image icon = ImageLoader::getInstance()->createChannelLogo((*chanList)[listIndex]->number, sa_tv);
593 int index = sl.addOption(eventText, reinterpret_cast<void*>(listIndex), first, icon);
595 if (currentEvent) currentEvent->index = index;
602 sl.hintSetCurrent(setcurrenthelper);
606 sl.hintSetCurrent(saveIndex);
607 sl.hintSetTop(saveTop);
613 void VEpgListAdvanced::draw(bool doIndexPop)
615 // Single channel mode
621 unsigned int chanlistsize = chanList->size();
625 for (listIndex = 0; listIndex < chanlistsize; listIndex++)
627 chan = (*chanList)[listIndex];
629 if (chan->number == channelNumber) break;
632 sprintf(tempA, tr("Schedule - %s"), (*chanList)[listIndex]->name.c_str());
638 setTitleText(tr("Now"));
643 setTitleText(tr("Next"));
648 drawData(doIndexPop);
658 LOCALTIME_R(&now, &btime);
659 strftime(freeSpace, 299, "%d/%m/%y", &btime);
660 drawTextRJ(freeSpace, getWidth(), 5, DrawStyle::LIGHTTEXT);
667 w.nextSymbol = WSymbol::UP;
668 w.setPosition(20, area.h - 35);
671 w.nextSymbol = WSymbol::DOWN;
672 w.setPosition(50, area.h - 35);
675 w.nextSymbol = WSymbol::SKIPBACK;
676 w.setPosition(85, area.h - 35);
679 w.nextSymbol = WSymbol::SKIPFORWARD;
680 w.setPosition(115, area.h - 35);
683 drawTextRJ(tr("[ok] = info"), 560 + 70, sl.getY2() + 4, DrawStyle::LIGHTTEXT);
685 // All static stuff done
688 Event* VEpgListAdvanced::getCurrentOptionEvent(u4& channel)
690 if (mode == OneChannel)
692 channel = channelNumber;
694 EventList* eventList = eventLista[0];
695 if (!eventList) return NULL;
697 for (Event* currentEvent : *eventList)
699 if (currentEvent->index == sl.getCurrentOption()) return currentEvent;
702 else if ((mode == Now) || (mode == Next))
704 u4 slCurrentOptionData = reinterpret_cast<u4>(sl.getCurrentOptionData());
705 EventList* eventList = eventLista[slCurrentOptionData];
706 if (!eventList) return NULL;
707 channel = (*chanList)[slCurrentOptionData]->number;
709 if ((mode == Now) && (eventList->size() > 0)) return (*eventList)[0];
710 else if ((mode == Next) && (eventList->size() > 1)) return (*eventList)[1];
716 void VEpgListAdvanced::updateSelection()
720 if (mode == OneChannel)
722 Image icon = ImageLoader::getInstance()->createChannelLogo(channelNumber, sa_tv);
723 setTitleBarIcon(icon);
726 Event* toShow = getCurrentOptionEvent(channel);
730 toShow->loadinfos(channel);
731 std::stringstream description;
733 description << "\n" << toShow->title << "\n\n";
734 description << toShow->subtitle << "\n";
735 description << toShow->description;
737 TVMediaStruct poster;
740 if (toShow->movieInfo)
742 poster = toShow->movieInfo->poster;
745 if (toShow->seriesInfo)
747 if (toShow->seriesInfo->seasonposter.height)
749 poster = toShow->seriesInfo->seasonposter;
751 else if (toShow->seriesInfo->posters.size())
753 poster = toShow->seriesInfo->posters[0];
759 epgTVmedia.setImage(poster.image, WTVMedia::ZoomHorizontal);
760 epgTVmedia.setVisible(true);
764 if (toShow->epgImage)
766 Image icon = ImageLoader::getInstance()->createEventThumb(channel, toShow->id);
767 epgTVmedia.setImage(icon, WTVMedia::ZoomHorizontal);
768 epgTVmedia.setVisible(true);
770 else if (mode != OneChannel)
772 Image icon = ImageLoader::getInstance()->createChannelLogo(channel);
773 epgTVmedia.setImage(icon, WTVMedia::ZoomHorizontal);
774 epgTVmedia.setVisible(true);
778 epgTVmedia.setVisible(false);
782 epg.setText(description.str().c_str());
788 if (mode != OneChannel)
790 Image icon = ImageLoader::getInstance()->createChannelLogo(channel);
791 epgTVmedia.setImage(icon, WTVMedia::ZoomHorizontal);
792 epgTVmedia.setVisible(true);
796 epgTVmedia.setVisible(false);
801 int VEpgListAdvanced::handleCommand(int command)
810 boxstack->update(this);
811 return BoxStack::COMMAND_HANDLED;
819 boxstack->update(this);
820 return BoxStack::COMMAND_HANDLED;
823 case Input::SKIPBACK:
828 boxstack->update(this);
829 return BoxStack::COMMAND_HANDLED;
832 case Input::SKIPFORWARD:
837 boxstack->update(this);
838 return BoxStack::COMMAND_HANDLED;
844 return BoxStack::COMMAND_HANDLED;
850 return BoxStack::COMMAND_HANDLED;
856 return BoxStack::COMMAND_HANDLED;
862 return BoxStack::COMMAND_HANDLED;
867 if (sl.getNumOptions() == 0) return 2;
870 Event* current = getCurrentOptionEvent(channel);
874 LogNT::getInstance()->debug(TAG, "Found the option you pointed at. {} {}", current->title.c_str(), current->id);
875 unsigned int chanlistsize = chanList->size();
879 for (listIndex = 0; listIndex < chanlistsize; listIndex++)
881 chan = (*chanList)[listIndex];
882 if (chan->number == channel) break;
885 VEpgSummary* vr = new VEpgSummary(current, (*chanList)[listIndex]);
888 boxstack->update(vr);
890 return BoxStack::COMMAND_HANDLED;
893 // should not get to here
894 return BoxStack::ABANDON_COMMAND;
899 return BoxStack::DELETE_ME;
903 // stop command getting to any more views
904 return BoxStack::ABANDON_COMMAND;
907 void VEpgListAdvanced::processMessage(Message* m)
909 LogNT::getInstance()->debug(TAG, "Got message value {}", m->message);
911 if (m->message == Message::MOUSE_MOVE)
913 if (sl.mouseMove(m->parameter - getScreenX(), m->tag - getScreenY()))
916 boxstack->update(this);
919 else if (m->message == Message::MOUSE_LBDOWN)
921 int x = m->parameter - getScreenX();
922 int y = m->tag - getScreenY();
923 if (sl.mouseLBDOWN(x, y))
925 Input::sendInputKey(Input::OK);
927 else if (boxRed.mouseLBDOWN(x, y))
929 boxstack->handleCommand(Input::RED);
931 else if (boxGreen.mouseLBDOWN(x, y))
933 boxstack->handleCommand(Input::GREEN); // FIXME FIXME FIXME all these green? is this right?
935 else if (boxYellow.mouseLBDOWN(x, y))
937 boxstack->handleCommand(Input::GREEN);
939 else if (boxBlue.mouseLBDOWN(x, y))
941 boxstack->handleCommand(Input::GREEN);
945 if (coordsOutsideBox(m))
947 Input::sendInputKey(Input::BACK);
953 void VEpgListAdvanced::quickUpdate() //only quick for plattform that need it!
957 #ifdef GRADIENT_DRAWING