Implemented 'channel grouping'

This commit is contained in:
Klaus Schmidinger 2000-09-09 14:57:43 +02:00
parent d4eb96f725
commit c00d4ea326
17 changed files with 437 additions and 168 deletions

View File

@ -10,6 +10,8 @@ Carsten Koch <Carsten.Koch@icem.de>
Plamen Ganev <pganev@com-it.net>
for fixing the frequency offset for Hotbird channels
for adding the 'xtvrc2vdr' tool (see Tools/xtvrc2vdr)
for adding the 'dvbrc2vdr' tool (see Tools/dvbrc2vdr)
for implementing "channel grouping"
Heino Goldenstein <heino.goldenstein@microplex.de>
for modifying scrolling through lists to make it page up and down
@ -19,3 +21,6 @@ Guido Fiala <gfiala@s.netic.de>
Robert Schneider <Robert.Schneider@lotus.com>
for implementing EIT support for displaying the current/next info
Niels de Carpentier <niels@casema.net>
for adding a workaround for a driver timing problem in cDvbApi::Cmd().

35
FORMATS Normal file
View File

@ -0,0 +1,35 @@
Video Disk Recorder File Formats
--------------------------------
* channels.conf
This file contains the channel setup.
It consists of two types of lines: "group delimiters" and "channel
definitions".
A "group delimiter" is a line starting with a ':' as the very first
character, followed by arbitrary text.
Example: ":First group"
A "channel definition" is a line with channel data, where the fields
are separated by ':' characters:
Example: "RTL:12188:h:1:27500:163:104:0:12003"
The fields in a channel definition have the following meaning (from left
to right):
- Name: the channel's name (if the name originally contains a ':' character
it has to be replaced by '|')
- Frequency in MHz (as an integer)
- Polarization (one of 'h', 'H', 'v', 'V')
- Diseqc number
- Symbol rate
- Video PID
- Audio PID
- Conditional Access (0 = Free To Air, 1 = can be decrypted by the first
DVB card, 2 = can be decrypted by the second DVB card)
- Program Number
* timers.conf
TODO

20
HISTORY
View File

@ -138,3 +138,23 @@ Video Disk Recorder Revision History
done for some of the channels in the default 'channels.conf'. Some other
parameters in the default 'channels.conf' have also been updated, so please
make sure your timers still use the correct channels!
2000-09-09: Version 0.63
- Workaround for a driver timing problem in cDvbApi::Cmd(), which sometimes caused
the OSD to no longer be displayed (thanks to Niels de Carpentier).
- Added the '-m486' option to the compiler call.
- If a channel name contains a colon (':') it is now replaced with a '|' in
channels.conf.
- Not everybody appears to like the "page scrolling" mechanism introduced by
Heino Goldenstein in version 0.61, so the Makefile now reacts on NO_PAGE_SCROLL=1
to suppress that.
- The new 'dvbrc2vdr' tool (thanks to Plamen Ganev!) can be used to convert
'dvbrc' channel files into 'vdr' format.
- Channels can now be "grouped" (thanks to Plamen Ganev!). See MANUAL for details.
There is currently no mechanism to define and maintain "Channel groups" via
the menu, so you'll have to insert "Channel group" control lines into your
'channels.conf' file manually (for example with a text editor).
XXX additional fields for 'preferred' etc???
- Started a new file named FORMATS with a description of the various file
formats used by VDR.

16
MANUAL
View File

@ -12,8 +12,8 @@ Video Disk Recorder User's Manual
Up Ch up Crsr up Crsr up Crsr up Crsr up Crsr up Play
Down Ch down Crsr down Crsr down Crsr down Crsr down Crsr down Pause
Left - - - Disable Decrement - Search back
Right - - - Enable Increment - Search forward
Left Prev group - - Disable Decrement - Search back
Right Next group - - Enable Increment - Search forward
Ok Ch display Select Switch Edit Accept Play Progress disp.
Menu Menu on Menu off Menu off Menu off Menu off Menu off Menu on
Back - Menu off Main menu Main menu Discard Main menu -
@ -72,6 +72,18 @@ Video Disk Recorder User's Manual
To bring up the channel display without switching channels you can press
the "Ok" button.
* Switching through channel groups
If the 'channels.conf' file contains "group separators" you can switch
through these groups by pressing the "Left" and "Right" key while no
menu is being displayed. The channel display will show the name of the
group, and if you press the "Ok" button while the group name is being
displayed, you will switch to the first channel of that group.
Channel groups can be whatever you decide them to be. You can either
group your channels by "Bouquet", by language, genre or whatever your
preferences may be.
* Instant Recording
You can start recording the current channel by pressing the "Red" button

View File

@ -4,7 +4,7 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
# $Id: Makefile 1.7 2000/09/03 09:26:24 kls Exp $
# $Id: Makefile 1.8 2000/09/03 15:38:18 kls Exp $
DVBDIR = ../DVB
@ -21,8 +21,12 @@ ifdef DEBUG_OSD
DEFINES += -DDEBUG_OSD
endif
ifdef NO_PAGE_SCROLL
DEFINES += -DNO_PAGE_SCROLL
endif
%.o: %.c
g++ -g -O2 -Wall -c $(DEFINES) $(INCLUDES) $<
g++ -g -O2 -Wall -m486 -c $(DEFINES) $(INCLUDES) $<
all: vdr

