vdr
1.7.27
|
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 }