vdr  1.7.31
osdbase.c
Go to the documentation of this file.
1 /*
2  * osdbase.c: Basic interface to the On Screen Display
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * $Id: osdbase.c 2.6 2012/04/23 09:41:22 kls Exp $
8  */
9 
10 #include "osdbase.h"
11 #include <string.h>
12 #include "device.h"
13 #include "i18n.h"
14 #include "menuitems.h"
15 #include "remote.h"
16 #include "status.h"
17 
18 // --- cOsdItem --------------------------------------------------------------
19 
21 {
22  text = NULL;
23  state = State;
24  selectable = true;
25  fresh = true;
26 }
27 
28 cOsdItem::cOsdItem(const char *Text, eOSState State, bool Selectable)
29 {
30  text = NULL;
31  state = State;
33  fresh = true;
34  SetText(Text);
35 }
36 
38 {
39  free(text);
40 }
41 
42 void cOsdItem::SetText(const char *Text, bool Copy)
43 {
44  free(text);
45  text = Copy ? strdup(Text ? Text : "") : (char *)Text; // text assumes ownership!
46 }
47 
48 void cOsdItem::SetSelectable(bool Selectable)
49 {
51 }
52 
53 void cOsdItem::SetFresh(bool Fresh)
54 {
55  fresh = Fresh;
56 }
57 
59 {
60  return Key == kOk ? state : osUnknown;
61 }
62 
63 // --- cOsdObject ------------------------------------------------------------
64 
65 void cOsdObject::Show(void)
66 {
67  if (isMenu)
68  ((cOsdMenu *)this)->Display();
69 }
70 
71 // --- cOsdMenu --------------------------------------------------------------
72 
75 
76 cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4)
77 {
78  isMenu = true;
79  digit = 0;
80  key_nr = -1;
81  hasHotkeys = false;
82  displayMenuItems = 0;
83  title = NULL;
85  SetTitle(Title);
86  SetCols(c0, c1, c2, c3, c4);
87  first = 0;
88  current = marked = -1;
89  subMenu = NULL;
90  helpRed = helpGreen = helpYellow = helpBlue = NULL;
91  helpDisplayed = false;
92  status = NULL;
93  if (!displayMenuCount++)
95 }
96 
98 {
99  free(title);
100  delete subMenu;
101  free(status);
102  displayMenu->Clear();
104  if (!--displayMenuCount)
106 }
107 
109 {
110  menuCategory = MenuCategory;
111 }
112 
114 {
115  if (displayMenu) {
116  displayMenu->Clear();
117  delete displayMenu;
118  }
120 }
121 
122 const char *cOsdMenu::hk(const char *s)
123 {
124  static cString buffer;
125  if (s && hasHotkeys) {
126  if (digit == 0 && '1' <= *s && *s <= '9' && *(s + 1) == ' ')
127  digit = -1; // prevents automatic hotkeys - input already has them
128  if (digit >= 0) {
129  digit++;
130  buffer = cString::sprintf(" %2d%s %s", digit, (digit > 9) ? "" : " ", s);
131  s = buffer;
132  }
133  }
134  return s;
135 }
136 
137 void cOsdMenu::SetCols(int c0, int c1, int c2, int c3, int c4)
138 {
139  cols[0] = c0;
140  cols[1] = c1;
141  cols[2] = c2;
142  cols[3] = c3;
143  cols[4] = c4;
144 }
145 
146 void cOsdMenu::SetHasHotkeys(bool HasHotkeys)
147 {
148  hasHotkeys = HasHotkeys;
149  digit = 0;
150 }
151 
152 void cOsdMenu::SetStatus(const char *s)
153 {
154  free(status);
155  status = s ? strdup(s) : NULL;
157 }
158 
159 void cOsdMenu::SetTitle(const char *Title)
160 {
161  free(title);
162  title = strdup(Title);
163 }
164 
165 void cOsdMenu::DisplayHelp(bool Force)
166 {
167  if (!helpDisplayed || Force) {
170  helpDisplayed = true;
171  }
172 }
173 
174 void cOsdMenu::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue)
175 {
176  // strings are NOT copied - must be constants!!!
177  helpRed = Red;
178  helpGreen = Green;
179  helpYellow = Yellow;
180  helpBlue = Blue;
181  DisplayHelp(true);
182 }
183 
184 void cOsdMenu::Del(int Index)
185 {
186  cList<cOsdItem>::Del(Get(Index));
187  int count = Count();
188  while (current < count && !SelectableItem(current))
189  current++;
190  if (current == count) {
191  while (current > 0 && !SelectableItem(current))
192  current--;
193  }
194  if (Index == first && first > 0)
195  first--;
196 }
197 
198 void cOsdMenu::Add(cOsdItem *Item, bool Current, cOsdItem *After)
199 {
200  cList<cOsdItem>::Add(Item, After);
201  if (Current)
202  current = Item->Index();
203 }
204 
205 void cOsdMenu::Ins(cOsdItem *Item, bool Current, cOsdItem *Before)
206 {
207  cList<cOsdItem>::Ins(Item, Before);
208  if (Current)
209  current = Item->Index();
210 }
211 
213 {
214  if (subMenu) {
215  subMenu->Display();
216  return;
217  }
219  displayMenu->Clear();
224  displayMenu->SetTabs(cols[0], cols[1], cols[2], cols[3], cols[4]);//XXX
227  DisplayHelp(true);
228  int count = Count();
229  if (count > 0) {
230  int ni = 0;
231  for (cOsdItem *item = First(); item; item = Next(item)) {
232  cStatus::MsgOsdItem(item->Text(), ni++);
233  if (current < 0 && item->Selectable())
234  current = item->Index();
235  }
236  if (current < 0)
237  current = 0; // just for safety - there HAS to be a current item!
238  first = min(first, max(0, count - displayMenuItems)); // in case the menu size has changed
239  if (current - first >= displayMenuItems || current < first) {
241  if (first + displayMenuItems > count)
242  first = count - displayMenuItems;
243  if (first < 0)
244  first = 0;
245  }
246  int i = first;
247  int n = 0;
248  for (cOsdItem *item = Get(first); item; item = Next(item)) {
249  bool CurrentSelectable = (i == current) && item->Selectable();
250  displayMenu->SetItem(item->Text(), i - first, CurrentSelectable, item->Selectable());
251  if (CurrentSelectable)
252  cStatus::MsgOsdCurrentItem(item->Text());
253  if (++n == displayMenuItems)
254  break;
255  i++;
256  }
257  }
258  displayMenu->SetScrollbar(count, first);
259  if (!isempty(status))
261 }
262 
264 {
265  current = Item ? Item->Index() : -1;
266 }
267 
269 {
270  cOsdItem *item = Get(current);
271  if (item)
272  item->Set();
273 }
274 
275 void cOsdMenu::DisplayCurrent(bool Current)
276 {
277  cOsdItem *item = Get(current);
278  if (item) {
279  displayMenu->SetItem(item->Text(), current - first, Current && item->Selectable(), item->Selectable());
280  if (Current && item->Selectable())
282  if (!Current)
283  item->SetFresh(true); // leaving the current item resets 'fresh'
284  if (cMenuEditItem *MenuEditItem = dynamic_cast<cMenuEditItem *>(item)) {
285  if (!MenuEditItem->DisplayHelp())
286  DisplayHelp();
287  else
288  helpDisplayed = false;
289  }
290  }
291 }
292 
294 {
295  if (Item) {
296  int Index = Item->Index();
297  int Offset = Index - first;
298  if (Offset >= 0 && Offset < first + displayMenuItems) {
299  bool Current = Index == current;
300  displayMenu->SetItem(Item->Text(), Offset, Current && Item->Selectable(), Item->Selectable());
301  if (Current && Item->Selectable())
303  }
304  }
305 }
306 
307 void cOsdMenu::Clear(void)
308 {
309  if (marked >= 0)
310  SetStatus(NULL);
311  first = 0;
312  current = marked = -1;
314 }
315 
317 {
318  cOsdItem *item = Get(idx);
319  return item && item->Selectable();
320 }
321 
323 {
325  int tmpCurrent = current;
326  int lastOnScreen = first + displayMenuItems - 1;
327  int last = Count() - 1;
328  if (last < 0)
329  return;
330  while (--tmpCurrent != current) {
331  if (tmpCurrent < 0) {
332  if (first > 0) {
333  // make non-selectable items at the beginning visible:
334  first = 0;
335  Display();
336  return;
337  }
338  if (Setup.MenuScrollWrap)
339  tmpCurrent = last + 1;
340  else
341  return;
342  }
343  else if (SelectableItem(tmpCurrent))
344  break;
345  }
346  if (first <= tmpCurrent && tmpCurrent <= lastOnScreen)
347  DisplayCurrent(false);
348  current = tmpCurrent;
349  if (current < first) {
350  first = Setup.MenuScrollPage ? max(0, current - displayMenuItems + 1) : current;
351  Display();
352  }
353  else if (current > lastOnScreen) {
354  first = max(0, current - displayMenuItems + 1);
355  Display();
356  }
357  else
358  DisplayCurrent(true);
359 }
360 
362 {
364  int tmpCurrent = current;
365  int lastOnScreen = first + displayMenuItems - 1;
366  int last = Count() - 1;
367  if (last < 0)
368  return;
369  while (++tmpCurrent != current) {
370  if (tmpCurrent > last) {
371  if (first < last - displayMenuItems) {
372  // make non-selectable items at the end visible:
373  first = last - displayMenuItems + 1;
374  Display();
375  return;
376  }
377  if (Setup.MenuScrollWrap)
378  tmpCurrent = -1;
379  else
380  return;
381  }
382  else if (SelectableItem(tmpCurrent))
383  break;
384  }
385  if (first <= tmpCurrent && tmpCurrent <= lastOnScreen)
386  DisplayCurrent(false);
387  current = tmpCurrent;
388  if (current > lastOnScreen) {
389  first = Setup.MenuScrollPage ? current : max(0, current - displayMenuItems + 1);
390  if (first + displayMenuItems > last)
391  first = max(0, last - displayMenuItems + 1);
392  Display();
393  }
394  else if (current < first) {
395  first = current;
396  Display();
397  }
398  else
399  DisplayCurrent(true);
400 }
401 
403 {
405  int oldCurrent = current;
406  int oldFirst = first;
409  int last = Count() - 1;
410  if (current < 0)
411  current = 0;
412  if (first < 0)
413  first = 0;
414  int tmpCurrent = current;
415  while (!SelectableItem(tmpCurrent) && --tmpCurrent >= 0)
416  ;
417  if (tmpCurrent < 0) {
418  tmpCurrent = current;
419  while (++tmpCurrent <= last && !SelectableItem(tmpCurrent))
420  ;
421  }
422  current = tmpCurrent <= last ? tmpCurrent : -1;
423  if (current >= 0) {
424  if (current < first)
425  first = current;
426  else if (current - first >= displayMenuItems)
428  }
429  if (current != oldCurrent || first != oldFirst) {
430  Display();
431  DisplayCurrent(true);
432  }
433  else if (Setup.MenuScrollWrap)
434  CursorUp();
435 }
436 
438 {
440  int oldCurrent = current;
441  int oldFirst = first;
444  int last = Count() - 1;
445  if (current > last)
446  current = last;
447  if (first + displayMenuItems > last)
448  first = max(0, last - displayMenuItems + 1);
449  int tmpCurrent = current;
450  while (!SelectableItem(tmpCurrent) && ++tmpCurrent <= last)
451  ;
452  if (tmpCurrent > last) {
453  tmpCurrent = current;
454  while (--tmpCurrent >= 0 && !SelectableItem(tmpCurrent))
455  ;
456  }
457  current = tmpCurrent > 0 ? tmpCurrent : -1;
458  if (current >= 0) {
459  if (current < first)
460  first = current;
461  else if (current - first >= displayMenuItems)
463  }
464  if (current != oldCurrent || first != oldFirst) {
465  Display();
466  DisplayCurrent(true);
467  }
468  else if (Setup.MenuScrollWrap)
469  CursorDown();
470 }
471 
472 void cOsdMenu::Mark(void)
473 {
474  if (Count() && marked < 0) {
475  marked = current;
476  SetStatus(tr("Up/Dn for new location - OK to move"));
477  }
478 }
479 
480 #define MENUKEY_TIMEOUT 1500
481 
483 {
484  bool match = false;
485  bool highlight = false;
486  int item_nr;
487  int i;
488 
489  if (Key == kNone) {
490  if (lastActivity.TimedOut())
491  Key = kOk;
492  else
493  return osContinue;
494  }
495  else
497  for (cOsdItem *item = Last(); item; item = Prev(item)) {
498  const char *s = item->Text();
499  i = 0;
500  item_nr = 0;
501  if (s && (s = skipspace(s)) != '\0' && '0' <= s[i] && s[i] <= '9') {
502  do {
503  item_nr = item_nr * 10 + (s[i] - '0');
504  }
505  while ( !((s[++i] == '\t')||(s[i] == ' ')) && (s[i] != '\0') && ('0' <= s[i]) && (s[i] <= '9'));
506  if ((Key == kOk) && (item_nr == key_nr)) {
507  current = item->Index();
508  RefreshCurrent();
509  Display();
510  cRemote::Put(kOk, true);
511  key_nr = -1;
512  break;
513  }
514  else if (Key != kOk) {
515  if (!highlight && (item_nr == (Key - k0))) {
516  highlight = true;
517  current = item->Index();
518  }
519  if (!match && (key_nr == -1) && ((item_nr / 10) == (Key - k0))) {
520  match = true;
521  key_nr = (Key - k0);
522  }
523  else if (((key_nr == -1) && (item_nr == (Key - k0))) || (!match && (key_nr >= 0) && (item_nr == (10 * key_nr + Key - k0)))) {
524  current = item->Index();
525  cRemote::Put(kOk, true);
526  key_nr = -1;
527  break;
528  }
529  }
530  }
531  }
532  if ((!match) && (Key != kNone))
533  key_nr = -1;
534  return osContinue;
535 }
536 
538 {
539  delete subMenu;
540  subMenu = SubMenu;
541  subMenu->Display();
542  return osContinue; // convenience return value
543 }
544 
546 {
547  delete subMenu;
548  subMenu = NULL;
549  RefreshCurrent();
550  Display();
551  return osContinue; // convenience return value
552 }
553 
555 {
556  if (subMenu) {
557  eOSState state = subMenu->ProcessKey(Key);
558  if (state == osBack)
559  return CloseSubMenu();
560  return state;
561  }
562 
563  cOsdItem *item = Get(current);
564  if (marked < 0 && item) {
565  eOSState state = item->ProcessKey(Key);
566  if (state != osUnknown) {
567  DisplayCurrent(true);
568  return state;
569  }
570  }
571  switch (int(Key)) {
572  case kNone:
573  case k0...k9: return hasHotkeys ? HotKey(Key) : osUnknown;
574  case kUp|k_Repeat:
575  case kUp: CursorUp(); break;
576  case kDown|k_Repeat:
577  case kDown: CursorDown(); break;
578  case kLeft|k_Repeat:
579  case kLeft: PageUp(); break;
580  case kRight|k_Repeat:
581  case kRight: PageDown(); break;
582  case kBack: return osBack;
583  case kOk: if (marked >= 0) {
584  SetStatus(NULL);
585  if (marked != current)
586  Move(marked, current);
587  marked = -1;
588  break;
589  }
590  // else run into default
591  default: if (marked < 0)
592  return osUnknown;
593  }
594  return osContinue;
595 }