diff options
author | horchi <vdr@jwendel.de> | 2019-10-31 10:02:19 +0100 |
---|---|---|
committer | horchi <vdr@jwendel.de> | 2019-10-31 10:02:19 +0100 |
commit | 05355f62ef7e7fd574ff1631caf86f04b0c3364c (patch) | |
tree | b039255fbaf77da459758142c2bb6f11ff7f5a19 | |
parent | f9c8dd36d36efa145806f7bef97b46382a5442f3 (diff) | |
download | vdr-plugin-epg2vdr-05355f62ef7e7fd574ff1631caf86f04b0c3364c.tar.gz vdr-plugin-epg2vdr-05355f62ef7e7fd574ff1631caf86f04b0c3364c.tar.bz2 |
development of recording menu
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | epg2vdr.c | 10 | ||||
-rw-r--r-- | menu.h | 42 | ||||
-rw-r--r-- | menurec.c | 516 | ||||
-rw-r--r-- | po/de_DE.po | 35 | ||||
-rw-r--r-- | po/it_IT.po | 35 |
6 files changed, 621 insertions, 19 deletions
@@ -64,7 +64,7 @@ OBJS = $(PLUGIN).o \ service.o update.o plgconfig.o parameters.o \ timer.o recording.o recinfofile.o \ status.o ttools.o svdrpclient.o \ - menu.o menusched.o menutimers.o menudone.o menusearchtimer.o + menu.o menusched.o menutimers.o menudone.o menusearchtimer.o menurec.o LIBS = $(HLIB) LIBS += -lrt -larchive -lcrypto @@ -81,7 +81,8 @@ enum EpgMenuState emsDones, emsProgram, emsUpdate, - emsReload + emsReload, + emsRecordings }; cEpgPluginMenu::cEpgPluginMenu(const char* title, cPluginEPG2VDR* aPlugin) @@ -101,6 +102,7 @@ cEpgPluginMenu::cEpgPluginMenu(const char* title, cPluginEPG2VDR* aPlugin) } cOsdMenu::Add(new cOsdItem(hk(tr("Program")), (eOSState)emsProgram)); + cOsdMenu::Add(new cOsdItem(hk(tr("Recordings")), (eOSState)emsRecordings)); cOsdMenu::Add(new cOsdItem(hk(tr("Update")), (eOSState)emsUpdate)); cOsdMenu::Add(new cOsdItem(hk(tr("Reload")), (eOSState)emsReload)); @@ -139,6 +141,12 @@ eOSState cEpgPluginMenu::ProcessKey(eKeys key) break; } + case emsRecordings: + { + state = AddSubMenu(new cMenuDbRecordings()); + break; + } + case emsUpdate: { Skins.Message(mtInfo, tr("Update EPG")); @@ -5,8 +5,7 @@ * */ -#ifndef __EPG2VDR_MENU_H -#define __EPG2VDR_MENU_H +#pragma once #include <vdr/osdbase.h> #include <vdr/menuitems.h> @@ -550,6 +549,43 @@ class cMenuEpgMatchRecordings : public cOsdMenu virtual eOSState ProcessKey(eKeys Key); }; + +//*************************************************************************** +// class cMenuDbRecordings //*************************************************************************** -#endif // __EPG2VDR_MENU_H +class cRecordingFilter; + +class cMenuDbRecordings : public cOsdMenu +{ + public: + + cMenuDbRecordings(const char* Base = nullptr, int Level = 0, bool OpenSubMenus = false, const cRecordingFilter* Filter = nullptr); + virtual ~cMenuDbRecordings(); + virtual eOSState ProcessKey(eKeys Key); + static void SetPath(const char *Path); + static void SetRecording(const char *FileName); + + protected: + + cString DirectoryName(void); + + private: + + char *base; + int level; + cStateKey recordingsStateKey; + int helpKeys; + const cRecordingFilter *filter; + static cString path; + static cString fileName; + void SetHelpKeys(void); + void Set(bool Refresh = false); + bool Open(bool OpenSubMenus = false); + eOSState Play(void); + eOSState Rewind(void); + // eOSState Delete(void); + // eOSState Info(void); + eOSState Sort(void); + eOSState Commands(eKeys Key = kNone); +}; diff --git a/menurec.c b/menurec.c new file mode 100644 index 0000000..9837090 --- /dev/null +++ b/menurec.c @@ -0,0 +1,516 @@ +/* + * menurec.c: EPG2VDR plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include <vdr/menuitems.h> +#include <vdr/status.h> +#include <vdr/menu.h> +#include <vdr/videodir.h> +#include <vdr/interface.h> + +#include "lib/xml.h" + +#include "plgconfig.h" +#include "menu.h" +#include "ttools.h" + +//*************************************************************************** +// Class cMenuDbRecordingItem +//*************************************************************************** + +class cMenuDbRecordingItem : public cOsdItem +{ + public: + + cMenuDbRecordingItem(const cRecording *Recording, int Level); + ~cMenuDbRecordingItem(); + void IncrementCounter(bool New); + const char* Name() const { return name; } + int Level() const { return level; } + const cRecording* Recording() const { return recording; } + bool IsDirectory() const { return name != NULL; } + void SetRecording(const cRecording *Recording) { recording = Recording; } + virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable); + + private: + + const cRecording *recording; + int level; + char *name; + int totalEntries, newEntries; +}; + +cMenuDbRecordingItem::cMenuDbRecordingItem(const cRecording* Recording, int Level) +{ + recording = Recording; + level = Level; + name = NULL; + totalEntries = newEntries = 0; + SetText(Recording->Title('\t', true, Level)); + + // a folder? + + if (*Text() == '\t') + name = strdup(Text() + 2); // 'Text() + 2' to skip the two '\t' + else + { + // -> actual recording + + int Usage = Recording->IsInUse(); + + if ((Usage & ruDst) != 0 && (Usage & (ruMove | ruCopy)) != 0) + SetSelectable(false); + } +} + +cMenuDbRecordingItem::~cMenuDbRecordingItem() +{ + free(name); +} + +void cMenuDbRecordingItem::IncrementCounter(bool New) +{ + totalEntries++; + if (New) + newEntries++; + SetText(cString::sprintf("%d\t\t%d\t%s", totalEntries, newEntries, name)); +} + +void cMenuDbRecordingItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) +{ + if (!DisplayMenu->SetItemRecording(recording, Index, Current, Selectable, level, totalEntries, newEntries)) + DisplayMenu->SetItem(Text(), Index, Current, Selectable); +} + +//*************************************************************************** +// Class cMenuDbRecordings +//*************************************************************************** + +cString cMenuDbRecordings::path; +cString cMenuDbRecordings::fileName; + +cMenuDbRecordings::cMenuDbRecordings(const char* Base, int Level, bool OpenSubMenus, const cRecordingFilter* Filter) + : cOsdMenu(Base ? Base : tr("Recordings"), 9, 6, 6) +{ + SetMenuCategory(mcRecording); + base = Base ? strdup(Base) : NULL; + level = Setup.RecordingDirs ? Level : -1; + filter = Filter; + helpKeys = -1; + Display(); // this keeps the higher level menus from showing up briefly when pressing 'Back' during replay + Set(); + + if (Current() < 0) + SetCurrent(First()); + + else if (OpenSubMenus && (cReplayControl::LastReplayed() || *path || *fileName)) + { + if (!*path || Level < strcountchr(path, FOLDERDELIMCHAR)) + { + if (Open(true)) + return; + } + } + + Display(); + SetHelpKeys(); +} + +cMenuDbRecordings::~cMenuDbRecordings() +{ + cMenuDbRecordingItem* ri = (cMenuDbRecordingItem*)Get(Current()); + + if (ri) + { + if (!ri->IsDirectory()) + SetRecording(ri->Recording()->FileName()); + } + + free(base); +} + +void cMenuDbRecordings::SetHelpKeys(void) +{ + cMenuDbRecordingItem* ri = (cMenuDbRecordingItem*)Get(Current()); + int NewHelpKeys = 0; + + if (ri) + { + if (ri->IsDirectory()) + NewHelpKeys = 1; + else + NewHelpKeys = 2; + } + + if (NewHelpKeys != helpKeys) + { + switch (NewHelpKeys) + { + case 0: SetHelp(NULL); break; + case 1: SetHelp(tr("Button$Open"), NULL, NULL, tr("Button$Edit")); break; + case 2: SetHelp(RecordingCommands.Count() ? tr("Commands") : tr("Button$Play"), tr("Button$Rewind"), tr("Button$Delete"), tr("Button$Info")); + default: ; + } + + helpKeys = NewHelpKeys; + } +} + +void cMenuDbRecordings::Set(bool Refresh) +{ + if (!cRecordings::GetRecordingsRead(recordingsStateKey)) + return ; + + recordingsStateKey.Remove(); + const char *CurrentRecording = *fileName ? *fileName : cReplayControl::LastReplayed(); + cRecordings *Recordings = cRecordings::GetRecordingsWrite(recordingsStateKey); // write access is necessary for sorting! + cMenuDbRecordingItem *LastItem = NULL; + + if (!CurrentRecording) + { + if (cMenuDbRecordingItem *ri = (cMenuDbRecordingItem*)Get(Current())) + CurrentRecording = ri->Recording()->FileName(); + } + + int current = Current(); + Clear(); + GetRecordingsSortMode(DirectoryName()); + Recordings->Sort(); + + for (const cRecording *Recording = Recordings->First(); Recording; Recording = Recordings->Next(Recording)) + { + if ((!filter || filter->Filter(Recording)) && (!base || (strstr(Recording->Name(), base) == Recording->Name() && Recording->Name()[strlen(base)] == FOLDERDELIMCHAR))) + { + cMenuDbRecordingItem *Item = new cMenuDbRecordingItem(Recording, level); + cMenuDbRecordingItem *LastDir = NULL; + + if (Item->IsDirectory()) + { + // Sorting may ignore non-alphanumeric characters, so we need to explicitly handle directories in case they only differ in such characters: + + for (cMenuDbRecordingItem* p = LastItem; p; p = dynamic_cast<cMenuDbRecordingItem*>(p->Prev())) + { + if (p->Name() && strcmp(p->Name(), Item->Name()) == 0) + { + LastDir = p; + break; + } + } + } + + if (*Item->Text() && !LastDir) + { + Add(Item); + LastItem = Item; + + if (Item->IsDirectory()) + LastDir = Item; + } + else + delete Item; + + if (LastItem || LastDir) + { + if (*path) + { + if (strcmp(path, Recording->Folder()) == 0) + SetCurrent(LastDir ? LastDir : LastItem); + } + else if (CurrentRecording && strcmp(CurrentRecording, Recording->FileName()) == 0) + SetCurrent(LastDir ? LastDir : LastItem); + } + + if (LastDir) + LastDir->IncrementCounter(Recording->IsNew()); + } + } + + if (Current() < 0) + SetCurrent(Get(current)); // last resort, in case the recording was deleted + + SetMenuSortMode(RecordingsSortMode == rsmName ? msmName : msmTime); + recordingsStateKey.Remove(false); // sorting doesn't count as a real modification + + if (Refresh) + Display(); +} + +void cMenuDbRecordings::SetPath(const char *Path) +{ + path = Path; +} + +void cMenuDbRecordings::SetRecording(const char *FileName) +{ + fileName = FileName; +} + +cString cMenuDbRecordings::DirectoryName(void) +{ + cString d(cVideoDirectory::Name()); + + if (base) + { + char *s = ExchangeChars(strdup(base), true); + d = AddDirectory(d, s); + free(s); + } + + return d; +} + +bool cMenuDbRecordings::Open(bool OpenSubMenus) +{ + cMenuDbRecordingItem* ri = (cMenuDbRecordingItem*)Get(Current()); + + if (ri && ri->IsDirectory() && (!*path || strcountchr(path, FOLDERDELIMCHAR) > 0)) + { + const char* t = ri->Name(); + cString buffer; + + if (base) + { + buffer = cString::sprintf("%s%c%s", base, FOLDERDELIMCHAR, t); + t = buffer; + } + + AddSubMenu(new cMenuDbRecordings(t, level + 1, OpenSubMenus, filter)); + return true; + } + + return false; +} + +eOSState cMenuDbRecordings::Play(void) +{ + cMenuDbRecordingItem* ri = (cMenuDbRecordingItem*)Get(Current()); + + if (ri) + { + if (ri->IsDirectory()) + Open(); + else + { + cReplayControl::SetRecording(ri->Recording()->FileName()); + return osReplay; + } + } + + return osContinue; +} + +eOSState cMenuDbRecordings::Rewind(void) +{ + if (HasSubMenu() || Count() == 0) + return osContinue; + + cMenuDbRecordingItem *ri = (cMenuDbRecordingItem *)Get(Current()); + + if (ri && !ri->IsDirectory()) + { + cDevice::PrimaryDevice()->StopReplay(); // must do this first to be able to rewind the currently replayed recording + cResumeFile ResumeFile(ri->Recording()->FileName(), ri->Recording()->IsPesRecording()); + ResumeFile.Delete(); + return Play(); + } + + return osContinue; +} + +/* +eOSState cMenuDbRecordings::Delete(void) +{ + if (HasSubMenu() || Count() == 0) + return osContinue; + cMenuDbRecordingItem *ri = (cMenuDbRecordingItem *)Get(Current()); + if (ri && !ri->IsDirectory()) { + if (Interface->Confirm(tr("Delete recording?"))) { + if (cRecordControl *rc = cRecordControls::GetRecordControl(ri->Recording()->FileName())) { + if (Interface->Confirm(tr("Timer still recording - really delete?"))) { + if (cTimer *Timer = rc->Timer()) { + LOCK_TIMERS_WRITE; + Timer->Skip(); + cRecordControls::Process(Timers, time(NULL)); + if (Timer->IsSingleEvent()) { + Timers->Del(Timer); + isyslog("deleted timer %s", *Timer->ToDescr()); + } + } + } + else + return osContinue; + } + cString FileName; + { + LOCK_RECORDINGS_READ; + if (const cRecording *Recording = Recordings->GetByName(ri->Recording()->FileName())) { + FileName = Recording->FileName(); + if (RecordingsHandler.GetUsage(FileName)) { + if (!Interface->Confirm(tr("Recording is being edited - really delete?"))) + return osContinue; + } + } + } + RecordingsHandler.Del(FileName); // must do this w/o holding a lock, because the cleanup section in cDirCopier::Action() might request one! + if (cReplayControl::NowReplaying() && strcmp(cReplayControl::NowReplaying(), FileName) == 0) + cControl::Shutdown(); + cRecordings *Recordings = cRecordings::GetRecordingsWrite(recordingsStateKey); + Recordings->SetExplicitModify(); + cRecording *Recording = Recordings->GetByName(FileName); + if (!Recording || Recording->Delete()) { + cReplayControl::ClearLastReplayed(FileName); + Recordings->DelByName(FileName); + cOsdMenu::Del(Current()); + SetHelpKeys(); + cVideoDiskUsage::ForceCheck(); + Recordings->SetModified(); + recordingsStateKey.Remove(); + Display(); + if (!Count()) + return osUserRecEmpty; + return osUserRecRemoved; + } + else + Skins.Message(mtError, tr("Error while deleting recording!")); + recordingsStateKey.Remove(); + } + } + return osContinue; +} +*/ +/* +eOSState cMenuDbRecordings::Info(void) +{ + if (HasSubMenu() || Count() == 0) + return osContinue; + if (cMenuDbRecordingItem *ri = (cMenuDbRecordingItem *)Get(Current())) { + if (ri->IsDirectory()) + return AddSubMenu(new cMenuPathEdit(cString(ri->Recording()->Name(), strchrn(ri->Recording()->Name(), FOLDERDELIMCHAR, ri->Level() + 1)))); + else + return AddSubMenu(new cMenuRecording(ri->Recording(), true)); + } + return osContinue; +} +*/ +eOSState cMenuDbRecordings::Commands(eKeys Key) +{ + if (HasSubMenu() || Count() == 0) + return osContinue; + + cMenuDbRecordingItem *ri = (cMenuDbRecordingItem *)Get(Current()); + + if (ri && !ri->IsDirectory()) + { + cMenuCommands *menu; + eOSState state = AddSubMenu(menu = new cMenuCommands(tr("Recording commands"), &RecordingCommands, cString::sprintf("\"%s\"", *strescape(ri->Recording()->FileName(), "\\\"$")))); + + if (Key != kNone) + state = menu->ProcessKey(Key); + + return state; + } + + return osContinue; +} + +eOSState cMenuDbRecordings::Sort(void) +{ + if (HasSubMenu()) + return osContinue; + + if (const cMenuDbRecordingItem *ri = (cMenuDbRecordingItem *)Get(Current())) + SetRecording(ri->Recording()->FileName()); // makes sure the Recordings menu will reposition to the current recording + + IncRecordingsSortMode(DirectoryName()); + recordingsStateKey.Reset(); + Set(true); + + return osContinue; +} + +eOSState cMenuDbRecordings::ProcessKey(eKeys Key) +{ + eOSState state = cOsdMenu::ProcessKey(Key); + + if (state == osUnknown) + { + switch (Key) + { + case kPlayPause: + case kPlay: + case kOk: return Play(); + case kRed: return (helpKeys > 1 && RecordingCommands.Count()) ? Commands() : Play(); + case kGreen: return Rewind(); + // case kYellow: return Delete(); + case kInfo: + // case kBlue: return Info(); + case k0: return Sort(); + case k1: + case k2: + case k3: + case k4: + case k5: + case k6: + case k7: + case k8: + case k9: return Commands(Key); + + default: break; + } + } + + /* + else if (state == osUserRecRenamed) { + // a recording was renamed (within the same folder), so let's refresh the menu + CloseSubMenu(false); // this is the cMenuRecordingEdit/cMenuPathEdit + path = NULL; + fileName = NULL; + state = osContinue; + } + else if (state == osUserRecMoved) { + // a recording was moved to a different folder, so let's delete the old item + CloseSubMenu(false); // this is the cMenuRecordingEdit/cMenuPathEdit + path = NULL; + fileName = NULL; + cOsdMenu::Del(Current()); + Set(); // the recording might have been moved into a new subfolder of this folder + if (!Count()) + return osUserRecEmpty; + Display(); + state = osUserRecRemoved; + } + else if (state == osUserRecRemoved) { + // a recording was removed from a sub folder, so update the current item + if (cOsdMenu *m = SubMenu()) { + if (cMenuDbRecordingItem *ri = (cMenuDbRecordingItem *)Get(Current())) { + if (cMenuDbRecordingItem *riSub = (cMenuDbRecordingItem *)m->Get(m->Current())) + ri->SetRecording(riSub->Recording()); + } + } + // no state change here, this report goes upstream! + } + else if (state == osUserRecEmpty) { + // a subfolder became empty, so let's go back up + CloseSubMenu(false); // this is the now empty submenu + cOsdMenu::Del(Current()); // the menu entry of the now empty subfolder + Set(); // in case a recording was moved into a new subfolder of this folder + if (base && !Count()) // base: don't go up beyond the top level Recordings menu + return state; + Display(); + state = osContinue; + } + */ + + if (!HasSubMenu()) + { + Set(true); + + if (Key != kNone) + SetHelpKeys(); + } + + return state; +} diff --git a/po/de_DE.po b/po/de_DE.po index 6a8c208..1c824d5 100644 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: <vdr@jwendel.de>\n" -"POT-Creation-Date: 2019-10-28 16:00+0100\n" +"POT-Creation-Date: 2019-10-31 09:16+0100\n" "PO-Revision-Date: 2009-08-27 21:40+0200\n" "Last-Translator: Klaus Schmidinger <kls@cadsoft.de>\n" "Language-Team: <vdr@linuxtv.org>\n" @@ -28,6 +28,9 @@ msgstr "Timerhistorie" msgid "Program" msgstr "Programm" +msgid "Recordings" +msgstr "" + #, fuzzy msgid "Update" msgstr "Automatisches Update" @@ -175,6 +178,30 @@ msgstr "Timerhistorie - Gelöscht" msgid "Delete timer from journal?" msgstr "Eintrag aus Timerhistorie löschen?" +msgid "Button$Open" +msgstr "" + +msgid "Button$Edit" +msgstr "" + +msgid "Commands" +msgstr "" + +msgid "Button$Play" +msgstr "" + +msgid "Button$Rewind" +msgstr "" + +msgid "Button$Delete" +msgstr "Löschen" + +msgid "Button$Info" +msgstr "Information" + +msgid "Recording commands" +msgstr "" + msgid "Search matching Events" msgstr "Wiederholungen" @@ -226,9 +253,6 @@ msgstr "Suchergebnisse" msgid "Button$Schedule" msgstr "Programm" -msgid "Button$Info" -msgstr "Information" - msgid "Can't switch channel!" msgstr "Umschalten nicht möglich!" @@ -328,9 +352,6 @@ msgstr "An/Aus" msgid "Button$New" msgstr "Neu" -msgid "Button$Delete" -msgstr "Löschen" - msgid "Delete timer?" msgstr "Timer löschen" diff --git a/po/it_IT.po b/po/it_IT.po index bedc5ca..a405004 100644 --- a/po/it_IT.po +++ b/po/it_IT.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: <vdr@jwendel.de>\n" -"POT-Creation-Date: 2019-10-28 16:00+0100\n" +"POT-Creation-Date: 2019-10-31 09:16+0100\n" "PO-Revision-Date: 2009-08-27 21:45+0100\n" "Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n" "Language-Team: <vdr@linuxtv.org>\n" @@ -33,6 +33,9 @@ msgstr "" msgid "Program" msgstr "" +msgid "Recordings" +msgstr "" + #, fuzzy msgid "Update" msgstr "Aggiorn. automatico" @@ -180,6 +183,30 @@ msgstr "" msgid "Delete timer from journal?" msgstr "" +msgid "Button$Open" +msgstr "" + +msgid "Button$Edit" +msgstr "" + +msgid "Commands" +msgstr "" + +msgid "Button$Play" +msgstr "" + +msgid "Button$Rewind" +msgstr "" + +msgid "Button$Delete" +msgstr "" + +msgid "Button$Info" +msgstr "" + +msgid "Recording commands" +msgstr "" + msgid "Search matching Events" msgstr "" @@ -231,9 +258,6 @@ msgstr "" msgid "Button$Schedule" msgstr "" -msgid "Button$Info" -msgstr "" - msgid "Can't switch channel!" msgstr "" @@ -333,9 +357,6 @@ msgstr "" msgid "Button$New" msgstr "" -msgid "Button$Delete" -msgstr "" - msgid "Delete timer?" msgstr "" |