vdr  2.2.0
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 3.3 2015/01/15 10:11:11 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 
49 {
51 }
52 
53 void cOsdItem::SetFresh(bool Fresh)
54 {
55  fresh = Fresh;
56 }
57 
58 void cOsdItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
59 {
60  DisplayMenu->SetItem(Text(), Index, Current, Selectable);
61 }
62 
64 {
65  return Key == kOk ? state : osUnknown;
66 }
67 
68 // --- cOsdObject ------------------------------------------------------------
69 
70 void cOsdObject::Show(void)
71 {
72  if (isMenu)
73  ((cOsdMenu *)this)->Display();
74 }
75 
76 // --- cOsdMenu --------------------------------------------------------------
77 
80 
81 cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4)
82 {
83  isMenu = true;
84  digit = 0;
85  key_nr = -1;
86  hasHotkeys = false;
87  displayMenuItems = 0;
88  title = NULL;
89  menuCategory = mcUnknown;
90  menuSortMode = msmUnknown;
91  SetTitle(Title);
92  SetCols(c0, c1, c2, c3, c4);
93  first = 0;
94  current = marked = -1;
95  subMenu = NULL;
96  helpRed = helpGreen = helpYellow = helpBlue = NULL;
97  helpDisplayed = false;
98  status = NULL;
99  if (!displayMenuCount++)
100  SetDisplayMenu();
101 }
102 
104 {
105  free(title);
106  delete subMenu;
107  free(status);
108  displayMenu->Clear();
110  if (!--displayMenuCount)
111  DELETENULL(displayMenu);
112 }
113 
115 {
116  menuCategory = MenuCategory;
117 }
118 
120 {
121  menuSortMode = MenuSortMode;
122 }
123 
125 {
126  if (displayMenu) {
127  displayMenu->Clear();
128  delete displayMenu;
129  }
130  displayMenu = Skins.Current()->DisplayMenu();
131 }
132 
133 const char *cOsdMenu::hk(const char *s)
134 {
135  static cString buffer;
136  if (s && hasHotkeys) {
137  if (digit == 0 && '1' <= *s && *s <= '9' && *(s + 1) == ' ')
138  digit = -1; // prevents automatic hotkeys - input already has them
139  if (digit >= 0) {
140  digit++;
141  buffer = cString::sprintf(" %2d%s %s", digit, (digit > 9) ? "" : " ", s);
142  s = buffer;
143  }
144  }
145  return s;
146 }
147 
148 void cOsdMenu::SetCols(int c0, int c1, int c2, int c3, int c4)
149 {
150  cols[0] = c0;
151  cols[1] = c1;
152  cols[2] = c2;
153  cols[3] = c3;
154  cols[4] = c4;
155 }
156 
157 void cOsdMenu::SetHasHotkeys(bool HasHotkeys)
158 {
159  hasHotkeys = HasHotkeys;
160  digit = 0;
161 }
162 
163 void cOsdMenu::SetStatus(const char *s)
164 {
165  free(status);
166  status = s ? strdup(s) : NULL;
167  displayMenu->SetMessage(mtStatus, s);
168 }
169 
170 void cOsdMenu::SetTitle(const char *Title)
171 {
172  free(title);
173  title = strdup(Title);
174 }
175 
176 void cOsdMenu::DisplayHelp(bool Force)
177 {
178  if (!helpDisplayed || Force) {
179  displayMenu->SetButtons(helpRed, helpGreen, helpYellow, helpBlue);
180  cStatus::MsgOsdHelpKeys(helpRed, helpGreen, helpYellow, helpBlue);
181  helpDisplayed = true;
182  }
183 }
184 
185 void cOsdMenu::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue)
186 {
187  // strings are NOT copied - must be constants!!!
188  helpRed = Red;
189  helpGreen = Green;
190  helpYellow = Yellow;
191  helpBlue = Blue;
192  DisplayHelp(true);
193 }
194 
196 {
197  cList<cOsdItem>::Del(Get(Index));
198  int count = Count();
199  while (current < count && !SelectableItem(current))
200  current++;
201  if (current == count) {
202  while (current > 0 && !SelectableItem(current))
203  current--;
204  }
205  if (Index == first && first > 0)
206  first--;
207 }
208 
209 void cOsdMenu::Add(cOsdItem *Item, bool Current, cOsdItem *After)
210 {
211  cList<cOsdItem>::Add(Item, After);
212  if (Current)
213  current = Item->Index();
214 }
215 
216 void cOsdMenu::Ins(cOsdItem *Item, bool Current, cOsdItem *Before)
217 {
218  cList<cOsdItem>::Ins(Item, Before);
219  if (Current)
220  current = Item->Index();
221 }
222 
224 {
225  if (subMenu) {
226  subMenu->Display();
227  return;
228  }
229  displayMenu->SetMessage(mtStatus, NULL);
230  displayMenu->Clear();
232  if (menuCategory != displayMenu->MenuCategory())
233  displayMenu->SetMenuCategory(menuCategory);
234  displayMenu->SetMenuSortMode(menuSortMode);
235  displayMenuItems = displayMenu->MaxItems();
236  displayMenu->SetTabs(cols[0], cols[1], cols[2], cols[3], cols[4]);//XXX
237  displayMenu->SetTitle(title);
238  cStatus::MsgOsdTitle(title);
239  DisplayHelp(true);
240  int count = Count();
241  if (count > 0) {
242  int ni = 0;
243  for (cOsdItem *item = First(); item; item = Next(item)) {
244  cStatus::MsgOsdItem(item->Text(), ni++);
245  if (current < 0 && item->Selectable())
246  current = item->Index();
247  }
248  if (current < 0)
249  current = 0; // just for safety - there HAS to be a current item!
250  first = min(first, max(0, count - displayMenuItems)); // in case the menu size has changed
251  if (current - first >= displayMenuItems || current < first) {
252  first = current - displayMenuItems / 2;
253  if (first + displayMenuItems > count)
254  first = count - displayMenuItems;
255  if (first < 0)
256  first = 0;
257  }
258  int i = first;
259  int n = 0;
260  for (cOsdItem *item = Get(first); item; item = Next(item)) {
261  bool CurrentSelectable = (i == current) && item->Selectable();
262  item->SetMenuItem(displayMenu, i - first, CurrentSelectable, item->Selectable());
263  if (CurrentSelectable)
264  cStatus::MsgOsdCurrentItem(item->Text());
265  if (++n == displayMenuItems)
266  break;
267  i++;
268  }
269  }
270  displayMenu->SetScrollbar(count, first);
271  if (!isempty(status))
272  displayMenu->SetMessage(mtStatus, status);
273 }
274 
276 {
277  current = Item ? Item->Index() : -1;
278 }
279 
281 {
282  cOsdItem *item = Get(current);
283  if (item)
284  item->Set();
285 }
286 
287 void cOsdMenu::DisplayCurrent(bool Current)
288 {
289  cOsdItem *item = Get(current);
290  if (item) {
291  item->SetMenuItem(displayMenu, current - first, Current && item->Selectable(), item->Selectable());
292  if (Current && item->Selectable())
294  if (!Current)
295  item->SetFresh(true); // leaving the current item resets 'fresh'
296  if (cMenuEditItem *MenuEditItem = dynamic_cast<cMenuEditItem *>(item)) {
297  if (!MenuEditItem->DisplayHelp(Current))
298  DisplayHelp();
299  else
300  helpDisplayed = false;
301  }
302  }
303 }
304 
306 {
307  if (Item) {
308  int Index = Item->Index();
309  int Offset = Index - first;
310  if (Offset >= 0 && Offset < first + displayMenuItems) {
311  bool Current = Index == current;
312  Item->SetMenuItem(displayMenu, Offset, Current && Item->Selectable(), Item->Selectable());
313  if (Current && Item->Selectable())
315  }
316  }
317 }
318 
319 void cOsdMenu::Clear(void)
320 {
321  if (marked >= 0)
322  SetStatus(NULL);
323  first = 0;
324  current = marked = -1;
326 }
327 
329 {
330  cOsdItem *item = Get(idx);
331  return item && item->Selectable();
332 }
333 
335 {
336  displayMenuItems = displayMenu->MaxItems();
337  int tmpCurrent = current;
338  int lastOnScreen = first + displayMenuItems - 1;
339  int last = Count() - 1;
340  if (last < 0)
341  return;
342  while (--tmpCurrent != current) {
343  if (tmpCurrent < 0) {
344  if (first > 0) {
345  // make non-selectable items at the beginning visible:
346  first = 0;
347  Display();
348  return;
349  }
350  if (Setup.MenuScrollWrap)
351  tmpCurrent = last + 1;
352  else
353  return;
354  }
355  else if (SelectableItem(tmpCurrent))
356  break;
357  }
358  if (first <= tmpCurrent && tmpCurrent <= lastOnScreen)
359  DisplayCurrent(false);
360  current = tmpCurrent;
361  if (current < first) {
362  first = Setup.MenuScrollPage ? max(0, current - displayMenuItems + 1) : current;
363  Display();
364  }
365  else if (current > lastOnScreen) {
366  first = max(0, current - displayMenuItems + 1);
367  Display();
368  }
369  else
370  DisplayCurrent(true);
371 }
372 
374 {
375  displayMenuItems = displayMenu->MaxItems();
376  int tmpCurrent = current;
377  int lastOnScreen = first + displayMenuItems - 1;
378  int last = Count() - 1;
379  if (last < 0)
380  return;
381  while (++tmpCurrent != current) {
382  if (tmpCurrent > last) {
383  if (first < last - displayMenuItems) {
384  // make non-selectable items at the end visible:
385  first = last - displayMenuItems + 1;
386  Display();
387  return;
388  }
389  if (Setup.MenuScrollWrap)
390  tmpCurrent = -1;
391  else
392  return;
393  }
394  else if (SelectableItem(tmpCurrent))
395  break;
396  }
397  if (first <= tmpCurrent && tmpCurrent <= lastOnScreen)
398  DisplayCurrent(false);
399  current = tmpCurrent;
400  if (current > lastOnScreen) {
401  first = Setup.MenuScrollPage ? current : max(0, current - displayMenuItems + 1);
402  if (first + displayMenuItems > last)
403  first = max(0, last - displayMenuItems + 1);
404  Display();
405  }
406  else if (current < first) {
407  first = current;
408  Display();
409  }
410  else
411  DisplayCurrent(true);
412 }
413 
415 {
416  displayMenuItems = displayMenu->MaxItems();
417  int oldCurrent = current;
418  int oldFirst = first;
419  current -= displayMenuItems;
420  first -= displayMenuItems;
421  int last = Count() - 1;
422  if (current < 0)
423  current = 0;
424  if (first < 0)
425  first = 0;
426  int tmpCurrent = current;
427  while (!SelectableItem(tmpCurrent) && --tmpCurrent >= 0)
428  ;
429  if (tmpCurrent < 0) {
430  tmpCurrent = current;
431  while (++tmpCurrent <= last && !SelectableItem(tmpCurrent))
432  ;
433  }
434  current = tmpCurrent <= last ? tmpCurrent : -1;
435  if (current >= 0) {
436  if (current < first)
437  first = current;
438  else if (current - first >= displayMenuItems)
439  first = current - displayMenuItems + 1;
440  }
441  if (current != oldCurrent || first != oldFirst) {
442  Display();
443  DisplayCurrent(true);
444  }
445  else if (Setup.MenuScrollWrap)
446  CursorUp();
447 }
448 
450 {
451  displayMenuItems = displayMenu->MaxItems();
452  int oldCurrent = current;
453  int oldFirst = first;
454  current += displayMenuItems;
455  first += displayMenuItems;
456  int last = Count() - 1;
457  if (current > last)
458  current = last;
459  if (first + displayMenuItems > last)
460  first = max(0, last - displayMenuItems + 1);
461  int tmpCurrent = current;
462  while (!SelectableItem(tmpCurrent) && ++tmpCurrent <= last)
463  ;
464  if (tmpCurrent > last) {
465  tmpCurrent = current;
466  while (--tmpCurrent >= 0 && !SelectableItem(tmpCurrent))
467  ;
468  }
469  current = tmpCurrent > 0 ? tmpCurrent : -1;
470  if (current >= 0) {
471  if (current < first)
472  first = current;
473  else if (current - first >= displayMenuItems)
474  first = current - displayMenuItems + 1;
475  }
476  if (current != oldCurrent || first != oldFirst) {
477  Display();
478  DisplayCurrent(true);
479  }
480  else if (Setup.MenuScrollWrap)
481  CursorDown();
482 }
483 
484 void cOsdMenu::Mark(void)
485 {
486  if (Count() && marked < 0) {
487  marked = current;
488  SetStatus(tr("Up/Dn for new location - OK to move"));
489  }
490 }
491 
492 #define MENUKEY_TIMEOUT 1500
493 
495 {
496  bool match = false;
497  bool highlight = false;
498  int item_nr;
499  int i;
500 
501  if (Key == kNone) {
502  if (lastActivity.TimedOut())
503  Key = kOk;
504  else
505  return osContinue;
506  }
507  else
508  lastActivity.Set(MENUKEY_TIMEOUT);
509  for (cOsdItem *item = Last(); item; item = Prev(item)) {
510  const char *s = item->Text();
511  i = 0;
512  item_nr = 0;
513  if (s && (s = skipspace(s)) != '\0' && '0' <= s[i] && s[i] <= '9') {
514  do {
515  item_nr = item_nr * 10 + (s[i] - '0');
516  }
517  while ( !((s[++i] == '\t')||(s[i] == ' ')) && (s[i] != '\0') && ('0' <= s[i]) && (s[i] <= '9'));
518  if ((Key == kOk) && (item_nr == key_nr)) {
519  current = item->Index();
520  RefreshCurrent();
521  Display();
522  cRemote::Put(kOk, true);
523  key_nr = -1;
524  break;
525  }
526  else if (Key != kOk) {
527  if (!highlight && (item_nr == (Key - k0))) {
528  highlight = true;
529  current = item->Index();
530  }
531  if (!match && (key_nr == -1) && ((item_nr / 10) == (Key - k0))) {
532  match = true;
533  key_nr = (Key - k0);
534  }
535  else if (((key_nr == -1) && (item_nr == (Key - k0))) || (!match && (key_nr >= 0) && (item_nr == (10 * key_nr + Key - k0)))) {
536  current = item->Index();
537  cRemote::Put(kOk, true);
538  key_nr = -1;
539  break;
540  }
541  }
542  }
543  }
544  if ((!match) && (Key != kNone))
545  key_nr = -1;
546  return osContinue;
547 }
548 
550 {
551  delete subMenu;
552  subMenu = SubMenu;
553  subMenu->Display();
554  return osContinue; // convenience return value
555 }
556 
558 {
559  delete subMenu;
560  subMenu = NULL;
561  if (ReDisplay) {
562  RefreshCurrent();
563  Display();
564  }
565  return osContinue; // convenience return value
566 }
567 
569 {
570  if (subMenu) {
571  eOSState state = subMenu->ProcessKey(Key);
572  if (state == osBack)
573  return CloseSubMenu();
574  return state;
575  }
576 
577  cOsdItem *item = Get(current);
578  if (marked < 0 && item) {
579  eOSState state = item->ProcessKey(Key);
580  if (state != osUnknown) {
581  DisplayCurrent(true);
582  return state;
583  }
584  }
585  switch (int(Key)) {
586  case kNone:
587  case k0...k9: return hasHotkeys ? HotKey(Key) : osUnknown;
588  case kUp|k_Repeat:
589  case kUp: CursorUp(); break;
590  case kDown|k_Repeat:
591  case kDown: CursorDown(); break;
592  case kLeft|k_Repeat:
593  case kLeft: PageUp(); break;
594  case kRight|k_Repeat:
595  case kRight: PageDown(); break;
596  case kBack: return osBack;
597  case kOk: if (marked >= 0) {
598  SetStatus(NULL);
599  if (marked != current)
600  Move(marked, current);
601  marked = -1;
602  break;
603  }
604  // else run into default
605  default: if (marked < 0)
606  return osUnknown;
607  }
608  return osContinue;
609 }
virtual ~cOsdMenu()
Definition: osdbase.c:103
void SetStatus(const char *s)
Definition: osdbase.c:163
void DisplayItem(cOsdItem *Item)
Definition: osdbase.c:305
static void MsgOsdCurrentItem(const char *Text)
Definition: status.c:110
virtual void Del(int Index)
Definition: osdbase.c:195
bool isempty(const char *s)
Definition: tools.c:297
int Index(void) const
Definition: tools.c:1989
Definition: keys.h:23
void Add(cOsdItem *Item, bool Current=false, cOsdItem *After=NULL)
Definition: osdbase.c:209
cOsdItem(eOSState State=osUnknown)
Definition: osdbase.c:20
void Add(cListObject *Object, cListObject *After=NULL)
Definition: tools.c:2014
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1080
Definition: keys.h:17
Definition: keys.h:61
virtual void Clear(void)
Definition: osdbase.c:319
eOSState HotKey(eKeys Key)
Definition: osdbase.c:494
void RefreshCurrent(void)
Definition: osdbase.c:280
T max(T a, T b)
Definition: tools.h:55
bool selectable
Definition: osdbase.h:53
void PageUp(void)
Definition: osdbase.c:414
virtual cSkinDisplayMenu * DisplayMenu(void)=0
Creates and returns a new object for displaying a menu.
bool fresh
Definition: osdbase.h:55
void SetFresh(bool Fresh)
Definition: osdbase.c:53
void SetDisplayMenu(void)
Definition: osdbase.c:124
T min(T a, T b)
Definition: tools.h:54
#define MENUKEY_TIMEOUT
Definition: osdbase.c:492
static void MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue)
Definition: status.c:98
virtual void SetItem(const char *Text, int Index, bool Current, bool Selectable)=0
Sets the item at the given Index to Text.
virtual void Clear(void)
Definition: tools.c:2087
Definition: keys.h:55
static cSkinDisplayMenu * displayMenu
Definition: osdbase.h:88
eOSState
Definition: osdbase.h:18
const char * Text(void) const
Definition: osdbase.h:64
void SetHasHotkeys(bool HasHotkeys=true)
Definition: osdbase.c:157
bool Selectable(void) const
Definition: osdbase.h:60
virtual eOSState ProcessKey(eKeys Key)
Definition: osdbase.c:568
void PageDown(void)
Definition: osdbase.c:449
void Ins(cOsdItem *Item, bool Current=false, cOsdItem *Before=NULL)
Definition: osdbase.c:216
void SetCols(int c0, int c1=0, int c2=0, int c3=0, int c4=0)
Definition: osdbase.c:148
void SetMenuCategory(eMenuCategory MenuCategory)
Definition: osdbase.c:114
cListObject * Next(void) const
Definition: tools.h:468
static void MsgOsdTitle(const char *Title)
Definition: status.c:86
void CursorDown(void)
Definition: osdbase.c:373
cOsdMenu(const char *Title, int c0=0, int c1=0, int c2=0, int c3=0, int c4=0)
Definition: osdbase.c:81
void SetText(const char *Text, bool Copy=true)
Definition: osdbase.c:42
void SetSelectable(bool Selectable)
Definition: osdbase.c:48
Definition: keys.h:18
void Ins(cListObject *Object, cListObject *Before=NULL)
Definition: tools.c:2030
virtual ~cOsdItem()
Definition: osdbase.c:37
virtual void Display(void)
Definition: osdbase.c:223
cSetup Setup
Definition: config.c:373
bool Put(uint64_t Code, bool Repeat=false, bool Release=false)
Definition: remote.c:124
Definition: keys.h:20
void Mark(void)
Definition: osdbase.c:484
static int displayMenuCount
Definition: osdbase.h:89
Definition: skins.h:24
virtual eOSState ProcessKey(eKeys Key)
Definition: osdbase.c:63
virtual void Show(void)
Definition: osdbase.c:70
void DisplayHelp(bool Force=false)
Definition: osdbase.c:176
Definition: keys.h:21
void SetCurrent(cOsdItem *Item)
Definition: osdbase.c:275
void DisplayCurrent(bool Current)
Definition: osdbase.c:287
void SetMenuSortMode(eMenuSortMode MenuSortMode)
Definition: osdbase.c:119
cListObject * Prev(void) const
Definition: tools.h:467
eOSState CloseSubMenu(bool ReDisplay=true)
Definition: osdbase.c:557
void Del(cListObject *Object, bool DeleteObject=true)
Definition: tools.c:2046
eMenuCategory
Definition: skins.h:91
static void MsgOsdClear(void)
Definition: status.c:80
const char * hk(const char *s)
Definition: osdbase.c:133
#define tr(s)
Definition: i18n.h:85
void DELETENULL(T *&p)
Definition: tools.h:48
char * skipspace(const char *s)
Definition: tools.h:200
void SetHelp(const char *Red, const char *Green=NULL, const char *Yellow=NULL, const char *Blue=NULL)
Definition: osdbase.c:185
bool SelectableItem(int idx)
Definition: osdbase.c:328
char * text
Definition: osdbase.h:51
void CursorUp(void)
Definition: osdbase.c:334
Definition: keys.h:28
int MenuScrollWrap
Definition: config.h:266
virtual void Set(void)
Definition: osdbase.h:65
int MenuScrollPage
Definition: config.h:265
Definition: osdbase.h:34
eOSState state
Definition: osdbase.h:52
void SetTitle(const char *Title)
Definition: osdbase.c:170
cSkin * Current(void)
Returns a pointer to the current skin.
Definition: skins.h:436
eOSState AddSubMenu(cOsdMenu *SubMenu)
Definition: osdbase.c:549
static void MsgOsdItem(const char *Text, int Index)
Definition: status.c:104
virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
Definition: osdbase.c:58
eKeys
Definition: keys.h:16
Definition: tools.h:168
eMenuSortMode
Definition: skins.h:124
Definition: keys.h:22
cSkins Skins
Definition: skins.c:219