diff options
author | Klaus Schmidinger <vdr@tvdr.de> | 2015-09-01 11:14:27 +0200 |
---|---|---|
committer | Klaus Schmidinger <vdr@tvdr.de> | 2015-09-01 11:14:27 +0200 |
commit | 3cd5294d8a337ee5cd2ec894c9fbe04ad3a7690d (patch) | |
tree | da57ce74189de9bfb27e1a747063c37cd62de501 /menu.c | |
parent | 8a7bc6a0bbf60cae8b6391a630880aad5cba3363 (diff) | |
download | vdr-3cd5294d8a337ee5cd2ec894c9fbe04ad3a7690d.tar.gz vdr-3cd5294d8a337ee5cd2ec894c9fbe04ad3a7690d.tar.bz2 |
Implemented strict locking of global lists
Diffstat (limited to 'menu.c')
-rw-r--r-- | menu.c | 1288 |
1 files changed, 721 insertions, 567 deletions
@@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.c 4.2 2015/04/18 13:20:41 kls Exp $ + * $Id: menu.c 4.3 2015/08/31 13:36:05 kls Exp $ */ #include "menu.h" @@ -48,8 +48,8 @@ #define NODISKSPACEDELTA 300 // seconds between "Not enough disk space to start recording!" messages #define MAXCHNAMWIDTH 16 // maximum number of characters of channels' short names shown in schedules menus -#define CHNUMWIDTH (numdigits(Channels.MaxNumber()) + 1) -#define CHNAMWIDTH (min(MAXCHNAMWIDTH, Channels.MaxShortChannelNameLength() + 1)) +#define CHNUMWIDTH (numdigits(cChannels::MaxNumber()) + 1) +#define CHNAMWIDTH (min(MAXCHNAMWIDTH, cChannels::MaxShortChannelNameLength() + 1)) // --- cMenuEditCaItem ------------------------------------------------------- @@ -159,20 +159,23 @@ eOSState cMenuEditSrcItem::ProcessKey(eKeys Key) class cMenuEditChannel : public cOsdMenu { private: + cStateKey *channelsStateKey; cChannel *channel; cChannel data; cSourceParam *sourceParam; char name[256]; void Setup(void); public: - cMenuEditChannel(cChannel *Channel, bool New = false); + cMenuEditChannel(cStateKey *ChannelsStateKey, cChannel *Channel, bool New = false); + cChannel *Channel(void) { return channel; } virtual eOSState ProcessKey(eKeys Key); }; -cMenuEditChannel::cMenuEditChannel(cChannel *Channel, bool New) +cMenuEditChannel::cMenuEditChannel(cStateKey *ChannelsStateKey, cChannel *Channel, bool New) :cOsdMenu(tr("Edit channel"), 16) { SetMenuCategory(mcChannelEdit); + channelsStateKey = ChannelsStateKey; channel = Channel; sourceParam = NULL; *name = 0; @@ -239,32 +242,37 @@ eOSState cMenuEditChannel::ProcessKey(eKeys Key) if (state == osUnknown) { if (Key == kOk) { + cChannels *Channels =cChannels::GetChannelsWrite(*channelsStateKey); + bool Modified = false; if (sourceParam) sourceParam->GetData(&data); - if (Channels.HasUniqueChannelID(&data, channel)) { + if (Channels->HasUniqueChannelID(&data, channel)) { data.name = strcpyrealloc(data.name, name); if (channel) { *channel = data; - isyslog("edited channel %d %s", channel->Number(), *data.ToText()); + isyslog("edited channel %d %s", channel->Number(), *channel->ToText()); state = osBack; } else { channel = new cChannel; *channel = data; - Channels.Add(channel); - Channels.ReNumber(); - isyslog("added channel %d %s", channel->Number(), *data.ToText()); + Channels->Add(channel); + Channels->ReNumber(); + isyslog("added channel %d %s", channel->Number(), *channel->ToText()); state = osUser1; } - Channels.SetModified(true); + Channels->SetModifiedByUser(); + Modified = true; } else { Skins.Message(mtError, tr("Channel settings are not unique!")); state = osContinue; } + channelsStateKey->Remove(Modified); } } if (Key != kNone && (data.source & cSource::st_Mask) != (oldSource & cSource::st_Mask)) { + LOCK_CHANNELS_WRITE; if (sourceParam) sourceParam->GetData(&data); Setup(); @@ -279,21 +287,21 @@ public: enum eChannelSortMode { csmNumber, csmName, csmProvider }; private: static eChannelSortMode sortMode; - cChannel *channel; + const cChannel *channel; public: - cMenuChannelItem(cChannel *Channel); + cMenuChannelItem(const cChannel *Channel); static void SetSortMode(eChannelSortMode SortMode) { sortMode = SortMode; } static void IncSortMode(void) { sortMode = eChannelSortMode((sortMode == csmProvider) ? csmNumber : sortMode + 1); } static eChannelSortMode SortMode(void) { return sortMode; } virtual int Compare(const cListObject &ListObject) const; virtual void Set(void); - cChannel *Channel(void) { return channel; } + const cChannel *Channel(void) { return channel; } virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable); }; cMenuChannelItem::eChannelSortMode cMenuChannelItem::sortMode = csmNumber; -cMenuChannelItem::cMenuChannelItem(cChannel *Channel) +cMenuChannelItem::cMenuChannelItem(const cChannel *Channel) { channel = Channel; if (channel->GroupSep()) @@ -340,11 +348,12 @@ void cMenuChannelItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, boo class cMenuChannels : public cOsdMenu { private: + cStateKey channelsStateKey; int number; cTimeMs numberTimer; - void Setup(void); + void Set(bool Force = false); cChannel *GetChannel(int Index); - void Propagate(void); + void Propagate(cChannels *Channels); protected: eOSState Number(eKeys Key); eOSState Switch(void); @@ -363,38 +372,41 @@ cMenuChannels::cMenuChannels(void) { SetMenuCategory(mcChannel); number = 0; - Setup(); - Channels.IncBeingEdited(); + Set(); } cMenuChannels::~cMenuChannels() { - Channels.DecBeingEdited(); } -void cMenuChannels::Setup(void) +void cMenuChannels::Set(bool Force) { - cChannel *currentChannel = GetChannel(Current()); - if (!currentChannel) - currentChannel = Channels.GetByNumber(cDevice::CurrentChannel()); - cMenuChannelItem *currentItem = NULL; - Clear(); - for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { - if (!channel->GroupSep() || cMenuChannelItem::SortMode() == cMenuChannelItem::csmNumber && *channel->Name()) { - cMenuChannelItem *item = new cMenuChannelItem(channel); - Add(item); - if (channel == currentChannel) - currentItem = item; + if (Force) + channelsStateKey.Reset(); + if (const cChannels *Channels = cChannels::GetChannelsRead(channelsStateKey)) { + const cChannel *CurrentChannel = GetChannel(Current()); + if (!CurrentChannel) + CurrentChannel = Channels->GetByNumber(cDevice::CurrentChannel()); + cMenuChannelItem *CurrentItem = NULL; + Clear(); + for (const cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { + if (!Channel->GroupSep() || cMenuChannelItem::SortMode() == cMenuChannelItem::csmNumber && *Channel->Name()) { + cMenuChannelItem *Item = new cMenuChannelItem(Channel); + Add(Item); + if (Channel == CurrentChannel) + CurrentItem = Item; + } } - } - SetMenuSortMode(cMenuChannelItem::SortMode() == cMenuChannelItem::csmName ? msmName : - cMenuChannelItem::SortMode() == cMenuChannelItem::csmProvider ? msmProvider : - msmNumber); - if (cMenuChannelItem::SortMode() != cMenuChannelItem::csmNumber) - Sort(); - SetCurrent(currentItem); - SetHelp(tr("Button$Edit"), tr("Button$New"), tr("Button$Delete"), tr("Button$Mark")); - Display(); + SetMenuSortMode(cMenuChannelItem::SortMode() == cMenuChannelItem::csmName ? msmName : + cMenuChannelItem::SortMode() == cMenuChannelItem::csmProvider ? msmProvider : + msmNumber); + if (cMenuChannelItem::SortMode() != cMenuChannelItem::csmNumber) + Sort(); + SetCurrent(CurrentItem); + SetHelp(tr("Button$Edit"), tr("Button$New"), tr("Button$Delete"), tr("Button$Mark")); + Display(); + channelsStateKey.Remove(); + } } cChannel *cMenuChannels::GetChannel(int Index) @@ -403,13 +415,13 @@ cChannel *cMenuChannels::GetChannel(int Index) return p ? (cChannel *)p->Channel() : NULL; } -void cMenuChannels::Propagate(void) +void cMenuChannels::Propagate(cChannels *Channels) { - Channels.ReNumber(); + Channels->ReNumber(); for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next()) ci->Set(); Display(); - Channels.SetModified(true); + Channels->SetModifiedByUser(); } eOSState cMenuChannels::Number(eKeys Key) @@ -420,9 +432,10 @@ eOSState cMenuChannels::Number(eKeys Key) number = 0; if (!number && Key == k0) { cMenuChannelItem::IncSortMode(); - Setup(); + Set(true); } else { + LOCK_CHANNELS_READ; number = number * 10 + Key - k0; for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next()) { if (!ci->Channel()->GroupSep() && ci->Channel()->Number() == number) { @@ -440,6 +453,7 @@ eOSState cMenuChannels::Switch(void) { if (HasSubMenu()) return osContinue; + LOCK_CHANNELS_READ; cChannel *ch = GetChannel(Current()); if (ch) return cDevice::PrimaryDevice()->SwitchChannel(ch, true) ? osEnd : osContinue; @@ -450,9 +464,10 @@ eOSState cMenuChannels::Edit(void) { if (HasSubMenu() || Count() == 0) return osContinue; + LOCK_CHANNELS_READ; cChannel *ch = GetChannel(Current()); if (ch) - return AddSubMenu(new cMenuEditChannel(ch)); + return AddSubMenu(new cMenuEditChannel(&channelsStateKey, ch)); return osContinue; } @@ -460,79 +475,97 @@ eOSState cMenuChannels::New(void) { if (HasSubMenu()) return osContinue; - return AddSubMenu(new cMenuEditChannel(GetChannel(Current()), true)); + LOCK_CHANNELS_READ; + return AddSubMenu(new cMenuEditChannel(&channelsStateKey, GetChannel(Current()), true)); } eOSState cMenuChannels::Delete(void) { if (!HasSubMenu() && Count() > 0) { - int CurrentChannelNr = cDevice::CurrentChannel(); - cChannel *CurrentChannel = Channels.GetByNumber(CurrentChannelNr); + LOCK_TIMERS_READ; // must lock timers before channels! + cChannels *Channels = cChannels::GetChannelsWrite(channelsStateKey); int Index = Current(); - cChannel *channel = GetChannel(Current()); - int DeletedChannel = channel->Number(); + cChannel *Channel = GetChannel(Current()); + if (!Channels->Contains(Channel)) { + channelsStateKey.Remove(false); + channelsStateKey.Reset(); // makes sure the menu is refreshed + return osContinue; + } + bool Deleted = false; + int CurrentChannelNr = cDevice::CurrentChannel(); + cChannel *CurrentChannel = Channels->GetByNumber(CurrentChannelNr); + int DeletedChannel = Channel->Number(); // Check if there is a timer using this channel: - if (channel->HasTimer()) { + if (Timers->UsesChannel(Channel)) { Skins.Message(mtError, tr("Channel is being used by a timer!")); return osContinue; } if (Interface->Confirm(tr("Delete channel?"))) { - if (CurrentChannel && channel == CurrentChannel) { - int n = Channels.GetNextNormal(CurrentChannel->Index()); + if (CurrentChannel && Channel == CurrentChannel) { + int n = Channels->GetNextNormal(CurrentChannel->Index()); if (n < 0) - n = Channels.GetPrevNormal(CurrentChannel->Index()); - CurrentChannel = Channels.Get(n); + n = Channels->GetPrevNormal(CurrentChannel->Index()); + CurrentChannel = Channels->Get(n); CurrentChannelNr = 0; // triggers channel switch below } - Channels.Del(channel); + Channels->Del(Channel); cOsdMenu::Del(Index); - Propagate(); - Channels.SetModified(true); + Propagate(Channels); + Channels->SetModifiedByUser(); isyslog("channel %d deleted", DeletedChannel); + Deleted = true; if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) { if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) - Channels.SwitchTo(CurrentChannel->Number()); + Channels->SwitchTo(CurrentChannel->Number()); else cDevice::SetCurrentChannel(CurrentChannel); } } + channelsStateKey.Remove(Deleted); } return osContinue; } void cMenuChannels::Move(int From, int To) { - int CurrentChannelNr = cDevice::CurrentChannel(); - cChannel *CurrentChannel = Channels.GetByNumber(CurrentChannelNr); - cChannel *FromChannel = GetChannel(From); - cChannel *ToChannel = GetChannel(To); - if (FromChannel && ToChannel) { - int FromNumber = FromChannel->Number(); - int ToNumber = ToChannel->Number(); - Channels.Move(FromChannel, ToChannel); - cOsdMenu::Move(From, To); - Propagate(); - Channels.SetModified(true); - isyslog("channel %d moved to %d", FromNumber, ToNumber); - if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) { - if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) - Channels.SwitchTo(CurrentChannel->Number()); - else - cDevice::SetCurrentChannel(CurrentChannel); + if (cChannels *Channels = cChannels::GetChannelsWrite(channelsStateKey)) { + int CurrentChannelNr = cDevice::CurrentChannel(); + cChannel *CurrentChannel = Channels->GetByNumber(CurrentChannelNr); + cChannel *FromChannel = GetChannel(From); + cChannel *ToChannel = GetChannel(To); + if (FromChannel && ToChannel) { + int FromNumber = FromChannel->Number(); + int ToNumber = ToChannel->Number(); + Channels->Move(FromChannel, ToChannel); + cOsdMenu::Move(From, To); + Propagate(Channels); + Channels->SetModifiedByUser(); + isyslog("channel %d moved to %d", FromNumber, ToNumber); + if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) { + if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) + Channels->SwitchTo(CurrentChannel->Number()); + else + cDevice::SetCurrentChannel(CurrentChannel); + } } + channelsStateKey.Remove(); } } eOSState cMenuChannels::ProcessKey(eKeys Key) { + if (!HasSubMenu()) + Set(); // react on any changes to the channels list eOSState state = cOsdMenu::ProcessKey(Key); switch (state) { case osUser1: { - cChannel *channel = Channels.Last(); - if (channel) { - Add(new cMenuChannelItem(channel), true); - return CloseSubMenu(); + if (cMenuEditChannel *MenuEditChannel = dynamic_cast<cMenuEditChannel *>(SubMenu())) { + if (cChannel *Channel = MenuEditChannel->Channel()) { + LOCK_CHANNELS_READ; + Add(new cMenuChannelItem(Channel), true); + return CloseSubMenu(); + } } } break; @@ -767,7 +800,7 @@ void cMenuFolder::SetHelpKeys(void) } #define FOLDERDELIMCHARSUBST 0x01 -static void AddRecordingFolders(cList<cNestedItem> *List, char *Path) +static void AddRecordingFolders(const cRecordings *Recordings, cList<cNestedItem> *List, char *Path) { if (Path) { char *p = strchr(Path, FOLDERDELIMCHARSUBST); @@ -782,13 +815,12 @@ static void AddRecordingFolders(cList<cNestedItem> *List, char *Path) List->Add(Folder = new cNestedItem(Path)); if (p) { Folder->SetSubItems(true); - AddRecordingFolders(Folder->SubItems(), p); + AddRecordingFolders(Recordings, Folder->SubItems(), p); } } else { - cThreadLock RecordingsLock(&Recordings); cStringList Dirs; - for (cRecording *Recording = Recordings.First(); Recording; Recording = Recordings.Next(Recording)) { + for (const cRecording *Recording = Recordings->First(); Recording; Recording = Recordings->Next(Recording)) { cString Folder = Recording->Folder(); strreplace((char *)*Folder, FOLDERDELIMCHAR, FOLDERDELIMCHARSUBST); // makes sure parent folders come before subfolders if (Dirs.Find(Folder) < 0) @@ -796,18 +828,21 @@ static void AddRecordingFolders(cList<cNestedItem> *List, char *Path) } Dirs.Sort(); for (int i = 0; i < Dirs.Size(); i++) { - char *s = Dirs[i]; - if (*s) - AddRecordingFolders(&Folders, s); + if (char *s = Dirs[i]) + AddRecordingFolders(Recordings, &Folders, s); } } } void cMenuFolder::Set(const char *CurrentFolder) { - static int RecordingsState = -1; - if (list == &Folders && Recordings.StateChanged(RecordingsState)) - AddRecordingFolders(&Folders, NULL); + static cStateKey RecordingsStateKey; + if (list == &Folders) { + if (const cRecordings *Recordings = cRecordings::GetRecordingsRead(RecordingsStateKey)) { + AddRecordingFolders(Recordings, &Folders, NULL); + RecordingsStateKey.Remove(); + } + } firstFolder = NULL; Clear(); if (!isempty(dir)) { @@ -937,10 +972,13 @@ eOSState cMenuFolder::ProcessKey(eKeys Key) // --- cMenuEditTimer -------------------------------------------------------- +const cTimer *cMenuEditTimer::addedTimer = NULL; + cMenuEditTimer::cMenuEditTimer(cTimer *Timer, bool New) :cOsdMenu(tr("Edit timer"), 12) { SetMenuCategory(mcTimerEdit); + addedTimer = NULL; file = NULL; day = firstday = NULL; timer = Timer; @@ -962,14 +1000,19 @@ cMenuEditTimer::cMenuEditTimer(cTimer *Timer, bool New) SetFirstDayItem(); } SetHelpKeys(); - Timers.IncBeingEdited(); } cMenuEditTimer::~cMenuEditTimer() { if (timer && addIfConfirmed) delete timer; // apparently it wasn't confirmed - Timers.DecBeingEdited(); +} + +const cTimer *cMenuEditTimer::AddedTimer(void) +{ + const cTimer *Timer = addedTimer; + addedTimer = NULL; + return Timer; } void cMenuEditTimer::SetHelpKeys(void) @@ -1015,28 +1058,32 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key) if (state == osUnknown) { switch (Key) { - case kOk: { - cChannel *ch = Channels.GetByNumber(channel); - if (ch) - data.channel = ch; - else { - Skins.Message(mtError, tr("*** Invalid Channel ***")); - break; - } - if (!*data.file) - strcpy(data.file, data.Channel()->ShortName(true)); - if (timer) { - if (memcmp((void *)timer, &data, sizeof(data)) != 0) - *timer = data; - if (addIfConfirmed) - Timers.Add(timer); - timer->SetEventFromSchedule(); - timer->Matches(); - Timers.SetModified(); - isyslog("timer %s %s (%s)", *timer->ToDescr(), addIfConfirmed ? "added" : "modified", timer->HasFlags(tfActive) ? "active" : "inactive"); - addIfConfirmed = false; - } - } + case kOk: if (timer) { + LOCK_TIMERS_WRITE; + if (!addIfConfirmed && !Timers->Contains(timer)) { + Skins.Message(mtWarning, tr("Timer has been deleted!")); + break; + } + LOCK_CHANNELS_READ; + if (const cChannel *Channel = Channels->GetByNumber(channel)) + data.channel = Channel; + else { + Skins.Message(mtError, tr("*** Invalid Channel ***")); + break; + } + if (!*data.file) + strcpy(data.file, data.Channel()->ShortName(true)); + *timer = data; + if (addIfConfirmed) { + Timers->Add(timer); + addedTimer = timer; + } + isyslog("timer %s %s (%s)", *timer->ToDescr(), addIfConfirmed ? "added" : "modified", timer->HasFlags(tfActive) ? "active" : "inactive"); + LOCK_SCHEDULES_READ; + timer->SetEventFromSchedule(Schedules); + timer->Matches(); + addIfConfirmed = false; + } return osBack; case kRed: return AddSubMenu(new cMenuFolder(tr("Select folder"), &Folders, data.file)); case kGreen: if (day) { @@ -1063,16 +1110,16 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key) class cMenuTimerItem : public cOsdItem { private: - cTimer *timer; + const cTimer *timer; public: - cMenuTimerItem(cTimer *Timer); + cMenuTimerItem(const cTimer *Timer); virtual int Compare(const cListObject &ListObject) const; virtual void Set(void); - cTimer *Timer(void) { return timer; } + const cTimer *Timer(void) { return timer; } virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable); }; -cMenuTimerItem::cMenuTimerItem(cTimer *Timer) +cMenuTimerItem::cMenuTimerItem(const cTimer *Timer) { timer = Timer; Set(); @@ -1128,13 +1175,15 @@ void cMenuTimerItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool class cMenuTimers : public cOsdMenu { private: + cStateKey timersStateKey; int helpKeys; + void Set(void); eOSState Edit(void); eOSState New(void); eOSState Delete(void); eOSState OnOff(void); eOSState Info(void); - cTimer *CurrentTimer(void); + cTimer *GetTimer(void); void SetHelpKeys(void); public: cMenuTimers(void); @@ -1147,33 +1196,45 @@ cMenuTimers::cMenuTimers(void) { SetMenuCategory(mcTimer); helpKeys = -1; - for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer)) { - timer->SetEventFromSchedule(); // make sure the event is current - Add(new cMenuTimerItem(timer)); - } - Sort(); - SetCurrent(First()); - SetHelpKeys(); - Timers.IncBeingEdited(); + cMenuEditTimer::AddedTimer(); // to clear any leftovers + Set(); } cMenuTimers::~cMenuTimers() { - Timers.DecBeingEdited(); } -cTimer *cMenuTimers::CurrentTimer(void) +void cMenuTimers::Set(void) +{ + if (const cTimers *Timers = cTimers::GetTimersRead(timersStateKey)) { + const cTimer *CurrentTimer = GetTimer(); + cMenuTimerItem *CurrentItem = NULL; + Clear(); + for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) { + cMenuTimerItem *Item = new cMenuTimerItem(Timer); + Add(Item); + if (Timer == CurrentTimer) + CurrentItem = Item; + } + Sort(); + SetCurrent(CurrentItem ? CurrentItem : First()); + SetHelpKeys(); + Display(); + timersStateKey.Remove(); + } +} + +cTimer *cMenuTimers::GetTimer(void) { cMenuTimerItem *item = (cMenuTimerItem *)Get(Current()); - return item ? item->Timer() : NULL; + return item ? (cTimer *)item->Timer() : NULL; } void cMenuTimers::SetHelpKeys(void) { int NewHelpKeys = 0; - cTimer *timer = CurrentTimer(); - if (timer) { - if (timer->Event()) + if (const cTimer *Timer = GetTimer()) { + if (Timer->Event()) NewHelpKeys = 2; else NewHelpKeys = 1; @@ -1188,18 +1249,20 @@ eOSState cMenuTimers::OnOff(void) { if (HasSubMenu()) return osContinue; - cTimer *timer = CurrentTimer(); - if (timer) { - timer->OnOff(); - timer->SetEventFromSchedule(); + cTimers::GetTimersWrite(timersStateKey); + cTimer *Timer = GetTimer(); + if (Timer) { + Timer->OnOff(); + LOCK_SCHEDULES_READ; + Timer->SetEventFromSchedule(Schedules); RefreshCurrent(); DisplayCurrent(true); - if (timer->FirstDay()) - isyslog("timer %s first day set to %s", *timer->ToDescr(), *timer->PrintFirstDay()); + if (Timer->FirstDay()) + isyslog("timer %s first day set to %s", *Timer->ToDescr(), *Timer->PrintFirstDay()); else - isyslog("timer %s %sactivated", *timer->ToDescr(), timer->HasFlags(tfActive) ? "" : "de"); - Timers.SetModified(); + isyslog("timer %s %sactivated", *Timer->ToDescr(), Timer->HasFlags(tfActive) ? "" : "de"); } + timersStateKey.Remove(Timer != NULL); return osContinue; } @@ -1207,8 +1270,8 @@ eOSState cMenuTimers::Edit(void) { if (HasSubMenu() || Count() == 0) return osContinue; - isyslog("editing timer %s", *CurrentTimer()->ToDescr()); - return AddSubMenu(new cMenuEditTimer(CurrentTimer())); + isyslog("editing timer %s", *GetTimer()->ToDescr()); + return AddSubMenu(new cMenuEditTimer(GetTimer())); } eOSState cMenuTimers::New(void) @@ -1220,25 +1283,28 @@ eOSState cMenuTimers::New(void) eOSState cMenuTimers::Delete(void) { + cTimers *Timers = cTimers::GetTimersWrite(timersStateKey); // Check if this timer is active: - cTimer *ti = CurrentTimer(); - if (ti) { + cTimer *Timer = GetTimer(); + if (Timer) { if (Interface->Confirm(tr("Delete timer?"))) { - if (ti->Recording()) { + if (Timer->Recording()) { if (Interface->Confirm(tr("Timer still recording - really delete?"))) { - ti->Skip(); - cRecordControls::Process(time(NULL)); + Timer->Skip(); + cRecordControls::Process(Timers, time(NULL)); } else - return osContinue; + Timer = NULL; + } + if (Timer) { + isyslog("deleting timer %s", *Timer->ToDescr()); + Timers->Del(Timer); + cOsdMenu::Del(Current()); + Display(); } - isyslog("deleting timer %s", *ti->ToDescr()); - Timers.Del(ti); - cOsdMenu::Del(Current()); - Timers.SetModified(); - Display(); } } + timersStateKey.Remove(Timer != NULL); return osContinue; } @@ -1246,17 +1312,19 @@ eOSState cMenuTimers::Info(void) { if (HasSubMenu() || Count() == 0) return osContinue; - cTimer *ti = CurrentTimer(); - if (ti && ti->Event()) - return AddSubMenu(new cMenuEvent(ti->Event())); + LOCK_TIMERS_READ; + LOCK_CHANNELS_READ; + cTimer *Timer = GetTimer(); + if (Timer && Timer->Event()) + return AddSubMenu(new cMenuEvent(Timers, Channels, Timer->Event())); return osContinue; } eOSState cMenuTimers::ProcessKey(eKeys Key) { - int TimerNumber = HasSubMenu() ? Count() : -1; + if (!HasSubMenu()) + Set(); eOSState state = cOsdMenu::ProcessKey(Key); - if (state == osUnknown) { switch (Key) { case kOk: return Edit(); @@ -1269,9 +1337,10 @@ eOSState cMenuTimers::ProcessKey(eKeys Key) default: break; } } - if (TimerNumber >= 0 && !HasSubMenu() && Timers.Get(TimerNumber)) { - // a newly created timer was confirmed with Ok - Add(new cMenuTimerItem(Timers.Get(TimerNumber)), true); + if (const cTimer *Timer = cMenuEditTimer::AddedTimer()) { + // a newly created timer was confirmed with Ok and the proper item needs to be added: + LOCK_TIMERS_READ; + Add(new cMenuTimerItem(Timer), true); Display(); } if (Key != kNone) @@ -1281,19 +1350,19 @@ eOSState cMenuTimers::ProcessKey(eKeys Key) // --- cMenuEvent ------------------------------------------------------------ -cMenuEvent::cMenuEvent(const cEvent *Event, bool CanSwitch, bool Buttons) +cMenuEvent::cMenuEvent(const cTimers *Timers, const cChannels *Channels, const cEvent *Event, bool CanSwitch, bool Buttons) :cOsdMenu(tr("Event")) { SetMenuCategory(mcEvent); event = Event; if (event) { - cChannel *channel = Channels.GetByChannelID(event->ChannelID(), true); - if (channel) { - SetTitle(channel->Name()); - eTimerMatch TimerMatch = tmNone; - Timers.GetMatch(event, &TimerMatch); - if (Buttons) + if (const cChannel *Channel = Channels->GetByChannelID(event->ChannelID(), true)) { + SetTitle(Channel->Name()); + if (Buttons) { + eTimerMatch TimerMatch = tmNone; + Timers->GetMatch(event, &TimerMatch); SetHelp(TimerMatch == tmFull ? tr("Button$Timer") : tr("Button$Record"), NULL, NULL, CanSwitch ? tr("Button$Switch") : NULL); + } } } } @@ -1349,24 +1418,24 @@ public: const cChannel *channel; bool withDate; eTimerMatch timerMatch; - cMenuScheduleItem(const cEvent *Event, cChannel *Channel = NULL, bool WithDate = false); + cMenuScheduleItem(const cTimers *Timers, const cEvent *Event, const cChannel *Channel = NULL, bool WithDate = false); static void SetSortMode(eScheduleSortMode SortMode) { sortMode = SortMode; } static void IncSortMode(void) { sortMode = eScheduleSortMode((sortMode == ssmAllAll) ? ssmAllThis : sortMode + 1); } static eScheduleSortMode SortMode(void) { return sortMode; } virtual int Compare(const cListObject &ListObject) const; - bool Update(bool Force = false); + bool Update(const cTimers *Timers, bool Force = false); virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable); }; cMenuScheduleItem::eScheduleSortMode cMenuScheduleItem::sortMode = ssmAllThis; -cMenuScheduleItem::cMenuScheduleItem(const cEvent *Event, cChannel *Channel, bool WithDate) +cMenuScheduleItem::cMenuScheduleItem(const cTimers *Timers, const cEvent *Event, const cChannel *Channel, bool WithDate) { event = Event; channel = Channel; withDate = WithDate; timerMatch = tmNone; - Update(true); + Update(Timers, true); } int cMenuScheduleItem::Compare(const cListObject &ListObject) const @@ -1382,11 +1451,10 @@ int cMenuScheduleItem::Compare(const cListObject &ListObject) const static const char *TimerMatchChars = " tT"; -bool cMenuScheduleItem::Update(bool Force) +bool cMenuScheduleItem::Update(const cTimers *Timers, bool Force) { - bool result = false; eTimerMatch OldTimerMatch = timerMatch; - Timers.GetMatch(event, &timerMatch); + Timers->GetMatch(event, &timerMatch); if (Force || timerMatch != OldTimerMatch) { cString buffer; char t = TimerMatchChars[timerMatch]; @@ -1401,9 +1469,9 @@ bool cMenuScheduleItem::Update(bool Force) else buffer = cString::sprintf("%.*s\t%s\t%c%c%c\t%s", Utf8SymChars(eds, 6), *eds, *event->GetTimeString(), t, v, r, event->Title()); SetText(buffer); - result = true; + return true; } - return result; + return false; } void cMenuScheduleItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) @@ -1419,7 +1487,7 @@ private: bool now; bool canSwitch; int helpKeys; - int timerState; + cStateKey timersStateKey; eOSState Record(void); eOSState Switch(void); static int currentChannel; @@ -1427,7 +1495,7 @@ private: bool Update(void); void SetHelpKeys(void); public: - cMenuWhatsOn(const cSchedules *Schedules, bool Now, int CurrentChannelNr); + cMenuWhatsOn(const cTimers *Timers, const cChannels *Channels, const cSchedules *Schedules, bool Now, int CurrentChannelNr); static int CurrentChannel(void) { return currentChannel; } static void SetCurrentChannel(int ChannelNr) { currentChannel = ChannelNr; } static const cEvent *ScheduleEvent(void); @@ -1437,22 +1505,18 @@ public: int cMenuWhatsOn::currentChannel = 0; const cEvent *cMenuWhatsOn::scheduleEvent = NULL; -cMenuWhatsOn::cMenuWhatsOn(const cSchedules *Schedules, bool Now, int CurrentChannelNr) +cMenuWhatsOn::cMenuWhatsOn(const cTimers *Timers, const cChannels *Channels, const cSchedules *Schedules, bool Now, int CurrentChannelNr) :cOsdMenu(Now ? tr("What's on now?") : tr("What's on next?"), CHNUMWIDTH, CHNAMWIDTH, 6, 4) { SetMenuCategory(Now ? mcScheduleNow : mcScheduleNext); now = Now; canSwitch = false; helpKeys = 0; - timerState = 0; - Timers.Modified(timerState); - for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { + for (const cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { if (!Channel->GroupSep()) { - const cSchedule *Schedule = Schedules->GetSchedule(Channel); - if (Schedule) { - const cEvent *Event = Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent(); - if (Event) - Add(new cMenuScheduleItem(Event, Channel), Channel->Number() == CurrentChannelNr); + if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) { + if (const cEvent *Event = Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent()) + Add(new cMenuScheduleItem(Timers, Event, Channel), Channel->Number() == CurrentChannelNr); } } } @@ -1464,11 +1528,12 @@ cMenuWhatsOn::cMenuWhatsOn(const cSchedules *Schedules, bool Now, int CurrentCha bool cMenuWhatsOn::Update(void) { bool result = false; - if (Timers.Modified(timerState)) { + if (const cTimers *Timers = cTimers::GetTimersRead(timersStateKey)) { for (cOsdItem *item = First(); item; item = Next(item)) { - if (((cMenuScheduleItem *)item)->Update()) + if (((cMenuScheduleItem *)item)->Update(Timers)) result = true; } + timersStateKey.Remove(); } return result; } @@ -1487,7 +1552,8 @@ void cMenuWhatsOn::SetHelpKeys(void) NewHelpKeys |= 0x04; // "Next" else NewHelpKeys |= 0x08; // "Now" - if (cChannel *Channel = Channels.GetByChannelID(item->event->ChannelID(), true)) { + LOCK_CHANNELS_READ; + if (const cChannel *Channel = Channels->GetByChannelID(item->event->ChannelID(), true)) { if (Channel->Number() != cDevice::CurrentChannel()) { NewHelpKeys |= 0x10; // "Switch" canSwitch = true; @@ -1512,8 +1578,13 @@ eOSState cMenuWhatsOn::Switch(void) { cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current()); if (item) { - cChannel *channel = Channels.GetByChannelID(item->event->ChannelID(), true); - if (channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true)) + LOCK_CHANNELS_READ; + const cChannel *Channel = Channels->GetByChannelID(item->event->ChannelID(), true); + if (Channel) { + if (!cDevice::PrimaryDevice()->SwitchChannel(Channel, true)) + Channel = NULL; + } + if (Channel) return osEnd; } Skins.Message(mtError, tr("Can't switch channel!")); @@ -1522,33 +1593,32 @@ eOSState cMenuWhatsOn::Switch(void) eOSState cMenuWhatsOn::Record(void) { - cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current()); - if (item) { - if (item->timerMatch == tmFull) { - eTimerMatch tm = tmNone; - cTimer *timer = Timers.GetMatch(item->event, &tm); - if (timer) - return AddSubMenu(new cMenuEditTimer(timer)); - } - cTimer *timer = new cTimer(item->event); - cTimer *t = Timers.GetTimer(timer); - if (t) { - delete timer; - timer = t; - return AddSubMenu(new cMenuEditTimer(timer)); - } - else { - Timers.Add(timer); - Timers.SetModified(); - isyslog("timer %s added (active)", *timer->ToDescr()); - if (timer->Matches(0, false, NEWTIMERLIMIT)) - return AddSubMenu(new cMenuEditTimer(timer)); - if (HasSubMenu()) - CloseSubMenu(); - if (Update()) - Display(); - SetHelpKeys(); - } + if (cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current())) { + { + LOCK_TIMERS_WRITE; + LOCK_SCHEDULES_READ; + Timers->SetExplicitModify(); + if (item->timerMatch == tmFull) { + if (cTimer *Timer = Timers->GetMatch(item->event)) + return AddSubMenu(new cMenuEditTimer(Timer)); + } + cTimer *Timer = new cTimer(item->event); + if (cTimer *t = Timers->GetTimer(Timer)) { + delete Timer; + Timer = t; + return AddSubMenu(new cMenuEditTimer(Timer)); + } + if (Timer->Matches(0, false, NEWTIMERLIMIT)) + return AddSubMenu(new cMenuEditTimer(Timer, true)); + Timers->Add(Timer); + Timers->SetModified(); + isyslog("timer %s added (active)", *Timer->ToDescr()); + } + if (HasSubMenu()) + CloseSubMenu(); + if (Update()) + Display(); + SetHelpKeys(); } return osContinue; } @@ -1576,8 +1646,11 @@ eOSState cMenuWhatsOn::ProcessKey(eKeys Key) return Switch(); break; case kInfo: - case kOk: if (Count()) - return AddSubMenu(new cMenuEvent(((cMenuScheduleItem *)Get(Current()))->event, canSwitch, true)); + case kOk: if (Count()) { + LOCK_TIMERS_READ; + LOCK_CHANNELS_READ; + return AddSubMenu(new cMenuEvent(Timers, Channels, ((cMenuScheduleItem *)Get(Current()))->event, canSwitch, true)); + } break; default: break; } @@ -1595,19 +1668,20 @@ eOSState cMenuWhatsOn::ProcessKey(eKeys Key) class cMenuSchedule : public cOsdMenu { private: - cSchedulesLock schedulesLock; - const cSchedules *schedules; + cStateKey timersStateKey; + cStateKey schedulesStateKey; + int scheduleState; bool now, next; bool canSwitch; int helpKeys; - int timerState; + void Set(const cChannel *Channel = NULL, bool Force = false); eOSState Number(void); eOSState Record(void); eOSState Switch(void); - void PrepareScheduleAllThis(const cEvent *Event, const cChannel *Channel); - void PrepareScheduleThisThis(const cEvent *Event, const cChannel *Channel); - void PrepareScheduleThisAll(const cEvent *Event, const cChannel *Channel); - void PrepareScheduleAllAll(const cEvent *Event, const cChannel *Channel); + bool PrepareScheduleAllThis(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel); + bool PrepareScheduleThisThis(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel); + bool PrepareScheduleThisAll(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel); + bool PrepareScheduleAllAll(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel); bool Update(void); void SetHelpKeys(void); public: @@ -1620,19 +1694,13 @@ cMenuSchedule::cMenuSchedule(void) :cOsdMenu("") { SetMenuCategory(mcSchedule); + scheduleState = -1; now = next = false; canSwitch = false; helpKeys = 0; - timerState = 0; - Timers.Modified(timerState); cMenuScheduleItem::SetSortMode(cMenuScheduleItem::ssmAllThis); - cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel()); - if (channel) { - cMenuWhatsOn::SetCurrentChannel(channel->Number()); - schedules = cSchedules::Schedules(schedulesLock); - PrepareScheduleAllThis(NULL, channel); - SetHelpKeys(); - } + cMenuWhatsOn::SetCurrentChannel(cDevice::CurrentChannel()); + Set(NULL, true); } cMenuSchedule::~cMenuSchedule() @@ -1640,87 +1708,131 @@ cMenuSchedule::~cMenuSchedule() cMenuWhatsOn::ScheduleEvent(); // makes sure any posted data is cleared } -void cMenuSchedule::PrepareScheduleAllThis(const cEvent *Event, const cChannel *Channel) +void cMenuSchedule::Set(const cChannel *Channel, bool Force) { - Clear(); - SetCols(7, 6, 4); - SetTitle(cString::sprintf(tr("Schedule - %s"), Channel->Name())); - if (schedules && Channel) { - const cSchedule *Schedule = schedules->GetSchedule(Channel); - if (Schedule) { + if (Force) { + schedulesStateKey.Reset(); + scheduleState = -1; + } + LOCK_TIMERS_READ; + LOCK_CHANNELS_READ; + if (const cSchedules *Schedules = cSchedules::GetSchedulesRead(schedulesStateKey)) { + cMenuScheduleItem *CurrentItem = (cMenuScheduleItem *)Get(Current()); + const cEvent *Event = NULL; + if (!Channel) { + if (CurrentItem) { + Event = CurrentItem->event; + Channel = Channels->GetByChannelID(Event->ChannelID(), true); + } + else + Channel = Channels->GetByNumber(cDevice::CurrentChannel()); + } + bool Refresh = false; + switch (cMenuScheduleItem::SortMode()) { + case cMenuScheduleItem::ssmAllThis: Refresh = PrepareScheduleAllThis(Timers, Schedules, Event, Channel); break; + case cMenuScheduleItem::ssmThisThis: Refresh = PrepareScheduleThisThis(Timers, Schedules, Event, Channel); break; + case cMenuScheduleItem::ssmThisAll: Refresh = Force && PrepareScheduleThisAll(Timers, Schedules, Event, Channel); break; + case cMenuScheduleItem::ssmAllAll: Refresh = Force && PrepareScheduleAllAll(Timers, Schedules, Event, Channel); break; + default: esyslog("ERROR: unknown SortMode %d (%s %d)", cMenuScheduleItem::SortMode(), __FUNCTION__, __LINE__); + } + if (Refresh) { + CurrentItem = (cMenuScheduleItem *)Get(Current()); + Sort(); + SetCurrent(CurrentItem); + SetHelpKeys(); + Display(); + } + schedulesStateKey.Remove(); + } +} + +bool cMenuSchedule::PrepareScheduleAllThis(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel) +{ + if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) { + if (Schedule->Modified(scheduleState)) { + Clear(); + SetCols(7, 6, 4); + SetTitle(cString::sprintf(tr("Schedule - %s"), Channel->Name())); const cEvent *PresentEvent = Event ? Event : Schedule->GetPresentEvent(); time_t now = time(NULL) - Setup.EPGLinger * 60; for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) { if (ev->EndTime() > now || ev == PresentEvent) - Add(new cMenuScheduleItem(ev), ev == PresentEvent); + Add(new cMenuScheduleItem(Timers, ev), ev == PresentEvent); } + return true; } } + return false; } -void cMenuSchedule::PrepareScheduleThisThis(const cEvent *Event, const cChannel *Channel) +bool cMenuSchedule::PrepareScheduleThisThis(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel) { - Clear(); - SetCols(7, 6, 4); - SetTitle(cString::sprintf(tr("This event - %s"), Channel->Name())); - if (schedules && Channel && Event) { - const cSchedule *Schedule = schedules->GetSchedule(Channel); - if (Schedule) { - time_t now = time(NULL) - Setup.EPGLinger * 60; - for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) { - if ((ev->EndTime() > now || ev == Event) && !strcmp(ev->Title(), Event->Title())) - Add(new cMenuScheduleItem(ev), ev == Event); - } + if (Event) { + if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) { + if (Schedule->Modified(scheduleState)) { + Clear(); + SetCols(7, 6, 4); + SetTitle(cString::sprintf(tr("This event - %s"), Channel->Name())); + time_t now = time(NULL) - Setup.EPGLinger * 60; + for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) { + if ((ev->EndTime() > now || ev == Event) && !strcmp(ev->Title(), Event->Title())) + Add(new cMenuScheduleItem(Timers, ev), ev == Event); + } + return true; + } } } + return false; } -void cMenuSchedule::PrepareScheduleThisAll(const cEvent *Event, const cChannel *Channel) +bool cMenuSchedule::PrepareScheduleThisAll(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel) { Clear(); SetCols(CHNUMWIDTH, CHNAMWIDTH, 7, 6, 4); SetTitle(tr("This event - all channels")); - if (schedules && Event) { - for (cChannel *ch = Channels.First(); ch; ch = Channels.Next(ch)) { - const cSchedule *Schedule = schedules->GetSchedule(ch); - if (Schedule) { + if (Event) { + LOCK_CHANNELS_READ; + for (const cChannel *ch = Channels->First(); ch; ch = Channels->Next(ch)) { + if (const cSchedule *Schedule = Schedules->GetSchedule(ch)) { time_t now = time(NULL) - Setup.EPGLinger * 60; for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) { if ((ev->EndTime() > now || ev == Event) && !strcmp(ev->Title(), Event->Title())) - Add(new cMenuScheduleItem(ev, ch, true), ev == Event && ch == Channel); + Add(new cMenuScheduleItem(Timers, ev, ch, true), ev == Event && ch == Channel); } } } } + return true; } -void cMenuSchedule::PrepareScheduleAllAll(const cEvent *Event, const cChannel *Channel) +bool cMenuSchedule::PrepareScheduleAllAll(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel) { Clear(); SetCols(CHNUMWIDTH, CHNAMWIDTH, 7, 6, 4); SetTitle(tr("All events - all channels")); - if (schedules) { - for (cChannel *ch = Channels.First(); ch; ch = Channels.Next(ch)) { - const cSchedule *Schedule = schedules->GetSchedule(ch); - if (Schedule) { - time_t now = time(NULL) - Setup.EPGLinger * 60; - for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) { - if (ev->EndTime() > now || ev == Event) - Add(new cMenuScheduleItem(ev, ch, true), ev == Event && ch == Channel); - } - } + LOCK_CHANNELS_READ; + cStateKey StateKey; + for (const cChannel *ch = Channels->First(); ch; ch = Channels->Next(ch)) { + if (const cSchedule *Schedule = Schedules->GetSchedule(ch)) { + time_t now = time(NULL) - Setup.EPGLinger * 60; + for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) { + if (ev->EndTime() > now || ev == Event) + Add(new cMenuScheduleItem(Timers, ev, ch, true), ev == Event && ch == Channel); + } } - } + } + return true; } bool cMenuSchedule::Update(void) { bool result = false; - if (Timers.Modified(timerState)) { + if (const cTimers *Timers = cTimers::GetTimersRead(timersStateKey)) { for (cOsdItem *item = First(); item; item = Next(item)) { - if (((cMenuScheduleItem *)item)->Update()) + if (((cMenuScheduleItem *)item)->Update(Timers)) result = true; } + timersStateKey.Remove(); } return result; } @@ -1735,7 +1847,8 @@ void cMenuSchedule::SetHelpKeys(void) NewHelpKeys |= 0x02; // "Timer" else NewHelpKeys |= 0x01; // "Record" - if (cChannel *Channel = Channels.GetByChannelID(item->event->ChannelID(), true)) { + LOCK_CHANNELS_READ; + if (const cChannel *Channel = Channels->GetByChannelID(item->event->ChannelID(), true)) { if (Channel->Number() != cDevice::CurrentChannel()) { NewHelpKeys |= 0x10; // "Switch" canSwitch = true; @@ -1752,58 +1865,38 @@ void cMenuSchedule::SetHelpKeys(void) eOSState cMenuSchedule::Number(void) { cMenuScheduleItem::IncSortMode(); - cMenuScheduleItem *CurrentItem = (cMenuScheduleItem *)Get(Current()); - const cChannel *Channel = NULL; - const cEvent *Event = NULL; - if (CurrentItem) { - Event = CurrentItem->event; - Channel = Channels.GetByChannelID(Event->ChannelID(), true); - } - else - Channel = Channels.GetByNumber(cDevice::CurrentChannel()); - switch (cMenuScheduleItem::SortMode()) { - case cMenuScheduleItem::ssmAllThis: PrepareScheduleAllThis(Event, Channel); break; - case cMenuScheduleItem::ssmThisThis: PrepareScheduleThisThis(Event, Channel); break; - case cMenuScheduleItem::ssmThisAll: PrepareScheduleThisAll(Event, Channel); break; - case cMenuScheduleItem::ssmAllAll: PrepareScheduleAllAll(Event, Channel); break; - default: esyslog("ERROR: unknown SortMode %d (%s %d)", cMenuScheduleItem::SortMode(), __FUNCTION__, __LINE__); - } - CurrentItem = (cMenuScheduleItem *)Get(Current()); - Sort(); - SetCurrent(CurrentItem); - Display(); + Set(NULL, true); return osContinue; } eOSState cMenuSchedule::Record(void) { - cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current()); - if (item) { - if (item->timerMatch == tmFull) { - eTimerMatch tm = tmNone; - cTimer *timer = Timers.GetMatch(item->event, &tm); - if (timer) - return AddSubMenu(new cMenuEditTimer(timer)); - } - cTimer *timer = new cTimer(item->event); - cTimer *t = Timers.GetTimer(timer); - if (t) { - delete timer; - timer = t; - return AddSubMenu(new cMenuEditTimer(timer)); - } - else { - Timers.Add(timer); - Timers.SetModified(); - isyslog("timer %s added (active)", *timer->ToDescr()); - if (timer->Matches(0, false, NEWTIMERLIMIT)) - return AddSubMenu(new cMenuEditTimer(timer)); - if (HasSubMenu()) - CloseSubMenu(); - if (Update()) - Display(); - SetHelpKeys(); - } + if (cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current())) { + { + LOCK_TIMERS_WRITE; + LOCK_SCHEDULES_READ; + Timers->SetExplicitModify(); + if (item->timerMatch == tmFull) { + if (cTimer *Timer = Timers->GetMatch(item->event)) + return AddSubMenu(new cMenuEditTimer(Timer)); + } + cTimer *Timer = new cTimer(item->event); + if (cTimer *t = Timers->GetTimer(Timer)) { + delete Timer; + Timer = t; + return AddSubMenu(new cMenuEditTimer(Timer)); + } + if (Timer->Matches(0, false, NEWTIMERLIMIT)) + return AddSubMenu(new cMenuEditTimer(Timer, true)); + Timers->Add(Timer); + Timers->SetModified(); + isyslog("timer %s added (active)", *Timer->ToDescr()); + } + if (HasSubMenu()) + CloseSubMenu(); + if (Update()) + Display(); + SetHelpKeys(); } return osContinue; } @@ -1812,10 +1905,14 @@ eOSState cMenuSchedule::Switch(void) { cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current()); if (item) { - if (cChannel *Channel = Channels.GetByChannelID(item->event->ChannelID(), true)) { - if (Channels.SwitchTo(Channel->Number())) - return osEnd; + LOCK_CHANNELS_READ; + const cChannel *Channel = NULL; + if (Channel = Channels->GetByChannelID(item->event->ChannelID(), true)) { + if (!Channels->SwitchTo(Channel->Number())) + Channel = NULL; } + if (Channel) + return osEnd; } Skins.Message(mtError, tr("Can't switch channel!")); return osContinue; @@ -1823,6 +1920,8 @@ eOSState cMenuSchedule::Switch(void) eOSState cMenuSchedule::ProcessKey(eKeys Key) { + if (!HasSubMenu()) + Set(); // react on any changes to the schedules list bool HadSubMenu = HasSubMenu(); eOSState state = cOsdMenu::ProcessKey(Key); @@ -1831,43 +1930,50 @@ eOSState cMenuSchedule::ProcessKey(eKeys Key) case k0: return Number(); case kRecord: case kRed: return Record(); - case kGreen: if (schedules) { - if (!now && !next) { - int ChannelNr = 0; - if (Count()) { - cChannel *channel = Channels.GetByChannelID(((cMenuScheduleItem *)Get(Current()))->event->ChannelID(), true); - if (channel) - ChannelNr = channel->Number(); - } - now = true; - return AddSubMenu(new cMenuWhatsOn(schedules, true, ChannelNr)); - } - now = !now; - next = !next; - return AddSubMenu(new cMenuWhatsOn(schedules, now, cMenuWhatsOn::CurrentChannel())); - } - case kYellow: if (schedules) - return AddSubMenu(new cMenuWhatsOn(schedules, false, cMenuWhatsOn::CurrentChannel())); - break; + case kGreen: { + LOCK_TIMERS_READ; + LOCK_CHANNELS_READ; + LOCK_SCHEDULES_READ; + if (!now && !next) { + int ChannelNr = 0; + if (Count()) { + if (const cChannel *Channel = Channels->GetByChannelID(((cMenuScheduleItem *)Get(Current()))->event->ChannelID(), true)) + ChannelNr = Channel->Number(); + } + now = true; + return AddSubMenu(new cMenuWhatsOn(Timers, Channels, Schedules, now, ChannelNr)); + } + now = !now; + next = !next; + return AddSubMenu(new cMenuWhatsOn(Timers, Channels, Schedules, now, cMenuWhatsOn::CurrentChannel())); + } + case kYellow: { + LOCK_TIMERS_READ; + LOCK_CHANNELS_READ; + LOCK_SCHEDULES_READ; + return AddSubMenu(new cMenuWhatsOn(Timers, Channels, Schedules, false, cMenuWhatsOn::CurrentChannel())); + } case kBlue: if (canSwitch) return Switch(); break; case kInfo: - case kOk: if (Count()) - return AddSubMenu(new cMenuEvent(((cMenuScheduleItem *)Get(Current()))->event, canSwitch, true)); + case kOk: if (Count()) { + LOCK_TIMERS_READ; + LOCK_CHANNELS_READ; + LOCK_SCHEDULES_READ; + return AddSubMenu(new cMenuEvent(Timers, Channels, ((cMenuScheduleItem *)Get(Current()))->event, canSwitch, true)); + } break; default: break; } } else if (!HasSubMenu()) { now = next = false; - const cEvent *ei = cMenuWhatsOn::ScheduleEvent(); - if (ei) { - cChannel *channel = Channels.GetByChannelID(ei->ChannelID(), true); - if (channel) { + if (const cEvent *ei = cMenuWhatsOn::ScheduleEvent()) { + LOCK_CHANNELS_READ; + if (const cChannel *Channel = Channels->GetByChannelID(ei->ChannelID(), true)) { cMenuScheduleItem::SetSortMode(cMenuScheduleItem::ssmAllThis); - PrepareScheduleAllThis(NULL, channel); - Display(); + Set(Channel, true); } } else if (HadSubMenu && Update()) @@ -2219,7 +2325,10 @@ cMenuPathEdit::cMenuPathEdit(const char *Path) else s = path; strn0cpy(name, s, sizeof(name)); - pathIsInUse = Recordings.PathIsInUse(path); + { + LOCK_RECORDINGS_READ; + pathIsInUse = Recordings->PathIsInUse(path); + } cOsdItem *p; Add(p = folderItem = new cMenuEditStrItem(tr("Folder"), folder, sizeof(folder))); p->SetSelectable(!pathIsInUse); @@ -2258,14 +2367,17 @@ eOSState cMenuPathEdit::ApplyChanges(void) cString NewPath = *folder ? cString::sprintf("%s%c%s", folder, FOLDERDELIMCHAR, name) : name; NewPath.CompactChars(FOLDERDELIMCHAR); if (strcmp(NewPath, path)) { - int NumRecordings = Recordings.GetNumRecordingsInPath(path); + LOCK_RECORDINGS_WRITE; + Recordings->SetExplicitModify(); + int NumRecordings = Recordings->GetNumRecordingsInPath(path); if (NumRecordings > 1 && !Interface->Confirm(cString::sprintf(tr("Move entire folder containing %d recordings?"), NumRecordings))) return osContinue; - if (!Recordings.MoveRecordings(path, NewPath)) { + if (!Recordings->MoveRecordings(path, NewPath)) { Skins.Message(mtError, tr("Error while moving folder!")); return osContinue; } cMenuRecordings::SetPath(NewPath); // makes sure the Recordings menu will reposition to the new path + Recordings->SetModified(); return osUser1; } return osBack; @@ -2294,9 +2406,9 @@ eOSState cMenuPathEdit::ProcessKey(eKeys Key) class cMenuRecordingEdit : public cOsdMenu { private: - cRecording *recording; + const cRecording *recording; cString originalFileName; - int recordingsState; + cStateKey recordingsStateKey; char folder[PATH_MAX]; char name[NAME_MAX]; int priority; @@ -2319,17 +2431,16 @@ private: eOSState DeleteMarks(void); eOSState ApplyChanges(void); public: - cMenuRecordingEdit(cRecording *Recording); + cMenuRecordingEdit(const cRecording *Recording); virtual eOSState ProcessKey(eKeys Key); }; -cMenuRecordingEdit::cMenuRecordingEdit(cRecording *Recording) +cMenuRecordingEdit::cMenuRecordingEdit(const cRecording *Recording) :cOsdMenu(tr("Edit recording"), 12) { SetMenuCategory(mcRecordingEdit); recording = Recording; originalFileName = recording->FileName(); - Recordings.StateChanged(recordingsState); // just to get the current state strn0cpy(folder, recording->Folder(), sizeof(folder)); strn0cpy(name, recording->BaseName(), sizeof(name)); priority = recording->Priority(); @@ -2390,13 +2501,15 @@ void cMenuRecordingEdit::SetHelpKeys(void) bool cMenuRecordingEdit::RefreshRecording(void) { - if (Recordings.StateChanged(recordingsState)) { - if ((recording = Recordings.GetByName(originalFileName)) != NULL) + if (const cRecordings *Recordings = cRecordings::GetRecordingsRead(recordingsStateKey)) { + if ((recording = Recordings->GetByName(originalFileName)) != NULL) Set(); else { + recordingsStateKey.Remove(); Skins.Message(mtWarning, tr("Recording vanished!")); return false; } + recordingsStateKey.Remove(); } return true; } @@ -2453,7 +2566,7 @@ eOSState cMenuRecordingEdit::RemoveName(void) eOSState cMenuRecordingEdit::DeleteMarks(void) { if (buttonDeleteMarks && Interface->Confirm(tr("Delete editing marks for this recording?"))) { - if (recording->DeleteMarks()) + if (cMarks::DeleteMarksFile(recording)) SetHelpKeys(); else Skins.Message(mtError, tr("Error while deleting editing marks!")); @@ -2463,10 +2576,18 @@ eOSState cMenuRecordingEdit::DeleteMarks(void) eOSState cMenuRecordingEdit::ApplyChanges(void) { + cStateKey StateKey; + cRecordings *Recordings = cRecordings::GetRecordingsWrite(StateKey); + cRecording *Recording = Recordings->GetByName(recording->FileName()); + if (!Recording) { + Skins.Message(mtWarning, tr("Recording vanished!")); + return osBack; + } bool Modified = false; if (priority != recording->Priority() || lifetime != recording->Lifetime()) { - if (!recording->ChangePriorityLifetime(priority, lifetime)) { + if (!Recording->ChangePriorityLifetime(priority, lifetime)) { Skins.Message(mtError, tr("Error while changing priority/lifetime!")); + StateKey.Remove(Modified); return osContinue; } Modified = true; @@ -2477,17 +2598,21 @@ eOSState cMenuRecordingEdit::ApplyChanges(void) } cString NewName = *folder ? cString::sprintf("%s%c%s", folder, FOLDERDELIMCHAR, name) : name; NewName.CompactChars(FOLDERDELIMCHAR); - if (strcmp(NewName, recording->Name())) { - if (!recording->ChangeName(NewName)) { + if (strcmp(NewName, Recording->Name())) { + if (!Recording->ChangeName(NewName)) { Skins.Message(mtError, tr("Error while changing folder/name!")); + StateKey.Remove(Modified); return osContinue; } Modified = true; } if (Modified) { - cMenuRecordings::SetRecording(recording->FileName()); // makes sure the Recordings menu will reposition to the renamed recording + cMenuRecordings::SetRecording(Recording->FileName()); // makes sure the Recordings menu will reposition to the renamed recording + Recordings->TouchUpdate(); + StateKey.Remove(Modified); return osUser1; } + StateKey.Remove(Modified); return osBack; } @@ -2517,24 +2642,23 @@ eOSState cMenuRecordingEdit::ProcessKey(eKeys Key) class cMenuRecording : public cOsdMenu { private: - cRecording *recording; + const cRecording *recording; cString originalFileName; - int recordingsState; + cStateKey recordingsStateKey; bool withButtons; bool RefreshRecording(void); public: - cMenuRecording(cRecording *Recording, bool WithButtons = false); + cMenuRecording(const cRecording *Recording, bool WithButtons = false); virtual void Display(void); virtual eOSState ProcessKey(eKeys Key); }; -cMenuRecording::cMenuRecording(cRecording *Recording, bool WithButtons) +cMenuRecording::cMenuRecording(const cRecording *Recording, bool WithButtons) :cOsdMenu(tr("Recording info")) { SetMenuCategory(mcRecordingInfo); recording = Recording; originalFileName = recording->FileName(); - Recordings.StateChanged(recordingsState); // just to get the current state withButtons = WithButtons; if (withButtons) SetHelp(tr("Button$Play"), tr("Button$Rewind"), NULL, tr("Button$Edit")); @@ -2542,13 +2666,15 @@ cMenuRecording::cMenuRecording(cRecording *Recording, bool WithButtons) bool cMenuRecording::RefreshRecording(void) { - if (Recordings.StateChanged(recordingsState)) { - if ((recording = Recordings.GetByName(originalFileName)) != NULL) + if (const cRecordings *Recordings = cRecordings::GetRecordingsRead(recordingsStateKey)) { + if ((recording = Recordings->GetByName(originalFileName)) != NULL) Display(); else { + recordingsStateKey.Remove(); Skins.Message(mtWarning, tr("Recording vanished!")); return false; } + recordingsStateKey.Remove(); } return true; } @@ -2611,23 +2737,23 @@ eOSState cMenuRecording::ProcessKey(eKeys Key) class cMenuRecordingItem : public cOsdItem { private: - cRecording *recording; + const cRecording *recording; int level; char *name; int totalEntries, newEntries; public: - cMenuRecordingItem(cRecording *Recording, int Level); + cMenuRecordingItem(const cRecording *Recording, int Level); ~cMenuRecordingItem(); void IncrementCounter(bool New); const char *Name(void) { return name; } int Level(void) { return level; } - cRecording *Recording(void) { return recording; } + const cRecording *Recording(void) { return recording; } bool IsDirectory(void) { return name != NULL; } - void SetRecording(cRecording *Recording) { recording = Recording; } + void SetRecording(const cRecording *Recording) { recording = Recording; } virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable); }; -cMenuRecordingItem::cMenuRecordingItem(cRecording *Recording, int Level) +cMenuRecordingItem::cMenuRecordingItem(const cRecording *Recording, int Level) { recording = Recording; level = Level; @@ -2669,7 +2795,6 @@ cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus, base = Base ? strdup(Base) : NULL; level = Setup.RecordingDirs ? Level : -1; filter = Filter; - Recordings.StateChanged(recordingsState); // just to get the current state helpKeys = -1; Display(); // this keeps the higher level menus from showing up briefly when pressing 'Back' during replay Set(); @@ -2717,50 +2842,54 @@ void cMenuRecordings::SetHelpKeys(void) void cMenuRecordings::Set(bool Refresh) { - const char *CurrentRecording = *fileName ? *fileName : cReplayControl::LastReplayed(); - cMenuRecordingItem *LastItem = NULL; - cThreadLock RecordingsLock(&Recordings); - if (Refresh) { - if (cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current())) - CurrentRecording = ri->Recording()->FileName(); - } - Clear(); - GetRecordingsSortMode(DirectoryName()); - Recordings.Sort(); - for (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))) { - cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level); - cMenuRecordingItem *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 (cMenuRecordingItem *p = LastItem; p; p = dynamic_cast<cMenuRecordingItem *>(p->Prev())) { - if (p->Name() && strcmp(p->Name(), Item->Name()) == 0) { - LastDir = p; - break; + if (cRecordings::GetRecordingsRead(recordingsStateKey)) { + recordingsStateKey.Remove(); + const char *CurrentRecording = *fileName ? *fileName : cReplayControl::LastReplayed(); + cRecordings *Recordings = cRecordings::GetRecordingsWrite(recordingsStateKey); // write access is necessary for sorting! + cMenuRecordingItem *LastItem = NULL; + if (Refresh) { + if (cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current())) + CurrentRecording = ri->Recording()->FileName(); + } + 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))) { + cMenuRecordingItem *Item = new cMenuRecordingItem(Recording, level); + cMenuRecordingItem *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 (cMenuRecordingItem *p = LastItem; p; p = dynamic_cast<cMenuRecordingItem *>(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) + } + 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); } - else if (CurrentRecording && strcmp(CurrentRecording, recording->FileName()) == 0) - SetCurrent(LastDir ? LastDir : LastItem); + if (LastDir) + LastDir->IncrementCounter(Recording->IsNew()); } - if (LastDir) - LastDir->IncrementCounter(recording->IsNew()); } - } - SetMenuSortMode(RecordingsSortMode == rsmName ? msmName : msmTime); + SetMenuSortMode(RecordingsSortMode == rsmName ? msmName : msmTime); + recordingsStateKey.Remove(false); // sorting doesn't count as a real modification + } if (Refresh) Display(); } @@ -2837,50 +2966,60 @@ eOSState cMenuRecordings::Delete(void) cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current()); if (ri && !ri->IsDirectory()) { if (Interface->Confirm(tr("Delete recording?"))) { - cRecordControl *rc = cRecordControls::GetRecordControl(ri->Recording()->FileName()); - if (rc) { + if (cRecordControl *rc = cRecordControls::GetRecordControl(ri->Recording()->FileName())) { if (Interface->Confirm(tr("Timer still recording - really delete?"))) { - cTimer *timer = rc->Timer(); - if (timer) { - timer->Skip(); - cRecordControls::Process(time(NULL)); - if (timer->IsSingleEvent()) { - isyslog("deleting timer %s", *timer->ToDescr()); - Timers.Del(timer); + if (cTimer *Timer = rc->Timer()) { + LOCK_TIMERS_WRITE; + Timer->Skip(); + cRecordControls::Process(Timers, time(NULL)); + if (Timer->IsSingleEvent()) { + isyslog("deleting timer %s", *Timer->ToDescr()); + Timers->Del(Timer); } - Timers.SetModified(); } } else return osContinue; } - cRecording *recording = ri->Recording(); - cString FileName = recording->FileName(); + cRecordings *Recordings = cRecordings::GetRecordingsWrite(recordingsStateKey); + Recordings->SetExplicitModify(); + cRecording *Recording = Recordings->GetByName(ri->Recording()->FileName()); + if (!Recording) { + Skins.Message(mtWarning, tr("Recording vanished!")); + recordingsStateKey.Remove(); + return osContinue; + } + cString FileName = Recording->FileName(); if (RecordingsHandler.GetUsage(FileName)) { if (Interface->Confirm(tr("Recording is being edited - really delete?"))) { RecordingsHandler.Del(FileName); - recording = Recordings.GetByName(FileName); // RecordingsHandler.Del() might have deleted it if it was the edited version - // we continue with the code below even if recording is NULL, + Recording = Recordings->GetByName(FileName); // RecordingsHandler.Del() might have deleted it if it was the edited version + // we continue with the code below even if Recording is NULL, // in order to have the menu updated etc. } - else + else { + recordingsStateKey.Remove(); return osContinue; + } } if (cReplayControl::NowReplaying() && strcmp(cReplayControl::NowReplaying(), FileName) == 0) cControl::Shutdown(); - if (!recording || recording->Delete()) { + if (!Recording || Recording->Delete()) { cReplayControl::ClearLastReplayed(FileName); - Recordings.DelByName(FileName); + Recordings->DelByName(FileName); cOsdMenu::Del(Current()); SetHelpKeys(); cVideoDiskUsage::ForceCheck(); Display(); + Recordings->SetModified(); + recordingsStateKey.Remove(); if (!Count()) return osBack; return osUser2; } else Skins.Message(mtError, tr("Error while deleting recording!")); + recordingsStateKey.Remove(false); } } return osContinue; @@ -2925,6 +3064,8 @@ eOSState cMenuRecordings::Sort(void) eOSState cMenuRecordings::ProcessKey(eKeys Key) { + if (!HasSubMenu()) + Set(); // react on any changes to the recordings list bool HadSubMenu = HasSubMenu(); eOSState state = cOsdMenu::ProcessKey(Key); @@ -2940,9 +3081,6 @@ eOSState cMenuRecordings::ProcessKey(eKeys Key) case kBlue: return Info(); case k0: return Sort(); case k1...k9: return Commands(Key); - case kNone: if (Recordings.StateChanged(recordingsState)) - Set(true); - break; default: break; } } @@ -3136,8 +3274,10 @@ eOSState cMenuSetupOSD::ProcessKey(eKeys Key) ModifiedAppearance = true; if (strcmp(data.FontFix, Setup.FontFix) || !DoubleEqual(data.FontFixSizeP, Setup.FontFixSizeP)) ModifiedAppearance = true; - if (data.AlwaysSortFoldersFirst != Setup.AlwaysSortFoldersFirst || data.RecordingDirs != Setup.RecordingDirs) - Recordings.ClearSortNames(); + if (data.AlwaysSortFoldersFirst != Setup.AlwaysSortFoldersFirst || data.RecordingDirs != Setup.RecordingDirs) { + LOCK_RECORDINGS_WRITE; + Recordings->ClearSortNames(); + } } int oldSkinIndex = skinIndex; @@ -3614,7 +3754,8 @@ eOSState cMenuSetupCAM::Activate(void) CamSlot->CancelActivation(); else if (CamSlot->CanActivate()) { if (CamSlot->Priority() < LIVEPRIORITY) { // don't interrupt recordings - if (cChannel *Channel = Channels.GetByNumber(cDevice::CurrentChannel())) { + LOCK_CHANNELS_READ; + if (const cChannel *Channel = Channels->GetByNumber(cDevice::CurrentChannel())) { for (int i = 0; i < cDevice::NumDevices(); i++) { if (cDevice *Device = cDevice::GetDevice(i)) { if (Device->ProvidesChannel(Channel)) { @@ -3745,8 +3886,10 @@ cMenuSetupReplay::cMenuSetupReplay(void) void cMenuSetupReplay::Store(void) { - if (Setup.ResumeID != data.ResumeID) - Recordings.ResetResume(); + if (Setup.ResumeID != data.ResumeID) { + LOCK_RECORDINGS_WRITE; + Recordings->ResetResume(); + } cMenuSetupBase::Store(); } @@ -4078,8 +4221,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key) case osSetup: return AddSubMenu(new cMenuSetup); case osCommands: return AddSubMenu(new cMenuCommands(tr("Commands"), &Commands)); case osStopRecord: if (Interface->Confirm(tr("Stop recording?"))) { - cOsdItem *item = Get(Current()); - if (item) { + if (cOsdItem *item = Get(Current())) { cRecordControls::Stop(item->Text() + strlen(tr(STOP_RECORDING))); return osEnd; } @@ -4146,25 +4288,20 @@ static void SetTrackDescriptions(int LiveChannel) { cDevice::PrimaryDevice()->ClrAvailableTracks(true); const cComponents *Components = NULL; - cSchedulesLock SchedulesLock; if (LiveChannel) { - cChannel *Channel = Channels.GetByNumber(LiveChannel); - if (Channel) { - const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock); - if (Schedules) { - const cSchedule *Schedule = Schedules->GetSchedule(Channel); - if (Schedule) { - const cEvent *Present = Schedule->GetPresentEvent(); - if (Present) - Components = Present->Components(); - } + LOCK_CHANNELS_READ; + if (const cChannel *Channel = Channels->GetByNumber(LiveChannel)) { + LOCK_SCHEDULES_READ; + if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) { + const cEvent *Present = Schedule->GetPresentEvent(); + if (Present) + Components = Present->Components(); } } } else if (cReplayControl::NowReplaying()) { - cThreadLock RecordingsLock(&Recordings); - cRecording *Recording = Recordings.GetByName(cReplayControl::NowReplaying()); - if (Recording) + LOCK_RECORDINGS_READ; + if (const cRecording *Recording = Recordings->GetByName(cReplayControl::NowReplaying())) Components = Recording->Info()->Components(); } if (Components) { @@ -4204,7 +4341,9 @@ cDisplayChannel::cDisplayChannel(int Number, bool Switched) timeout = Switched || Setup.TimeoutRequChInfo; cOsdProvider::OsdSizeChanged(osdState); // just to get the current state positioner = NULL; - channel = Channels.GetByNumber(Number); + channel = NULL; + LOCK_CHANNELS_READ; + channel = Channels->GetByNumber(Number); lastPresent = lastFollowing = NULL; if (channel) { DisplayChannel(); @@ -4226,7 +4365,9 @@ cDisplayChannel::cDisplayChannel(eKeys FirstKey) withInfo = Setup.ShowInfoOnChSwitch; displayChannel = Skins.Current()->DisplayChannel(withInfo); positioner = NULL; - channel = Channels.GetByNumber(cDevice::CurrentChannel()); + channel = NULL; + LOCK_CHANNELS_READ; + channel = Channels->GetByNumber(cDevice::CurrentChannel()); ProcessKey(FirstKey); } @@ -4247,20 +4388,16 @@ void cDisplayChannel::DisplayChannel(void) void cDisplayChannel::DisplayInfo(void) { if (withInfo && channel) { - cSchedulesLock SchedulesLock; - const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock); - if (Schedules) { - const cSchedule *Schedule = Schedules->GetSchedule(channel); - if (Schedule) { - const cEvent *Present = Schedule->GetPresentEvent(); - const cEvent *Following = Schedule->GetFollowingEvent(); - if (Present != lastPresent || Following != lastFollowing) { - SetTrackDescriptions(channel->Number()); - displayChannel->SetEvents(Present, Following); - cStatus::MsgOsdProgramme(Present ? Present->StartTime() : 0, Present ? Present->Title() : NULL, Present ? Present->ShortText() : NULL, Following ? Following->StartTime() : 0, Following ? Following->Title() : NULL, Following ? Following->ShortText() : NULL); - lastPresent = Present; - lastFollowing = Following; - } + LOCK_SCHEDULES_READ; + if (const cSchedule *Schedule = Schedules->GetSchedule(channel)) { + const cEvent *Present = Schedule->GetPresentEvent(); + const cEvent *Following = Schedule->GetFollowingEvent(); + if (Present != lastPresent || Following != lastFollowing) { + SetTrackDescriptions(channel->Number()); + displayChannel->SetEvents(Present, Following); + cStatus::MsgOsdProgramme(Present ? Present->StartTime() : 0, Present ? Present->Title() : NULL, Present ? Present->ShortText() : NULL, Following ? Following->StartTime() : 0, Following ? Following->Title() : NULL, Following ? Following->ShortText() : NULL); + lastPresent = Present; + lastFollowing = Following; } } } @@ -4272,13 +4409,14 @@ void cDisplayChannel::Refresh(void) displayChannel->SetEvents(NULL, NULL); } -cChannel *cDisplayChannel::NextAvailableChannel(cChannel *Channel, int Direction) +const cChannel *cDisplayChannel::NextAvailableChannel(const cChannel *Channel, int Direction) { if (Direction) { + LOCK_CHANNELS_READ; while (Channel) { - Channel = Direction > 0 ? Channels.Next(Channel) : Channels.Prev(Channel); + Channel = Direction > 0 ? Channels->Next(Channel) : Channels->Prev(Channel); if (!Channel && Setup.ChannelsWrap) - Channel = Direction > 0 ? Channels.First() : Channels.Last(); + Channel = Direction > 0 ? Channels->First() : Channels->Last(); if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, LIVEPRIORITY, true, true)) return Channel; } @@ -4292,7 +4430,7 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) delete displayChannel; displayChannel = Skins.Current()->DisplayChannel(withInfo); } - cChannel *NewChannel = NULL; + const cChannel *NewChannel = NULL; if (Key != kNone) lastTime.Set(); switch (int(Key)) { @@ -4305,18 +4443,19 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) case k1 ... k9: group = -1; if (number >= 0) { - if (number > Channels.MaxNumber()) + if (number > cChannels::MaxNumber()) number = Key - k0; else number = number * 10 + Key - k0; - channel = Channels.GetByNumber(number); + LOCK_CHANNELS_READ + channel = Channels->GetByNumber(number); Refresh(); withInfo = false; // Lets see if there can be any useful further input: int n = channel ? number * 10 : 0; int m = 10; - cChannel *ch = channel; - while (ch && (ch = Channels.Next(ch)) != NULL) { + const cChannel *ch = channel; + while (ch && (ch = Channels->Next(ch)) != NULL) { if (!ch->GroupSep()) { if (n <= ch->Number() && ch->Number() < n + m) { n = 0; @@ -4344,23 +4483,23 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) case kNext|k_Repeat: case kNext: case kPrev|k_Repeat: - case kPrev: + case kPrev: { withInfo = false; number = 0; + LOCK_CHANNELS_READ; if (group < 0) { - cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel()); - if (channel) - group = channel->Index(); + if (const cChannel *Channel = Channels->GetByNumber(cDevice::CurrentChannel())) + group = Channel->Index(); } if (group >= 0) { int SaveGroup = group; if (NORMALKEY(Key) == kRight || NORMALKEY(Key) == kNext) - group = Channels.GetNextGroup(group) ; + group = Channels->GetNextGroup(group) ; else - group = Channels.GetPrevGroup(group < 1 ? 1 : group); + group = Channels->GetPrevGroup(group < 1 ? 1 : group); if (group < 0) group = SaveGroup; - channel = Channels.Get(group); + channel = Channels->Get(group); if (channel) { Refresh(); if (!channel->GroupSep()) @@ -4368,6 +4507,7 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) } } break; + } case kUp|k_Repeat: case kUp: case kDown|k_Repeat: @@ -4377,9 +4517,8 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) case kChanDn|k_Repeat: case kChanDn: { eKeys k = NORMALKEY(Key); - cChannel *ch = NextAvailableChannel(channel, (k == kUp || k == kChanUp) ? 1 : -1); - if (ch) - channel = ch; + if (const cChannel *Channel = NextAvailableChannel(channel, (k == kUp || k == kChanUp) ? 1 : -1)) + channel = Channel; else if (channel && channel->Number() != cDevice::CurrentChannel()) Key = k; // immediately switches channel when hitting the beginning/end of the channel list with k_Repeat } @@ -4399,7 +4538,8 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) break; case kNone: if (number && Setup.ChannelEntryTimeout && int(lastTime.Elapsed()) > Setup.ChannelEntryTimeout) { - channel = Channels.GetByNumber(number); + LOCK_CHANNELS_READ; + channel = Channels->GetByNumber(number); if (channel) NewChannel = channel; withInfo = true; @@ -4411,9 +4551,10 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) //TODO //XXX case kGreen: return osEventNow; //XXX case kYellow: return osEventNext; - case kOk: + case kOk: { + LOCK_CHANNELS_READ; if (group >= 0) { - channel = Channels.Get(Channels.GetNextNormal(group)); + channel = Channels->Get(Channels->GetNextNormal(group)); if (channel) NewChannel = channel; withInfo = true; @@ -4421,15 +4562,17 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) Refresh(); } else if (number > 0) { - channel = Channels.GetByNumber(number); + channel = Channels->GetByNumber(number); if (channel) NewChannel = channel; withInfo = true; number = 0; Refresh(); } - else + else { return osEnd; + } + } break; default: if ((Key & (k_Repeat | k_Release)) == 0) { @@ -4438,16 +4581,17 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) } }; if (positioner || !timeout || lastTime.Elapsed() < (uint64_t)(Setup.ChannelInfoTime * 1000)) { + LOCK_CHANNELS_READ; if (Key == kNone && !number && group < 0 && !NewChannel && channel && channel->Number() != cDevice::CurrentChannel()) { // makes sure a channel switch through the SVDRP CHAN command is displayed - channel = Channels.GetByNumber(cDevice::CurrentChannel()); + channel = Channels->GetByNumber(cDevice::CurrentChannel()); Refresh(); lastTime.Set(); } DisplayInfo(); if (NewChannel) { SetTrackDescriptions(NewChannel->Number()); // to make them immediately visible in the channel display - Channels.SwitchTo(NewChannel->Number()); + Channels->SwitchTo(NewChannel->Number()); SetTrackDescriptions(NewChannel->Number()); // switching the channel has cleared them channel = NewChannel; } @@ -4758,14 +4902,13 @@ eOSState cDisplaySubtitleTracks::ProcessKey(eKeys Key) // --- cRecordControl -------------------------------------------------------- -cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause) +cRecordControl::cRecordControl(cDevice *Device, cTimers *Timers, cTimer *Timer, bool Pause) { // Whatever happens here, the timers will be modified in some way... - Timers.SetModified(); - // We're going to manipulate an event here, so we need to prevent + Timers->SetModified(); + // We're going to work with an event here, so we need to prevent // others from modifying any EPG data: - cSchedulesLock SchedulesLock; - cSchedules::Schedules(SchedulesLock); + LOCK_SCHEDULES_READ; event = NULL; fileName = NULL; @@ -4775,7 +4918,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause) timer = Timer; if (!timer) { timer = new cTimer(true, Pause); - Timers.Add(timer); + Timers->Add(timer); instantId = cString::sprintf(cDevice::NumDevices() > 1 ? "%s - %d" : "%s", timer->Channel()->Name(), device->CardIndex() + 1); } timer->SetPending(true); @@ -4796,7 +4939,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause) timer->OnOff(); } else { - Timers.Del(timer); + Timers->Del(timer); if (!cReplayControl::LastReplayed()) // an instant recording, maybe from cRecordControls::PauseLiveVideo() cReplayControl::SetRecording(fileName); } @@ -4814,7 +4957,8 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause) cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true); if (!Timer && !cReplayControl::LastReplayed()) // an instant recording, maybe from cRecordControls::PauseLiveVideo() cReplayControl::SetRecording(fileName); - Recordings.AddByName(fileName); + LOCK_RECORDINGS_WRITE; + Recordings->AddByName(fileName); return; } else @@ -4823,7 +4967,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause) else timer->SetDeferred(DEFERTIMER); if (!Timer) { - Timers.Del(timer); + Timers->Del(timer); timer = NULL; } } @@ -4838,21 +4982,17 @@ cRecordControl::~cRecordControl() bool cRecordControl::GetEvent(void) { - const cChannel *channel = timer->Channel(); + const cChannel *Channel = timer->Channel(); time_t Time = timer->HasFlags(tfInstant) ? timer->StartTime() + INSTANT_REC_EPG_LOOKAHEAD : timer->StartTime() + (timer->StopTime() - timer->StartTime()) / 2; for (int seconds = 0; seconds <= MAXWAIT4EPGINFO; seconds++) { { - cSchedulesLock SchedulesLock; - const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock); - if (Schedules) { - const cSchedule *Schedule = Schedules->GetSchedule(channel); - if (Schedule) { - event = Schedule->GetEventAround(Time); - if (event) { - if (seconds > 0) - dsyslog("got EPG info after %d seconds", seconds); - return true; - } + LOCK_SCHEDULES_READ; + if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) { + event = Schedule->GetEventAround(Time); + if (event) { + if (seconds > 0) + dsyslog("got EPG info after %d seconds", seconds); + return true; } } } @@ -4873,7 +5013,6 @@ void cRecordControl::Stop(bool ExecuteUserCommand) cStatus::MsgRecording(device, NULL, fileName, false); if (ExecuteUserCommand) cRecordingUserCommand::InvokeCommand(RUC_AFTERRECORDING, fileName); - Timers.SetModified(); } } @@ -4893,7 +5032,7 @@ bool cRecordControl::Process(time_t t) cRecordControl *cRecordControls::RecordControls[MAXRECORDCONTROLS] = { NULL }; int cRecordControls::state = 0; -bool cRecordControls::Start(cTimer *Timer, bool Pause) +bool cRecordControls::Start(cTimers *Timers, cTimer *Timer, bool Pause) { static time_t LastNoDiskSpaceMessage = 0; int FreeMB = 0; @@ -4913,29 +5052,28 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause) LastNoDiskSpaceMessage = 0; ChangeState(); + LOCK_CHANNELS_READ; int ch = Timer ? Timer->Channel()->Number() : cDevice::CurrentChannel(); - cChannel *channel = Channels.GetByNumber(ch); - - if (channel) { + if (const cChannel *Channel = Channels->GetByNumber(ch)) { int Priority = Timer ? Timer->Priority() : Pause ? Setup.PausePriority : Setup.DefaultPriority; - cDevice *device = cDevice::GetDevice(channel, Priority, false); + cDevice *device = cDevice::GetDevice(Channel, Priority, false); if (device) { - dsyslog("switching device %d to channel %d (%s)", device->DeviceNumber() + 1, channel->Number(), channel->Name()); - if (!device->SwitchChannel(channel, false)) { + dsyslog("switching device %d to channel %d (%s)", device->DeviceNumber() + 1, Channel->Number(), Channel->Name()); + if (!device->SwitchChannel(Channel, false)) { ShutdownHandler.RequestEmergencyExit(); return false; } if (!Timer || Timer->Matches()) { for (int i = 0; i < MAXRECORDCONTROLS; i++) { if (!RecordControls[i]) { - RecordControls[i] = new cRecordControl(device, Timer, Pause); + RecordControls[i] = new cRecordControl(device, Timers, Timer, Pause); return RecordControls[i]->Process(time(NULL)); } } } } else if (!Timer || !Timer->Pending()) { - isyslog("no free DVB device to record channel %d (%s)!", ch, channel->Name()); + isyslog("no free DVB device to record channel %d (%s)!", ch, Channel->Name()); Skins.Message(mtError, tr("No free DVB device to record!")); } } @@ -4944,19 +5082,25 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause) return false; } +bool cRecordControls::Start(bool Pause) +{ + LOCK_TIMERS_WRITE; + return Start(Timers, NULL, Pause); +} + void cRecordControls::Stop(const char *InstantId) { + LOCK_TIMERS_WRITE; ChangeState(); for (int i = 0; i < MAXRECORDCONTROLS; i++) { if (RecordControls[i]) { const char *id = RecordControls[i]->InstantId(); if (id && strcmp(id, InstantId) == 0) { - cTimer *timer = RecordControls[i]->Timer(); + cTimer *Timer = RecordControls[i]->Timer(); RecordControls[i]->Stop(); - if (timer) { - isyslog("deleting timer %s", *timer->ToDescr()); - Timers.Del(timer); - Timers.SetModified(); + if (Timer) { + isyslog("deleting timer %s", *Timer->ToDescr()); + Timers->Del(Timer); } break; } @@ -4968,7 +5112,7 @@ bool cRecordControls::PauseLiveVideo(void) { Skins.Message(mtStatus, tr("Pausing live video...")); cReplayControl::SetRecording(NULL); // make sure the new cRecordControl will set cReplayControl::LastReplayed() - if (Start(NULL, true)) { + if (Start(true)) { cReplayControl *rc = new cReplayControl(true); cControl::Launch(rc); cControl::Attach(); @@ -5012,19 +5156,22 @@ cRecordControl *cRecordControls::GetRecordControl(const cTimer *Timer) return NULL; } -void cRecordControls::Process(time_t t) +bool cRecordControls::Process(cTimers *Timers, time_t t) { + bool Result = false; for (int i = 0; i < MAXRECORDCONTROLS; i++) { if (RecordControls[i]) { if (!RecordControls[i]->Process(t)) { DELETENULL(RecordControls[i]); ChangeState(); + Result = true; } } } + return Result; } -void cRecordControls::ChannelDataModified(cChannel *Channel) +void cRecordControls::ChannelDataModified(const cChannel *Channel) { for (int i = 0; i < MAXRECORDCONTROLS; i++) { if (RecordControls[i]) { @@ -5146,19 +5293,25 @@ void cReplayControl::Stop(void) if (rc && rc->InstantId()) { if (Active()) { if (Setup.DelTimeshiftRec == 2 || Interface->Confirm(tr("Delete timeshift recording?"))) { - cTimer *timer = rc->Timer(); - rc->Stop(false); // don't execute user command - if (timer) { - isyslog("deleting timer %s", *timer->ToDescr()); - Timers.Del(timer); - Timers.SetModified(); - } + { + LOCK_TIMERS_WRITE; + Timers->SetExplicitModify(); + cTimer *Timer = rc->Timer(); + rc->Stop(false); // don't execute user command + if (Timer) { + isyslog("deleting timer %s", *Timer->ToDescr()); + Timers->Del(Timer); + Timers->SetModified(); + } + } cDvbPlayerControl::Stop(); - cRecording *recording = Recordings.GetByName(fileName); - if (recording) { - if (recording->Delete()) { - Recordings.DelByName(fileName); + LOCK_RECORDINGS_WRITE; + Recordings->SetExplicitModify(); + if (cRecording *Recording = Recordings->GetByName(fileName)) { + if (Recording->Delete()) { + Recordings->DelByName(fileName); ClearLastReplayed(fileName); + Recordings->SetModified(); } else Skins.Message(mtError, tr("Error while deleting recording!")); @@ -5184,7 +5337,8 @@ const char *cReplayControl::NowReplaying(void) const char *cReplayControl::LastReplayed(void) { - if (!Recordings.GetByName(fileName)) + LOCK_RECORDINGS_READ; + if (!Recordings->GetByName(fileName)) fileName = NULL; return fileName; } @@ -5269,7 +5423,8 @@ bool cReplayControl::ShowProgress(bool Initial) } if (Initial) { if (*fileName) { - if (cRecording *Recording = Recordings.GetByName(fileName)) + LOCK_RECORDINGS_READ; + if (const cRecording *Recording = Recordings->GetByName(fileName)) displayReplay->SetRecording(Recording); } lastCurrent = lastTotal = -1; @@ -5392,15 +5547,12 @@ void cReplayControl::MarkToggle(void) int Current, Total; if (GetIndex(Current, Total, true)) { lastCurrent = -1; // triggers redisplay - if (cMark *m = marks.Get(Current)) { - marks.Lock(); + cStateKey StateKey; + marks.Lock(StateKey); + if (cMark *m = marks.Get(Current)) marks.Del(m); - marks.Unlock(); - } else { - marks.Lock(); marks.Add(Current); - marks.Unlock(); bool Play, Forward; int Speed; if (Setup.PauseOnMarkSet || GetReplayMode(Play, Forward, Speed) && !Play) { @@ -5408,6 +5560,7 @@ void cReplayControl::MarkToggle(void) displayFrames = true; } } + StateKey.Remove(); ShowTimed(2); marksModified = true; } @@ -5515,15 +5668,16 @@ void cReplayControl::EditTest(void) cOsdObject *cReplayControl::GetInfo(void) { - cRecording *Recording = Recordings.GetByName(cReplayControl::LastReplayed()); - if (Recording) + LOCK_RECORDINGS_READ; + if (const cRecording *Recording = Recordings->GetByName(cReplayControl::LastReplayed())) return new cMenuRecording(Recording, false); return NULL; } const cRecording *cReplayControl::GetRecording(void) { - if (const cRecording *Recording = Recordings.GetByName(LastReplayed())) + LOCK_RECORDINGS_READ; + if (const cRecording *Recording = Recordings->GetByName(LastReplayed())) return Recording; return NULL; } |