vdr/osdbase.c
Klaus Schmidinger 3df1b6d139 Version 1.7.25
Original announce message:
VDR developer version 1.7.25 is now available at

       ftp://ftp.tvdr.de/vdr/Developer/vdr-1.7.25.tar.bz2

A 'diff' against the previous version is available at

       ftp://ftp.tvdr.de/vdr/Developer/vdr-1.7.24-1.7.25.diff

MD5 checksums:

a3fd482a3dd8027706d4f32a88c6bd13  vdr-1.7.25.tar.bz2
f34adcdc0cdab378431d8946872d6b59  vdr-1.7.24-1.7.25.diff

WARNING:
========

This is a developer version. Even though I use it in my productive
environment. I strongly recommend that you only use it under controlled
conditions and for testing and debugging.

From the HISTORY file:
- The fps value for channels where it differs from the default is now set correctly
  when pausing live video.
- Increased the average character estimate for calculating tab positions in skins,
  to better suit wide fonts (reported by Rudi Hofer).
- Fixed getting the subsystem ids of DVB devices in case they have been rearranged
  via udev rules.
- Added several cTimer::Set...() functions (suggested by Alexander Rieger).
- Changed the return value of cTimer::SetFile() to 'void'.
- Revoked "Fixed a possible deadlock in time shift mode" because it caused trouble with
  output on vdr-xine and dxr3, and also short glitches when replaying on any output
  device.
- Updated the Estonian OSD texts (thanks to Arthur Konovalov).
- Updated the Finnish OSD texts (thanks to Rolf Ahrenberg).
- Fixed handling subtitle color palettes on channels where subtitles appear
  "word by word" (thanks to Rolf Ahrenberg).
- Fixed upscaling cBitmaps with anti-aliasing (thanks to Rolf Ahrenberg for reporting
  a problem with color palettes in subtitles).
- Fixed getting the video aspect ratio for scaling subtitles.
- Removed the "PrimaryLimit". Old "full featured" DVB cards can be run with the
  --outputonly option to avoid problems with recording high bandwidth channels.
  Besides, with HDTV becoming ever more popular those cards are pretty much obsolete
  by now (the TT S2-6400 has no problems recording and replaying high bandwidth
  channels simultaneously). And, last but not least, people using software players
  won't notice this change, anyway.
- Since cReceivers can have priorities between -99 and 99, the priority
  for an unused device has been changed from -1 to -100.
- If the first event in a schedule has a table id of 0x00, any incoming EIT data for
  that schedule from the DVB stream will be completely ignored. This way an external
  EPG source can fill the schedules with EPG data that will not be messed up with
  that from the DVB data stream. Note, though, that this means VDR can not do VPS
  controlled recordings with such events!
- Added some typecasts to silence gcc compiler warnings (thanks to Rolf Ahrenberg).
- Fixed handling overlapping timers in case a VPS timer with higher priority needs
  to interrupt a timer with lower priority.
- The code for the RCU remote control unit has been moved into a separate plugin
  named "rcu".
  The REMOTE=RCU option in the 'make' call for VDR is now obsolete.
  The command line option --rcu is now obsolete. Use -Prcu instead. If you have
  used --rcu with a device path, use -P"rcu -d<device>".
- Added support for automatically selecting subtitles when playing old PES
  recordings made with the subtitles plugin (thanks to Anssi Hannula).
- Revised priority handling to allow receivers with a priority that is lower than
  that of live viewing (with suggestions from Frank Schmirler):
  + An idle device (one that is not used for live viewing and has no receiver
    attached to it) now has priority IDLEPRIORITY (-100).
  + An unused CAM slot now has priority IDLEPRIORITY.
  + The default priority of a cReceiver is now MINPRIORITY (-99).
  + A device that is used only for live viewing (no matter whether it's in Transfer
    Mode or real live mode) now has priority TRANSFERPRIORITY (-1).
  + The function cDevice::Receiving() now returns true if there is any receiver
    attached to the device. Its boolean parameter has no meaning any more.
  + The default value for the Priority parameter of the function cDevice::ProvidesChannel()
    has been changed to IDLEPRIORITY.
- Added a Query parameter to cDevice::GetDevice(), so that devices can be queried
  without side effects when zapping.
- Replaced min(max()) calls with the new function constrain().
- Fixed handling OSD color button texts in case a menu item has texts of its own
  (reported by Rolf Ahrenberg). If a plugin creates derived cMenuEditItems that set
  color button texts, these should not set the texts directly by calling
  cSkinDisplay::Current()->SetButtons(), but rather call the new member function
  cMenuEditItem::SetHelp().
