]> git.vomp.tv Git - vompclient.git/blob - vtimerlist.cc
Rewrite timers class using std::thread/mutex/cond/chrono
[vompclient.git] / vtimerlist.cc
1 /*
2     Copyright 2004-2020 Chris Tallon
3
4     This file is part of VOMP.
5
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.
10
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.
15
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/>.
18 */
19
20 #include <stdio.h>
21 #include <string.h>
22 #include <cstdio>
23 #include <vector>
24
25 #include "message.h"
26 #include "input.h"
27 #include "wsymbol.h"
28 #include "colour.h"
29 #include "video.h"
30 #include "i18n.h"
31 #include "vtimeredit.h"
32 #include "command.h"
33 #include "boxstack.h"
34 #include "vinfo.h"
35 #include "log.h"
36 #include "staticartwork.h"
37
38 #include "vtimerlist.h"
39
40 VTimerList::VTimerList()
41 {
42   clockRegion.x = 420;
43   clockRegion.y = 0;
44   clockRegion.w = 150;
45   clockRegion.h = 30;
46
47   setSize(570, 420);
48   createBuffer();
49
50   indicatorsRegion.x = 6;
51   indicatorsRegion.y = 44;
52   indicatorsRegion.w = 18;
53   indicatorsRegion.h = 15 * (getFontHeight() + 1);
54
55   if (Video::getInstance()->getFormat() == Video::PAL)
56   {
57     setPosition(80, 70);
58   }
59   else
60   {
61     setPosition(70, 35);
62   }
63
64   setTitleBarOn(1);
65   setTitleText(tr("Timers"));
66   setTitleBarColour(DrawStyle::TITLEBARBACKGROUND);
67   TVMediaInfo *info= new TVMediaInfo();
68   info->setStaticArtwork(sa_timers);
69   setTitleBarIcon(info);
70
71   sl.setPosition(30, 30 + 5);
72   sl.setSize(area.w - 40, area.h - 30 - 15 - 30);
73   add(&sl);
74 }
75
76 void VTimerList::preDelete()
77 {
78   Timers::getInstance()->cancelTimer(this, 1);
79 }
80
81 VTimerList::~VTimerList()
82 {
83   if (recTimerList)
84   {
85     for (UINT i = 0; i < recTimerList->size(); i++)
86     {
87       delete (*recTimerList)[i];
88     }
89
90     recTimerList->clear();
91     delete recTimerList;
92   }
93 }
94
95 void VTimerList::draw()
96 {
97   // Draw statics
98
99   TBBoxx::draw();
100
101   WSymbol w;
102   TEMPADD(&w);
103
104   w.nextSymbol = WSymbol::UP;
105   w.setPosition(20, 385);
106   w.draw();
107
108   w.nextSymbol = WSymbol::DOWN;
109   w.setPosition(50, 385);
110   w.draw();
111
112   w.nextSymbol = WSymbol::SKIPBACK;
113   w.setPosition(85, 385);
114   w.draw();
115
116   w.nextSymbol = WSymbol::SKIPFORWARD;
117   w.setPosition(115, 385);
118   w.draw();
119
120   drawTextRJ("[ok] = edit", 560, 385, DrawStyle::LIGHTTEXT);
121
122   drawClock();
123   drawShowing();
124   drawIndicators();
125 }
126
127 bool VTimerList::load()
128 {
129   recTimerList = VDR::getInstance()->getRecTimersList();
130
131   if (!recTimerList) return false;
132
133   char strA[300];
134   char strB[300];
135
136   struct tm btime;
137
138   // FIXME all drawing stuff in this class and sl.clear somewhere?!
139
140   sl.addColumn(0);
141   sl.addColumn(110);
142
143   RecTimer* recTimer;
144   int first = 1;
145
146   for (UINT i = 0; i < recTimerList->size(); i++)
147   {
148     recTimer = (*recTimerList)[i];
149     time_t rectime = recTimer->startTime;
150     LOCALTIME_R(&rectime, &btime);
151     strftime(strA, 299, "%d/%m %H:%M ", &btime);
152     int check = SNPRINTF(strB, 300, "%s\t%s", strA, recTimer->getName());
153     if ((check < 0) || (check > 299)) Log::getInstance()->log("VTimerList", Log::ERR, "String too big");
154     sl.addOption(strB, recTimer, first);
155     first = 0;
156   }
157
158   return true;
159 }
160
161 void VTimerList::drawClock()
162 {
163   // Blank the area first
164 #ifndef GRADIENT_DRAWING
165   rectangle(area.w - 150, 0, 150, 30, titleBarColour);
166 #endif
167
168   char timeString[20];
169   time_t t;
170   time(&t);
171   struct tm tms;
172   LOCALTIME_R(&t, &tms);
173   strftime(timeString, 19, "%d/%m %H:%M:%S", &tms);
174   drawTextRJ(timeString, 560, 5, DrawStyle::LIGHTTEXT);
175
176   Timers::getInstance()->setTimerT(this, 1, t + 1);
177 }
178
179 void VTimerList::drawShowing()
180 {
181   int topOption = sl.getTopOption() + 1;
182   if (sl.getNumOptions() == 0) topOption = 0;
183 #ifndef GRADIENT_DRAWING
184   rectangle(220, 385, 180, 25, DrawStyle::VIEWBACKGROUND);
185 #endif
186   char showing[200];
187   sprintf(showing, tr("%i to %i of %i"), topOption, sl.getBottomOption(), sl.getNumOptions());
188   drawText(showing, 220, 385, DrawStyle::LIGHTTEXT);
189 }
190
191 void VTimerList::quickUpdate() { //only quick for plattform that need it!
192 #ifdef GRADIENT_DRAWING
193       draw();
194 #else
195       sl.draw();
196       drawShowing();
197       drawIndicators();
198 #endif
199 }
200
201 void VTimerList::drawIndicators()
202 {
203   int top = sl.getTopOption();
204   int bottom = sl.getBottomOption();
205   int yinc = getFontHeight() + 1;
206   RecTimer* recTimer;
207 #ifndef GRADIENT_DRAWING
208   rectangle(6, 44, 18, 15*yinc, DrawStyle::VIEWBACKGROUND);
209 #endif
210   // The indexes recorded from the wselectlist into the index member of the RecTimer
211   // Is the same as the position in the vector of RecTimers
212   // Because they are in order, they don't change order and wselectlist starts from 0 up consecutively
213
214   int ypos = 44;
215   for (int current = top; current < bottom; current++)
216   {
217     recTimer = (*recTimerList)[current];
218
219     if (recTimer->recording) // Flashing red square
220     {
221       if (flipflop)
222       {
223         rectangle(6, ypos, 18, 16, DrawStyle::RED);
224         drawText("R", 8, ypos-3, DrawStyle::LIGHTTEXT);
225       }
226     }
227     else if (recTimer->pending)
228     {
229       rectangle(6, ypos, 18, 16, DrawStyle::RED);
230       drawText("X", 8, ypos-3, DrawStyle::BLACK);
231     }
232     else if (recTimer->active == 0)
233     {
234       rectangle(6, ypos, 18, 16, DrawStyle::SELECTHIGHLIGHT);
235       drawText("X", 8, ypos-3, DrawStyle::BLACK);
236     }
237     else
238     {
239 //      if (flipflop) rectangle(6, ypos, 18, 16, DrawStyle::GREEN);
240     }
241
242     ypos += yinc;
243   }
244 }
245
246 void VTimerList::timercall(int /* clientReference */)
247 {
248         flipflop = !flipflop;
249 #ifndef GRADIENT_DRAWING
250   drawClock();
251   BoxStack::getInstance()->update(this, &clockRegion);
252
253   drawIndicators();
254   BoxStack::getInstance()->update(this, &indicatorsRegion);
255 #else
256   draw();
257   BoxStack::getInstance()->update(this);
258 #endif
259 }
260
261 int VTimerList::handleCommand(int command)
262 {
263   switch(command)
264   {
265     case Input::UP:
266     {
267       sl.up();
268       quickUpdate();
269       BoxStack::getInstance()->update(this);
270       return 2;
271     }
272     case Input::DOWN:
273     {
274       sl.down();
275       quickUpdate();
276       BoxStack::getInstance()->update(this);
277       return 2;
278     }
279     case Input::SKIPBACK:
280     {
281       sl.pageUp();
282       quickUpdate();
283       BoxStack::getInstance()->update(this);
284       return 2;
285     }
286     case Input::SKIPFORWARD:
287     {
288       sl.pageDown();
289       quickUpdate();
290       BoxStack::getInstance()->update(this);
291       return 2;
292     }
293     case Input::OK:
294     {
295       RecTimer* recTimer = NULL;
296       if (recTimerList) recTimer = reinterpret_cast<RecTimer*>(sl.getCurrentOptionData());
297       if (recTimer == NULL) return 2;
298
299       VTimerEdit* v = new VTimerEdit(recTimer);
300       v->setParent(this);
301       v->draw();
302       BoxStack::getInstance()->add(v);
303       BoxStack::getInstance()->update(v);
304
305       return 2;
306     }
307     case Input::BACK:
308     {
309       return 4;
310     }
311   }
312   // stop command getting to any more views
313   return 1;
314 }
315
316 void VTimerList::processMessage(Message* m)
317 {
318   if (m->message == Message::MOUSE_MOVE)
319   {
320     if (sl.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
321     {
322         quickUpdate();
323       BoxStack::getInstance()->update(this);
324     }
325   }
326   else if (m->message == Message::MOUSE_LBDOWN)
327   {
328     if (sl.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))
329     {
330       BoxStack::getInstance()->handleCommand(Input::OK); //simulate OK press
331     }
332     else if (coordsOutsideBox(m))
333     {
334       BoxStack::getInstance()->handleCommand(Input::BACK); //simulate cancel press
335     }
336   }
337   else if (m->message == Message::DELETE_SELECTED_TIMER)
338   {
339     RecTimer* recTimer = reinterpret_cast<RecTimer*>(sl.getCurrentOptionData());
340     if (recTimer == NULL) return;
341     Log::getInstance()->log("VTimerList", Log::DEBUG, "Got timer to delete");
342
343   
344     ULONG retval = VDR::getInstance()->deleteTimer(recTimer);
345     if (!VDR::getInstance()->isConnected()) { Command::getInstance()->connectionLost(); return; }
346     Log::getInstance()->log("VTimerList", Log::DEBUG, "Got return fron delete timer: %lu", retval);
347     
348     if (retval != 10)
349     {
350       VInfo* errorBox = new VInfo();
351       errorBox->setSize(360, 200);
352       errorBox->createBuffer();
353       if (Video::getInstance()->getFormat() == Video::PAL)
354         errorBox->setPosition(190, 170);
355       else
356         errorBox->setPosition(180, 120);
357
358     
359       if (retval == 1) errorBox->setOneLiner(tr("Timers being edited at VDR, please try later"));
360       else if (retval == 3)  errorBox->setOneLiner(tr("Unable to delete timer - timer is running"));
361       else if (retval == 4)  errorBox->setOneLiner(tr("Error - timer not found at VDR"));
362       else                   errorBox->setOneLiner(tr("Unknown error"));
363
364       errorBox->setExitable();
365       errorBox->setBorderOn(1);
366       errorBox->setTitleBarColour(DrawStyle::DANGER);
367       errorBox->okButton();
368       errorBox->draw();
369       BoxStack::getInstance()->add(errorBox);
370       BoxStack::getInstance()->update(errorBox);
371     }
372     
373     int saveIndex = sl.getCurrentOption();
374     int saveTop = sl.getTopOption();
375      
376     if (recTimerList)
377     {
378       for (UINT i = 0; i < recTimerList->size(); i++)
379       {
380         delete (*recTimerList)[i];
381       }
382
383       recTimerList->clear();
384       delete recTimerList;
385     }
386
387     sl.clear();
388     load();
389
390     sl.hintSetCurrent(saveIndex);
391     sl.hintSetTop(saveTop);
392     draw();
393     BoxStack::getInstance()->update(this);
394   }
395 }
396