vdr  2.0.4
skins.c
Go to the documentation of this file.
1 /*
2  * skins.c: The optical appearance of the OSD
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * $Id: skins.c 2.10 2012/06/02 11:44:14 kls Exp $
8  */
9 
10 #include "skins.h"
11 #include "interface.h"
12 #include "status.h"
13 
14 // --- cSkinQueuedMessage ----------------------------------------------------
15 
17  friend class cSkins;
18 private:
20  char *message;
21  int seconds;
22  int timeout;
25  int state;
28 public:
29  cSkinQueuedMessage(eMessageType Type, const char *s, int Seconds, int Timeout);
30  virtual ~cSkinQueuedMessage();
31  };
32 
33 cSkinQueuedMessage::cSkinQueuedMessage(eMessageType Type, const char *s, int Seconds, int Timeout)
34 {
35  type = Type;
36  message = s ? strdup(s) : NULL;
37  seconds = Seconds;
38  timeout = Timeout;
40  key = kNone;
41  state = 0; // waiting
42 }
43 
45 {
46  free(message);
47 }
48 
50 
51 // --- cSkinDisplay ----------------------------------------------------------
52 
54 
56 {
57  current = this;
58  editableWidth = 100; //XXX
59 }
60 
62 {
63  current = NULL;
64 }
65 
66 // --- cSkinDisplayMenu ------------------------------------------------------
67 
69 {
71  SetTabs(0);
72 }
73 
75 {
77 }
78 
79 void cSkinDisplayMenu::SetTabs(int Tab1, int Tab2, int Tab3, int Tab4, int Tab5)
80 {
81  tabs[0] = 0;
82  tabs[1] = Tab1 ? tabs[0] + Tab1 : 0;
83  tabs[2] = Tab2 ? tabs[1] + Tab2 : 0;
84  tabs[3] = Tab3 ? tabs[2] + Tab3 : 0;
85  tabs[4] = Tab4 ? tabs[3] + Tab4 : 0;
86  tabs[5] = Tab5 ? tabs[4] + Tab5 : 0;
87  for (int i = 1; i < MaxTabs; i++)
88  tabs[i] *= AvgCharWidth();
89 }
90 
91 void cSkinDisplayMenu::Scroll(bool Up, bool Page)
92 {
93  textScroller.Scroll(Up, Page);
94 }
95 
96 const char *cSkinDisplayMenu::GetTabbedText(const char *s, int Tab)
97 {
98  if (!s)
99  return NULL;
100  static char buffer[1000];
101  const char *a = s;
102  const char *b = strchrnul(a, '\t');
103  while (*b && Tab-- > 0) {
104  a = b + 1;
105  b = strchrnul(a, '\t');
106  }
107  if (!*b)
108  return (Tab <= 0) ? a : NULL;
109  unsigned int n = b - a;
110  if (n >= sizeof(buffer))
111  n = sizeof(buffer) - 1;
112  strncpy(buffer, a, n);
113  buffer[n] = 0;
114  return buffer;
115 }
116 
117 void cSkinDisplayMenu::SetScrollbar(int Total, int Offset)
118 {
119 }
120 
122 {
123  return 0;
124 }
125 
127 {
128  return NULL;
129 }
130 
131 // --- cSkinDisplayReplay::cProgressBar --------------------------------------
132 
133 cSkinDisplayReplay::cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent)
134 :cBitmap(Width, Height, 2)
135 {
136  total = Total;
137  if (total > 0) {
138  int p = Pos(Current);
139  DrawRectangle(0, 0, p, Height - 1, ColorSeen);
140  DrawRectangle(p + 1, 0, Width - 1, Height - 1, ColorRest);
141  if (Marks) {
142  bool Start = true;
143  for (const cMark *m = Marks->First(); m; m = Marks->Next(m)) {
144  int p1 = Pos(m->Position());
145  if (Start) {
146  const cMark *m2 = Marks->Next(m);
147  int p2 = Pos(m2 ? m2->Position() : total);
148  int h = Height / 3;
149  DrawRectangle(p1, h, p2, Height - h, ColorSelected);
150  }
151  Mark(p1, Start, m->Position() == Current, ColorMark, ColorCurrent);
152  Start = !Start;
153  }
154  }
155  }
156 }
157 
158 void cSkinDisplayReplay::cProgressBar::Mark(int x, bool Start, bool Current, tColor ColorMark, tColor ColorCurrent)
159 {
160  DrawRectangle(x, 0, x, Height() - 1, ColorMark);
161  const int d = Height() / (Current ? 3 : 9);
162  for (int i = 0; i < d; i++) {
163  int h = Start ? i : Height() - 1 - i;
164  DrawRectangle(x - d + i, h, x + d - i, h, Current ? ColorCurrent : ColorMark);
165  }
166 }
167 
168 // --- cSkinDisplayReplay ----------------------------------------------------
169 
171 {
172  marks = NULL;
173 }
174 
176 {
177  SetTitle(Recording->Title());
178 }
179 
181 {
182  marks = Marks;
183 }
184 
185 // --- cSkin -----------------------------------------------------------------
186 
187 cSkin::cSkin(const char *Name, cTheme *Theme)
188 {
189  name = strdup(Name);
190  theme = Theme;
191  if (theme)
192  cThemes::Save(name, theme);
193  Skins.Add(this);
194 }
195 
197 {
198  free(name);
199 }
200 
201 // --- cSkins ----------------------------------------------------------------
202 
204 
206 {
207  displayMessage = NULL;
208 }
209 
211 {
212  delete displayMessage;
213 }
214 
215 bool cSkins::SetCurrent(const char *Name)
216 {
217  if (Name) {
218  for (cSkin *Skin = First(); Skin; Skin = Next(Skin)) {
219  if (strcmp(Skin->Name(), Name) == 0) {
220  isyslog("setting current skin to \"%s\"", Name);
221  current = Skin;
222  return true;
223  }
224  }
225  }
226  current = First();
227  if (current)
228  isyslog("skin \"%s\" not available - using \"%s\" instead", Name, current->Name());
229  else
230  esyslog("ERROR: no skin available");
231  return current != NULL;
232 }
233 
234 eKeys cSkins::Message(eMessageType Type, const char *s, int Seconds)
235 {
236  if (!cThread::IsMainThread()) {
237  dsyslog("cSkins::Message() called from background thread - ignored! (Use cSkins::QueueMessage() instead)");
238  return kNone;
239  }
240  switch (Type) {
241  case mtInfo: isyslog("info: %s", s); break;
242  case mtWarning: isyslog("warning: %s", s); break;
243  case mtError: esyslog("ERROR: %s", s); break;
244  default: ;
245  }
246  if (!Current())
247  return kNone;
248  if (!cSkinDisplay::Current()) {
249  if (displayMessage)
250  delete displayMessage;
251  displayMessage = Current()->DisplayMessage();
252  }
253  cSkinDisplay::Current()->SetMessage(Type, s);
256  eKeys k = kNone;
257  if (Type != mtStatus) {
258  k = Interface->Wait(Seconds);
259  if (displayMessage) {
260  delete displayMessage;
261  displayMessage = NULL;
263  }
264  else {
265  cSkinDisplay::Current()->SetMessage(Type, NULL);
267  }
268  }
269  else if (!s && displayMessage) {
270  delete displayMessage;
271  displayMessage = NULL;
273  }
274  return k;
275 }
276 
277 int cSkins::QueueMessage(eMessageType Type, const char *s, int Seconds, int Timeout)
278 {
279  if (Type == mtStatus) {
280  dsyslog("cSkins::QueueMessage() called with mtStatus - ignored!");
281  return kNone;
282  }
283  if (isempty(s)) {
284  if (!cThread::IsMainThread()) {
286  for (cSkinQueuedMessage *m = SkinQueuedMessages.Last(); m; m = SkinQueuedMessages.Prev(m)) {
287  if (m->threadId == cThread::ThreadId() && m->state == 0)
288  m->state = 2; // done
289  }
291  }
292  else
293  dsyslog("cSkins::QueueMessage() called with empty message from main thread - ignored!");
294  return kNone;
295  }
296  int k = kNone;
297  if (Timeout > 0) {
298  if (cThread::IsMainThread()) {
299  dsyslog("cSkins::QueueMessage() called from main thread with Timeout = %d - ignored!", Timeout);
300  return k;
301  }
302  cSkinQueuedMessage *m = new cSkinQueuedMessage(Type, s, Seconds, Timeout);
304  SkinQueuedMessages.Add(m);
305  m->mutex.Lock();
307  if (m->condVar.TimedWait(m->mutex, Timeout * 1000))
308  k = m->key;
309  else
310  k = -1; // timeout, nothing has been displayed
311  m->state = 2; // done
312  m->mutex.Unlock();
313  }
314  else {
316  // Check if there is a waiting message w/o timeout for this thread:
317  if (Timeout == -1) {
318  for (cSkinQueuedMessage *m = SkinQueuedMessages.Last(); m; m = SkinQueuedMessages.Prev(m)) {
319  if (m->threadId == cThread::ThreadId()) {
320  if (m->state == 0 && m->timeout == -1)
321  m->state = 2; // done
322  break;
323  }
324  }
325  }
326  // Add the new message:
327  SkinQueuedMessages.Add(new cSkinQueuedMessage(Type, s, Seconds, Timeout));
329  }
330  return k;
331 }
332 
334 {
335  if (!cThread::IsMainThread()) {
336  dsyslog("cSkins::ProcessQueuedMessages() called from background thread - ignored!");
337  return;
338  }
339  cSkinQueuedMessage *msg = NULL;
340  // Get the first waiting message:
342  for (cSkinQueuedMessage *m = SkinQueuedMessages.First(); m; m = SkinQueuedMessages.Next(m)) {
343  if (m->state == 0) { // waiting
344  m->state = 1; // active
345  msg = m;
346  break;
347  }
348  }
350  // Display the message:
351  if (msg) {
352  msg->mutex.Lock();
353  if (msg->state == 1) { // might have changed since we got it
354  msg->key = Skins.Message(msg->type, msg->message, msg->seconds);
355  if (msg->timeout == 0)
356  msg->state = 2; // done
357  else
358  msg->condVar.Broadcast();
359  }
360  msg->mutex.Unlock();
361  }
362  // Remove done messages from the queue:
364  for (;;) {
365  cSkinQueuedMessage *m = SkinQueuedMessages.First();
366  if (m && m->state == 2) { // done
367  SkinQueuedMessages.Del(m);
368  }
369  else
370  break;
371  }
373 }
374 
375 void cSkins::Flush(void)
376 {
377  if (cSkinDisplay::Current())
379 }
380 
381 void cSkins::Clear(void)
382 {
383  if (displayMessage) {
384  delete displayMessage;
385  displayMessage = NULL;
386  }
388 }
389