vdr/menuitems.c
Klaus Schmidinger 7df66b0587 Version 1.7.20
Original announce message:
VDR developer version 1.7.20 is now available at

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

A 'diff' against the previous version is available at

       ftp://ftp.tvdr.de/vdr/Developer/vdr-1.7.19-1.7.20.diff

MD5 checksums:

eda2911fff1715ba5b1482b20ad18188  vdr-1.7.20.tar.bz2
a8f5bcaf3294cc9fce87283a618d5ce1  vdr-1.7.19-1.7.20.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.

This version contains functions to determine the "signal strength"
and "signal quality" through cDevice. If you are using a DVB card that
contains an stb0899 frontend chip (like the TT-budget S2-3200) you may
want to apply the patches from

   ftp://ftp.tvdr.de/vdr/Developer/Driver-Patches

to the LinuxDVB driver source in order to receive useful results from
that frontend.

From the HISTORY file:
- Added some missing 'const' to tChannelID (reported by Sundararaj Reel).
- The isnumber() function now checks the given pointer for NULL (thanks to Holger
  Dengler).
- Now checking Setup.InitialChannel for NULL before using it (reported by
  Christoph Haubrich).
- cSkins::Message() now blocks calls from background threads (thanks to Michael
  Eiler for reporting a crash in such a scenario).
- Fixed the return value of the svdrpsend.pl script in case of an error (thanks to
  Jonas Diemer).
- Increased MAXCAIDS to 12 (thanks to Jerome Lacarriere).
- Fixed handling DiSEqC codes (thanks to Mark Hawes for reporting the bug, and
  Udo Richter for suggesting the fix).
- Added a mechanism to defer timer handling in case of problems (reported by
  Frank Niederwipper).