- Moved the call to cStatus::MsgChannelSwitch(this, 0) to the beginning of
  cDevice::SetChannel(), so that any receivers that have been attached to the
  device by plugins may be detached before the final call to GetDevice().
  This actually reverts "Only calling cStatus::MsgChannelSwitch() if a channel
  is actually going to be switched or has actually been switched successfully"
  which was made in version 1.1.10, so please report if this has any unwanted
  side effects.
2012-03-10 23:52:09 +01:00

543 lines
13 KiB
C

/*
* osdbase.c: Basic interface to the On Screen Display
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: osdbase.c 2.4 2012/03/02 15:49:57 kls Exp $
*/
#include "osdbase.h"
#include <string.h>
#include "device.h"
#include "i18n.h"
#include "menuitems.h"
#include "remote.h"
#include "status.h"
// --- cOsdItem --------------------------------------------------------------
cOsdItem::cOsdItem(eOSState State)
{
text = NULL;
state = State;
selectable = true;
fresh = true;
}
cOsdItem::cOsdItem(const char *Text, eOSState State, bool Selectable)
{
text = NULL;
state = State;
selectable = Selectable;
fresh = true;
SetText(Text);
}
cOsdItem::~cOsdItem()
{
free(text);
}
void cOsdItem::SetText(const char *Text, bool Copy)
{
free(text);
text = Copy ? strdup(Text ? Text : "") : (char *)Text; // text assumes ownership!
}
void cOsdItem::SetSelectable(bool Selectable)
{
selectable = Selectable;
}
void cOsdItem::SetFresh(bool Fresh)
{
fresh = Fresh;
}
eOSState cOsdItem::ProcessKey(eKeys Key)
{
return Key == kOk ? state : osUnknown;
}
// --- cOsdObject ------------------------------------------------------------
void cOsdObject::Show(void)
{
if (isMenu)
((cOsdMenu *)this)->Display();
}
// --- cOsdMenu --------------------------------------------------------------
cSkinDisplayMenu *cOsdMenu::displayMenu = NULL;
int cOsdMenu::displayMenuCount = 0;
int cOsdMenu::displayMenuItems = 0;//XXX dynamic???
cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4)
{
isMenu = true;
digit = 0;
hasHotkeys = false;
title = NULL;
SetTitle(Title);
SetCols(c0, c1, c2, c3, c4);
first = 0;
current = marked = -1;
subMenu = NULL;
helpRed = helpGreen = helpYellow = helpBlue = NULL;
helpDisplayed = false;
status = NULL;
if (!displayMenuCount++)
SetDisplayMenu();
}
cOsdMenu::~cOsdMenu()
{
free(title);
delete subMenu;
free(status);
displayMenu->Clear();
cStatus::MsgOsdClear();
if (!--displayMenuCount)
DELETENULL(displayMenu);
}
void cOsdMenu::SetDisplayMenu(void)
{
if (displayMenu) {
displayMenu->Clear();
delete displayMenu;
}
displayMenu = Skins.Current()->DisplayMenu();
displayMenuItems = displayMenu->MaxItems();
}
const char *cOsdMenu::hk(const char *s)
{
static cString buffer;
if (s && hasHotkeys) {
if (digit == 0 && '1' <= *s && *s <= '9' && *(s + 1) == ' ')
digit = -1; // prevents automatic hotkeys - input already has them
if (digit >= 0) {
digit++;
buffer = cString::sprintf(" %c %s", (digit < 10) ? '0' + digit : ' ' , s);
s = buffer;
}
}
return s;
}
void cOsdMenu::SetCols(int c0, int c1, int c2, int c3, int c4)
{
cols[0] = c0;
cols[1] = c1;
cols[2] = c2;
cols[3] = c3;
cols[4] = c4;
}
void cOsdMenu::SetHasHotkeys(bool HasHotkeys)
{
hasHotkeys = HasHotkeys;
digit = 0;
}
void cOsdMenu::SetStatus(const char *s)
{
free(status);
status = s ? strdup(s) : NULL;
displayMenu->SetMessage(mtStatus, s);
}
void cOsdMenu::SetTitle(const char *Title)
{
free(title);
title = strdup(Title);
}
void cOsdMenu::DisplayHelp(bool Force)
{
if (!helpDisplayed || Force) {
displayMenu->SetButtons(helpRed, helpGreen, helpYellow, helpBlue);
cStatus::MsgOsdHelpKeys(helpRed, helpGreen, helpYellow, helpBlue);
helpDisplayed = true;
}
}
void cOsdMenu::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue)
{
// strings are NOT copied - must be constants!!!
helpRed = Red;
helpGreen = Green;
helpYellow = Yellow;
helpBlue = Blue;
DisplayHelp(true);
}
void cOsdMenu::Del(int Index)
{
cList<cOsdItem>::Del(Get(Index));
int count = Count();
while (current < count && !SelectableItem(current))
current++;
if (current == count) {
while (current > 0 && !SelectableItem(current))
current--;
}
if (Index == first && first > 0)
first--;
}
void cOsdMenu::Add(cOsdItem *Item, bool Current, cOsdItem *After)
{
cList<cOsdItem>::Add(Item, After);
if (Current)
current = Item->Index();
}
void cOsdMenu::Ins(cOsdItem *Item, bool Current, cOsdItem *Before)
{
cList<cOsdItem>::Ins(Item, Before);
if (Current)
current = Item->Index();
}
void cOsdMenu::Display(void)
{
if (subMenu) {
subMenu->Display();
return;
}
displayMenu->SetMessage(mtStatus, NULL);
displayMenu->Clear();
cStatus::MsgOsdClear();
displayMenu->SetTabs(cols[0], cols[1], cols[2], cols[3], cols[4]);//XXX
displayMenu->SetTitle(title);
cStatus::MsgOsdTitle(title);
DisplayHelp(true);
int count = Count();
if (count > 0) {
int ni = 0;
for (cOsdItem *item = First(); item; item = Next(item)) {
cStatus::MsgOsdItem(item->Text(), ni++);
if (current < 0 && item->Selectable())
current = item->Index();
}
if (current < 0)
current = 0; // just for safety - there HAS to be a current item!
first = min(first, max(0, count - displayMenuItems)); // in case the menu size has changed
if (current - first >= displayMenuItems || current < first) {
first = current - displayMenuItems / 2;
if (first + displayMenuItems > count)
first = count - displayMenuItems;
if (first < 0)
first = 0;
}
int i = first;
int n = 0;
for (cOsdItem *item = Get(first); item; item = Next(item)) {
bool CurrentSelectable = (i == current) && item->Selectable();
displayMenu->SetItem(item->Text(), i - first, CurrentSelectable, item->Selectable());
if (CurrentSelectable)
cStatus::MsgOsdCurrentItem(item->Text());
if (++n == displayMenuItems)
break;
i++;
}
}
displayMenu->SetScrollbar(count, first);
if (!isempty(status))
displayMenu->SetMessage(mtStatus, status);
}
void cOsdMenu::SetCurrent(cOsdItem *Item)
{
current = Item ? Item->Index() : -1;
}
void cOsdMenu::RefreshCurrent(void)
{
cOsdItem *item = Get(current);
if (item)
item->Set();
}
void cOsdMenu::DisplayCurrent(bool Current)
{
cOsdItem *item = Get(current);
if (item) {
displayMenu->SetItem(item->Text(), current - first, Current && item->Selectable(), item->Selectable());
if (Current && item->Selectable())
cStatus::MsgOsdCurrentItem(item->Text());
if (!Current)
item->SetFresh(true); // leaving the current item resets 'fresh'
if (cMenuEditItem *MenuEditItem = dynamic_cast<cMenuEditItem *>(item)) {
if (!MenuEditItem->DisplayHelp())
DisplayHelp();
else
helpDisplayed = false;
}
}
}
void cOsdMenu::DisplayItem(cOsdItem *Item)
{
if (Item) {
int Index = Item->Index();
int Offset = Index - first;
if (Offset >= 0 && Offset < first + displayMenuItems) {
bool Current = Index == current;
displayMenu->SetItem(Item->Text(), Offset, Current && Item->Selectable(), Item->Selectable());
if (Current && Item->Selectable())
cStatus::MsgOsdCurrentItem(Item->Text());
}
}
}
void cOsdMenu::Clear(void)
{
if (marked >= 0)
SetStatus(NULL);
first = 0;
current = marked = -1;
cList<cOsdItem>::Clear();
}
bool cOsdMenu::SelectableItem(int idx)
{
cOsdItem *item = Get(idx);
return item && item->Selectable();
}
void cOsdMenu::CursorUp(void)
{
int tmpCurrent = current;
int lastOnScreen = first + displayMenuItems - 1;
int last = Count() - 1;
if (last < 0)
return;
while (--tmpCurrent != current) {
if (tmpCurrent < 0) {
if (first > 0) {
// make non-selectable items at the beginning visible:
first = 0;
Display();
return;
}
if (Setup.MenuScrollWrap)
tmpCurrent = last + 1;
else
return;
}
else if (SelectableItem(tmpCurrent))
break;
}
if (first <= tmpCurrent && tmpCurrent <= lastOnScreen)
DisplayCurrent(false);
current = tmpCurrent;
if (current < first) {
first = Setup.MenuScrollPage ? max(0, current - displayMenuItems + 1) : current;
Display();
}
else if (current > lastOnScreen) {
first = max(0, current - displayMenuItems + 1);
Display();
}
else
DisplayCurrent(true);
}
void cOsdMenu::CursorDown(void)
{
int tmpCurrent = current;
int lastOnScreen = first + displayMenuItems - 1;
int last = Count() - 1;
if (last < 0)
return;
while (++tmpCurrent != current) {
if (tmpCurrent > last) {
if (first < last - displayMenuItems) {
// make non-selectable items at the end visible:
first = last - displayMenuItems + 1;
Display();
return;
}
if (Setup.MenuScrollWrap)
tmpCurrent = -1;
else
return;
}
else if (SelectableItem(tmpCurrent))
break;
}
if (first <= tmpCurrent && tmpCurrent <= lastOnScreen)
DisplayCurrent(false);
current = tmpCurrent;
if (current > lastOnScreen) {
first = Setup.MenuScrollPage ? current : max(0, current - displayMenuItems + 1);
if (first + displayMenuItems > last)
first = max(0, last - displayMenuItems + 1);
Display();
}
else if (current < first) {
first = current;
Display();
}
else
DisplayCurrent(true);
}
void cOsdMenu::PageUp(void)
{
int oldCurrent = current;
int oldFirst = first;
current -= displayMenuItems;
first -= displayMenuItems;
int last = Count() - 1;
if (current < 0)
current = 0;
if (first < 0)
first = 0;
int tmpCurrent = current;
while (!SelectableItem(tmpCurrent) && --tmpCurrent >= 0)
;
if (tmpCurrent < 0) {
tmpCurrent = current;
while (++tmpCurrent <= last && !SelectableItem(tmpCurrent))
;
}
current = tmpCurrent <= last ? tmpCurrent : -1;
if (current >= 0) {
if (current < first)
first = current;
else if (current - first >= displayMenuItems)
first = current - displayMenuItems + 1;
}
if (current != oldCurrent || first != oldFirst) {
Display();
DisplayCurrent(true);
}
else if (Setup.MenuScrollWrap)
CursorUp();
}
void cOsdMenu::PageDown(void)
{
int oldCurrent = current;
int oldFirst = first;
current += displayMenuItems;
first += displayMenuItems;
int last = Count() - 1;
if (current > last)
current = last;
if (first + displayMenuItems > last)
first = max(0, last - displayMenuItems + 1);
int tmpCurrent = current;
while (!SelectableItem(tmpCurrent) && ++tmpCurrent <= last)
;
if (tmpCurrent > last) {
tmpCurrent = current;
while (--tmpCurrent >= 0 && !SelectableItem(tmpCurrent))
;
}
current = tmpCurrent > 0 ? tmpCurrent : -1;
if (current >= 0) {
if (current < first)
first = current;
else if (current - first >= displayMenuItems)
first = current - displayMenuItems + 1;
}
if (current != oldCurrent || first != oldFirst) {
Display();
DisplayCurrent(true);
}
else if (Setup.MenuScrollWrap)
CursorDown();
}
void cOsdMenu::Mark(void)
{
if (Count() && marked < 0) {
marked = current;
SetStatus(tr("Up/Dn for new location - OK to move"));
}
}
eOSState cOsdMenu::HotKey(eKeys Key)
{
for (cOsdItem *item = First(); item; item = Next(item)) {
const char *s = item->Text();
if (s && (s = skipspace(s)) != NULL) {
if (*s == Key - k1 + '1') {
current = item->Index();
RefreshCurrent();
Display();
cRemote::Put(kOk, true);
break;
}
}
}
return osContinue;
}
eOSState cOsdMenu::AddSubMenu(cOsdMenu *SubMenu)
{
delete subMenu;
subMenu = SubMenu;
subMenu->Display();
return osContinue; // convenience return value
}
eOSState cOsdMenu::CloseSubMenu()
{
delete subMenu;
subMenu = NULL;
RefreshCurrent();
Display();
return osContinue; // convenience return value
}
eOSState cOsdMenu::ProcessKey(eKeys Key)
{
if (subMenu) {
eOSState state = subMenu->ProcessKey(Key);
if (state == osBack)
return CloseSubMenu();
return state;
}
cOsdItem *item = Get(current);
if (marked < 0 && item) {
eOSState state = item->ProcessKey(Key);
if (state != osUnknown) {
DisplayCurrent(true);
return state;
}
}
switch (int(Key)) {
case k0: return osUnknown;
case k1...k9: return hasHotkeys ? HotKey(Key) : osUnknown;
case kUp|k_Repeat:
case kUp: CursorUp(); break;
case kDown|k_Repeat:
case kDown: CursorDown(); break;
case kLeft|k_Repeat:
case kLeft: PageUp(); break;
case kRight|k_Repeat:
case kRight: PageDown(); break;
case kBack: return osBack;
case kOk: if (marked >= 0) {
SetStatus(NULL);
if (marked != current)
Move(marked, current);
marked = -1;
break;
}
// else run into default
default: if (marked < 0)
return osUnknown;
}
return osContinue;
}