summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhorchi <vdr@jwendel.de>2019-10-31 10:02:19 +0100
committerhorchi <vdr@jwendel.de>2019-10-31 10:02:19 +0100
commit05355f62ef7e7fd574ff1631caf86f04b0c3364c (patch)
treeb039255fbaf77da459758142c2bb6f11ff7f5a19
parentf9c8dd36d36efa145806f7bef97b46382a5442f3 (diff)
downloadvdr-plugin-epg2vdr-05355f62ef7e7fd574ff1631caf86f04b0c3364c.tar.gz
vdr-plugin-epg2vdr-05355f62ef7e7fd574ff1631caf86f04b0c3364c.tar.bz2
development of recording menu
-rw-r--r--Makefile2
-rw-r--r--epg2vdr.c10
-rw-r--r--menu.h42
-rw-r--r--menurec.c516
-rw-r--r--po/de_DE.po35
-rw-r--r--po/it_IT.po35
6 files changed, 621 insertions, 19 deletions
diff --git a/Makefile b/Makefile
index 1403938..18b46e3 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/epg2vdr.c b/epg2vdr.c
index 11ca501..c51cc7e 100644
--- a/epg2vdr.c
+++ b/epg2vdr.c
@@ -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"));
diff --git a/menu.h b/menu.h
index b6957f9..5cb10d2 100644
--- a/menu.h
+++ b/menu.h
@@ -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 ""