mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
- Completed the Finnish OSD texts (thanks to Rolf Ahrenberg). - Fixed some descriptor handling in 'libsi' (thanks to Stéphane Esté-Gracias). - Fixed handling the current menu item (thanks to Marc Hoppe). - Fixed assigning events to timers (they no longer get "stuck"). - Added log entries whenever the running status of an event changes (currently only logging the first 30 channels). - Fixed handling timers in VPS margin if the EPG scan is turned on (the EPG scan switched the device away from the channel, so it wouldn't see the change of the running status). - Fixed handling "itemized" texts in EPG data (thanks to Stéphane Esté-Gracias for pointing out this problem, and Marcel Wiesweg for improving 'libsi'). - Fixed handling VPS times at year boundaries. - Avoiding too many consecutive "ring buffer overflow" messages (which only slowed down performance even more). - Taking the Sid into account when detecting version changes in processing the PMT (thanks to Stéphane Esté-Gracias for pointing out this problem). - Completed the Russian OSD texts (thanks to Vyacheslav Dikonov). - Any newline characters in the 'description' of EPG events are now preserved to allow texts to be displayed the way the tv stations have formatted them. This was also necessary to better display itemized texts. - Fixed detecting the running status in case an empty EPG event is broadcast (thanks to Michael Pennewiß for pointing this out). - Improved performance when paging through very long menu lists. - Removed cSchedule::GetEventNumber() and cSchedule::NumEvents(). There is now cSchedule::Events() that returns the list of events directly. - Avoiding occasional bad responsiveness to user interaction caused by assigning events to timers. - Now explicitly turning on the LNB power at startup, because newer drivers don't do this any more (thanks to Oliver Endriss for pointing this out).
607 lines
16 KiB
C
607 lines
16 KiB
C
/*
|
|
* menuitems.c: General purpose menu items
|
|
*
|
|
* See the main source file 'vdr.c' for copyright information and
|
|
* how to reach the author.
|
|
*
|
|
* $Id: menuitems.c 1.15 2004/02/24 12:38:43 kls Exp $
|
|
*/
|
|
|
|
#include "menuitems.h"
|
|
#include <ctype.h>
|
|
#include "i18n.h"
|
|
#include "plugin.h"
|
|
#include "status.h"
|
|
|
|
const char *FileNameChars = " abcdefghijklmnopqrstuvwxyz0123456789-.#~";
|
|
|
|
// --- cMenuEditItem ---------------------------------------------------------
|
|
|
|
cMenuEditItem::cMenuEditItem(const char *Name)
|
|
{
|
|
name = strdup(Name);
|
|
value = NULL;
|
|
}
|
|
|
|
cMenuEditItem::~cMenuEditItem()
|
|
{
|
|
free(name);
|
|
free(value);
|
|
}
|
|
|
|
void cMenuEditItem::SetValue(const char *Value)
|
|
{
|
|
free(value);
|
|
value = strdup(Value);
|
|
char *buffer = NULL;
|
|
asprintf(&buffer, "%s:\t%s", name, value);
|
|
SetText(buffer, false);
|
|
Display();
|
|
cStatus::MsgOsdCurrentItem(buffer);
|
|
}
|
|
|
|
// --- cMenuEditIntItem ------------------------------------------------------
|
|
|
|
cMenuEditIntItem::cMenuEditIntItem(const char *Name, int *Value, int Min, int Max)
|
|
:cMenuEditItem(Name)
|
|
{
|
|
value = Value;
|
|
min = Min;
|
|
max = Max;
|
|
Set();
|
|
}
|
|
|
|
void cMenuEditIntItem::Set(void)
|
|
{
|
|
char buf[16];
|
|
snprintf(buf, sizeof(buf), "%d", *value);
|
|
SetValue(buf);
|
|
}
|
|
|
|
eOSState cMenuEditIntItem::ProcessKey(eKeys Key)
|
|
{
|
|
eOSState state = cMenuEditItem::ProcessKey(Key);
|
|
|
|
if (state == osUnknown) {
|
|
int newValue = *value;
|
|
Key = NORMALKEY(Key);
|
|
switch (Key) {
|
|
case kNone: break;
|
|
case k0 ... k9:
|
|
if (fresh) {
|
|
*value = 0;
|
|
fresh = false;
|
|
}
|
|
newValue = *value * 10 + (Key - k0);
|
|
break;
|
|
case kLeft: // TODO might want to increase the delta if repeated quickly?
|
|
newValue = *value - 1;
|
|
fresh = true;
|
|
break;
|
|
case kRight:
|
|
newValue = *value + 1;
|
|
fresh = true;
|
|
break;
|
|
default:
|
|
if (*value < min) { *value = min; Set(); }
|
|
if (*value > max) { *value = max; Set(); }
|
|
return state;
|
|
}
|
|
if ((!fresh || min <= newValue) && newValue <= max) {
|
|
*value = newValue;
|
|
Set();
|
|
}
|
|
state = osContinue;
|
|
}
|
|
return state;
|
|
}
|
|
|
|
// --- cMenuEditBoolItem -----------------------------------------------------
|
|
|
|
cMenuEditBoolItem::cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString, const char *TrueString)
|
|
:cMenuEditIntItem(Name, Value, 0, 1)
|
|
{
|
|
falseString = FalseString ? FalseString : tr("no");
|
|
trueString = TrueString ? TrueString : tr("yes");
|
|
Set();
|
|
}
|
|
|
|
void cMenuEditBoolItem::Set(void)
|
|
{
|
|
char buf[16];
|
|
snprintf(buf, sizeof(buf), "%s", *value ? trueString : falseString);
|
|
SetValue(buf);
|
|
}
|
|
|
|
// --- cMenuEditBitItem ------------------------------------------------------
|
|
|
|
cMenuEditBitItem::cMenuEditBitItem(const char *Name, int *Value, int Mask, const char *FalseString, const char *TrueString)
|
|
:cMenuEditBoolItem(Name, &bit, FalseString, TrueString)
|
|
{
|
|
value = Value;
|
|
bit = (*value & Mask) != 0;
|
|
mask = Mask;
|
|
Set();
|
|
}
|
|
|
|
void cMenuEditBitItem::Set(void)
|
|
{
|
|
*value = bit ? *value | mask : *value & ~mask;
|
|
cMenuEditBoolItem::Set();
|
|
}
|
|
|
|
// --- cMenuEditNumItem ------------------------------------------------------
|
|
|
|
cMenuEditNumItem::cMenuEditNumItem(const char *Name, char *Value, int Length, bool Blind)
|
|
:cMenuEditItem(Name)
|
|
{
|
|
value = Value;
|
|
length = Length;
|
|
blind = Blind;
|
|
Set();
|
|
}
|
|
|
|
void cMenuEditNumItem::Set(void)
|
|
{
|
|
if (blind) {
|
|
char buf[length + 1];
|
|
int i;
|
|
for (i = 0; i < length && value[i]; i++)
|
|
buf[i] = '*';
|
|
buf[i] = 0;
|
|
SetValue(buf);
|
|
}
|
|
else
|
|
SetValue(value);
|
|
}
|
|
|
|
eOSState cMenuEditNumItem::ProcessKey(eKeys Key)
|
|
{
|
|
eOSState state = cMenuEditItem::ProcessKey(Key);
|
|
|
|
if (state == osUnknown) {
|
|
Key = NORMALKEY(Key);
|
|
switch (Key) {
|
|
case kLeft: {
|
|
int l = strlen(value);
|
|
if (l > 0)
|
|
value[l - 1] = 0;
|
|
}
|
|
break;
|
|
case k0 ... k9: {
|
|
int l = strlen(value);
|
|
if (l < length) {
|
|
value[l] = Key - k0 + '0';
|
|
value[l + 1] = 0;
|
|
}
|
|
}
|
|
break;
|
|
default: return state;
|
|
}
|
|
Set();
|
|
state = osContinue;
|
|
}
|
|
return state;
|
|
}
|
|
|
|
// --- cMenuEditChrItem ------------------------------------------------------
|
|
|
|
cMenuEditChrItem::cMenuEditChrItem(const char *Name, char *Value, const char *Allowed)
|
|
:cMenuEditItem(Name)
|
|
{
|
|
value = Value;
|
|
allowed = strdup(Allowed);
|
|
current = strchr(allowed, *Value);
|
|
if (!current)
|
|
current = allowed;
|
|
Set();
|
|
}
|
|
|
|
cMenuEditChrItem::~cMenuEditChrItem()
|
|
{
|
|
free(allowed);
|
|
}
|
|
|
|
void cMenuEditChrItem::Set(void)
|
|
{
|
|
char buf[2];
|
|
snprintf(buf, sizeof(buf), "%c", *value);
|
|
SetValue(buf);
|
|
}
|
|
|
|
eOSState cMenuEditChrItem::ProcessKey(eKeys Key)
|
|
{
|
|
eOSState state = cMenuEditItem::ProcessKey(Key);
|
|
|
|
if (state == osUnknown) {
|
|
if (NORMALKEY(Key) == kLeft) {
|
|
if (current > allowed)
|
|
current--;
|
|
}
|
|
else if (NORMALKEY(Key) == kRight) {
|
|
if (*(current + 1))
|
|
current++;
|
|
}
|
|
else
|
|
return state;
|
|
*value = *current;
|
|
Set();
|
|
state = osContinue;
|
|
}
|
|
return state;
|
|
}
|
|
|
|
// --- cMenuEditStrItem ------------------------------------------------------
|
|
|
|
cMenuEditStrItem::cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed)
|
|
:cMenuEditItem(Name)
|
|
{
|
|
value = Value;
|
|
length = Length;
|
|
allowed = strdup(Allowed);
|
|
pos = -1;
|
|
insert = uppercase = false;
|
|
newchar = true;
|
|
Set();
|
|
}
|
|
|
|
cMenuEditStrItem::~cMenuEditStrItem()
|
|
{
|
|
free(allowed);
|
|
}
|
|
|
|
void cMenuEditStrItem::SetHelpKeys(void)
|
|
{
|
|
if (pos >= 0)
|
|
Interface->Help(tr("ABC/abc"), tr(insert ? "Overwrite" : "Insert"), tr("Delete"));
|
|
else
|
|
Interface->Help(NULL);
|
|
}
|
|
|
|
void cMenuEditStrItem::Set(void)
|
|
{
|
|
char buf[1000];
|
|
const char *fmt = insert && newchar ? "[]%c%s" : "[%c]%s";
|
|
|
|
if (pos >= 0) {
|
|
strncpy(buf, value, pos);
|
|
snprintf(buf + pos, sizeof(buf) - pos - 2, fmt, *(value + pos), value + pos + 1);
|
|
int width = Interface->Width() - Interface->GetCols()[0];
|
|
if (cOsd::WidthInCells(buf) <= width) {
|
|
// the whole buffer fits on the screen
|
|
SetValue(buf);
|
|
return;
|
|
}
|
|
width *= cOsd::CellWidth();
|
|
width -= cOsd::Width('>'); // assuming '<' and '>' have the same with
|
|
int w = 0;
|
|
int i = 0;
|
|
int l = strlen(buf);
|
|
while (i < l && w <= width)
|
|
w += cOsd::Width(buf[i++]);
|
|
if (i >= pos + 4) {
|
|
// the cursor fits on the screen
|
|
buf[i - 1] = '>';
|
|
buf[i] = 0;
|
|
SetValue(buf);
|
|
return;
|
|
}
|
|
// the cursor doesn't fit on the screen
|
|
w = 0;
|
|
if (buf[i = pos + 3]) {
|
|
buf[i] = '>';
|
|
buf[i + 1] = 0;
|
|
}
|
|
else
|
|
i--;
|
|
while (i >= 0 && w <= width)
|
|
w += cOsd::Width(buf[i--]);
|
|
buf[++i] = '<';
|
|
SetValue(buf + i);
|
|
}
|
|
else
|
|
SetValue(value);
|
|
}
|
|
|
|
char cMenuEditStrItem::Inc(char c, bool Up)
|
|
{
|
|
const char *p = strchr(allowed, c);
|
|
if (!p)
|
|
p = allowed;
|
|
if (Up) {
|
|
if (!*++p)
|
|
p = allowed;
|
|
}
|
|
else if (--p < allowed)
|
|
p = allowed + strlen(allowed) - 1;
|
|
return *p;
|
|
}
|
|
|
|
eOSState cMenuEditStrItem::ProcessKey(eKeys Key)
|
|
{
|
|
switch (Key) {
|
|
case kRed: // Switch between upper- and lowercase characters
|
|
if (pos >= 0 && (!insert || !newchar)) {
|
|
uppercase = !uppercase;
|
|
value[pos] = uppercase ? toupper(value[pos]) : tolower(value[pos]);
|
|
}
|
|
break;
|
|
case kGreen: // Toggle insert/overwrite modes
|
|
if (pos >= 0) {
|
|
insert = !insert;
|
|
newchar = true;
|
|
}
|
|
SetHelpKeys();
|
|
break;
|
|
case kYellow|k_Repeat:
|
|
case kYellow: // Remove the character at current position; in insert mode it is the character to the right of cursor
|
|
if (pos >= 0) {
|
|
if (strlen(value) > 1) {
|
|
if (!insert || pos < int(strlen(value)) - 1)
|
|
memmove(value + pos, value + pos + 1, strlen(value) - pos);
|
|
// reduce position, if we removed the last character
|
|
if (pos == int(strlen(value)))
|
|
pos--;
|
|
}
|
|
else if (strlen(value) == 1)
|
|
value[0] = ' '; // This is the last character in the string, replace it with a blank
|
|
if (isalpha(value[pos]))
|
|
uppercase = isupper(value[pos]);
|
|
newchar = true;
|
|
}
|
|
break;
|
|
case kLeft|k_Repeat:
|
|
case kLeft: if (pos > 0) {
|
|
if (!insert || newchar)
|
|
pos--;
|
|
newchar = true;
|
|
}
|
|
if (!insert && isalpha(value[pos]))
|
|
uppercase = isupper(value[pos]);
|
|
break;
|
|
case kRight|k_Repeat:
|
|
case kRight: if (pos < length - 2 && pos < int(strlen(value)) ) {
|
|
if (++pos >= int(strlen(value))) {
|
|
if (pos >= 2 && value[pos - 1] == ' ' && value[pos - 2] == ' ')
|
|
pos--; // allow only two blanks at the end
|
|
else {
|
|
value[pos] = ' ';
|
|
value[pos + 1] = 0;
|
|
}
|
|
}
|
|
}
|
|
newchar = true;
|
|
if (!insert && isalpha(value[pos]))
|
|
uppercase = isupper(value[pos]);
|
|
if (pos == 0)
|
|
SetHelpKeys();
|
|
break;
|
|
case kUp|k_Repeat:
|
|
case kUp:
|
|
case kDown|k_Repeat:
|
|
case kDown: if (pos >= 0) {
|
|
if (insert && newchar) {
|
|
// create a new character in insert mode
|
|
if (int(strlen(value)) < length - 1) {
|
|
memmove(value + pos + 1, value + pos, strlen(value) - pos + 1);
|
|
value[pos] = ' ';
|
|
}
|
|
}
|
|
if (uppercase)
|
|
value[pos] = toupper(Inc(tolower(value[pos]), NORMALKEY(Key) == kUp));
|
|
else
|
|
value[pos] = Inc( value[pos], NORMALKEY(Key) == kUp);
|
|
newchar = false;
|
|
}
|
|
else
|
|
return cMenuEditItem::ProcessKey(Key);
|
|
break;
|
|
case kOk: if (pos >= 0) {
|
|
pos = -1;
|
|
newchar = true;
|
|
stripspace(value);
|
|
SetHelpKeys();
|
|
break;
|
|
}
|
|
// run into default
|
|
default: if (pos >= 0 && BASICKEY(Key) == kKbd) {
|
|
int c = KEYKBD(Key);
|
|
if (c <= 0xFF) {
|
|
const char *p = strchr(allowed, tolower(c));
|
|
if (p) {
|
|
int l = strlen(value);
|
|
if (insert && l < length - 1)
|
|
memmove(value + pos + 1, value + pos, l - pos + 1);
|
|
value[pos] = c;
|
|
if (pos < length - 2)
|
|
pos++;
|
|
if (pos >= l) {
|
|
value[pos] = ' ';
|
|
value[pos + 1] = 0;
|
|
}
|
|
}
|
|
else {
|
|
switch (c) {
|
|
case 0x7F: // backspace
|
|
if (pos > 0) {
|
|
pos--;
|
|
return ProcessKey(kYellow);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
switch (c) {
|
|
case kfHome: pos = 0; break;
|
|
case kfEnd: pos = strlen(value) - 1; break;
|
|
case kfIns: return ProcessKey(kGreen);
|
|
case kfDel: return ProcessKey(kYellow);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
return cMenuEditItem::ProcessKey(Key);
|
|
}
|
|
Set();
|
|
return osContinue;
|
|
}
|
|
|
|
// --- cMenuEditStraItem -----------------------------------------------------
|
|
|
|
cMenuEditStraItem::cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings)
|
|
:cMenuEditIntItem(Name, Value, 0, NumStrings - 1)
|
|
{
|
|
strings = Strings;
|
|
Set();
|
|
}
|
|
|
|
void cMenuEditStraItem::Set(void)
|
|
{
|
|
SetValue(strings[*value]);
|
|
}
|
|
|
|
// --- cMenuTextItem ---------------------------------------------------------
|
|
|
|
cMenuTextItem::cMenuTextItem(const char *Text, int X, int Y, int W, int H, eDvbColor FgColor, eDvbColor BgColor, eDvbFont Font)
|
|
{
|
|
x = X;
|
|
y = Y;
|
|
w = W;
|
|
h = H;
|
|
fgColor = FgColor;
|
|
bgColor = BgColor;
|
|
font = Font;
|
|
offset = 0;
|
|
eDvbFont oldFont = Interface->SetFont(font);
|
|
text = Interface->WrapText(Text, w - 1, &lines);
|
|
Interface->SetFont(oldFont);
|
|
if (h < 0)
|
|
h = lines;
|
|
}
|
|
|
|
cMenuTextItem::~cMenuTextItem()
|
|
{
|
|
free(text);
|
|
}
|
|
|
|
void cMenuTextItem::Clear(void)
|
|
{
|
|
Interface->Fill(x, y, w, h, bgColor);
|
|
}
|
|
|
|
void cMenuTextItem::Display(int Offset, eDvbColor FgColor, eDvbColor BgColor)
|
|
{
|
|
int l = 0;
|
|
char *t = text;
|
|
eDvbFont oldFont = Interface->SetFont(font);
|
|
while (*t) {
|
|
char *n = strchr(t, '\n');
|
|
if (l >= offset) {
|
|
if (n)
|
|
*n = 0;
|
|
Interface->Write(x, y + l - offset, t, fgColor, bgColor);
|
|
if (n)
|
|
*n = '\n';
|
|
else
|
|
break;
|
|
}
|
|
if (!n)
|
|
break;
|
|
t = n + 1;
|
|
if (++l >= h + offset)
|
|
break;
|
|
}
|
|
Interface->SetFont(oldFont);
|
|
// scroll indicators use inverted color scheme!
|
|
if (CanScrollUp()) Interface->Write(x + w - 1, y, "^", bgColor, fgColor);
|
|
if (CanScrollDown()) Interface->Write(x + w - 1, y + h - 1, "v", bgColor, fgColor);
|
|
cStatus::MsgOsdTextItem(text);
|
|
}
|
|
|
|
void cMenuTextItem::ScrollUp(bool Page)
|
|
{
|
|
if (CanScrollUp()) {
|
|
Clear();
|
|
offset = max(offset - (Page ? h : 1), 0);
|
|
Display();
|
|
}
|
|
cStatus::MsgOsdTextItem(NULL, true);
|
|
}
|
|
|
|
void cMenuTextItem::ScrollDown(bool Page)
|
|
{
|
|
if (CanScrollDown()) {
|
|
Clear();
|
|
offset = min(offset + (Page ? h : 1), lines - h);
|
|
Display();
|
|
}
|
|
cStatus::MsgOsdTextItem(NULL, false);
|
|
}
|
|
|
|
eOSState cMenuTextItem::ProcessKey(eKeys Key)
|
|
{
|
|
switch (Key) {
|
|
case kLeft|k_Repeat:
|
|
case kLeft:
|
|
case kUp|k_Repeat:
|
|
case kUp: ScrollUp(NORMALKEY(Key) == kLeft); break;
|
|
case kRight|k_Repeat:
|
|
case kRight:
|
|
case kDown|k_Repeat:
|
|
case kDown: ScrollDown(NORMALKEY(Key) == kRight); break;
|
|
default: return osUnknown;
|
|
}
|
|
return osContinue;
|
|
}
|
|
|
|
// --- cMenuSetupPage --------------------------------------------------------
|
|
|
|
cMenuSetupPage::cMenuSetupPage(void)
|
|
:cOsdMenu("", 33)
|
|
{
|
|
plugin = NULL;
|
|
}
|
|
|
|
void cMenuSetupPage::SetSection(const char *Section)
|
|
{
|
|
char buf[40];
|
|
snprintf(buf, sizeof(buf), "%s - %s", tr("Setup"), Section);
|
|
SetTitle(buf);
|
|
}
|
|
|
|
eOSState cMenuSetupPage::ProcessKey(eKeys Key)
|
|
{
|
|
eOSState state = cOsdMenu::ProcessKey(Key);
|
|
|
|
if (state == osUnknown) {
|
|
switch (Key) {
|
|
case kOk: Store();
|
|
state = osBack;
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
return state;
|
|
}
|
|
|
|
void cMenuSetupPage::SetPlugin(cPlugin *Plugin)
|
|
{
|
|
plugin = Plugin;
|
|
char buf[40];
|
|
snprintf(buf, sizeof(buf), "%s '%s'", tr("Plugin"), plugin->Name());
|
|
SetSection(buf);
|
|
}
|
|
|
|
void cMenuSetupPage::SetupStore(const char *Name, const char *Value)
|
|
{
|
|
if (plugin)
|
|
plugin->SetupStore(Name, Value);
|
|
}
|
|
|
|
void cMenuSetupPage::SetupStore(const char *Name, int Value)
|
|
{
|
|
if (plugin)
|
|
plugin->SetupStore(Name, Value);
|
|
}
|