summaryrefslogtreecommitdiff
path: root/menu.c
diff options
context:
space:
mode:
authorKlaus Schmidinger <Klaus (dot) Schmidinger (at) tvdr (dot) de>2010-01-31 15:42:00 +0100
committerKlaus Schmidinger <Klaus (dot) Schmidinger (at) tvdr (dot) de>2010-01-31 15:42:00 +0100
commit09a17d56e2a3f975a0467e8da4ca26c946b6ccf7 (patch)
tree4ca95499f117bf8bf0a51149bb85493d93ee111e /menu.c
parent939081e274d0a9868e5ba9a7951666ad508afb96 (diff)
downloadvdr-patch-lnbsharing-09a17d56e2a3f975a0467e8da4ca26c946b6ccf7.tar.gz
vdr-patch-lnbsharing-09a17d56e2a3f975a0467e8da4ca26c946b6ccf7.tar.bz2
Version 1.7.12vdr-1.7.12
- Changed the EVCONTENTMASK_* macros to enums and changed "mask" to "group". - Updated the Estonian OSD texts (thanks to Arthur Konovalov). - The "Edit timer" menu can now set the folder for the recording from a list of folders stored in "folders.conf". - Updated the Italian OSD texts (thanks to Diego Pierotto). - If svdrphosts.conf contains only the address of the local host, the SVDRP port is opened only for the local host (thanks to Manuel Reimer). - Renamed 'runvdr' to 'runvdr.template' and no longer copying it to the BINDIR in 'make install' (thanks to Martin Dauskardt). - Added plain text error messages to log entries from cOsd::SetAreas() (suggested by Rolf Ahrenberg). - cPalette::ClosestColor() now treats fully transparent colors as "equal"; improved cDvbSpuBitmap::getMinBpp() (thanks to Matthieu Castet and Johann Friedrichs). - The new setup option "Miscellaneous/Channels wrap" controls whether the current channel wraps around the beginning or end of the channel list when zapping (thanks to Matti Lehtimäki). - Fixed determining the frame duration on channels where the PTS deltas jitter by +/-1 around 1800. - The PCR pid in generated PMTs is now set to the channel's PCR pid again. - Fixed determining the frame duration on channels where the PTS deltas jitter by +/-1 around 3600. - The PCR pid is now recorded for channels where this is different from the video PID. To facilitate this, the interfaces of cTransfer, cTransferControl, cRecorder and cReceiver have been modified, so that the PIDs are no longer given in separate parameters, but rather the whole channel is handed down for processing. The old constructor of cReceiver is still available, but it is recommended to plugin authors that they switch to the new interface as soon as possible. When replaying such a recording, the PCR packets are sent to PlayTsVideo() - The files "commands.conf" and "reccmd.conf" can now contain nested lists of commands. See vdr.5 for information about the new file format.
Diffstat (limited to 'menu.c')
-rw-r--r--menu.c402
1 files changed, 377 insertions, 25 deletions
diff --git a/menu.c b/menu.c
index b0077a0..7ddf0cf 100644
--- a/menu.c
+++ b/menu.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.c 2.10 2009/12/06 11:29:05 kls Exp $
+ * $Id: menu.c 2.14 2010/01/31 12:43:24 kls Exp $
*/
#include "menu.h"
@@ -623,11 +623,276 @@ eOSState cMenuText::ProcessKey(eKeys Key)
return state;
}
+// --- cMenuFolderItem -------------------------------------------------------
+
+class cMenuFolderItem : public cOsdItem {
+private:
+ cNestedItem *folder;
+public:
+ cMenuFolderItem(cNestedItem *Folder);
+ cNestedItem *Folder(void) { return folder; }
+ };
+
+cMenuFolderItem::cMenuFolderItem(cNestedItem *Folder)
+:cOsdItem(Folder->Text())
+{
+ folder = Folder;
+ if (folder->SubItems())
+ SetText(cString::sprintf("%s...", folder->Text()));
+}
+
+// --- cMenuEditFolder -------------------------------------------------------
+
+class cMenuEditFolder : public cOsdMenu {
+private:
+ cList<cNestedItem> *list;
+ cNestedItem *folder;
+ char name[PATH_MAX];
+ int subFolder;
+ eOSState Confirm(void);
+public:
+ cMenuEditFolder(const char *Dir, cList<cNestedItem> *List, cNestedItem *Folder = NULL);
+ cString GetFolder(void);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+cMenuEditFolder::cMenuEditFolder(const char *Dir, cList<cNestedItem> *List, cNestedItem *Folder)
+:cOsdMenu(Folder ? tr("Edit folder") : tr("New folder"), 12)
+{
+ list = List;
+ folder = Folder;
+ if (folder) {
+ strn0cpy(name, folder->Text(), sizeof(name));
+ subFolder = folder->SubItems() != NULL;
+ }
+ else {
+ *name = 0;
+ subFolder = 0;
+ cRemote::Put(kRight, true); // go right into string editing mode
+ }
+ if (!isempty(Dir)) {
+ cOsdItem *DirItem = new cOsdItem(Dir);
+ DirItem->SetSelectable(false);
+ Add(DirItem);
+ }
+ Add(new cMenuEditStrItem( tr("Name"), name, sizeof(name)));
+ Add(new cMenuEditBoolItem(tr("Sub folder"), &subFolder));
+}
+
+cString cMenuEditFolder::GetFolder(void)
+{
+ return folder ? folder->Text() : "";
+}
+
+eOSState cMenuEditFolder::Confirm(void)
+{
+ if (!folder || strcmp(folder->Text(), name) != 0) {
+ // each name may occur only once in a folder list
+ for (cNestedItem *Folder = list->First(); Folder; Folder = list->Next(Folder)) {
+ if (strcmp(Folder->Text(), name) == 0) {
+ Skins.Message(mtError, tr("Folder name already exists!"));
+ return osContinue;
+ }
+ }
+ char *p = strpbrk(name, "\\{}#~"); // FOLDERDELIMCHAR
+ if (p) {
+ Skins.Message(mtError, cString::sprintf(tr("Folder name must not contain '%c'!"), *p));
+ return osContinue;
+ }
+ }
+ if (folder) {
+ folder->SetText(name);
+ folder->SetSubItems(subFolder);
+ }
+ else
+ list->Add(folder = new cNestedItem(name, subFolder));
+ return osEnd;
+}
+
+eOSState cMenuEditFolder::ProcessKey(eKeys Key)
+{
+ eOSState state = cOsdMenu::ProcessKey(Key);
+
+ if (state == osUnknown) {
+ switch (Key) {
+ case kOk: return Confirm();
+ case kRed:
+ case kGreen:
+ case kYellow:
+ case kBlue: return osContinue;
+ default: break;
+ }
+ }
+ return state;
+}
+
+// --- cMenuFolder -----------------------------------------------------------
+
+cMenuFolder::cMenuFolder(const char *Title, cNestedItemList *NestedItemList, const char *Path)
+:cOsdMenu(Title)
+{
+ list = nestedItemList = NestedItemList;
+ firstFolder = NULL;
+ editing = false;
+ Set();
+ SetHelpKeys();
+ DescendPath(Path);
+}
+
+cMenuFolder::cMenuFolder(const char *Title, cList<cNestedItem> *List, cNestedItemList *NestedItemList, const char *Dir, const char *Path)
+:cOsdMenu(Title)
+{
+ list = List;
+ nestedItemList = NestedItemList;
+ dir = Dir;
+ firstFolder = NULL;
+ editing = false;
+ Set();
+ SetHelpKeys();
+ DescendPath(Path);
+}
+
+void cMenuFolder::SetHelpKeys(void)
+{
+ SetHelp(firstFolder ? tr("Button$Select") : NULL, tr("Button$New"), firstFolder ? tr("Button$Delete") : NULL, firstFolder ? tr("Button$Edit") : NULL);
+}
+
+void cMenuFolder::Set(const char *CurrentFolder)
+{
+ firstFolder = NULL;
+ Clear();
+ if (!isempty(dir)) {
+ cOsdItem *DirItem = new cOsdItem(dir);
+ DirItem->SetSelectable(false);
+ Add(DirItem);
+ }
+ list->Sort();
+ for (cNestedItem *Folder = list->First(); Folder; Folder = list->Next(Folder)) {
+ cOsdItem *FolderItem = new cMenuFolderItem(Folder);
+ Add(FolderItem, CurrentFolder ? strcmp(Folder->Text(), CurrentFolder) == 0 : false);
+ if (!firstFolder)
+ firstFolder = FolderItem;
+ }
+}
+
+void cMenuFolder::DescendPath(const char *Path)
+{
+ if (Path) {
+ const char *p = strchr(Path, FOLDERDELIMCHAR);
+ if (p) {
+ for (cMenuFolderItem *Folder = (cMenuFolderItem *)firstFolder; Folder; Folder = (cMenuFolderItem *)Next(Folder)) {
+ if (strncmp(Folder->Folder()->Text(), Path, p - Path) == 0) {
+ SetCurrent(Folder);
+ if (Folder->Folder()->SubItems())
+ AddSubMenu(new cMenuFolder(Title(), Folder->Folder()->SubItems(), nestedItemList, !isempty(dir) ? *cString::sprintf("%s%c%s", *dir, FOLDERDELIMCHAR, Folder->Folder()->Text()) : Folder->Folder()->Text(), p + 1));
+ break;
+ }
+ }
+ }
+ }
+}
+
+eOSState cMenuFolder::Select(void)
+{
+ if (firstFolder) {
+ cMenuFolderItem *Folder = (cMenuFolderItem *)Get(Current());
+ if (Folder) {
+ if (Folder->Folder()->SubItems())
+ return AddSubMenu(new cMenuFolder(Title(), Folder->Folder()->SubItems(), nestedItemList, !isempty(dir) ? *cString::sprintf("%s%c%s", *dir, FOLDERDELIMCHAR, Folder->Folder()->Text()) : Folder->Folder()->Text()));
+ else
+ return osEnd;
+ }
+ }
+ return osContinue;
+}
+
+eOSState cMenuFolder::New(void)
+{
+ editing = true;
+ return AddSubMenu(new cMenuEditFolder(dir, list));
+}
+
+eOSState cMenuFolder::Delete(void)
+{
+ if (!HasSubMenu() && firstFolder) {
+ cMenuFolderItem *Folder = (cMenuFolderItem *)Get(Current());
+ if (Folder && Interface->Confirm(Folder->Folder()->SubItems() ? tr("Delete folder and all sub folders?") : tr("Delete folder?"))) {
+ list->Del(Folder->Folder());
+ Del(Folder->Index());
+ firstFolder = Get(isempty(dir) ? 0 : 1);
+ Display();
+ SetHelpKeys();
+ nestedItemList->Save();
+ }
+ }
+ return osContinue;
+}
+
+eOSState cMenuFolder::Edit(void)
+{
+ if (!HasSubMenu() && firstFolder) {
+ cMenuFolderItem *Folder = (cMenuFolderItem *)Get(Current());
+ if (Folder) {
+ editing = true;
+ return AddSubMenu(new cMenuEditFolder(dir, list, Folder->Folder()));
+ }
+ }
+ return osContinue;
+}
+
+eOSState cMenuFolder::SetFolder(void)
+{
+ cMenuEditFolder *mef = (cMenuEditFolder *)SubMenu();
+ if (mef) {
+ Set(mef->GetFolder());
+ SetHelpKeys();
+ Display();
+ nestedItemList->Save();
+ }
+ return CloseSubMenu();
+}
+
+cString cMenuFolder::GetFolder(void)
+{
+ if (firstFolder) {
+ cMenuFolderItem *Folder = (cMenuFolderItem *)Get(Current());
+ if (Folder) {
+ cMenuFolder *mf = (cMenuFolder *)SubMenu();
+ if (mf)
+ return cString::sprintf("%s%c%s", Folder->Folder()->Text(), FOLDERDELIMCHAR, *mf->GetFolder());
+ return Folder->Folder()->Text();
+ }
+ }
+ return "";
+}
+
+eOSState cMenuFolder::ProcessKey(eKeys Key)
+{
+ if (!HasSubMenu())
+ editing = false;
+ eOSState state = cOsdMenu::ProcessKey(Key);
+
+ if (state == osUnknown) {
+ switch (Key) {
+ case kOk:
+ case kRed: return Select();
+ case kGreen: return New();
+ case kYellow: return Delete();
+ case kBlue: return Edit();
+ default: state = osContinue;
+ }
+ }
+ else if (state == osEnd && HasSubMenu() && editing)
+ state = SetFolder();
+ return state;
+}
+
// --- cMenuEditTimer --------------------------------------------------------
cMenuEditTimer::cMenuEditTimer(cTimer *Timer, bool New)
:cOsdMenu(tr("Edit timer"), 12)
{
+ file = NULL;
firstday = NULL;
timer = Timer;
addIfConfirmed = New;
@@ -644,9 +909,10 @@ cMenuEditTimer::cMenuEditTimer(cTimer *Timer, bool New)
Add(new cMenuEditBitItem( tr("VPS"), &data.flags, tfVps));
Add(new cMenuEditIntItem( tr("Priority"), &data.priority, 0, MAXPRIORITY));
Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME));
- Add(new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file)));
+ Add(file = new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file)));
SetFirstDayItem();
}
+ SetHelpKeys();
Timers.IncBeingEdited();
}
@@ -657,6 +923,11 @@ cMenuEditTimer::~cMenuEditTimer()
Timers.DecBeingEdited();
}
+void cMenuEditTimer::SetHelpKeys(void)
+{
+ SetHelp(tr("Button$Folder"));
+}
+
void cMenuEditTimer::SetFirstDayItem(void)
{
if (!firstday && !data.IsSingleEvent()) {
@@ -670,6 +941,26 @@ void cMenuEditTimer::SetFirstDayItem(void)
}
}
+eOSState cMenuEditTimer::SetFolder(void)
+{
+ cMenuFolder *mf = (cMenuFolder *)SubMenu();
+ if (mf) {
+ cString Folder = mf->GetFolder();
+ char *p = strrchr(data.file, FOLDERDELIMCHAR);
+ if (p)
+ p++;
+ else
+ p = data.file;
+ if (!isempty(*Folder))
+ strn0cpy(data.file, cString::sprintf("%s%c%s", *Folder, FOLDERDELIMCHAR, p), sizeof(data.file));
+ else if (p != data.file)
+ memmove(data.file, p, strlen(p) + 1);
+ SetCurrent(file);
+ Display();
+ }
+ return CloseSubMenu();
+}
+
eOSState cMenuEditTimer::ProcessKey(eKeys Key)
{
eOSState state = cOsdMenu::ProcessKey(Key);
@@ -699,13 +990,15 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key)
}
}
return osBack;
- case kRed:
+ case kRed: return AddSubMenu(new cMenuFolder(tr("Select folder"), &Folders, data.file));
case kGreen:
case kYellow:
case kBlue: return osContinue;
default: break;
}
}
+ else if (state == osEnd && HasSubMenu())
+ state = SetFolder();
if (Key != kNone)
SetFirstDayItem();
return state;
@@ -1490,44 +1783,100 @@ eOSState cMenuSchedule::ProcessKey(eKeys Key)
class cMenuCommands : public cOsdMenu {
private:
- cCommands *commands;
- char *parameters;
+ cList<cNestedItem> *commands;
+ cString parameters;
+ cString title;
+ cString command;
+ bool confirm;
+ char *result;
+ bool Parse(const char *s);
eOSState Execute(void);
public:
- cMenuCommands(const char *Title, cCommands *Commands, const char *Parameters = NULL);
+ cMenuCommands(const char *Title, cList<cNestedItem> *Commands, const char *Parameters = NULL);
virtual ~cMenuCommands();
virtual eOSState ProcessKey(eKeys Key);
};
-cMenuCommands::cMenuCommands(const char *Title, cCommands *Commands, const char *Parameters)
+cMenuCommands::cMenuCommands(const char *Title, cList<cNestedItem> *Commands, const char *Parameters)
:cOsdMenu(Title)
{
+ result = NULL;
SetHasHotkeys();
commands = Commands;
- parameters = Parameters ? strdup(Parameters) : NULL;
- for (cCommand *command = commands->First(); command; command = commands->Next(command))
- Add(new cOsdItem(hk(command->Title())));
+ parameters = Parameters;
+ for (cNestedItem *Command = commands->First(); Command; Command = commands->Next(Command)) {
+ const char *s = Command->Text();
+ if (Command->SubItems())
+ Add(new cOsdItem(hk(cString::sprintf("%s...", s))));
+ else if (Parse(s))
+ Add(new cOsdItem(hk(title)));
+ }
}
cMenuCommands::~cMenuCommands()
{
- free(parameters);
+ free(result);
+}
+
+bool cMenuCommands::Parse(const char *s)
+{
+ const char *p = strchr(s, ':');
+ if (p) {
+ int l = p - s;
+ if (l > 0) {
+ char t[l + 1];
+ stripspace(strn0cpy(t, s, l + 1));
+ l = strlen(t);
+ if (l > 1 && t[l - 1] == '?') {
+ t[l - 1] = 0;
+ confirm = true;
+ }
+ else
+ confirm = false;
+ title = t;
+ command = skipspace(p + 1);
+ return true;
+ }
+ }
+ return false;
}
eOSState cMenuCommands::Execute(void)
{
- cCommand *command = commands->Get(Current());
- if (command) {
- bool confirmed = true;
- if (command->Confirm())
- confirmed = Interface->Confirm(cString::sprintf("%s?", command->Title()));
- if (confirmed) {
- Skins.Message(mtStatus, cString::sprintf("%s...", command->Title()));
- const char *Result = command->Execute(parameters);
- Skins.Message(mtStatus, NULL);
- if (Result)
- return AddSubMenu(new cMenuText(command->Title(), Result, fontFix));
- return osEnd;
+ cNestedItem *Command = commands->Get(Current());
+ if (Command) {
+ if (Command->SubItems())
+ return AddSubMenu(new cMenuCommands(Title(), Command->SubItems(), parameters));
+ if (Parse(Command->Text())) {
+ if (!confirm || Interface->Confirm(cString::sprintf("%s?", *title))) {
+ Skins.Message(mtStatus, cString::sprintf("%s...", *title));
+ free(result);
+ result = NULL;
+ cString cmdbuf;
+ if (!isempty(parameters))
+ cmdbuf = cString::sprintf("%s %s", *command, *parameters);
+ const char *cmd = *cmdbuf ? *cmdbuf : *command;
+ dsyslog("executing command '%s'", cmd);
+ cPipe p;
+ if (p.Open(cmd, "r")) {
+ int l = 0;
+ int c;
+ while ((c = fgetc(p)) != EOF) {
+ if (l % 20 == 0)
+ result = (char *)realloc(result, l + 21);
+ result[l++] = char(c);
+ }
+ if (result)
+ result[l] = 0;
+ p.Close();
+ }
+ else
+ esyslog("ERROR: can't open pipe for command '%s'", cmd);
+ Skins.Message(mtStatus, NULL);
+ if (result)
+ return AddSubMenu(new cMenuText(title, result, fontFix));
+ return osEnd;
+ }
}
}
return osContinue;
@@ -1921,7 +2270,7 @@ void cMenuRecordings::Set(bool Refresh)
Clear();
Recordings.Sort();
for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
- if (!base || (strstr(recording->Name(), base) == recording->Name() && recording->Name()[strlen(base)] == '~')) {
+ if (!base || (strstr(recording->Name(), base) == recording->Name() && recording->Name()[strlen(base)] == FOLDERDELIMCHAR)) {
cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level);
if (*Item->Text() && (!LastItem || strcmp(Item->Text(), LastItemText) != 0)) {
Add(Item);
@@ -2746,6 +3095,7 @@ cMenuSetupMisc::cMenuSetupMisc(void)
Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Channel entry timeout (ms)"), &data.ChannelEntryTimeout, 0));
Add(new cMenuEditChanItem(tr("Setup.Miscellaneous$Initial channel"), &data.InitialChannel, tr("Setup.Miscellaneous$as before")));
Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Initial volume"), &data.InitialVolume, -1, 255, tr("Setup.Miscellaneous$as before")));
+ Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Channels wrap"), &data.ChannelsWrap));
Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Emergency exit"), &data.EmergencyExit));
}
@@ -3245,6 +3595,8 @@ cChannel *cDisplayChannel::NextAvailableChannel(cChannel *Channel, int Direction
if (Direction) {
while (Channel) {
Channel = Direction > 0 ? Channels.Next(Channel) : Channels.Prev(Channel);
+ if (!Channel && Setup.ChannelsWrap)
+ Channel = Direction > 0 ? Channels.First() : Channels.Last();
if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, 0, true))
return Channel;
}
@@ -3759,7 +4111,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)
isyslog("record %s", fileName);
if (MakeDirs(fileName, true)) {
const cChannel *ch = timer->Channel();
- recorder = new cRecorder(fileName, ch->GetChannelID(), timer->Priority(), ch->Vpid(), ch->Apids(), ch->Dpids(), ch->Spids());
+ recorder = new cRecorder(fileName, ch, timer->Priority());
if (device->AttachReceiver(recorder)) {
Recording.WriteInfo();
cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true);