diff --git a/HISTORY b/HISTORY index f0934eec..7b71a7b7 100644 --- a/HISTORY +++ b/HISTORY @@ -879,7 +879,7 @@ Video Disk Recorder Revision History - Fixed DVD audio sync problems (thanks to Andreas Schultz). - Fixed external AC3 replay for DVDs (thanks to Andreas Schultz). -2002-01-13: Version 0.99pre2 +2002-01-20: Version 0.99pre2 - Fixed setting the OSD size in the 'Confirm' interface call (thanks to Deti Fliegl). @@ -896,3 +896,7 @@ Video Disk Recorder Revision History Carsten Koch). - No longer requiring 'libncurses' if compiling without DEBUG_OSD=1 and REMOTE=KBD (thanks to Lauri Pesonen). +- The "Recordings" menu now displays a hierarchical structure if there are + subdirectories for the recordings. This can be controlled through the + "RecordingDirs" parameter in the "Setup" menu. + See "MANUAL/Replaying a Recording" for details. diff --git a/MANUAL b/MANUAL index 52976e8a..99d91be9 100644 --- a/MANUAL +++ b/MANUAL @@ -159,6 +159,13 @@ Video Disk Recorder User's Manual All recordings are listed in the "Recordings" menu. Browse through the list with the "Up" and "Down" button and press "Ok" (or the "Red" button) to start playback. New recordings are marked with an '*'. + If the Setup parameter RecordingDirs has been set and there are recordings + from periodic timers organized in a subdirectory structure, only the + directory is displayed and it can be opened by pressing "Ok" (or the "Red" + button). A directory entry displays the total number of recordings within + that directory (and any possible subdirectory thereof) as well as the total + number of new recordings (as opposed to a recording's entry, which displays + the date and time of the recording). Playback can be stopped via the "Main" menu by selecting "Stop replaying", or by pressing the "Blue" button outside the menu. A previously stopped playback session can be resumed by pressing the "Blue" @@ -439,6 +446,9 @@ Video Disk Recorder User's Manual 0 = don't use the 'Subtitle' 1 = use it (and create subdirectories) + RecordingDirs = 1 Turns displaying the Recordings menu as a hierarchical + directory structure on or off. + VideoFormat = 0 The video format (or aspect ratio) of the tv set in use. 0 = 4:3 1 = 16:9 diff --git a/config.c b/config.c index 6ebaaacb..f9303514 100644 --- a/config.c +++ b/config.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.c 1.76 2001/10/20 13:09:38 kls Exp $ + * $Id: config.c 1.77 2002/01/19 16:06:42 kls Exp $ */ #include "config.h" @@ -807,6 +807,7 @@ cSetup::cSetup(void) DefaultPriority = 50; DefaultLifetime = 50; UseSubtitle = 1; + RecordingDirs = 1; VideoFormat = VIDEO_FORMAT_4_3; ChannelInfoPos = 0; OSDwidth = 52; @@ -848,6 +849,7 @@ bool cSetup::Parse(char *s) else if (!strcasecmp(Name, "DefaultPriority")) DefaultPriority = atoi(Value); else if (!strcasecmp(Name, "DefaultLifetime")) DefaultLifetime = atoi(Value); else if (!strcasecmp(Name, "UseSubtitle")) UseSubtitle = atoi(Value); + else if (!strcasecmp(Name, "RecordingDirs")) RecordingDirs = atoi(Value); else if (!strcasecmp(Name, "VideoFormat")) VideoFormat = atoi(Value); else if (!strcasecmp(Name, "ChannelInfoPos")) ChannelInfoPos = atoi(Value); else if (!strcasecmp(Name, "OSDwidth")) OSDwidth = atoi(Value); @@ -924,6 +926,7 @@ bool cSetup::Save(const char *FileName) fprintf(f, "DefaultPriority = %d\n", DefaultPriority); fprintf(f, "DefaultLifetime = %d\n", DefaultLifetime); fprintf(f, "UseSubtitle = %d\n", UseSubtitle); + fprintf(f, "RecordingDirs = %d\n", RecordingDirs); fprintf(f, "VideoFormat = %d\n", VideoFormat); fprintf(f, "ChannelInfoPos = %d\n", ChannelInfoPos); fprintf(f, "OSDwidth = %d\n", OSDwidth); diff --git a/config.h b/config.h index 4f27f768..960e5146 100644 --- a/config.h +++ b/config.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.h 1.87 2001/12/01 12:00:37 kls Exp $ + * $Id: config.h 1.88 2002/01/19 16:06:53 kls Exp $ */ #ifndef __CONFIG_H @@ -292,6 +292,7 @@ public: int PrimaryLimit; int DefaultPriority, DefaultLifetime; int UseSubtitle; + int RecordingDirs; int VideoFormat; int ChannelInfoPos; int OSDwidth, OSDheight; diff --git a/i18n.c b/i18n.c index 764dc338..41be3cca 100644 --- a/i18n.c +++ b/i18n.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: i18n.c 1.45 2001/10/28 16:04:58 kls Exp $ + * $Id: i18n.c 1.46 2002/01/19 16:25:33 kls Exp $ * * Slovenian translations provided by Miha Setina * Italian translations provided by Alberto Carraro @@ -285,6 +285,15 @@ const tPhrase Phrases[] = { "Résumé", "Sammendrag", }, + { "Open", + "Öffnen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, { "Switch", "Umschalten", "Preklopi", @@ -631,6 +640,15 @@ const tPhrase Phrases[] = { "Enregistrement en cours!", "Timer gjřr opptak!", }, + { "Error while accessing recording!", + "Fehler beim ansprechen der Aufzeichnung!", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, { "Error while deleting recording!", "Fehler beim Löschen der Aufzeichnung!", "Napaka pri odstranjevanju posnetka!", @@ -884,6 +902,15 @@ const tPhrase Phrases[] = { "", // TODO "", // TODO }, + { "RecordingDirs", + "Aufn. Verzeichnisse", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, { "VideoFormat", "Video Format", "", // TODO diff --git a/menu.c b/menu.c index e306d5be..3fe15c3c 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 1.142 2002/01/13 16:18:40 kls Exp $ + * $Id: menu.c 1.143 2002/01/20 14:01:40 kls Exp $ */ #include "menu.h" @@ -1494,46 +1494,155 @@ eOSState cMenuSchedule::ProcessKey(eKeys Key) // --- cMenuRecordingItem ---------------------------------------------------- class cMenuRecordingItem : public cOsdItem { +private: + char *fileName; + char *name; + int totalEntries, newEntries; public: - cRecording *recording; - cMenuRecordingItem(cRecording *Recording); - virtual void Set(void); + cMenuRecordingItem(cRecording *Recording, int Level); + ~cMenuRecordingItem(); + void IncrementCounter(bool New); + const char *Name(void) { return name; } + const char *FileName(void) { return fileName; } + bool IsDirectory(void) { return name != NULL; } }; -cMenuRecordingItem::cMenuRecordingItem(cRecording *Recording) +cMenuRecordingItem::cMenuRecordingItem(cRecording *Recording, int Level) { - recording = Recording; - Set(); + fileName = strdup(Recording->FileName()); + name = NULL; + totalEntries = newEntries = 0; + SetText(Recording->Title('\t', true, Level)); + if (*Text() == '\t') + name = strdup(Text() + 2); // 'Text() + 2' to skip the two '\t' } -void cMenuRecordingItem::Set(void) +cMenuRecordingItem::~cMenuRecordingItem() { - SetText(recording->Title('\t', true)); + delete fileName; + delete name; +} + +void cMenuRecordingItem::IncrementCounter(bool New) +{ + totalEntries++; + if (New) + newEntries++; + char *buffer = NULL; + asprintf(&buffer, "%d\t%d\t%s", totalEntries, newEntries, name); + SetText(buffer, false); } // --- cMenuRecordings ------------------------------------------------------- -cMenuRecordings::cMenuRecordings(void) -:cOsdMenu(tr("Recordings"), 6, 6) +cRecordings cMenuRecordings::Recordings; +int cMenuRecordings::helpKeys = -1; + +cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus) +:cOsdMenu(Base ? Base : tr("Recordings"), 6, 6) { - if (Recordings.Load()) { - const char *lastReplayed = cReplayControl::LastReplayed(); - cRecording *recording = Recordings.First(); - while (recording) { - Add(new cMenuRecordingItem(recording), lastReplayed && strcmp(lastReplayed, recording->FileName()) == 0); - recording = Recordings.Next(recording); - } + base = Base ? strdup(Base) : NULL; + level = Setup.RecordingDirs ? Level : -1; + if (Base || Recordings.Load()) { + const char *LastReplayed = cReplayControl::LastReplayed(); + cMenuRecordingItem *LastItem = NULL; + char *LastItemText = NULL; + for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) { + if (!Base || strstr(recording->Name(), Base) == recording->Name()) { + cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level); + if (*Item->Text() && (!LastItem || strcmp(Item->Text(), LastItemText) != 0)) { + Add(Item); + LastItem = Item; + delete LastItemText; + LastItemText = strdup(LastItem->Text()); // must use a copy because of the counters! + } + else + delete Item; + if (LastItem) { + if (LastReplayed && strcmp(LastReplayed, recording->FileName()) == 0) + SetCurrent(LastItem); + if (LastItem->IsDirectory()) + LastItem->IncrementCounter(recording->IsNew()); + } + } + } + delete LastItemText; + if (Current() < 0) + SetCurrent(First()); + else if (OpenSubMenus && Open(true)) + return; } - SetHelp(tr("Play"), tr("Rewind"), tr("Delete"), tr("Summary")); - Display(); + SetHelpKeys(); +} + +cMenuRecordings::~cMenuRecordings() +{ + delete base; +} + +void cMenuRecordings::SetHelpKeys(void) +{ + cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current()); + int NewHelpKeys = helpKeys; + if (ri) { + if (ri->IsDirectory()) + NewHelpKeys = 1; + else { + NewHelpKeys = 2; + cRecording *recording = GetRecording(ri); + if (recording && recording->Summary()) + NewHelpKeys = 3; + } + } + if (NewHelpKeys != helpKeys) { + switch (NewHelpKeys) { + case 0: SetHelp(NULL); break; + case 1: SetHelp(tr("Open")); break; + case 2: + case 3: SetHelp(tr("Play"), tr("Rewind"), tr("Delete"), NewHelpKeys == 3 ? tr("Summary") : NULL); + } + helpKeys = NewHelpKeys; + } +} + +cRecording *cMenuRecordings::GetRecording(cMenuRecordingItem *Item) +{ + cRecording *recording = Recordings.GetByName(Item->FileName()); + if (!recording) + Interface->Error(tr("Error while accessing recording!")); + return recording; +} + +bool cMenuRecordings::Open(bool OpenSubMenus) +{ + cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current()); + if (ri && ri->IsDirectory()) { + const char *t = ri->Name(); + char *buffer = NULL; + if (base) { + asprintf(&buffer, "%s~%s", base, t); + t = buffer; + } + AddSubMenu(new cMenuRecordings(t, level + 1, OpenSubMenus)); + delete buffer; + return true; + } + return false; } eOSState cMenuRecordings::Play(void) { cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current()); if (ri) { - cReplayControl::SetRecording(ri->recording->FileName(), ri->recording->Title()); - return osReplay; + if (ri->IsDirectory()) + Open(); + else { + cRecording *recording = GetRecording(ri); + if (recording) { + cReplayControl::SetRecording(recording->FileName(), recording->Title()); + return osReplay; + } + } } return osContinue; } @@ -1541,9 +1650,9 @@ eOSState cMenuRecordings::Play(void) eOSState cMenuRecordings::Rewind(void) { cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current()); - if (ri) { + if (ri && !ri->IsDirectory()) { cDvbApi::PrimaryDvbApi->StopReplay(); // must do this first to be able to rewind the currently replayed recording - cResumeFile ResumeFile(ri->recording->FileName()); + cResumeFile ResumeFile(ri->FileName()); ResumeFile.Delete(); return Play(); } @@ -1553,17 +1662,21 @@ eOSState cMenuRecordings::Rewind(void) eOSState cMenuRecordings::Del(void) { cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current()); - if (ri) { + if (ri && !ri->IsDirectory()) { //XXX what if this recording's file is currently in use??? //XXX if (!ti->recording) { if (Interface->Confirm(tr("Delete recording?"))) { - if (ri->recording->Delete()) { - cReplayControl::ClearLastReplayed(ri->recording->FileName()); - cOsdMenu::Del(Current()); - Display(); + cRecording *recording = GetRecording(ri); + if (recording) { + if (recording->Delete()) { + cReplayControl::ClearLastReplayed(ri->FileName()); + cOsdMenu::Del(Current()); + Recordings.Del(recording); + Display(); + } + else + Interface->Error(tr("Error while deleting recording!")); } - else - Interface->Error(tr("Error while deleting recording!")); } //XXX } //XXX else @@ -1577,8 +1690,11 @@ eOSState cMenuRecordings::Summary(void) if (HasSubMenu() || Count() == 0) return osContinue; cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current()); - if (ri && ri->recording->Summary() && *ri->recording->Summary()) - return AddSubMenu(new cMenuText(tr("Summary"), ri->recording->Summary())); + if (ri && !ri->IsDirectory()) { + cRecording *recording = GetRecording(ri); + if (recording && recording->Summary() && *recording->Summary()) + return AddSubMenu(new cMenuText(tr("Summary"), recording->Summary())); + } return osContinue; } @@ -1597,6 +1713,8 @@ eOSState cMenuRecordings::ProcessKey(eKeys Key) default: break; } } + if (!HasSubMenu() && Key != kNone) + SetHelpKeys(); return state; } @@ -1719,6 +1837,7 @@ void cMenuSetup::Set(void) Add(new cMenuEditIntItem( tr("DefaultPriority"), &data.DefaultPriority, 0, MAXPRIORITY)); Add(new cMenuEditIntItem( tr("DefaultLifetime"), &data.DefaultLifetime, 0, MAXLIFETIME)); Add(new cMenuEditBoolItem(tr("UseSubtitle"), &data.UseSubtitle)); + Add(new cMenuEditBoolItem(tr("RecordingDirs"), &data.RecordingDirs)); Add(new cMenuEditBoolItem(tr("VideoFormat"), &data.VideoFormat, "4:3", "16:9")); Add(new cMenuEditBoolItem(tr("ChannelInfoPos"), &data.ChannelInfoPos, tr("bottom"), tr("top"))); Add(new cMenuEditIntItem( tr("OSDwidth"), &data.OSDwidth, MINOSDWIDTH, MAXOSDWIDTH)); @@ -1871,7 +1990,7 @@ cMenuMain::cMenuMain(bool Replaying, eOSState State) // Initial submenus: switch (State) { - case osRecordings: AddSubMenu(new cMenuRecordings); break; + case osRecordings: AddSubMenu(new cMenuRecordings(NULL, 0, true)); break; #ifdef DVDSUPPORT case osDVD: AddSubMenu(new cMenuDVD); break; #endif //DVDSUPPORT diff --git a/menu.h b/menu.h index 250d2ff5..82fd6403 100644 --- a/menu.h +++ b/menu.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.h 1.34 2001/10/28 15:21:04 kls Exp $ + * $Id: menu.h 1.35 2002/01/20 13:38:34 kls Exp $ */ #ifndef _MENU_H @@ -55,15 +55,24 @@ public: }; #endif //DVDSUPPORT +class cMenuRecordingItem; + class cMenuRecordings : public cOsdMenu { private: - cRecordings Recordings; + static cRecordings Recordings; + char *base; + int level; + static int helpKeys; + void SetHelpKeys(void); + cRecording *GetRecording(cMenuRecordingItem *Item); + bool Open(bool OpenSubMenus = false); eOSState Play(void); eOSState Rewind(void); eOSState Del(void); eOSState Summary(void); public: - cMenuRecordings(void); + cMenuRecordings(const char *Base = NULL, int Level = 0, bool OpenSubMenus = false); + ~cMenuRecordings(); virtual eOSState ProcessKey(eKeys Key); }; diff --git a/osd.c b/osd.c index 02f43748..0e055771 100644 --- a/osd.c +++ b/osd.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.c 1.18 2001/08/25 13:15:16 kls Exp $ + * $Id: osd.c 1.19 2002/01/20 11:13:22 kls Exp $ */ #include "osd.h" @@ -150,6 +150,10 @@ void cOsdMenu::Add(cOsdItem *Item, bool Current) void cOsdMenu::Display(void) { + if (subMenu) { + subMenu->Display(); + return; + } visible = true; Interface->Clear(); Interface->SetCols(cols); @@ -179,6 +183,11 @@ void cOsdMenu::Display(void) Interface->Status(status); } +void cOsdMenu::SetCurrent(cOsdItem *Item) +{ + current = Item ? Item->Index() : -1; +} + void cOsdMenu::RefreshCurrent(void) { cOsdItem *item = Get(current); diff --git a/osd.h b/osd.h index 2d243f16..21c93154 100644 --- a/osd.h +++ b/osd.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.h 1.24 2001/08/25 12:56:46 kls Exp $ + * $Id: osd.h 1.25 2002/01/20 10:42:14 kls Exp $ */ #ifndef __OSD_H @@ -83,6 +83,7 @@ protected: bool visible; virtual void Clear(void); bool SpecialItem(int idx); + void SetCurrent(cOsdItem *Item); void RefreshCurrent(void); void DisplayCurrent(bool Current); void CursorUp(void); diff --git a/recording.c b/recording.c index fe56a6e6..2ce0cf78 100644 --- a/recording.c +++ b/recording.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recording.c 1.42 2001/10/20 10:28:28 kls Exp $ + * $Id: recording.c 1.43 2002/01/20 12:14:25 kls Exp $ */ #include "recording.h" @@ -184,6 +184,8 @@ void cResumeFile::Delete(void) // --- cRecording ------------------------------------------------------------ +#define RESUME_NOT_INITIALIZED (-2) + struct tCharExchange { char a; char b; }; tCharExchange CharExchange[] = { { '~', '/' }, @@ -213,6 +215,7 @@ char *ExchangeChars(char *s, bool ToFileSystem) cRecording::cRecording(cTimer *Timer, const char *Subtitle, const char *Summary) { + resume = RESUME_NOT_INITIALIZED; titleBuffer = NULL; sortBuffer = NULL; fileName = NULL; @@ -242,6 +245,7 @@ cRecording::cRecording(cTimer *Timer, const char *Subtitle, const char *Summary) cRecording::cRecording(const char *FileName) { + resume = RESUME_NOT_INITIALIZED; titleBuffer = NULL; sortBuffer = NULL; fileName = strdup(FileName); @@ -342,6 +346,15 @@ char *cRecording::SortName(void) return sortBuffer; } +int cRecording::GetResume(void) +{ + if (resume == RESUME_NOT_INITIALIZED) { + cResumeFile ResumeFile(FileName()); + resume = ResumeFile.Read(); + } + return resume; +} + bool cRecording::operator< (const cListObject &ListObject) { cRecording *r = (cRecording *)&ListObject; @@ -360,27 +373,47 @@ const char *cRecording::FileName(void) return fileName; } -const char *cRecording::Title(char Delimiter, bool NewIndicator) +const char *cRecording::Title(char Delimiter, bool NewIndicator, int Level) { - char New = ' '; - if (NewIndicator) { - cResumeFile ResumeFile(FileName()); - if (ResumeFile.Read() <= 0) - New = '*'; - } + char New = NewIndicator && IsNew() ? '*' : ' '; delete titleBuffer; titleBuffer = NULL; - struct tm tm_r; - struct tm *t = localtime_r(&start, &tm_r); - asprintf(&titleBuffer, "%02d.%02d%c%02d:%02d%c%c%s", - t->tm_mday, - t->tm_mon + 1, - Delimiter, - t->tm_hour, - t->tm_min, - New, - Delimiter, - name); + if (Level < 0 || Level == HierarchyLevels()) { + struct tm tm_r; + struct tm *t = localtime_r(&start, &tm_r); + const char *s; + if (Level > 0 && (s = strrchr(name, '~')) != NULL) + s++; + else + s = name; + asprintf(&titleBuffer, "%02d.%02d%c%02d:%02d%c%c%s", + t->tm_mday, + t->tm_mon + 1, + Delimiter, + t->tm_hour, + t->tm_min, + New, + Delimiter, + s); + } + else if (Level < HierarchyLevels()) { + const char *s = name; + const char *p = s; + while (*++s) { + if (*s == '~') { + if (Level--) + p = s + 1; + else + break; + } + } + titleBuffer = new char[s - p + 3]; + *titleBuffer = Delimiter; + *(titleBuffer + 1) = Delimiter; + strn0cpy(titleBuffer + 2, p, s - p + 1); + } + else + return ""; return titleBuffer; } @@ -395,6 +428,17 @@ const char *cRecording::PrefixFileName(char Prefix) return NULL; } +int cRecording::HierarchyLevels(void) +{ + const char *s = name; + int level = 0; + while (*++s) { + if (*s == '~') + level++; + } + return level; +} + bool cRecording::WriteSummary(void) { if (summary) { @@ -461,6 +505,15 @@ bool cRecordings::Load(bool Deleted) return result; } +cRecording *cRecordings::GetByName(const char *FileName) +{ + for (cRecording *recording = First(); recording; recording = Next(recording)) { + if (strcmp(recording->FileName(), FileName) == 0) + return recording; + } + return NULL; +} + // --- cMark ----------------------------------------------------------------- char *cMark::buffer = NULL; diff --git a/recording.h b/recording.h index aead97e3..c098271a 100644 --- a/recording.h +++ b/recording.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recording.h 1.18 2001/10/07 10:38:56 kls Exp $ + * $Id: recording.h 1.19 2002/01/20 11:35:32 kls Exp $ */ #ifndef __RECORDING_H @@ -31,6 +31,7 @@ public: class cRecording : public cListObject { friend class cRecordings; private: + int resume; char *titleBuffer; char *sortBuffer; char *fileName; @@ -38,6 +39,7 @@ private: char *summary; char *StripEpisodeName(char *s); char *SortName(void); + int GetResume(void); public: time_t start; int priority; @@ -46,10 +48,13 @@ public: cRecording(const char *FileName); ~cRecording(); virtual bool operator< (const cListObject &ListObject); + const char *Name(void) { return name; } const char *FileName(void); - const char *Title(char Delimiter = ' ', bool NewIndicator = false); + const char *Title(char Delimiter = ' ', bool NewIndicator = false, int Level = -1); const char *Summary(void) { return summary; } const char *PrefixFileName(char Prefix); + int HierarchyLevels(void); + bool IsNew(void) { return GetResume() <= 0; } bool WriteSummary(void); bool Delete(void); // Changes the file name so that it will no longer be visible in the "Recordings" menu @@ -62,6 +67,7 @@ public: class cRecordings : public cList { public: bool Load(bool Deleted = false); + cRecording *GetByName(const char *FileName); }; class cMark : public cListObject {