mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
- Added 'Hrvatska radiotelevizija' and 'RTV Slovenija' to ca.conf (thanks to Paul Gohn). - Implemented actual user input for CAM enquiry menus. - Since disk file systems apparently don't honor the O_NONBLOCK flag to read from a file in non-blocking mode the cDvbPlayer now uses a non blocking file reader class to make sure replay remains smooth even under heavy system load. - Increased the maximum possible packet size in remux.c to avoid corrupted streams with broadcasters that send extremely large PES packets (thanks to Teemu Rantanen). - Added TS error checking to remux.c (thanks to Teemu Rantanen). - Modified cRingBufferLinear to avoid excessive memmove() calls in 'Transfer Mode' and during recordings, which dramatically reduces CPU load. Thanks to Teemu Rantanen for pinpointing the problem with the excessive memmove() calls. - Updated 'channels.conf' (thanks to Achim Lange). - Added/improved Swedish language texts (thanks to Jan Ekholm). - Fixed the description of the "Scroll pages" OSD setup parameter ('yes' and 'no' were mixed up). - Fixed handling the LOG_LOCALn parameters in the -l option (thanks to Dimitrios Dimitrakos). - Changed EIT processing to always read a full section. - Fixed handling user defined CFLAGS in libdtv/libvdr/Makefile (thanks to Clemens Kirchgatterer and Robert Schiele). - Fixed skipping unavailable channels in the EPG scanner.
586 lines
15 KiB
C
586 lines
15 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.12 2003/01/18 13:34:40 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();
|
|
}
|
|
|
|
// --- 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;
|
|
if (k0 <= Key && Key <= k9) {
|
|
if (fresh) {
|
|
*value = 0;
|
|
fresh = false;
|
|
}
|
|
newValue = *value * 10 + (Key - k0);
|
|
}
|
|
else if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
|
|
newValue = *value - 1;
|
|
fresh = true;
|
|
}
|
|
else if (NORMALKEY(Key) == kRight) {
|
|
newValue = *value + 1;
|
|
fresh = true;
|
|
}
|
|
else {
|
|
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);
|
|
}
|
|
|
|
// --- 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);
|
|
}
|