152
config.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: config.c 1.18 2000/09/03 09:20:22 kls Exp $
* $Id: config.c 1.19 2000/09/09 14:50:58 kls Exp $
*/
#include "config.h"
@ -196,11 +196,23 @@ cChannel::cChannel(const cChannel *Channel)
apid = Channel ? Channel->apid : 256;
ca = Channel ? Channel->ca : 0;
pnr = Channel ? Channel->pnr : 0;
preferred = Channel ? Channel->preferred : 0;
groupSep = Channel ? Channel->groupSep : false;
}
const char *cChannel::ToText(cChannel *Channel)
{
asprintf(&buffer, "%s:%d:%c:%d:%d:%d:%d:%d:%d\n", Channel->name, Channel->frequency, Channel->polarization, Channel->diseqc, Channel->srate, Channel->vpid, Channel->apid, Channel->ca, Channel->pnr);
char buf[MaxChannelName * 2];
char *s = Channel->name;
if (strchr(s, ':')) {
s = strcpy(buf, s);
strreplace(s, ':', '|');
}
delete buffer;
if (Channel->groupSep)
asprintf(&buffer, ":%s\n", s);
else
asprintf(&buffer, "%s:%d:%c:%d:%d:%d:%d:%d:%d:%d\n", s, Channel->frequency, Channel->polarization, Channel->diseqc, Channel->srate, Channel->vpid, Channel->apid, Channel->ca, Channel->pnr, Channel->preferred);
return buffer;
}
@ -212,13 +224,32 @@ const char *cChannel::ToText(void)
bool cChannel::Parse(const char *s)
{
char *buffer = NULL;
if (9 == sscanf(s, "%a[^:]:%d:%c:%d:%d:%d:%d:%d:%d", &buffer, &frequency, &polarization, &diseqc, &srate, &vpid, &apid, &ca, &pnr)) {
strncpy(name, buffer, MaxChannelName - 1);
name[strlen(buffer)] = 0;
delete buffer;
return true;
if (*s == ':') {
if (*++s) {
strn0cpy(name, s, MaxChannelName);
name[strlen(name) - 1] = 0; // strip the '\n'
groupSep = true;
}
else
return false;
}
return false;
else {
groupSep = false;
int fields = sscanf(s, "%a[^:]:%d:%c:%d:%d:%d:%d:%d:%d:%d", &buffer, &frequency, &polarization, &diseqc, &srate, &vpid, &apid, &ca, &pnr, &preferred);
#define VER062_FIELDS 9
#define VER063_FIELDS 10
if (fields == VER062_FIELDS || fields == VER063_FIELDS) {
strn0cpy(name, buffer, MaxChannelName);
delete buffer;
if (fields == VER062_FIELDS) {
preferred = 0;
}
}
else
return false;
}
strreplace(name, '|', ':');
return true;
}
bool cChannel::Save(FILE *f)
@ -230,9 +261,9 @@ bool cChannel::Switch(cDvbApi *DvbApi)
{
if (!DvbApi)
DvbApi = cDvbApi::PrimaryDvbApi;
if (!DvbApi->Recording()) {
isyslog(LOG_INFO, "switching to channel %d", Index() + 1);
CurrentChannel = Index();
if (!DvbApi->Recording() && !groupSep) {
isyslog(LOG_INFO, "switching to channel %d", number);
CurrentChannel = number;
for (int i = 3; i--;) {
if (DvbApi->SetChannel(frequency, polarization, diseqc, srate, vpid, apid, ca, pnr)) {
EIT.SetProgramNumber(pnr);
@ -242,22 +273,10 @@ bool cChannel::Switch(cDvbApi *DvbApi)
}
return false;
}
Interface.Info("Channel locked (recording)!");
Interface.Info(DvbApi->Recording() ? "Channel locked (recording)!" : name);
return false;
}
bool cChannel::SwitchTo(int i, cDvbApi *DvbApi)
{
cChannel *channel = Channels.Get(i);
return channel && channel->Switch(DvbApi);
}
const char *cChannel::GetChannelName(int i)
{
cChannel *channel = Channels.Get(i);
return channel ? channel->name : NULL;
}
// -- cTimer -----------------------------------------------------------------
char *cTimer::buffer = NULL;
@ -267,7 +286,8 @@ cTimer::cTimer(bool Instant)
startTime = stopTime = 0;
recording = false;
active = Instant;
channel = CurrentChannel + 1;
cChannel *ch = Channels.GetByNumber(CurrentChannel);
channel = ch ? ch->number : 0;
time_t t = time(NULL);
struct tm *now = localtime(&t);
day = now->tm_mday;
@ -280,8 +300,8 @@ cTimer::cTimer(bool Instant)
lifetime = 99;
*file = 0;
summary = NULL;
if (Instant)
snprintf(file, sizeof(file), "@%s", cChannel::GetChannelName(CurrentChannel));
if (Instant && ch)
snprintf(file, sizeof(file), "@%s", ch->name);
}
cTimer::~cTimer()
@ -299,6 +319,7 @@ cTimer& cTimer::operator= (const cTimer &Timer)
const char *cTimer::ToText(cTimer *Timer)
{
delete buffer;
asprintf(&buffer, "%d:%d:%s:%04d:%04d:%d:%d:%s:%s\n", Timer->active, Timer->channel, PrintDay(Timer->day), Timer->start, Timer->stop, Timer->priority, Timer->lifetime, Timer->file, Timer->summary ? Timer->summary : "");
return buffer;
}
@ -368,11 +389,7 @@ bool cTimer::Parse(const char *s)
if (8 <= sscanf(s, "%d:%d:%a[^:]:%d:%d:%d:%d:%a[^:\n]:%a[^\n]", &active, &channel, &buffer1, &start, &stop, &priority, &lifetime, &buffer2, &summary)) {
//TODO add more plausibility checks
day = ParseDay(buffer1);
int l = strlen(buffer2);
if (l >= MaxFileName)
l = MaxFileName - 1;
strncpy(file, buffer2, l);
file[l] = 0;
strn0cpy(file, buffer2, MaxFileName);
delete buffer1;
delete buffer2;
return day != 0;
@ -470,10 +487,79 @@ cKeys Keys;
// -- cChannels --------------------------------------------------------------
int CurrentChannel = 0;
int CurrentChannel = 1;
int CurrentGroup = -1;
cChannels Channels;
bool cChannels::Load(const char *FileName)
{
if (cConfig<cChannel>::Load(FileName)) {
ReNumber();
return true;
}
return false;
}
int cChannels::GetNextGroup(int Idx)
{
cChannel *channel = Get(++Idx);
while (channel && !channel->groupSep)
channel = Get(++Idx);
return channel ? Idx : -1;
}
int cChannels::GetPrevGroup(int Idx)
{
cChannel *channel = Get(--Idx);
while (channel && !channel->groupSep)
channel = Get(--Idx);
return channel ? Idx : -1;
}
int cChannels::GetNextNormal(int Idx)
{
cChannel *channel = Get(++Idx);
while (channel && channel->groupSep)
channel = Get(++Idx);
return channel ? Idx : -1;
}
void cChannels::ReNumber( void )
{
int Number = 0;
cChannel *ch = (cChannel *)First();
while (ch) {
if (!ch->groupSep)
ch->number = ++Number;
ch = (cChannel *)ch->Next();
}
maxNumber = Number;
}
cChannel *cChannels::GetByNumber(int Number)
{
cChannel *channel = (cChannel *)First();
while (channel) {
if (channel->number == Number)
return channel;
channel = (cChannel *)channel->Next();
}
return NULL;
}
bool cChannels::SwitchTo(int Number, cDvbApi *DvbApi)
{
cChannel *channel = GetByNumber(Number);
return channel && channel->Switch(DvbApi);
}
const char *cChannels::GetChannelNameByNumber(int Number)
{
cChannel *channel = GetByNumber(Number);
return channel ? channel->name : NULL;
}
// -- cTimers ----------------------------------------------------------------
cTimers Timers;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: config.h 1.15 2000/09/03 09:37:30 kls Exp $
* $Id: config.h 1.16 2000/09/09 14:21:35 kls Exp $
*/
#ifndef __CONFIG_H
@ -17,7 +17,7 @@
#include "dvbapi.h"
#include "tools.h"
#define VDRVERSION "0.62"
#define VDRVERSION "0.63"
#define MaxBuffer 10000
@ -75,14 +75,15 @@ public:
int apid;
int ca;
int pnr;
int preferred; //TODO implement "preferred channel" mechanism
int number; // Sequence number assigned on load
bool groupSep;
cChannel(void);
cChannel(const cChannel *Channel);
const char *ToText(void);
bool Parse(const char *s);
bool Save(FILE *f);
bool Switch(cDvbApi *DvbApi = NULL);
static bool SwitchTo(int i, cDvbApi *DvbApi = NULL);
static const char *GetChannelName(int i);
};
class cTimer : public cListObject {
@ -130,7 +131,7 @@ private:
cList<T>::Clear();
}
public:
bool Load(const char *FileName)
virtual bool Load(const char *FileName)
{
isyslog(LOG_INFO, "loading %s", FileName);
bool result = true;
@ -182,14 +183,29 @@ public:
}
};
class cChannels : public cConfig<cChannel> {};
class cChannels : public cConfig<cChannel> {
protected:
int maxNumber;
public:
cChannels(void) { maxNumber = 0; }
virtual bool Load(const char *FileName);
int GetNextGroup(int Idx); // Get next channel group
int GetPrevGroup(int Idx); // Get previous channel group
int GetNextNormal(int Idx); // Get next normal channel (not group)
void ReNumber(void); // Recalculate 'number' based on channel type
cChannel *GetByNumber(int Number);
const char *GetChannelNameByNumber(int Number);
bool SwitchTo(int Number, cDvbApi *DvbApi = NULL);
int MaxNumber(void) { return maxNumber; }
};
class cTimers : public cConfig<cTimer> {
public:
cTimer *GetTimer(cTimer *Timer);
};
extern int CurrentChannel;
extern int CurrentGroup;
extern cChannels Channels;
extern cTimers Timers;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbapi.c 1.22 2000/08/06 14:06:14 kls Exp $
* $Id: dvbapi.c 1.23 2000/09/09 12:13:55 kls Exp $
*/
#include "dvbapi.h"
@ -1199,6 +1199,10 @@ void cDvbApi::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, co
dc.y1 = y1;
dc.data = (void *)data;
ioctl(videoDev, VIDIOCSOSDCOMMAND, &dc);
usleep(10); // XXX Workaround for a driver bug (cInterface::DisplayChannel() displayed texts at wrong places
// XXX and sometimes the OSD was no longer displayed).
// XXX Increase the value if the problem still persists on your particular system.
// TODO Check if this is still necessary with driver versions after 0.6.
}
}
#endif

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: interface.c 1.11 2000/09/03 10:17:21 kls Exp $
* $Id: interface.c 1.12 2000/09/09 14:17:48 kls Exp $
*/
#include "interface.h"
@ -72,10 +72,10 @@ eKeys cInterface::GetKey(bool Wait)
eKeys cInterface::Wait(int Seconds, bool KeepChar)
{
int t0 = time_ms();
int t0 = time_ms() + Seconds * 1000;
eKeys Key = kNone;
while (time_ms() - t0 < Seconds * 1000) {
while (time_ms() < t0) {
Key = GetKey();
if (Key != kNone)
break;
@ -112,11 +112,9 @@ void cInterface::Write(int x, int y, const char *s, eDvbColor FgColor, eDvbColor
cDvbApi::PrimaryDvbApi->Text(x, y, s, FgColor, BgColor);
}
void cInterface::WriteText(int x, int y, const char *s, bool Current)
void cInterface::WriteText(int x, int y, const char *s, eDvbColor FgColor, eDvbColor BgColor)
{
if (open) {
eDvbColor FgColor = Current ? clrBlack : clrWhite;
eDvbColor BgColor = Current ? clrCyan : clrBackground;
ClearEol(x, y, BgColor);
int col = 0;
for (;;) {
@ -315,36 +313,42 @@ void cInterface::LearnKeys(void)
}
}
void cInterface::DisplayChannel(int Number, const char *Name)
eKeys cInterface::DisplayChannel(int Number, const char *Name)
{
RcIo.Number(Number);
// Number = 0 is used for channel group display and no EIT
if (Number)
RcIo.Number(Number);
if (Name && !Recording()) {
Open(MenuColumns, EIT.IsValid() ? 5 : 1);
char buffer[MenuColumns + 1];
snprintf(buffer, sizeof(buffer), "%d %s", Number, Name ? Name : "");
//XXX Maybe show only those lines that have actual information???
Open(MenuColumns, Number && EIT.IsValid() ? 5 : 1);
int BufSize = MenuColumns + 1;
char buffer[BufSize];
if (Number)
snprintf(buffer, BufSize, "%d %s", Number, Name ? Name : "");
else
snprintf(buffer, BufSize, "%s", Name ? Name : "");
Write(0, 0, buffer);
time_t t = time(NULL);
struct tm *now = localtime(&t);
snprintf(buffer, sizeof(buffer), "%02d:%02d", now->tm_hour, now->tm_min);
snprintf(buffer, BufSize, "%02d:%02d", now->tm_hour, now->tm_min);
Write(-5, 0, buffer);
if (EIT.IsValid()) {
const int t = 7;
if (Number && EIT.IsValid()) {
const int t = 6;
int w = MenuColumns - t;
Write(0, 1, EIT.GetRunningTime(), clrYellow, clrBackground);
snprintf(buffer, sizeof(buffer), "%.*s", w, EIT.GetRunningTitle());
Write(t, 1, buffer, clrCyan, clrBackground);
snprintf(buffer, sizeof(buffer), "%.*s", w, EIT.GetRunningSubtitle());
Write(t, 2, buffer, clrCyan, clrBackground);
snprintf(buffer, BufSize, "%.*s", w, EIT.GetRunningTitle()); Write(t, 1, buffer, clrCyan, clrBackground);
snprintf(buffer, BufSize, "%.*s", w, EIT.GetRunningSubtitle()); Write(t, 2, buffer, clrCyan, clrBackground);
Write(0, 3, EIT.GetNextTime(), clrYellow, clrBackground);
snprintf(buffer, sizeof(buffer), "%.*s", w, EIT.GetNextTitle());
Write(t, 3, buffer, clrCyan, clrBackground);
snprintf(buffer, sizeof(buffer), "%.*s", w, EIT.GetNextSubtitle());
Write(t, 4, buffer, clrCyan, clrBackground);
snprintf(buffer, BufSize, "%.*s", w, EIT.GetNextTitle()); Write(t, 3, buffer, clrCyan, clrBackground);
snprintf(buffer, BufSize, "%.*s", w, EIT.GetNextSubtitle()); Write(t, 4, buffer, clrCyan, clrBackground);
}
if (Wait(5, true) == kOk)
eKeys Key = Wait(5, true);
if (Key == kOk)
GetKey();
Close();
return Key;
}
return kNone;
}
void cInterface::DisplayRecording(int Index, bool On)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: interface.h 1.9 2000/05/06 15:39:23 kls Exp $
* $Id: interface.h 1.10 2000/09/03 14:34:24 kls Exp $
*/
#ifndef __INTERFACE_H
@ -34,7 +34,7 @@ public:
void ClearEol(int x, int y, eDvbColor Color = clrBackground);
void SetCols(int *c);
void Write(int x, int y, const char *s, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground);
void WriteText(int x, int y, const char *s, bool Current = false);
void WriteText(int x, int y, const char *s, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBlack);
void Title(const char *s);
void Status(const char *s, eDvbColor FgColor = clrBlack, eDvbColor BgColor = clrCyan);
void Info(const char *s);
@ -42,7 +42,7 @@ public:
bool Confirm(const char *s);
void Help(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL);
void LearnKeys(void);
void DisplayChannel(int Number, const char *Name = NULL);
eKeys DisplayChannel(int Number, const char *Name = NULL);
void DisplayRecording(int Index, bool On);
bool Recording(void);
};

59
menu.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: menu.c 1.22 2000/08/06 07:02:52 kls Exp $
* $Id: menu.c 1.23 2000/09/09 14:43:37 kls Exp $
*/
#include "menu.h"
@ -145,7 +145,7 @@ public:
};
cMenuEditChanItem::cMenuEditChanItem(const char *Name, int *Value)
:cMenuEditIntItem(Name, Value, 1, Channels.Count())
:cMenuEditIntItem(Name, Value, 1, Channels.MaxNumber())
{
Set();
}
@ -153,7 +153,7 @@ cMenuEditChanItem::cMenuEditChanItem(const char *Name, int *Value)
void cMenuEditChanItem::Set(void)
{
char buf[255];
cChannel *channel = Channels.Get(*value - 1);
cChannel *channel = Channels.GetByNumber(*value);
if (channel)
snprintf(buf, sizeof(buf), "%d %s", *value, channel->name);
else
@ -513,6 +513,7 @@ cMenuEditChannel::cMenuEditChannel(int Index)
Add(new cMenuEditIntItem( "Apid", &data.apid, 0, 10000)); //TODO exact limits???
Add(new cMenuEditIntItem( "CA", &data.ca, 0, cDvbApi::NumDvbApis));
Add(new cMenuEditIntItem( "Pnr", &data.pnr, 0));
Add(new cMenuEditIntItem( "Preferred", &data.preferred, 0, 1)); //TODO implement "preferred channel" mechanism
}
}
@ -547,13 +548,18 @@ cMenuChannelItem::cMenuChannelItem(int Index, cChannel *Channel)
{
index = Index;
channel = Channel;
if (channel->groupSep)
SetColor(clrWhite, clrBlue);
Set();
}
void cMenuChannelItem::Set(void)
{
char *buffer = NULL;
asprintf(&buffer, "%d\t%s", index + 1, channel->name); // user visible channel numbers start with '1'
if (!channel->groupSep)
asprintf(&buffer, "%d\t%s", channel->number, channel->name );
else
asprintf(&buffer, "\t%s", channel->name);
SetText(buffer, false);
}
@ -583,9 +589,10 @@ cMenuChannels::cMenuChannels(void)
//TODO
int i = 0;
cChannel *channel;
int curr = ((channel = Channels.GetByNumber(CurrentChannel)) != NULL) ? channel->Index() : -1;
while ((channel = Channels.Get(i)) != NULL) {
Add(new cMenuChannelItem(i, channel), i == CurrentChannel);
Add(new cMenuChannelItem(i, channel), i == curr);
i++;
}
SetHelp("Edit", "New", "Delete", "Mark");
@ -613,9 +620,10 @@ eOSState cMenuChannels::New(void)
return osContinue;
cChannel *channel = new cChannel(Channels.Get(Current()));
Channels.Add(channel);
Channels.ReNumber();
Add(new cMenuChannelItem(channel->Index()/*XXX*/, channel), true);
Channels.Save();
isyslog(LOG_INFO, "channel %d added", channel->Index() + 1);
isyslog(LOG_INFO, "channel %d added", channel->number);
return AddSubMenu(new cMenuEditChannel(Current()));
}
@ -623,28 +631,30 @@ eOSState cMenuChannels::Del(void)
{
if (Count() > 0) {
int Index = Current();
cChannel *channel = Channels.Get(Index);
int DeletedChannel = channel->number;
// Check if there is a timer using this channel:
for (cTimer *ti = Timers.First(); ti; ti = (cTimer *)ti->Next()) {
if (ti->channel == Index + 1) {
if (ti->channel == DeletedChannel) {
Interface.Error("Channel is being used by a timer!");
return osContinue;
}
}
if (Interface.Confirm("Delete Channel?")) {
// Move and renumber the channels:
Channels.Del(Channels.Get(Index));
Channels.Del(channel);
Channels.ReNumber();
cOsdMenu::Del(Index);
int i = 0;
for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next())
ci->SetIndex(i++);
Channels.Save();
isyslog(LOG_INFO, "channel %d deleted", Index + 1);
isyslog(LOG_INFO, "channel %d deleted", DeletedChannel);
// Fix the timers:
bool TimersModified = false;
Index++; // user visible channel numbers start with '1'
for (cTimer *ti = Timers.First(); ti; ti = (cTimer *)ti->Next()) {
int OldChannel = ti->channel;
if (ti->channel > Index)
if (ti->channel > DeletedChannel)
ti->channel--;
if (ti->channel != OldChannel) {
TimersModified = true;
@ -661,25 +671,28 @@ eOSState cMenuChannels::Del(void)
void cMenuChannels::Move(int From, int To)
{
int FromNumber = Channels.Get(From)->number;
int ToNumber = Channels.Get(To)->number;
// Move and renumber the channels:
Channels.Move(From, To);
Channels.ReNumber();
cOsdMenu::Move(From, To);
int i = 0;
for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next())
ci->SetIndex(i++);
Channels.Save();
isyslog(LOG_INFO, "channel %d moved to %d", From + 1, To + 1);
isyslog(LOG_INFO, "channel %d moved to %d", FromNumber, ToNumber);
// Fix the timers:
bool TimersModified = false;
From++; // user visible channel numbers start with '1'
To++;
for (cTimer *ti = Timers.First(); ti; ti = (cTimer *)ti->Next()) {
int OldChannel = ti->channel;
if (ti->channel == From)
ti->channel = To;
else if (ti->channel > From && ti->channel <= To)
if (ti->channel == FromNumber)
ti->channel = ToNumber;
else if (ti->channel > FromNumber && ti->channel <= ToNumber)
ti->channel--;
else if (ti->channel < From && ti->channel >= To)
else if (ti->channel < FromNumber && ti->channel >= ToNumber)
ti->channel++;
if (ti->channel != OldChannel) {
TimersModified = true;
@ -791,7 +804,7 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key)
if (state == osUnknown) {
if (Key == kOk) {
if (!*data.file)
strcpy(data.file, cChannel::GetChannelName(data.channel - 1));
strcpy(data.file, Channels.GetChannelNameByNumber(data.channel));
if (timer && memcmp(timer, &data, sizeof(data)) != 0) {
*timer = data;
Timers.Save();
@ -1125,10 +1138,10 @@ cRecordControl::cRecordControl(cDvbApi *DvbApi, cTimer *Timer)
timer = new cTimer(true);
Timers.Add(timer);
Timers.Save();
asprintf(&instantId, cDvbApi::NumDvbApis > 1 ? "%s on %d" : "%s", cChannel::GetChannelName(timer->channel - 1), dvbApi->Index() + 1);
asprintf(&instantId, cDvbApi::NumDvbApis > 1 ? "%s on %d" : "%s", Channels.GetChannelNameByNumber(timer->channel), dvbApi->Index() + 1);
}
timer->SetRecording(true);
cChannel::SwitchTo(timer->channel - 1, dvbApi);
Channels.SwitchTo(timer->channel, dvbApi);
cRecording Recording(timer);
if (dvbApi->StartRecord(Recording.FileName()))
Recording.WriteSummary();
@ -1172,8 +1185,8 @@ cRecordControl *cRecordControls::RecordControls[MAXDVBAPI] = { NULL };
bool cRecordControls::Start(cTimer *Timer)
{
int ch = Timer ? Timer->channel - 1 : CurrentChannel;
cChannel *channel = Channels.Get(ch);
int ch = Timer ? Timer->channel : CurrentChannel;
cChannel *channel = Channels.GetByNumber(ch);
if (channel) {
cDvbApi *dvbApi = cDvbApi::GetDvbApi(channel->ca);
@ -1186,10 +1199,10 @@ bool cRecordControls::Start(cTimer *Timer)
}
}
else
esyslog(LOG_ERR, "ERROR: no free DVB device to record channel %d!", ch + 1);
esyslog(LOG_ERR, "ERROR: no free DVB device to record channel %d!", ch);
}
else
esyslog(LOG_ERR, "ERROR: channel %d not defined!", ch + 1);
esyslog(LOG_ERR, "ERROR: channel %d not defined!", ch);
return false;
}

100
osd.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: osd.c 1.5 2000/07/26 17:35:09 kls Exp $
* $Id: osd.c 1.6 2000/09/09 14:28:57 kls Exp $
*/
#include "osd.h"
@ -19,6 +19,9 @@ cOsdItem::cOsdItem(eOSState State)
offset = -1;
state = State;
fresh = false;
userColor = false;
fgColor = clrWhite;
bgColor = clrBackground;
}
cOsdItem::cOsdItem(char *Text, eOSState State)
@ -27,6 +30,9 @@ cOsdItem::cOsdItem(char *Text, eOSState State)
offset = -1;
state = State;
fresh = false;
userColor = false;
fgColor = clrWhite;
bgColor = clrBackground;
SetText(Text);
}
@ -41,15 +47,24 @@ void cOsdItem::SetText(const char *Text, bool Copy)
text = Copy ? strdup(Text) : Text;
}
void cOsdItem::Display(int Offset, bool Current)
void cOsdItem::SetColor(eDvbColor FgColor, eDvbColor BgColor)
{
userColor = true;
fgColor = FgColor;
bgColor = BgColor;
}
void cOsdItem::Display(int Offset, eDvbColor FgColor, eDvbColor BgColor)
{
if (Offset < 0) {
FgColor = clrBlack;
BgColor = clrCyan;
}
fresh |= Offset >= 0;
Current |= Offset < 0;
if (Offset >= 0)
offset = Offset;
//TODO current if Offset == -1 ???
if (offset >= 0)
Interface.WriteText(0, offset + 2, text, Current);
Interface.WriteText(0, offset + 2, text, userColor ? fgColor : FgColor, userColor ? bgColor : BgColor);
}
eOSState cOsdItem::ProcessKey(eKeys Key)
@ -100,6 +115,10 @@ void cOsdMenu::SetHelp(const char *Red, const char *Green, const char *Yellow, c
helpGreen = Green;
helpYellow = Yellow;
helpBlue = Blue;
if (visible)
Display();
//XXX Interface.Help(helpRed, helpGreen, helpYellow, helpBlue);
//XXX must clear unused button areas!
}
void cOsdMenu::Del(int Index)
@ -140,7 +159,7 @@ void cOsdMenu::Display(void)
for (int i = first; i < count; i++) {
cOsdItem *item = Get(i);
if (item)
item->Display(i - first, i == current);
item->Display(i - first, i == current ? clrBlack : clrWhite, i == current ? clrCyan : clrBackground);
if (++n == MAXOSDITEMS) //TODO get this from Interface!!!
break;
}
@ -159,49 +178,64 @@ void cOsdMenu::DisplayCurrent(bool Current)
{
cOsdItem *item = Get(current);
if (item)
item->Display(current - first, Current);
item->Display(current - first, Current ? clrBlack : clrWhite, Current ? clrCyan : clrBackground);
}
bool cOsdMenu::SpecialItem(int idx)
{
cOsdItem *item = Get(idx);
return item && item->HasUserColor();
}
void cOsdMenu::CursorUp(void)
{
if (current > 0) {
DisplayCurrent(false);
if (current == first) {
first -= MAXOSDITEMS;
if (first < 0)
first = 0;
if (current - MAXOSDITEMS > 0)
current -= MAXOSDITEMS;
else
current--;
int tmpCurrent = current;
while (--tmpCurrent >= 0 && SpecialItem(tmpCurrent));
if (tmpCurrent < 0)
return;
if (tmpCurrent >= first)
DisplayCurrent(false);
current = tmpCurrent;
if (current < first) {
first = first > MAXOSDITEMS - 1 ? first - (MAXOSDITEMS - 1) : 0;
#ifndef NO_PAGE_SCROLL
current = SpecialItem(first) ? first + 1 : first;
#endif
Display();
}
else {
current--;
else
DisplayCurrent(true);
}
}
}
void cOsdMenu::CursorDown(void)
{
int count = Count();
if (current < count - 1) {
DisplayCurrent(false);
if (current == first + MAXOSDITEMS - 1) {
first += MAXOSDITEMS;
if (first > count - MAXOSDITEMS)
first = count - MAXOSDITEMS;
if (current + MAXOSDITEMS < count)
current += MAXOSDITEMS;
else
current++;
int last = Count() - 1;
int lastOnScreen = first + MAXOSDITEMS - 1;
if (current < last) {
int tmpCurrent = current;
while (++tmpCurrent <= last && SpecialItem(tmpCurrent));
if (tmpCurrent > last)
return;
if (tmpCurrent <= lastOnScreen)
DisplayCurrent(false);
current = tmpCurrent;
if (current > lastOnScreen) {
first += MAXOSDITEMS - 1;
lastOnScreen = first + MAXOSDITEMS - 1;
if (lastOnScreen > last) {
first = last - (MAXOSDITEMS - 1);
lastOnScreen = last;
}
#ifndef NO_PAGE_SCROLL
current = SpecialItem(lastOnScreen) ? lastOnScreen - 1 : lastOnScreen;
#endif
Display();
}
else {
current++;
else
DisplayCurrent(true);
}
}
}

9
osd.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: osd.h 1.9 2000/05/27 15:35:41 kls Exp $
* $Id: osd.h 1.10 2000/09/03 14:50:22 kls Exp $
*/
#ifndef __OSD_H
@ -37,13 +37,17 @@ private:
eOSState state;
protected:
bool fresh;
bool userColor;
eDvbColor fgColor, bgColor;
public:
cOsdItem(eOSState State = osUnknown);
cOsdItem(char *Text, eOSState State = osUnknown);
virtual ~cOsdItem();
bool HasUserColor(void) { return userColor; }
void SetText(const char *Text, bool Copy = true);
void SetColor(eDvbColor FgColor, eDvbColor BgColor = clrBackground);
const char *Text(void) { return text; }
void Display(int Offset = -1, bool Current = false);
void Display(int Offset = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground);
virtual void Set(void) {}
virtual eOSState ProcessKey(eKeys Key);
};
@ -68,6 +72,7 @@ private:
const char *status;
protected:
bool visible;
bool SpecialItem(int idx);
void RefreshCurrent(void);
void DisplayCurrent(bool Current);
void CursorUp(void);

57
svdrp.c
View File

@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection.
*
* $Id: svdrp.c 1.5 2000/08/26 12:51:51 kls Exp $
* $Id: svdrp.c 1.6 2000/09/09 10:51:21 kls Exp $
*/
#define _GNU_SOURCE
@ -279,24 +279,24 @@ void cSVDRP::CmdChan(const char *Option)
if (*Option) {
int n = -1;
if (isnumber(Option)) {
int o = strtol(Option, NULL, 10) - 1;
if (o >= 0 && o < Channels.Count())
int o = strtol(Option, NULL, 10);
if (o >= 1 && o <= Channels.MaxNumber())
n = o;
}
else if (strcmp(Option, "-") == 0) {
n = CurrentChannel;
if (CurrentChannel > 0)
if (CurrentChannel > 1)
n--;
}
else if (strcmp(Option, "+") == 0) {
n = CurrentChannel;
if (CurrentChannel < Channels.Count() - 1)
if (CurrentChannel < Channels.MaxNumber())
n++;
}
else {
int i = 0;
int i = 1;
cChannel *channel;
while ((channel = Channels.Get(i)) != NULL) {
while ((channel = Channels.GetByNumber(i)) != NULL) {
if (strcasecmp(channel->name, Option) == 0) {
n = i;
break;
@ -312,10 +312,10 @@ void cSVDRP::CmdChan(const char *Option)
Reply(550, "Can't switch channel, interface is recording");
return;
}
cChannel *channel = Channels.Get(n);
cChannel *channel = Channels.GetByNumber(n);
if (channel) {
if (!channel->Switch()) {
Reply(554, "Error switching to channel \"%d\"", channel->Index() + 1);
Reply(554, "Error switching to channel \"%d\"", channel->number);
return;
}
}
@ -324,9 +324,9 @@ void cSVDRP::CmdChan(const char *Option)
return;
}
}
cChannel *channel = Channels.Get(CurrentChannel);
cChannel *channel = Channels.GetByNumber(CurrentChannel);
if (channel)
Reply(250, "%d %s", CurrentChannel + 1, channel->name);
Reply(250, "%d %s", CurrentChannel, channel->name);
else
Reply(550, "Unable to find channel \"%d\"", CurrentChannel);
}
@ -394,41 +394,41 @@ void cSVDRP::CmdLstc(const char *Option)
{
if (*Option) {
if (isnumber(Option)) {
cChannel *channel = Channels.Get(strtol(Option, NULL, 10) - 1);
cChannel *channel = Channels.GetByNumber(strtol(Option, NULL, 10));
if (channel)
Reply(250, "%d %s", channel->Index() + 1, channel->ToText());
Reply(250, "%d %s", channel->number, channel->ToText());
else
Reply(501, "Channel \"%s\" not defined", Option);
}
else {
int i = 0;
int i = 1;
cChannel *next = NULL;
while (i < Channels.Count()) {
cChannel *channel = Channels.Get(i);
while (i <= Channels.MaxNumber()) {
cChannel *channel = Channels.GetByNumber(i);
if (channel) {
if (strcasestr(channel->name, Option)) {
if (next)
Reply(-250, "%d %s", next->Index() + 1, next->ToText());
Reply(-250, "%d %s", next->number, next->ToText());
next = channel;
}
}
else {
Reply(501, "Channel \"%d\" not found", i + 1);
Reply(501, "Channel \"%d\" not found", i);
return;
}
i++;
}
if (next)
Reply(250, "%d %s", next->Index() + 1, next->ToText());
Reply(250, "%d %s", next->number, next->ToText());
}
}
else {
for (int i = 0; i < Channels.Count(); i++) {
cChannel *channel = Channels.Get(i);
for (int i = 1; i <= Channels.MaxNumber(); i++) {
cChannel *channel = Channels.GetByNumber(i);
if (channel)
Reply(i < Channels.Count() - 1 ? -250 : 250, "%d %s", channel->Index() + 1, channel->ToText());
Reply(i < Channels.MaxNumber() ? -250 : 250, "%d %s", channel->number, channel->ToText());
else
Reply(501, "Channel \"%d\" not found", i + 1);
Reply(501, "Channel \"%d\" not found", i);
}
}
}
@ -464,7 +464,7 @@ void cSVDRP::CmdModc(const char *Option)
int n = strtol(Option, &tail, 10);
if (tail && tail != Option) {
tail = skipspace(tail);
cChannel *channel = Channels.Get(n - 1);
cChannel *channel = Channels.GetByNumber(n);
if (channel) {
cChannel c = *channel;
if (!c.Parse(tail)) {
@ -473,8 +473,8 @@ void cSVDRP::CmdModc(const char *Option)
}
*channel = c;
Channels.Save();
isyslog(LOG_INFO, "channel %d modified", channel->Index() + 1);
Reply(250, "%d %s", channel->Index() + 1, channel->ToText());
isyslog(LOG_INFO, "channel %d modified", channel->number);
Reply(250, "%d %s", channel->number, channel->ToText());
}
else
Reply(501, "Channel \"%d\" not defined", n);
@ -537,9 +537,10 @@ void cSVDRP::CmdNewc(const char *Option)
cChannel *channel = new cChannel;
if (channel->Parse(Option)) {
Channels.Add(channel);
Channels.ReNumber();
Channels.Save();
isyslog(LOG_INFO, "channel %d added", channel->Index() + 1);
Reply(250, "%d %s", channel->Index() + 1, channel->ToText());
isyslog(LOG_INFO, "channel %d added", channel->number);
Reply(250, "%d %s", channel->number, channel->ToText());
}
else
Reply(501, "Error in channel settings");

12
tools.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: tools.c 1.13 2000/07/29 18:41:45 kls Exp $
* $Id: tools.c 1.14 2000/09/09 12:53:34 kls Exp $
*/
#define _GNU_SOURCE
@ -97,6 +97,14 @@ char *readline(FILE *f)
return NULL;
}
char *strn0cpy(char *dest, const char *src, size_t n)
{
char *s = dest;
for ( ; --n && (*dest = *src) != 0; dest++, src++) ;
*dest = 0;
return s;
}
char *strreplace(char *s, char c1, char c2)
{
char *p = s;
@ -418,6 +426,8 @@ void cListBase::Clear(void)
cListObject *cListBase::Get(int Index)
{
if (Index < 0)
return NULL;
cListObject *object = objects;
while (object && Index-- > 0)
object = object->Next();

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: tools.h 1.12 2000/07/29 10:56:00 kls Exp $
* $Id: tools.h 1.13 2000/09/09 12:53:10 kls Exp $
*/
#ifndef __TOOLS_H
@ -38,6 +38,7 @@ bool readint(int filedes, int &n);
int readstring(int filedes, char *buffer, int size, bool wait = false);
void purge(int filedes);
char *readline(FILE *f);
char *strn0cpy(char *dest, const char *src, size_t n);
char *strreplace(char *s, char c1, char c2);
char *skipspace(char *s);
int time_ms(void);

37
vdr.c
View File

@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/people/kls/vdr
*
* $Id: vdr.c 1.27 2000/07/29 19:01:57 kls Exp $
* $Id: vdr.c 1.28 2000/09/09 14:18:25 kls Exp $
*/
#include <getopt.h>
@ -53,6 +53,14 @@ void SignalHandler(int signum)
Interrupted = signum;
}
static eKeys ShowChannel(int Number, bool Group = false)
{
cChannel *channel = Group ? Channels.Get(Number) : Channels.GetByNumber(Number);
if (channel)
return Interface.DisplayChannel(channel->number, channel->name);
return kNone;
}
int main(int argc, char *argv[])
{
// Command line options:
@ -166,7 +174,7 @@ int main(int argc, char *argv[])
#endif
Interface.Init();
cChannel::SwitchTo(CurrentChannel);
Channels.SwitchTo(CurrentChannel);
// Signal handlers:
@ -185,16 +193,13 @@ int main(int argc, char *argv[])
while (!Interrupted) {
// Channel display:
if (CurrentChannel != LastChannel) {
if (!Menu) {
cChannel *channel = Channels.Get(CurrentChannel);
if (channel)
Interface.DisplayChannel(CurrentChannel + 1, channel->name);
}
if (!Menu)
ShowChannel(CurrentChannel);
LastChannel = CurrentChannel;
}
// Direct Channel Select (action):
if (dcNumber && time_ms() - dcTime > DIRECTCHANNELTIMEOUT) {
cChannel::SwitchTo(dcNumber - 1);
Channels.SwitchTo(dcNumber);
dcNumber = 0;
LastChannel = -1; // in case an invalid channel number was entered!
}
@ -246,11 +251,25 @@ int main(int argc, char *argv[])
}
}
break;
// Left/Right rotates trough channel groups:
case kLeft:
case kRight: if (!Interface.Recording()) {
int SaveGroup = CurrentGroup;
if (key == kRight)
CurrentGroup = Channels.GetNextGroup(CurrentGroup) ;
else
CurrentGroup = Channels.GetPrevGroup(CurrentGroup < 1 ? 1 : CurrentGroup);
if (CurrentGroup < 0)
CurrentGroup = SaveGroup;
if (ShowChannel(CurrentGroup, true) == kOk)
Channels.SwitchTo(Channels.Get(Channels.GetNextNormal(CurrentGroup))->number);
}
break;
// Up/Down Channel Select:
case kUp:
case kDown: if (!Interface.Recording()) {
int n = CurrentChannel + (key == kUp ? 1 : -1);
cChannel *channel = Channels.Get(n);
cChannel *channel = Channels.GetByNumber(n);
if (channel)
channel->Switch();
}