From 09a17d56e2a3f975a0467e8da4ca26c946b6ccf7 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sun, 31 Jan 2010 15:42:00 +0100 Subject: =?UTF-8?q?Version=201.7.12=20-=20Changed=20the=20EVCONTENTMASK=5F?= =?UTF-8?q?*=20macros=20to=20enums=20and=20changed=20"mask"=20to=20"group"?= =?UTF-8?q?.=20-=20Updated=20the=20Estonian=20OSD=20texts=20(thanks=20to?= =?UTF-8?q?=20Arthur=20Konovalov).=20-=20The=20"Edit=20timer"=20menu=20can?= =?UTF-8?q?=20now=20set=20the=20folder=20for=20the=20recording=20from=20a?= =?UTF-8?q?=20list=20of=20=20=20folders=20stored=20in=20"folders.conf".=20?= =?UTF-8?q?-=20Updated=20the=20Italian=20OSD=20texts=20(thanks=20to=20Dieg?= =?UTF-8?q?o=20Pierotto).=20-=20If=20svdrphosts.conf=20contains=20only=20t?= =?UTF-8?q?he=20address=20of=20the=20local=20host,=20the=20SVDRP=20port=20?= =?UTF-8?q?=20=20is=20opened=20only=20for=20the=20local=20host=20(thanks?= =?UTF-8?q?=20to=20Manuel=20Reimer).=20-=20Renamed=20'runvdr'=20to=20'runv?= =?UTF-8?q?dr.template'=20and=20no=20longer=20copying=20it=20to=20the=20BI?= =?UTF-8?q?NDIR=20=20=20in=20'make=20install'=20(thanks=20to=20Martin=20Da?= =?UTF-8?q?uskardt).=20-=20Added=20plain=20text=20error=20messages=20to=20?= =?UTF-8?q?log=20entries=20from=20cOsd::SetAreas()=20(suggested=20=20=20by?= =?UTF-8?q?=20Rolf=20Ahrenberg).=20-=20cPalette::ClosestColor()=20now=20tr?= =?UTF-8?q?eats=20fully=20transparent=20colors=20as=20"equal";=20improved?= =?UTF-8?q?=20=20=20cDvbSpuBitmap::getMinBpp()=20(thanks=20to=20Matthieu?= =?UTF-8?q?=20Castet=20and=20Johann=20Friedrichs).=20-=20The=20new=20setup?= =?UTF-8?q?=20option=20"Miscellaneous/Channels=20wrap"=20controls=20whethe?= =?UTF-8?q?r=20the=20current=20=20=20channel=20wraps=20around=20the=20begi?= =?UTF-8?q?nning=20or=20end=20of=20the=20channel=20list=20when=20zapping?= =?UTF-8?q?=20(thanks=20=20=20to=20Matti=20Lehtim=C3=A4ki).=20-=20Fixed=20?= =?UTF-8?q?determining=20the=20frame=20duration=20on=20channels=20where=20?= =?UTF-8?q?the=20PTS=20deltas=20jitter=20by=20=20=20+/-1=20around=201800.?= =?UTF-8?q?=20-=20The=20PCR=20pid=20in=20generated=20PMTs=20is=20now=20set?= =?UTF-8?q?=20to=20the=20channel's=20PCR=20pid=20again.=20-=20Fixed=20dete?= =?UTF-8?q?rmining=20the=20frame=20duration=20on=20channels=20where=20the?= =?UTF-8?q?=20PTS=20deltas=20jitter=20by=20=20=20+/-1=20around=203600.=20-?= =?UTF-8?q?=20The=20PCR=20pid=20is=20now=20recorded=20for=20channels=20whe?= =?UTF-8?q?re=20this=20is=20different=20from=20the=20video=20=20=20PID.=20?= =?UTF-8?q?To=20facilitate=20this,=20the=20interfaces=20of=20cTransfer,=20?= =?UTF-8?q?cTransferControl,=20cRecorder=20=20=20and=20cReceiver=20have=20?= =?UTF-8?q?been=20modified,=20so=20that=20the=20PIDs=20are=20no=20longer?= =?UTF-8?q?=20given=20in=20separate=20=20=20parameters,=20but=20rather=20t?= =?UTF-8?q?he=20whole=20channel=20is=20handed=20down=20for=20processing.?= =?UTF-8?q?=20The=20old=20=20=20constructor=20of=20cReceiver=20is=20still?= =?UTF-8?q?=20available,=20but=20it=20is=20recommended=20to=20plugin=20aut?= =?UTF-8?q?hors=20=20=20that=20they=20switch=20to=20the=20new=20interface?= =?UTF-8?q?=20as=20soon=20as=20possible.=20=20=20When=20replaying=20such?= =?UTF-8?q?=20a=20recording,=20the=20PCR=20packets=20are=20sent=20to=20Pla?= =?UTF-8?q?yTsVideo()=20-=20The=20files=20"commands.conf"=20and=20"reccmd.?= =?UTF-8?q?conf"=20can=20now=20contain=20nested=20lists=20of=20=20=20comma?= =?UTF-8?q?nds.=20See=20vdr.5=20for=20information=20about=20the=20new=20fi?= =?UTF-8?q?le=20format.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- menu.c | 402 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 377 insertions(+), 25 deletions(-) (limited to 'menu.c') 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 *list; + cNestedItem *folder; + char name[PATH_MAX]; + int subFolder; + eOSState Confirm(void); +public: + cMenuEditFolder(const char *Dir, cList *List, cNestedItem *Folder = NULL); + cString GetFolder(void); + virtual eOSState ProcessKey(eKeys Key); + }; + +cMenuEditFolder::cMenuEditFolder(const char *Dir, cList *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 *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 *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 *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 *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); -- cgit v1.2.3