- Fixed distortions that happened when splitting recording into several files
  (was a side effect of "Fixed detecting frames in case the Picture Start Code or
  Access Unit Delimiter extends over TS packet boundaries" in version 1.7.19).
  cRecorder::Action() now buffers TS packets in case the frame type is
  not yet known when a new payload starts. This adds no overhead for channels
  that broadcast the frame type within the first TS packet of a payload; it only
  kicks in if that information is not in the first TS packet.
- Fixed handling the channelID in cMenuEditChanItem (thanks to Udo Richter).
- cStringList::Sort() can now be called with a boolean parameter that controls
  case insensitive sorting (suggested by Sundararaj Reel).
- Now scanning new transponders before old ones, to make sure transponder changes
  are recognized (thanks to Reinhard Nissl).
- Implemented static cIndexFile::IndexFileName().
- The length (as number of frames) of a recording's index file can now be determined
  by a call to cIndexFile::GetLength() (suggested by Christoph Haubrich).
- Fixed some crashes in subtitle display (thanks to Rolf Ahrenberg).
- Made DELETENULL() thread safe (reported by Rolf Ahrenberg).
- The pic2mpg script of the 'pictures' plugin now generates HD images (thanks to
  Andre Weidemann for his support in using convert/ffmpeg). The old SD version is
  still available as pic2mpg-sd.
- Added a mutex to protect cOsd::Osds from simultaneous access from different threads
  (reported by Rolf Ahrenberg).
- The cutter now sets the 'broken link' flag for MPEG2 TS recordings (thanks to
  Oliver Endriss).
- Fixed language code entry for Portuguese.
- The new command line options --filesize (suggested by Marco Göbenich) and --split
  can be used together with --edit to set the maximum video file size and turn on
  splitting edited files at the editing marks. These options must be given before
  --edit to have an effect.
- cTimeMs is no longer initialized to the current time if the value given to the
  constructor is negative (avoids the "cTimeMs: using monotonic clock..." log message
  before VDR's starting log message).
2011-08-16 20:32:01 +02:00

1135 lines
31 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 2.10 2011/08/12 13:19:40 kls Exp $
*/
#include "menuitems.h"
#include <ctype.h>
#include <math.h>
#include <wctype.h>
#include "i18n.h"
#include "plugin.h"
#include "remote.h"
#include "skins.h"
#include "status.h"
#define AUTO_ADVANCE_TIMEOUT 1500 // ms before auto advance when entering characters via numeric keys
const char *FileNameChars = trNOOP("FileNameChars$ abcdefghijklmnopqrstuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&");
// --- cMenuEditItem ---------------------------------------------------------
cMenuEditItem::cMenuEditItem(const char *Name)
{
name = strdup(Name ? Name : "???");
}
cMenuEditItem::~cMenuEditItem()
{
free(name);
}
void cMenuEditItem::SetValue(const char *Value)
{
cString buffer = cString::sprintf("%s:\t%s", name, Value);
SetText(buffer);
cStatus::MsgOsdCurrentItem(buffer);
}
// --- cMenuEditIntItem ------------------------------------------------------
cMenuEditIntItem::cMenuEditIntItem(const char *Name, int *Value, int Min, int Max, const char *MinString, const char *MaxString)
:cMenuEditItem(Name)
{
value = Value;
min = Min;
max = Max;
minString = MinString;
maxString = MaxString;
if (*value < min)
*value = min;
else if (*value > max)
*value = max;
Set();
}
void cMenuEditIntItem::Set(void)
{
if (minString && *value == min)
SetValue(minString);
else if (maxString && *value == max)
SetValue(maxString);
else {
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;
bool IsRepeat = Key & k_Repeat;
Key = NORMALKEY(Key);
switch (Key) {
case kNone: break;
case k0 ... k9:
if (fresh) {
newValue = 0;
fresh = false;
}
newValue = newValue * 10 + (Key - k0);
break;
case kLeft: // TODO might want to increase the delta if repeated quickly?
newValue = *value - 1;
fresh = true;
if (!IsRepeat && newValue < min && max != INT_MAX)
newValue = max;
break;
case kRight:
newValue = *value + 1;
fresh = true;
if (!IsRepeat && newValue > max && min != INT_MIN)
newValue = min;
break;
default:
if (*value < min) { *value = min; Set(); }
if (*value > max) { *value = max; Set(); }
return state;
}
if (newValue != *value && (!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, uint *Value, uint 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;
}
// --- cMenuEditPrcItem ------------------------------------------------------
cMenuEditPrcItem::cMenuEditPrcItem(const char *Name, double *Value, double Min, double Max, int Decimals)
:cMenuEditItem(Name)
{
value = Value;
min = Min;
max = Max;
decimals = Decimals;
factor = 100;
while (Decimals-- > 0)
factor *= 10;
if (*value < min)
*value = min;
else if (*value > max)
*value = max;
Set();
}
void cMenuEditPrcItem::Set(void)
{
char buf[16];
snprintf(buf, sizeof(buf), "%.*f", decimals, *value * 100);
SetValue(buf);
}
eOSState cMenuEditPrcItem::ProcessKey(eKeys Key)
{
eOSState state = cMenuEditItem::ProcessKey(Key);
if (state == osUnknown) {
double newValue = round(*value * factor); // avoids precision problems
Key = NORMALKEY(Key);
switch (Key) {
case kNone: break;
case k0 ... k9:
if (fresh) {
newValue = 0;
fresh = false;
}
newValue = newValue * 10 + (Key - k0);
break;
case kLeft: // TODO might want to increase the delta if repeated quickly?
newValue--;
fresh = true;
break;
case kRight:
newValue++;
fresh = true;
break;
default:
if (*value < min) { *value = min; Set(); }
if (*value > max) { *value = max; Set(); }
return state;
}
newValue /= factor;
if (!DoubleEqual(newValue, *value) && (!fresh || min <= newValue) && newValue <= max) {
*value = newValue;
Set();
}
state = osContinue;
}
return state;
}
// --- cMenuEditChrItem ------------------------------------------------------
cMenuEditChrItem::cMenuEditChrItem(const char *Name, char *Value, const char *Allowed)
:cMenuEditItem(Name)
{
value = Value;
allowed = strdup(Allowed ? Allowed : "");
current = strchr(allowed, *Value);
if (!current)
current = allowed;
Set();
}
cMenuEditChrItem::~cMenuEditChrItem()
{
free(allowed);
}
void cMenuEditChrItem::Set(void)
{
char buf[2];
buf[0] = *value;
buf[1] = '\0';
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 = Allowed ? Allowed : tr(FileNameChars);
pos = -1;
offset = 0;
insert = uppercase = false;
newchar = true;
lengthUtf8 = 0;
valueUtf8 = NULL;
allowedUtf8 = NULL;
charMapUtf8 = NULL;
currentCharUtf8 = NULL;
lastKey = kNone;
Set();
}
cMenuEditStrItem::~cMenuEditStrItem()
{
delete[] valueUtf8;
delete[] allowedUtf8;
delete[] charMapUtf8;
}
void cMenuEditStrItem::EnterEditMode(void)
{
if (!valueUtf8) {
valueUtf8 = new uint[length];
lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
int l = strlen(allowed) + 1;
allowedUtf8 = new uint[l];
Utf8ToArray(allowed, allowedUtf8, l);
const char *charMap = tr("CharMap$ 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9");
l = strlen(charMap) + 1;
charMapUtf8 = new uint[l];
Utf8ToArray(charMap, charMapUtf8, l);
currentCharUtf8 = charMapUtf8;
AdvancePos();
}
}
void cMenuEditStrItem::LeaveEditMode(bool SaveValue)
{
if (valueUtf8) {
if (SaveValue) {
Utf8FromArray(valueUtf8, value, length);
stripspace(value);
}
lengthUtf8 = 0;
delete[] valueUtf8;
valueUtf8 = NULL;
delete[] allowedUtf8;
allowedUtf8 = NULL;
delete[] charMapUtf8;
charMapUtf8 = NULL;
pos = -1;
offset = 0;
newchar = true;
}
}
void cMenuEditStrItem::SetHelpKeys(void)
{
if (InEditMode())
cSkinDisplay::Current()->SetButtons(tr("Button$ABC/abc"), insert ? tr("Button$Overwrite") : tr("Button$Insert"), tr("Button$Delete"));
else
cSkinDisplay::Current()->SetButtons(NULL);
}
uint *cMenuEditStrItem::IsAllowed(uint c)
{
if (allowedUtf8) {
for (uint *a = allowedUtf8; *a; a++) {
if (c == *a)
return a;
}
}
return NULL;
}
void cMenuEditStrItem::AdvancePos(void)
{
if (pos < length - 2 && pos < lengthUtf8) {
if (++pos >= lengthUtf8) {
if (pos >= 2 && valueUtf8[pos - 1] == ' ' && valueUtf8[pos - 2] == ' ')
pos--; // allow only two blanks at the end
else {
valueUtf8[pos] = ' ';
valueUtf8[pos + 1] = 0;
lengthUtf8++;
}
}
}
newchar = true;
if (!insert && Utf8is(alpha, valueUtf8[pos]))
uppercase = Utf8is(upper, valueUtf8[pos]);
}
void cMenuEditStrItem::Set(void)
{
if (InEditMode()) {
// This is an ugly hack to make editing strings work with the 'skincurses' plugin.
const cFont *font = dynamic_cast<cSkinDisplayMenu *>(cSkinDisplay::Current())->GetTextAreaFont(false);
if (!font || font->Width("W") != 1) // all characters have with == 1 in the font used by 'skincurses'
font = cFont::GetFont(fontOsd);
int width = cSkinDisplay::Current()->EditableWidth();
width -= font->Width("[]");
width -= font->Width("<>"); // reserving this anyway make the whole thing simpler
if (pos < offset)
offset = pos;
int WidthFromOffset = 0;
int EndPos = lengthUtf8;
for (int i = offset; i < lengthUtf8; i++) {
WidthFromOffset += font->Width(valueUtf8[i]);
if (WidthFromOffset > width) {
if (pos >= i) {
do {
WidthFromOffset -= font->Width(valueUtf8[offset]);
offset++;
} while (WidthFromOffset > width && offset < pos);
EndPos = pos + 1;
}
else {
EndPos = i;
break;
}
}
}
char buf[1000];
char *p = buf;
if (offset)
*p++ = '<';
p += Utf8FromArray(valueUtf8 + offset, p, sizeof(buf) - (p - buf), pos - offset);
*p++ = '[';
if (insert && newchar)
*p++ = ']';
p += Utf8FromArray(&valueUtf8[pos], p, sizeof(buf) - (p - buf), 1);
if (!(insert && newchar))
*p++ = ']';
p += Utf8FromArray(&valueUtf8[pos + 1], p, sizeof(buf) - (p - buf), EndPos - pos - 1);
if (EndPos != lengthUtf8)
*p++ = '>';
*p = 0;
SetValue(buf);
}
else
SetValue(value);
}
uint cMenuEditStrItem::Inc(uint c, bool Up)
{
uint *p = IsAllowed(c);
if (!p)
p = allowedUtf8;
if (Up) {
if (!*++p)
p = allowedUtf8;
}
else if (--p < allowedUtf8) {
p = allowedUtf8;
while (*p && *(p + 1))
p++;
}
return *p;
}
void cMenuEditStrItem::Type(uint c)
{
if (insert && lengthUtf8 < length - 1)
Insert();
valueUtf8[pos] = c;
if (pos < length - 2)
pos++;
if (pos >= lengthUtf8) {
valueUtf8[pos] = ' ';
valueUtf8[pos + 1] = 0;
lengthUtf8 = pos + 1;
}
}
void cMenuEditStrItem::Insert(void)
{
memmove(valueUtf8 + pos + 1, valueUtf8 + pos, (lengthUtf8 - pos + 1) * sizeof(*valueUtf8));
lengthUtf8++;
valueUtf8[pos] = ' ';
}
void cMenuEditStrItem::Delete(void)
{
memmove(valueUtf8 + pos, valueUtf8 + pos + 1, (lengthUtf8 - pos) * sizeof(*valueUtf8));
lengthUtf8--;
}
eOSState cMenuEditStrItem::ProcessKey(eKeys Key)
{
bool SameKey = NORMALKEY(Key) == lastKey;
if (Key != kNone)
lastKey = NORMALKEY(Key);
else if (!newchar && k0 <= lastKey && lastKey <= k9 && autoAdvanceTimeout.TimedOut()) {
AdvancePos();
newchar = true;
currentCharUtf8 = NULL;
Set();
return osContinue;
}
switch (int(Key)) {
case kRed: // Switch between upper- and lowercase characters
if (InEditMode()) {
if (!insert || !newchar) {
uppercase = !uppercase;
valueUtf8[pos] = uppercase ? Utf8to(upper, valueUtf8[pos]) : Utf8to(lower, valueUtf8[pos]);
}
}
else
return osUnknown;
break;
case kGreen: // Toggle insert/overwrite modes
if (InEditMode()) {
insert = !insert;
newchar = true;
SetHelpKeys();
}
else
return osUnknown;
break;
case kYellow|k_Repeat:
case kYellow: // Remove the character at the current position; in insert mode it is the character to the right of the cursor
if (InEditMode()) {
if (lengthUtf8 > 1) {
if (!insert || pos < lengthUtf8 - 1)
Delete();
else if (insert && pos == lengthUtf8 - 1)
valueUtf8[pos] = ' '; // in insert mode, deleting the last character replaces it with a blank to keep the cursor position
// reduce position, if we removed the last character
if (pos == lengthUtf8)
pos--;
}
else if (lengthUtf8 == 1)
valueUtf8[0] = ' '; // This is the last character in the string, replace it with a blank
if (Utf8is(alpha, valueUtf8[pos]))
uppercase = Utf8is(upper, valueUtf8[pos]);
newchar = true;
}
else
return osUnknown;
break;
case kBlue|k_Repeat:
case kBlue: // consume the key only if in edit-mode
if (!InEditMode())
return osUnknown;
break;
case kLeft|k_Repeat:
case kLeft: if (pos > 0) {
if (!insert || newchar)
pos--;
newchar = true;
if (!insert && Utf8is(alpha, valueUtf8[pos]))
uppercase = Utf8is(upper, valueUtf8[pos]);
}
break;
case kRight|k_Repeat:
case kRight: if (InEditMode())
AdvancePos();
else {
EnterEditMode();
SetHelpKeys();
}
break;
case kUp|k_Repeat:
case kUp:
case kDown|k_Repeat:
case kDown: if (InEditMode()) {
if (insert && newchar) {
// create a new character in insert mode
if (lengthUtf8 < length - 1)
Insert();
}
if (uppercase)
valueUtf8[pos] = Utf8to(upper, Inc(Utf8to(lower, valueUtf8[pos]), NORMALKEY(Key) == kUp));
else
valueUtf8[pos] = Inc( valueUtf8[pos], NORMALKEY(Key) == kUp);
newchar = false;
}
else
return cMenuEditItem::ProcessKey(Key);
break;
case k0|k_Repeat ... k9|k_Repeat:
case k0 ... k9: {
if (InEditMode()) {
if (Setup.NumberKeysForChars) {
if (!SameKey) {
if (!newchar)
AdvancePos();
currentCharUtf8 = NULL;
}
if (!currentCharUtf8 || !*currentCharUtf8 || *currentCharUtf8 == '\t') {
// find the beginning of the character map entry for Key
int n = NORMALKEY(Key) - k0;
currentCharUtf8 = charMapUtf8;
while (n > 0 && *currentCharUtf8) {
if (*currentCharUtf8++ == '\t')
n--;
}
// find first allowed character
while (*currentCharUtf8 && *currentCharUtf8 != '\t' && !IsAllowed(*currentCharUtf8))
currentCharUtf8++;
}
if (*currentCharUtf8 && *currentCharUtf8 != '\t') {
if (insert && newchar) {
// create a new character in insert mode
if (lengthUtf8 < length - 1)
Insert();
}
valueUtf8[pos] = *currentCharUtf8;
if (uppercase)
valueUtf8[pos] = Utf8to(upper, valueUtf8[pos]);
// find next allowed character
do {
currentCharUtf8++;
} while (*currentCharUtf8 && *currentCharUtf8 != '\t' && !IsAllowed(*currentCharUtf8));
newchar = false;
autoAdvanceTimeout.Set(AUTO_ADVANCE_TIMEOUT);
}
}
else
Type('0' + NORMALKEY(Key) - k0);
}
else
return cMenuEditItem::ProcessKey(Key);
}
break;
case kBack:
case kOk: if (InEditMode()) {
LeaveEditMode(Key == kOk);
SetHelpKeys();
break;
}
// run into default
default: if (InEditMode() && BASICKEY(Key) == kKbd) {
int c = KEYKBD(Key);
if (c <= 0xFF) { // FIXME what about other UTF-8 characters?
if (IsAllowed(Utf8to(lower, c)))
Type(c);
else {
switch (c) {
case 0x7F: // backspace
if (pos > 0) {
pos--;
return ProcessKey(kYellow);
}
break;
default: ;
}
}
}
else {
switch (c) {
case kfHome: pos = 0; break;
case kfEnd: pos = lengthUtf8 - 1; break;
case kfIns: return ProcessKey(kGreen);
case kfDel: return ProcessKey(kYellow);
default: ;
}
}
}
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]);
}
// --- cMenuEditChanItem -----------------------------------------------------
cMenuEditChanItem::cMenuEditChanItem(const char *Name, int *Value, const char *NoneString)
:cMenuEditIntItem(Name, Value, NoneString ? 0 : 1, Channels.MaxNumber())
{
channelID = NULL;
noneString = NoneString;
dummyValue = 0;
Set();
}
cMenuEditChanItem::cMenuEditChanItem(const char *Name, cString *ChannelID, const char *NoneString)
:cMenuEditIntItem(Name, &dummyValue, NoneString ? 0 : 1, Channels.MaxNumber())
{
channelID = ChannelID;
noneString = NoneString;
cChannel *channel = Channels.GetByChannelID(tChannelID::FromString(*ChannelID));
dummyValue = channel ? channel->Number() : 0;
Set();
}
void cMenuEditChanItem::Set(void)
{
if (*value > 0) {
char buf[255];
cChannel *channel = Channels.GetByNumber(*value);
snprintf(buf, sizeof(buf), "%d %s", *value, channel ? channel->Name() : "");
SetValue(buf);
if (channelID)
*channelID = channel->GetChannelID().ToString();
}
else if (noneString) {
SetValue(noneString);
if (channelID)
*channelID = "";
}
}
eOSState cMenuEditChanItem::ProcessKey(eKeys Key)
{
int delta = 1;
switch (int(Key)) {
case kLeft|k_Repeat:
case kLeft: delta = -1;
case kRight|k_Repeat:
case kRight:
{
cChannel *channel = Channels.GetByNumber(*value + delta, delta);
if (channel)
*value = channel->Number();
else if (delta < 0 && noneString)
*value = 0;
if (channelID)
*channelID = channel ? channel->GetChannelID().ToString() : "";
Set();
}
break;
default: return cMenuEditIntItem::ProcessKey(Key);
}
return osContinue;
}
// --- cMenuEditTranItem -----------------------------------------------------
cMenuEditTranItem::cMenuEditTranItem(const char *Name, int *Value, int *Source)
:cMenuEditChanItem(Name, &number, "-")
{
number = 0;
source = Source;
transponder = Value;
cChannel *channel = Channels.First();
while (channel) {
if (!channel->GroupSep() && *source == channel->Source() && ISTRANSPONDER(channel->Transponder(), *Value)) {
number = channel->Number();
break;
}
channel = (cChannel *)channel->Next();
}
Set();
}
eOSState cMenuEditTranItem::ProcessKey(eKeys Key)
{
eOSState state = cMenuEditChanItem::ProcessKey(Key);
cChannel *channel = Channels.GetByNumber(number);
if (channel) {
*source = channel->Source();
*transponder = channel->Transponder();
}
else {
*source = 0;
*transponder = 0;
}
return state;
}
// --- cMenuEditDateItem -----------------------------------------------------
static int ParseWeekDays(const char *s)
{
time_t day;
int weekdays;
return cTimer::ParseDay(s, day, weekdays) ? weekdays : 0;
}
int cMenuEditDateItem::days[] = { ParseWeekDays("M------"),
ParseWeekDays("-T-----"),
ParseWeekDays("--W----"),
ParseWeekDays("---T---"),
ParseWeekDays("----F--"),
ParseWeekDays("-----S-"),
ParseWeekDays("------S"),
ParseWeekDays("MTWTF--"),
ParseWeekDays("MTWTFS-"),
ParseWeekDays("MTWTFSS"),
ParseWeekDays("-----SS"),
0 };
cMenuEditDateItem::cMenuEditDateItem(const char *Name, time_t *Value, int *WeekDays)
:cMenuEditItem(Name)
{
value = Value;
weekdays = WeekDays;
oldvalue = 0;
dayindex = weekdays ? FindDayIndex(*weekdays) : 0;
Set();
}
int cMenuEditDateItem::FindDayIndex(int WeekDays)
{
for (unsigned int i = 0; i < sizeof(days) / sizeof(int); i++)
if (WeekDays == days[i])
return i;
return 0;
}
void cMenuEditDateItem::Set(void)
{
#define DATEBUFFERSIZE 32
char buf[DATEBUFFERSIZE];
if (weekdays && *weekdays) {
SetValue(cTimer::PrintDay(0, *weekdays, false));
return;
}
else if (*value) {
struct tm tm_r;
localtime_r(value, &tm_r);
strftime(buf, DATEBUFFERSIZE, "%Y-%m-%d ", &tm_r);
strcat(buf, WeekDayName(tm_r.tm_wday));
}
else
*buf = 0;
SetValue(buf);
}
eOSState cMenuEditDateItem::ProcessKey(eKeys Key)
{
eOSState state = cMenuEditItem::ProcessKey(Key);
if (state == osUnknown) {
time_t now = time(NULL);
if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
if (!weekdays || !*weekdays) {
// Decrement single day:
time_t v = *value;
v -= SECSINDAY;
if (v < now) {
if (now <= v + SECSINDAY) { // switched from tomorrow to today
if (!weekdays)
v = 0;
}
else if (weekdays) { // switched from today to yesterday, so enter weekdays mode
v = 0;
dayindex = sizeof(days) / sizeof(int) - 2;
*weekdays = days[dayindex];
}
else // don't go before today
v = *value;
}
*value = v;
}
else {
// Decrement weekday index:
if (dayindex > 0)
*weekdays = days[--dayindex];
}
}
else if (NORMALKEY(Key) == kRight) {
if (!weekdays || !*weekdays) {
// Increment single day:
if (!*value)
*value = cTimer::SetTime(now, 0);
*value += SECSINDAY;
}
else {
// Increment weekday index:
*weekdays = days[++dayindex];
if (!*weekdays) { // was last weekday entry, so switch to today
*value = cTimer::SetTime(now, 0);
dayindex = 0;
}
}
}
else if (weekdays) {
if (Key == k0) {
// Toggle between weekdays and single day:
if (*weekdays) {
*value = cTimer::SetTime(oldvalue ? oldvalue : now, 0);
oldvalue = 0;
*weekdays = 0;
}
else {
*weekdays = days[cTimer::GetWDay(*value)];
dayindex = FindDayIndex(*weekdays);
oldvalue = *value;
*value = 0;
}
}
else if (k1 <= Key && Key <= k7) {
// Toggle individual weekdays:
if (*weekdays) {
int v = *weekdays ^ (1 << (Key - k1));
if (v != 0)
*weekdays = v; // can't let this become all 0
}
}
else
return state;
}
else
return state;
Set();
state = osContinue;
}
return state;
}
// --- cMenuEditTimeItem -----------------------------------------------------
cMenuEditTimeItem::cMenuEditTimeItem(const char *Name, int *Value)
:cMenuEditItem(Name)
{
value = Value;
hh = *value / 100;
mm = *value % 100;
pos = 0;
Set();
}
void cMenuEditTimeItem::Set(void)
{
char buf[10];
switch (pos) {
case 1: snprintf(buf, sizeof(buf), "%01d-:--", hh / 10); break;
case 2: snprintf(buf, sizeof(buf), "%02d:--", hh); break;
case 3: snprintf(buf, sizeof(buf), "%02d:%01d-", hh, mm / 10); break;
default: snprintf(buf, sizeof(buf), "%02d:%02d", hh, mm);
}
SetValue(buf);
}
eOSState cMenuEditTimeItem::ProcessKey(eKeys Key)
{
eOSState state = cMenuEditItem::ProcessKey(Key);
if (state == osUnknown) {
if (k0 <= Key && Key <= k9) {
if (fresh || pos > 3) {
pos = 0;
fresh = false;
}
int n = Key - k0;
switch (pos) {
case 0: if (n <= 2) {
hh = n * 10;
mm = 0;
pos++;
}
break;
case 1: if (hh + n <= 23) {
hh += n;
pos++;
}
break;
case 2: if (n <= 5) {
mm += n * 10;
pos++;
}
break;
case 3: if (mm + n <= 59) {
mm += n;
pos++;
}
break;
default: ;
}
}
else if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
if (--mm < 0) {
mm = 59;
if (--hh < 0)
hh = 23;
}
fresh = true;
}
else if (NORMALKEY(Key) == kRight) {
if (++mm > 59) {
mm = 0;
if (++hh > 23)
hh = 0;
}
fresh = true;
}
else
return state;
*value = hh * 100 + mm;
Set();
state = osContinue;
}
return state;
}
// --- cMenuEditMapItem ------------------------------------------------------
cMenuEditMapItem::cMenuEditMapItem(const char *Name, int *Value, const tDvbParameterMap *Map, const char *ZeroString)
:cMenuEditItem(Name)
{
value = Value;
map = Map;
zeroString = ZeroString;
Set();
}
void cMenuEditMapItem::Set(void)
{
const char *s = NULL;
int n = MapToUser(*value, map, &s);
if (n == 0 && zeroString)
SetValue(zeroString);
else if (n >= 0) {
if (s)
SetValue(s);
else {
char buf[16];
snprintf(buf, sizeof(buf), "%d", n);
SetValue(buf);
}
}
else
SetValue("???");
}
eOSState cMenuEditMapItem::ProcessKey(eKeys Key)
{
eOSState state = cMenuEditItem::ProcessKey(Key);
if (state == osUnknown) {
int newValue = *value;
int n = DriverIndex(*value, map);
if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
if (n-- > 0)
newValue = map[n].driverValue;
}
else if (NORMALKEY(Key) == kRight) {
if (map[++n].userValue >= 0)
newValue = map[n].driverValue;
}
else
return state;
if (newValue != *value) {
*value = newValue;
Set();
}
state = osContinue;
}
return state;
}
// --- cMenuSetupPage --------------------------------------------------------
cMenuSetupPage::cMenuSetupPage(void)
:cOsdMenu("", 33)
{
plugin = NULL;
}
void cMenuSetupPage::SetSection(const char *Section)
{
SetTitle(cString::sprintf("%s - %s", tr("Setup"), Section));
}
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;
SetSection(cString::sprintf("%s '%s'", tr("Plugin"), plugin->Name()));
}
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);
}