summaryrefslogtreecommitdiff
path: root/menurec.c
diff options
context:
space:
mode:
Diffstat (limited to 'menurec.c')
-rw-r--r--menurec.c516
1 files changed, 516 insertions, 0 deletions
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;
+}