vdr  1.7.27
osdbase.c
Go to the documentation of this file.
00001 /*
00002  * osdbase.c: Basic interface to the On Screen Display
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: osdbase.c 2.4 2012/03/02 15:49:57 kls Exp $
00008  */
00009 
00010 #include "osdbase.h"
00011 #include <string.h>
00012 #include "device.h"
00013 #include "i18n.h"
00014 #include "menuitems.h"
00015 #include "remote.h"
00016 #include "status.h"
00017 
00018 // --- cOsdItem --------------------------------------------------------------
00019 
00020 cOsdItem::cOsdItem(eOSState State)
00021 {
00022   text = NULL;
00023   state = State;
00024   selectable = true;
00025   fresh = true;
00026 }
00027 
00028 cOsdItem::cOsdItem(const char *Text, eOSState State, bool Selectable)
00029 {
00030   text = NULL;
00031   state = State;
00032   selectable = Selectable;
00033   fresh = true;
00034   SetText(Text);
00035 }
00036 
00037 cOsdItem::~cOsdItem()
00038 {
00039   free(text);
00040 }
00041 
00042 void cOsdItem::SetText(const char *Text, bool Copy)
00043 {
00044   free(text);
00045   text = Copy ? strdup(Text ? Text : "") : (char *)Text; // text assumes ownership!
00046 }
00047 
00048 void cOsdItem::SetSelectable(bool Selectable)
00049 {
00050   selectable = Selectable;
00051 }
00052 
00053 void cOsdItem::SetFresh(bool Fresh)
00054 {
00055   fresh = Fresh;
00056 }
00057 
00058 eOSState cOsdItem::ProcessKey(eKeys Key)
00059 {
00060   return Key == kOk ? state : osUnknown;
00061 }
00062 
00063 // --- cOsdObject ------------------------------------------------------------
00064 
00065 void cOsdObject::Show(void)
00066 {
00067   if (isMenu)
00068      ((cOsdMenu *)this)->Display();
00069 }
00070 
00071 // --- cOsdMenu --------------------------------------------------------------
00072 
00073 cSkinDisplayMenu *cOsdMenu::displayMenu = NULL;
00074 int cOsdMenu::displayMenuCount = 0;
00075 int cOsdMenu::displayMenuItems = 0;//XXX dynamic???
00076 
00077 cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4)
00078 {
00079   isMenu = true;
00080   digit = 0;
00081   key_nr = -1;
00082   hasHotkeys = false;
00083   title = NULL;
00084   SetTitle(Title);
00085   SetCols(c0, c1, c2, c3, c4);
00086   first = 0;
00087   current = marked = -1;
00088   subMenu = NULL;
00089   helpRed = helpGreen = helpYellow = helpBlue = NULL;
00090   helpDisplayed = false;
00091   status = NULL;
00092   if (!displayMenuCount++)
00093      SetDisplayMenu();
00094 }
00095 
00096 cOsdMenu::~cOsdMenu()
00097 {
00098   free(title);
00099   delete subMenu;
00100   free(status);
00101   displayMenu->Clear();
00102   cStatus::MsgOsdClear();
00103   if (!--displayMenuCount)
00104      DELETENULL(displayMenu);
00105 }
00106 
00107 void cOsdMenu::SetDisplayMenu(void)
00108 {
00109   if (displayMenu) {
00110      displayMenu->Clear();
00111      delete displayMenu;
00112      }
00113   displayMenu = Skins.Current()->DisplayMenu();
00114   displayMenuItems = displayMenu->MaxItems();
00115 }
00116 
00117 const char *cOsdMenu::hk(const char *s)
00118 {
00119   static cString buffer;
00120   if (s && hasHotkeys) {
00121      if (digit == 0 && '1' <= *s && *s <= '9' && *(s + 1) == ' ')
00122         digit = -1; // prevents automatic hotkeys - input already has them
00123      if (digit >= 0) {
00124         digit++;
00125         buffer = cString::sprintf(" %2d%s %s", digit, (digit > 9) ? "" : " ", s);
00126         s = buffer;
00127         }
00128      }
00129   return s;
00130 }
00131 
00132 void cOsdMenu::SetCols(int c0, int c1, int c2, int c3, int c4)
00133 {
00134   cols[0] = c0;
00135   cols[1] = c1;
00136   cols[2] = c2;
00137   cols[3] = c3;
00138   cols[4] = c4;
00139 }
00140 
00141 void cOsdMenu::SetHasHotkeys(bool HasHotkeys)
00142 {
00143   hasHotkeys = HasHotkeys;
00144   digit = 0;
00145 }
00146 
00147 void cOsdMenu::SetStatus(const char *s)
00148 {
00149   free(status);
00150   status = s ? strdup(s) : NULL;
00151   displayMenu->SetMessage(mtStatus, s);
00152 }
00153 
00154 void cOsdMenu::SetTitle(const char *Title)
00155 {
00156   free(title);
00157   title = strdup(Title);
00158 }
00159 
00160 void cOsdMenu::DisplayHelp(bool Force)
00161 {
00162   if (!helpDisplayed || Force) {
00163      displayMenu->SetButtons(helpRed, helpGreen, helpYellow, helpBlue);
00164      cStatus::MsgOsdHelpKeys(helpRed, helpGreen, helpYellow, helpBlue);
00165      helpDisplayed = true;
00166      }
00167 }
00168 
00169 void cOsdMenu::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue)
00170 {
00171   // strings are NOT copied - must be constants!!!
00172   helpRed    = Red;
00173   helpGreen  = Green;
00174   helpYellow = Yellow;
00175   helpBlue   = Blue;
00176   DisplayHelp(true);
00177 }
00178 
00179 void cOsdMenu::Del(int Index)
00180 {
00181   cList<cOsdItem>::Del(Get(Index));
00182   int count = Count();
00183   while (current < count && !SelectableItem(current))
00184         current++;
00185   if (current == count) {
00186      while (current > 0 && !SelectableItem(current))
00187            current--;
00188      }
00189   if (Index == first && first > 0)
00190      first--;
00191 }
00192 
00193 void cOsdMenu::Add(cOsdItem *Item, bool Current, cOsdItem *After)
00194 {
00195   cList<cOsdItem>::Add(Item, After);
00196   if (Current)
00197      current = Item->Index();
00198 }
00199 
00200 void cOsdMenu::Ins(cOsdItem *Item, bool Current, cOsdItem *Before)
00201 {
00202   cList<cOsdItem>::Ins(Item, Before);
00203   if (Current)
00204      current = Item->Index();
00205 }
00206 
00207 void cOsdMenu::Display(void)
00208 {
00209   if (subMenu) {
00210      subMenu->Display();
00211      return;
00212      }
00213   displayMenu->SetMessage(mtStatus, NULL);
00214   displayMenu->Clear();
00215   cStatus::MsgOsdClear();
00216   displayMenu->SetTabs(cols[0], cols[1], cols[2], cols[3], cols[4]);//XXX
00217   displayMenu->SetTitle(title);
00218   cStatus::MsgOsdTitle(title);
00219   DisplayHelp(true);
00220   int count = Count();
00221   if (count > 0) {
00222      int ni = 0;
00223      for (cOsdItem *item = First(); item; item = Next(item)) {
00224          cStatus::MsgOsdItem(item->Text(), ni++);
00225          if (current < 0 && item->Selectable())
00226             current = item->Index();
00227          }
00228      if (current < 0)
00229         current = 0; // just for safety - there HAS to be a current item!
00230      first = min(first, max(0, count - displayMenuItems)); // in case the menu size has changed
00231      if (current - first >= displayMenuItems || current < first) {
00232         first = current - displayMenuItems / 2;
00233         if (first + displayMenuItems > count)
00234            first = count - displayMenuItems;
00235         if (first < 0)
00236            first = 0;
00237         }
00238      int i = first;
00239      int n = 0;
00240      for (cOsdItem *item = Get(first); item; item = Next(item)) {
00241          bool CurrentSelectable = (i == current) && item->Selectable();
00242          displayMenu->SetItem(item->Text(), i - first, CurrentSelectable, item->Selectable());
00243          if (CurrentSelectable)
00244             cStatus::MsgOsdCurrentItem(item->Text());
00245          if (++n == displayMenuItems)
00246             break;
00247          i++;
00248          }
00249      }
00250   displayMenu->SetScrollbar(count, first);
00251   if (!isempty(status))
00252      displayMenu->SetMessage(mtStatus, status);
00253 }
00254 
00255 void cOsdMenu::SetCurrent(cOsdItem *Item)
00256 {
00257   current = Item ? Item->Index() : -1;
00258 }
00259 
00260 void cOsdMenu::RefreshCurrent(void)
00261 {
00262   cOsdItem *item = Get(current);
00263   if (item)
00264      item->Set();
00265 }
00266 
00267 void cOsdMenu::DisplayCurrent(bool Current)
00268 {
00269   cOsdItem *item = Get(current);
00270   if (item) {
00271      displayMenu->SetItem(item->Text(), current - first, Current && item->Selectable(), item->Selectable());
00272      if (Current && item->Selectable())
00273         cStatus::MsgOsdCurrentItem(item->Text());
00274      if (!Current)
00275         item->SetFresh(true); // leaving the current item resets 'fresh'
00276      if (cMenuEditItem *MenuEditItem = dynamic_cast<cMenuEditItem *>(item)) {
00277         if (!MenuEditItem->DisplayHelp())
00278            DisplayHelp();
00279         else
00280            helpDisplayed = false;
00281         }
00282      }
00283 }
00284 
00285 void cOsdMenu::DisplayItem(cOsdItem *Item)
00286 {
00287   if (Item) {
00288      int Index = Item->Index();
00289      int Offset = Index - first;
00290      if (Offset >= 0 && Offset < first + displayMenuItems) {
00291         bool Current = Index == current;
00292         displayMenu->SetItem(Item->Text(), Offset, Current && Item->Selectable(), Item->Selectable());
00293         if (Current && Item->Selectable())
00294            cStatus::MsgOsdCurrentItem(Item->Text());
00295         }
00296      }
00297 }
00298 
00299 void cOsdMenu::Clear(void)
00300 {
00301   if (marked >= 0)
00302      SetStatus(NULL);
00303   first = 0;
00304   current = marked = -1;
00305   cList<cOsdItem>::Clear();
00306 }
00307 
00308 bool cOsdMenu::SelectableItem(int idx)
00309 {
00310   cOsdItem *item = Get(idx);
00311   return item && item->Selectable();
00312 }
00313 
00314 void cOsdMenu::CursorUp(void)
00315 {
00316   displayMenuItems = displayMenu->MaxItems();
00317   int tmpCurrent = current;
00318   int lastOnScreen = first + displayMenuItems - 1;
00319   int last = Count() - 1;
00320   if (last < 0)
00321      return;
00322   while (--tmpCurrent != current) {
00323         if (tmpCurrent < 0) {
00324            if (first > 0) {
00325               // make non-selectable items at the beginning visible:
00326               first = 0;
00327               Display();
00328               return;
00329               }
00330            if (Setup.MenuScrollWrap)
00331               tmpCurrent = last + 1;
00332            else
00333               return;
00334            }
00335         else if (SelectableItem(tmpCurrent))
00336            break;
00337         }
00338   if (first <= tmpCurrent && tmpCurrent <= lastOnScreen)
00339      DisplayCurrent(false);
00340   current = tmpCurrent;
00341   if (current < first) {
00342      first = Setup.MenuScrollPage ? max(0, current - displayMenuItems + 1) : current;
00343      Display();
00344      }
00345   else if (current > lastOnScreen) {
00346      first = max(0, current - displayMenuItems + 1);
00347      Display();
00348      }
00349   else
00350      DisplayCurrent(true);
00351 }
00352 
00353 void cOsdMenu::CursorDown(void)
00354 {
00355   displayMenuItems = displayMenu->MaxItems();
00356   int tmpCurrent = current;
00357   int lastOnScreen = first + displayMenuItems - 1;
00358   int last = Count() - 1;
00359   if (last < 0)
00360      return;
00361   while (++tmpCurrent != current) {
00362         if (tmpCurrent > last) {
00363            if (first < last - displayMenuItems) {
00364               // make non-selectable items at the end visible:
00365               first = last - displayMenuItems + 1;
00366               Display();
00367               return;
00368               }
00369            if (Setup.MenuScrollWrap)
00370               tmpCurrent = -1;
00371            else
00372               return;
00373            }
00374         else if (SelectableItem(tmpCurrent))
00375            break;
00376         }
00377   if (first <= tmpCurrent && tmpCurrent <= lastOnScreen)
00378      DisplayCurrent(false);
00379   current = tmpCurrent;
00380   if (current > lastOnScreen) {
00381      first = Setup.MenuScrollPage ? current : max(0, current - displayMenuItems + 1);
00382      if (first + displayMenuItems > last)
00383         first = max(0, last - displayMenuItems + 1);
00384      Display();
00385      }
00386   else if (current < first) {
00387      first = current;
00388      Display();
00389      }
00390   else
00391      DisplayCurrent(true);
00392 }
00393 
00394 void cOsdMenu::PageUp(void)
00395 {
00396   displayMenuItems = displayMenu->MaxItems();
00397   int oldCurrent = current;
00398   int oldFirst = first;
00399   current -= displayMenuItems;
00400   first -= displayMenuItems;
00401   int last = Count() - 1;
00402   if (current < 0)
00403      current = 0;
00404   if (first < 0)
00405      first = 0;
00406   int tmpCurrent = current;
00407   while (!SelectableItem(tmpCurrent) && --tmpCurrent >= 0)
00408         ;
00409   if (tmpCurrent < 0) {
00410      tmpCurrent = current;
00411      while (++tmpCurrent <= last && !SelectableItem(tmpCurrent))
00412            ;
00413      }
00414   current = tmpCurrent <= last ? tmpCurrent : -1;
00415   if (current >= 0) {
00416      if (current < first)
00417         first = current;
00418      else if (current - first >= displayMenuItems)
00419         first = current - displayMenuItems + 1;
00420      }
00421   if (current != oldCurrent || first != oldFirst) {
00422      Display();
00423      DisplayCurrent(true);
00424      }
00425   else if (Setup.MenuScrollWrap)
00426      CursorUp();
00427 }
00428 
00429 void cOsdMenu::PageDown(void)
00430 {
00431   displayMenuItems = displayMenu->MaxItems();
00432   int oldCurrent = current;
00433   int oldFirst = first;
00434   current += displayMenuItems;
00435   first += displayMenuItems;
00436   int last = Count() - 1;
00437   if (current > last)
00438      current = last;
00439   if (first + displayMenuItems > last)
00440      first = max(0, last - displayMenuItems + 1);
00441   int tmpCurrent = current;
00442   while (!SelectableItem(tmpCurrent) && ++tmpCurrent <= last)
00443         ;
00444   if (tmpCurrent > last) {
00445      tmpCurrent = current;
00446      while (--tmpCurrent >= 0 && !SelectableItem(tmpCurrent))
00447            ;
00448      }
00449   current = tmpCurrent > 0 ? tmpCurrent : -1;
00450   if (current >= 0) {
00451      if (current < first)
00452         first = current;
00453      else if (current - first >= displayMenuItems)
00454         first = current - displayMenuItems + 1;
00455      }
00456   if (current != oldCurrent || first != oldFirst) {
00457      Display();
00458      DisplayCurrent(true);
00459      }
00460   else if (Setup.MenuScrollWrap)
00461      CursorDown();
00462 }
00463 
00464 void cOsdMenu::Mark(void)
00465 {
00466   if (Count() && marked < 0) {
00467      marked = current;
00468      SetStatus(tr("Up/Dn for new location - OK to move"));
00469      }
00470 }
00471 
00472 #define MENUKEY_TIMEOUT 1500
00473 
00474 eOSState cOsdMenu::HotKey(eKeys Key)
00475 {
00476   bool match = false;
00477   bool highlight = false;
00478   int  item_nr;
00479   int  i;
00480 
00481   if (Key == kNone) {
00482      if (lastActivity.TimedOut())
00483         Key = kOk;
00484      else
00485         return osContinue;
00486      }
00487   else
00488      lastActivity.Set(MENUKEY_TIMEOUT);
00489   for (cOsdItem *item = Last(); item; item = Prev(item)) {
00490       const char *s = item->Text();
00491       i = 0;
00492       item_nr = 0;
00493       if (s && (s = skipspace(s)) != '\0' && '0' <= s[i] && s[i] <= '9') {
00494          do {
00495             item_nr = item_nr * 10 + (s[i] - '0');
00496             }
00497          while ( !((s[++i] == '\t')||(s[i] == ' ')) && (s[i] != '\0') && ('0' <= s[i]) && (s[i] <= '9'));
00498          if ((Key == kOk) && (item_nr == key_nr)) {
00499             current = item->Index();
00500             RefreshCurrent();
00501             Display();
00502             cRemote::Put(kOk, true);
00503             key_nr = -1;
00504             break;
00505             }
00506          else if (Key != kOk) {
00507             if (!highlight && (item_nr == (Key - k0))) {
00508                highlight = true;
00509                current = item->Index();
00510                }
00511             if (!match && (key_nr == -1) && ((item_nr / 10) == (Key - k0))) {
00512                match = true;
00513                key_nr = (Key - k0);
00514                }
00515             else if (((key_nr == -1) && (item_nr == (Key - k0))) || (!match && (key_nr >= 0) && (item_nr == (10 * key_nr + Key - k0)))) {
00516                current = item->Index();
00517                cRemote::Put(kOk, true);
00518                key_nr = -1;
00519                break;
00520                }
00521             }
00522          }
00523       }
00524   if ((!match) && (Key != kNone))
00525      key_nr = -1;
00526   return osContinue;
00527 }
00528 
00529 eOSState cOsdMenu::AddSubMenu(cOsdMenu *SubMenu)
00530 {
00531   delete subMenu;
00532   subMenu = SubMenu;
00533   subMenu->Display();
00534   return osContinue; // convenience return value
00535 }
00536 
00537 eOSState cOsdMenu::CloseSubMenu()
00538 {
00539   delete subMenu;
00540   subMenu = NULL;
00541   RefreshCurrent();
00542   Display();
00543   return osContinue; // convenience return value
00544 }
00545 
00546 eOSState cOsdMenu::ProcessKey(eKeys Key)
00547 {
00548   if (subMenu) {
00549      eOSState state = subMenu->ProcessKey(Key);
00550      if (state == osBack)
00551         return CloseSubMenu();
00552      return state;
00553      }
00554 
00555   cOsdItem *item = Get(current);
00556   if (marked < 0 && item) {
00557      eOSState state = item->ProcessKey(Key);
00558      if (state != osUnknown) {
00559         DisplayCurrent(true);
00560         return state;
00561         }
00562      }
00563   switch (int(Key)) {
00564     case kNone:
00565     case k0...k9: return hasHotkeys ? HotKey(Key) : osUnknown;
00566     case kUp|k_Repeat:
00567     case kUp:   CursorUp();   break;
00568     case kDown|k_Repeat:
00569     case kDown: CursorDown(); break;
00570     case kLeft|k_Repeat:
00571     case kLeft: PageUp(); break;
00572     case kRight|k_Repeat:
00573     case kRight: PageDown(); break;
00574     case kBack: return osBack;
00575     case kOk:   if (marked >= 0) {
00576                    SetStatus(NULL);
00577                    if (marked != current)
00578                       Move(marked, current);
00579                    marked = -1;
00580                    break;
00581                    }
00582                 // else run into default
00583     default: if (marked < 0)
00584                 return osUnknown;
00585     }
00586   return osContinue;
00587 }