summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbball <bball@octorok.org>2009-01-24 10:19:38 -0800
committerbball <bball@octorok.org>2009-01-24 10:19:38 -0800
commitdefa3120d938f7af0d3cac46ca49c709e5422309 (patch)
treefcb316f1f9a5b1387598ec0b49d6288ceacdf297
downloadvdr-plugin-yaepghd-defa3120d938f7af0d3cac46ca49c709e5422309.tar.gz
vdr-plugin-yaepghd-defa3120d938f7af0d3cac46ca49c709e5422309.tar.bz2
- Initial commit.
-rw-r--r--COPYING340
-rw-r--r--HISTORY8
-rw-r--r--Makefile112
-rw-r--r--README53
-rw-r--r--i18n.c433
-rw-r--r--i18n.h16
-rw-r--r--po/.svn/entries28
-rw-r--r--po/.svn/format1
-rw-r--r--themes/.svn/entries43
-rw-r--r--themes/.svn/format1
-rw-r--r--themes/.svn/text-base/default.theme.svn-base44
-rw-r--r--themes/default.theme44
-rw-r--r--themes/default/.svn/entries41
-rw-r--r--themes/default/.svn/format1
-rw-r--r--themes/default/.svn/prop-base/bg.png.svn-base5
-rw-r--r--themes/default/.svn/text-base/bg.png.svn-basebin0 -> 8333 bytes
-rw-r--r--themes/default/bg.pngbin0 -> 8333 bytes
-rw-r--r--yaepghd.c3155
18 files changed, 4325 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..f90922e
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/HISTORY b/HISTORY
new file mode 100644
index 0000000..36efc59
--- /dev/null
+++ b/HISTORY
@@ -0,0 +1,8 @@
+VDR Plugin 'yaepghd' Revision History
+-------------------------------------
+
+2008-06-22: Version 0.0.1
+
+- Initial revision.
+- Rewrite of the yaepg plugin.
+- Support for themes.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1affdd3
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,112 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id$
+
+# The official name of this plugin.
+# This name will be used in the '-P...' option of VDR to load the plugin.
+# By default the main source file also carries this name.
+# IMPORTANT: the presence of this macro is important for the Make.config
+# file. So it must be defined, even if it is not used here!
+#
+PLUGIN = yaepghd
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g')
+
+### The C++ compiler and options:
+
+CXX ?= g++
+CXXFLAGS ?= -fPIC -g -O2 -Wall -Woverloaded-virtual -Wno-parentheses
+
+### The directory environment:
+
+VDRDIR = ../../..
+LIBDIR = ../../lib
+TMPDIR = /tmp
+
+### Allow user defined options to overwrite defaults:
+
+-include $(VDRDIR)/Make.config
+
+### The version number of VDR's plugin API (taken from VDR's "config.h"):
+
+APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h)
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### Includes and Defines (add further entries here):
+
+INCLUDES += -I$(VDRDIR)/include -I/usr/include/ImageMagick -I.
+
+LIBS = -lMagick -lMagick++ -L/usr/local/lib -lcurl
+
+DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).o i18n.o
+
+### The main target:
+
+all: libvdr-$(PLUGIN).so i18n
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+### Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Internationalization (I18N):
+
+PODIR = po
+LOCALEDIR = $(VDRDIR)/locale
+I18Npo = $(wildcard $(PODIR)/*.po)
+I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
+I18Npot = $(PODIR)/$(PLUGIN).pot
+
+%.mo: %.po
+ msgfmt -c -o $@ $<
+
+$(I18Npot): $(wildcard *.c)
+ xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='<see README>' -o $@ $^
+
+%.po: $(I18Npot)
+ msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
+ @touch $@
+
+$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
+ @mkdir -p $(dir $@)
+ cp $< $@
+
+.PHONY: i18n
+i18n: $(I18Nmsgs) $(I18Npot)
+
+### Targets:
+
+libvdr-$(PLUGIN).so: $(OBJS)
+ $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) -o $@
+ @cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
+
+dist: clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+ @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~ $(PODIR)/*.mo $(PODIR)/*.pot
diff --git a/README b/README
new file mode 100644
index 0000000..ff4b677
--- /dev/null
+++ b/README
@@ -0,0 +1,53 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: bball <bball950@yahoo.com>
+
+Project's homepage: http://projects.vdr-developer.org/projects/show/plg-yaepghd
+
+Latest version available at: URL
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+See the file COPYING for more information.
+
+Description:
+
+Yaepghd is a compelte rewrite of the original yaepg plugin. The
+original yaepg plugin was designed for FF cards and only used 4
+colors due to limitations on the amount of OSD memory on FF cards.
+Yaepghd requires output devices with an OSD capable of high color
+full screen grpahics (e.g. eHD or xine) and will _not_ work with FF
+cards (unless you have the 4MB mod).
+
+In the original yaepg the layout and sizes of all the components
+were hardcoded in the source code which made it difficult to
+customize. Yaepghd uses themes which allow a user to completely
+redfine the the way yaepghd looks.
+
+Themes must be placed in the $(CONFDIR)/plugins/yaepghd directory.
+Any fonts used by a theme must be added to the fontconfig cache.
+This can be done by either copying the fonts to one of your existing
+font directories or by adding the yaepghd theme directory to your
+font path. After this is done you must run fc-cache for the new
+font to be available.
+
+Keys:
+Guide
+Up/Down - Move the cursor up/down in the grid.
+Left/Right - Move the cursor left/right in the grid.
+Chan Up/Down - Page up/down within the grid.
+Ok - Tune to the selected channel and exit the plugin.
+Green - Scroll +12 hours in the grid.
+Red - Scroll -12 hours in the grid.
+Blue - Take a screenshot.
+Yellow - Tune to the selected channel.
+Back/Exit - Exit the plugin.
+0-9 - Perform direct channel change.
+
+Record Dialog
+Up/Down - Move the cursor between input boxes.
+Left/Right - Modify input box values.
+Ok - Add timer.
+Back/Exit - Cancel timer.
diff --git a/i18n.c b/i18n.c
new file mode 100644
index 0000000..9faa50c
--- /dev/null
+++ b/i18n.c
@@ -0,0 +1,433 @@
+/*
+ * i18n.c: Internationalization
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include "i18n.h"
+
+const tI18nPhrase Phrases[] = {
+ { "No Info",// English
+ "Keine Daten verfügbar.",// Deutsch
+ "",// Slovenski
+ "",// Italiano
+ "",// Nederlands
+ "",// Português
+ "",// Français
+ "",// Norsk
+ "",// suomi
+ "",// Polski
+ "",// Españo
+ "",// ÅëëçíéêÜ
+ "",// Svenska
+ "",// Romaneste
+ "",// Magyar
+ "",// Català
+#if VDRVERSNUM >= 10300
+ "" // ÀãááÚØÙ
+#endif
+ },
+ { "TV output format",
+ "TV Ausgabe",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Hide mainmenu entry",
+ "Hauptmenüeintrag verstecken",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Change channel automatically",
+ "Kanal automatisch wechseln",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Start",
+ "Anfang",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Stop",
+ "Halt",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Frequency",
+ "Frequenz",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Every",
+ "Jeden",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Mon",
+ "Mo",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Tue",
+ "Di",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Wed",
+ "Mi",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Thr",
+ "Do",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Fri",
+ "Fr",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Sat",
+ "Sa",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Sun",
+ "So",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Mon-Fri",
+ "Mo-Fr",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Sun-Sat",
+ "So-Sa",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Once",
+ "Sobald",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Timer added",
+ "Timer hinzugefügt",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Timer cancelled",
+ "Timer annullierte",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { "Time format",
+ "Zeit-Format",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+#if VDRVERSNUM >= 10300
+ "" // TODO
+#endif
+ },
+ { NULL }
+ };
diff --git a/i18n.h b/i18n.h
new file mode 100644
index 0000000..7837abc
--- /dev/null
+++ b/i18n.h
@@ -0,0 +1,16 @@
+/*
+ * i18n.h: Internationalization
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef _I18N__H
+#define _I18N__H
+
+#include <vdr/i18n.h>
+
+extern const tI18nPhrase Phrases[];
+
+#endif //_I18N__H
diff --git a/po/.svn/entries b/po/.svn/entries
new file mode 100644
index 0000000..e5df3b5
--- /dev/null
+++ b/po/.svn/entries
@@ -0,0 +1,28 @@
+8
+
+dir
+15
+svn+ssh://octorok.org/var/svn-repos/yaepghd/po
+svn+ssh://octorok.org/var/svn-repos/yaepghd
+
+
+
+2008-07-16T02:59:31.222533Z
+1
+bball
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+9aaac883-65b3-4621-aff0-d2a75fbc3057
+
diff --git a/po/.svn/format b/po/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/po/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/themes/.svn/entries b/themes/.svn/entries
new file mode 100644
index 0000000..ef8db33
--- /dev/null
+++ b/themes/.svn/entries
@@ -0,0 +1,43 @@
+8
+
+dir
+15
+svn+ssh://octorok.org/var/svn-repos/yaepghd/themes
+svn+ssh://octorok.org/var/svn-repos/yaepghd
+
+
+
+2008-08-28T01:07:19.002383Z
+14
+bball
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+9aaac883-65b3-4621-aff0-d2a75fbc3057
+
+default
+dir
+
+default.theme
+file
+
+
+
+
+2009-01-24T18:12:45.000000Z
+074544efc87897c6bd5f7647d14d0b87
+2008-08-28T01:07:19.002383Z
+14
+bball
+
diff --git a/themes/.svn/format b/themes/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/themes/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/themes/.svn/text-base/default.theme.svn-base b/themes/.svn/text-base/default.theme.svn-base
new file mode 100644
index 0000000..9bb9fad
--- /dev/null
+++ b/themes/.svn/text-base/default.theme.svn-base
@@ -0,0 +1,44 @@
+# Guide Widgets
+bgImage=default/bg.png
+gridEventFont="Accidental Presidency;20"
+gridChanFont="Accidental Presidency;20"
+gridTimeFont="Accidental Presidency;20"
+gridDateFont="Accidental Presidency;20"
+eventTitleFont="Accidental Presidency;26"
+eventTimeFont="Accidental Presidency;20"
+eventDescFont="Accidental Presidency;20"
+eventDateFont="Accidental Presidency;20"
+gridEventColor=FFFFFFFF
+gridSelFg=FF000000
+gridSelBg=FFF8AF31
+gridSepColor=FF113141
+gridChanColor=FFA4BACB
+gridTimeColor=FFA4BACB
+gridDateColor=FFA4BACB
+eventTitleColor=FF1B537C
+eventTimeColor=FF1B537C
+eventDescColor=FFFFFFFF
+eventDateColor=FF1B537C
+tlineBoxColor=FFFFFFFF
+gridEventGeom=154,288,516,234
+gridChanGeom=25,288,113,234
+gridTimeGeom=154,251,516,32
+gridDateGeom=25,251,113,32
+eventTitleGeom=144,20,306,70
+eventTimeGeom=144,96,306,37
+eventDescGeom=25,135,425,115
+eventDateGeom=25,96,117,37
+tlineLocGeom=154,283,516,3
+tlineBoxGeom=0,0,9,3
+vidWinGeom=456,32,196,130
+helpGeom=25,526,645,32
+gridHorizSpace=4
+gridNumChans=7
+leftArrowWidth=16
+rightArrowWidth=50
+textBorder=4
+textSpace=0
+
+# Record Dialog Widgets
+
+# Message Box Widgets
diff --git a/themes/default.theme b/themes/default.theme
new file mode 100644
index 0000000..9bb9fad
--- /dev/null
+++ b/themes/default.theme
@@ -0,0 +1,44 @@
+# Guide Widgets
+bgImage=default/bg.png
+gridEventFont="Accidental Presidency;20"
+gridChanFont="Accidental Presidency;20"
+gridTimeFont="Accidental Presidency;20"
+gridDateFont="Accidental Presidency;20"
+eventTitleFont="Accidental Presidency;26"
+eventTimeFont="Accidental Presidency;20"
+eventDescFont="Accidental Presidency;20"
+eventDateFont="Accidental Presidency;20"
+gridEventColor=FFFFFFFF
+gridSelFg=FF000000
+gridSelBg=FFF8AF31
+gridSepColor=FF113141
+gridChanColor=FFA4BACB
+gridTimeColor=FFA4BACB
+gridDateColor=FFA4BACB
+eventTitleColor=FF1B537C
+eventTimeColor=FF1B537C
+eventDescColor=FFFFFFFF
+eventDateColor=FF1B537C
+tlineBoxColor=FFFFFFFF
+gridEventGeom=154,288,516,234
+gridChanGeom=25,288,113,234
+gridTimeGeom=154,251,516,32
+gridDateGeom=25,251,113,32
+eventTitleGeom=144,20,306,70
+eventTimeGeom=144,96,306,37
+eventDescGeom=25,135,425,115
+eventDateGeom=25,96,117,37
+tlineLocGeom=154,283,516,3
+tlineBoxGeom=0,0,9,3
+vidWinGeom=456,32,196,130
+helpGeom=25,526,645,32
+gridHorizSpace=4
+gridNumChans=7
+leftArrowWidth=16
+rightArrowWidth=50
+textBorder=4
+textSpace=0
+
+# Record Dialog Widgets
+
+# Message Box Widgets
diff --git a/themes/default/.svn/entries b/themes/default/.svn/entries
new file mode 100644
index 0000000..1f46e79
--- /dev/null
+++ b/themes/default/.svn/entries
@@ -0,0 +1,41 @@
+8
+
+dir
+15
+svn+ssh://octorok.org/var/svn-repos/yaepghd/themes/default
+svn+ssh://octorok.org/var/svn-repos/yaepghd
+
+
+
+2008-08-16T17:45:41.242906Z
+13
+bball
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+9aaac883-65b3-4621-aff0-d2a75fbc3057
+
+bg.png
+file
+
+
+
+
+2009-01-24T18:12:45.000000Z
+d128710d21755319098f53e7c4617604
+2008-08-09T00:39:49.529127Z
+11
+bball
+has-props
+
diff --git a/themes/default/.svn/format b/themes/default/.svn/format
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/themes/default/.svn/format
@@ -0,0 +1 @@
+8
diff --git a/themes/default/.svn/prop-base/bg.png.svn-base b/themes/default/.svn/prop-base/bg.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/themes/default/.svn/prop-base/bg.png.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/themes/default/.svn/text-base/bg.png.svn-base b/themes/default/.svn/text-base/bg.png.svn-base
new file mode 100644
index 0000000..19a3783
--- /dev/null
+++ b/themes/default/.svn/text-base/bg.png.svn-base
Binary files differ
diff --git a/themes/default/bg.png b/themes/default/bg.png
new file mode 100644
index 0000000..19a3783
--- /dev/null
+++ b/themes/default/bg.png
Binary files differ
diff --git a/yaepghd.c b/yaepghd.c
new file mode 100644
index 0000000..364118c
--- /dev/null
+++ b/yaepghd.c
@@ -0,0 +1,3155 @@
+/*
+ * yaepghd.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+/**
+ * Includes
+ */
+#include <string>
+#include <vector>
+#include <map>
+#include <vdr/plugin.h>
+#include <vdr/osd.h>
+#include <vdr/device.h>
+#include <Magick++.h>
+#include <curl/curl.h>
+#include <assert.h>
+
+/**
+ * Macros
+ */
+#define REEL_EHD
+
+#ifdef DEBUG
+#define ASSERT assert
+#define YAEPG_ERROR(...) yaepg_error(__PRETTY_FUNCTION__, __VA_ARGS__)
+#define YAEPG_INFO(...) yaepg_info(__PRETTY_FUNCTION__, __VA_ARGS__)
+#else /* !DEBUG */
+#define ASSERT(_a)
+#define YAEPG_ERROR(...)
+#define YAEPG_INFO(...)
+#endif /* DEBUG */
+
+#ifndef MIN
+#define MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b))
+#endif
+#ifndef MAX
+#define MAX(_a, _b) ((_a) < (_b) ? (_b) : (_a))
+#endif
+#define ROUND(_f) (int)((_f) + 0.5f)
+
+/**
+ * Macros to retrieve theme values
+ */
+#define THEME_IMAGE(_name) cYaepgTheme::Instance()->Element(_name).u.bmp
+#define THEME_FONT(_name) cYaepgTheme::Instance()->Element(_name).u.font
+#define THEME_COLOR(_name) cYaepgTheme::Instance()->Element(_name).u.color
+#define THEME_GEOM(_name) cYaepgTheme::Instance()->Element(_name).u.geom
+#define THEME_IVAL(_name) cYaepgTheme::Instance()->Element(_name).u.ival
+
+#define BG_IMAGE THEME_IMAGE("bgImage")
+#define GRID_EVENT_FONT THEME_FONT("gridEventFont")
+#define GRID_CHAN_FONT THEME_FONT("gridChanFont")
+#define GRID_TIME_FONT THEME_FONT("gridTimeFont")
+#define GRID_DATE_FONT THEME_FONT("gridDateFont")
+#define EVENT_TITLE_FONT THEME_FONT("eventTitleFont")
+#define EVENT_TIME_FONT THEME_FONT("eventTimeFont")
+#define EVENT_DESC_FONT THEME_FONT("eventDescFont")
+#define EVENT_DATE_FONT THEME_FONT("eventDateFont")
+#define GRID_EVENT_COLOR THEME_COLOR("gridEventColor")
+#define GRID_SEL_FG THEME_COLOR("gridSelFg")
+#define GRID_SEL_BG THEME_COLOR("gridSelBg")
+#define GRID_CHAN_COLOR THEME_COLOR("gridChanColor")
+#define GRID_TIME_COLOR THEME_COLOR("gridTimeColor")
+#define GRID_DATE_COLOR THEME_COLOR("gridDateColor")
+#define GRID_SEP_COLOR THEME_COLOR("gridSepColor")
+#define EVENT_TITLE_COLOR THEME_COLOR("eventTitleColor")
+#define EVENT_TIME_COLOR THEME_COLOR("eventTimeColor")
+#define EVENT_DESC_COLOR THEME_COLOR("eventDescColor")
+#define EVENT_DATE_COLOR THEME_COLOR("eventDateColor")
+#define TLINE_BOX_COLOR THEME_COLOR("tlineBoxColor")
+#define GRID_EVENT_GEOM THEME_GEOM("gridEventGeom")
+#define GRID_CHAN_GEOM THEME_GEOM("gridChanGeom")
+#define GRID_TIME_GEOM THEME_GEOM("gridTimeGeom")
+#define GRID_DATE_GEOM THEME_GEOM("gridDateGeom")
+#define EVENT_TITLE_GEOM THEME_GEOM("eventTitleGeom")
+#define EVENT_TIME_GEOM THEME_GEOM("eventTimeGeom")
+#define EVENT_DESC_GEOM THEME_GEOM("eventDescGeom")
+#define EVENT_DATE_GEOM THEME_GEOM("eventDateGeom")
+#define TLINE_LOC_GEOM THEME_GEOM("tlineLocGeom")
+#define TLINE_BOX_GEOM THEME_GEOM("tlineBoxGeom")
+#define VID_WIN_GEOM THEME_GEOM("vidWinGeom")
+#define HELP_BAR_GEOM THEME_GEOM("helpGeom")
+#define GRID_NUM_CHANS THEME_IVAL("gridNumChans")
+#define LEFT_ARROW_WIDTH THEME_IVAL("leftArrowWidth")
+#define RIGHT_ARROW_WIDTH THEME_IVAL("rightArrowWidth")
+#define GRID_HORIZ_SPACE THEME_IVAL("gridHorizSpace")
+#define TEXT_BORDER THEME_IVAL("textBorder")
+#define TEXT_SPACE THEME_IVAL("textSpace")
+
+#define REC_DLG_IMG THEME_IMAGE("recDlgImage")
+#define REC_DLG_FONT THEME_FONT("recDlgFont")
+#define REC_DLG_COLOR THEME_COLOR("recDlgColor")
+#define REC_DLG_GEOM THEME_GEOM("recDlgGeom")
+#define REC_TITLE_GEOM THEME_GEOM("recTitleGeom")
+#define REC_TIME_GEOM THEME_GEOM("recTimeGeom")
+#define REC_START_GEOM THEME_GEOM("recStartGeom")
+#define REC_END_GEOM THEME_GEOM("recEndGeom")
+#define REC_FREQ_GEOM THEME_GEOM("recFreqGeom")
+#define REC_STINP_GEOM THEME_GEOM("recStInpGeom")
+#define REC_ENINP_GEOM THEME_GEOM("recEnInpGeom")
+#define REC_FRINP_GEOM THEME_GEOM("recFrInpGeom")
+
+#define MSG_BG_IMG THEME_IMAGE("msgBgImage")
+#define MSG_BOX_FONT THEME_FONT("msgBoxFont")
+#define MSG_BOX_COLOR THEME_COLOR("msgBoxColor")
+#define MSG_BOX_GEOM THEME_GEOM("msgBoxGeom")
+
+/* Manner in which channel is changed while in YAEPG */
+#define CHANNEL_CHANGE_MANUAL 0
+#define CHANNEL_CHANGE_MANUAL_INEPG 1
+#define CHANNEL_CHANGE_AUTO_INEPG 2
+
+#define TIME_24HR 0
+#define TIME_12HR 1
+#define FMT_AMPM(_hr) ((_hr) >= 12 ? "p" : "a")
+#define FMT_12HR(_hr) ((_hr) % 12 == 0 ? 12 : (_hr) % 12)
+
+/* Order of channels (UP or DOWN) */
+#define CHANNEL_ORDER_UP 0
+#define CHANNEL_ORDER_DOWN 1
+
+using namespace Magick;
+
+/**
+ * Private Data
+ */
+static const char *numToDay[7] = {
+ "Sun",
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thur",
+ "Fri",
+ "Sat"
+};
+
+static int iHideMenuEntry = false;
+static int iChannelChange = CHANNEL_CHANGE_MANUAL;
+static int iTimeFormat = TIME_12HR;
+static int iChannelOrder = CHANNEL_ORDER_DOWN;
+static char *sThemeName = "default";
+static const char *TIME_FORMATS[2] = { "24", "12" };
+static const char *CH_ORDER_FORMATS[2] = { "UP", "DOWN" };
+static const char *CH_CHANGE_MODES[3] = { "MANUAL", "MANUAL IN EPG", "AUTO IN EPG" };
+
+/**
+ * Pirvate Classes/Function Prototypes
+ */
+
+/**
+ * Class/Function Implementaion
+ */
+struct tGeom {
+ int x;
+ int y;
+ int w;
+ int h;
+};
+
+/* Logging functions */
+void
+yaepg_error(const char *func, const char *fmt, ...)
+{
+ char eMsg[128], eLine[256];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(eMsg, sizeof(eMsg), fmt, ap);
+ va_end(ap);
+ snprintf(eLine, sizeof(eLine), "ERROR: YaEPGHD: %s: %s", func, eMsg);
+ esyslog(eLine);
+}
+
+void
+yaepg_info(const char *func, const char *fmt, ...)
+{
+ char iMsg[128], iLine[256];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(iMsg, sizeof(iMsg), fmt, ap);
+ va_end(ap);
+ snprintf(iLine, sizeof(iLine), "INFO: YaEPGHD: %s: %s", func, iMsg);
+ isyslog(iLine);
+}
+
+/*
+ *****************************************************************************
+ * cYaepgTheme
+ *****************************************************************************
+ */
+class cYaepgTheme {
+public:
+ enum eElementType {
+ THEME_ELEM_FIRST,
+ THEME_IMAGE = THEME_ELEM_FIRST,
+ THEME_FONT,
+ THEME_COLOR,
+ THEME_GEOM,
+ THEME_IVAL,
+ THEME_ELEM_LAST = THEME_IVAL
+ };
+
+ struct tThemeElement {
+ eElementType type;
+ bool init;
+ union {
+ tColor color;
+ tGeom geom;
+ cFont *font;
+ cBitmap *bmp;
+ int ival;
+ } u;
+ };
+
+private:
+ static cYaepgTheme *instance;
+
+ std::map< std::string, tThemeElement > themeMap;
+ std::vector< cBitmap * > themeImages;
+ std::vector <cFont * > themeFonts;
+ std::map< std::string, int > fontMap;
+
+ cYaepgTheme(void);
+ ~cYaepgTheme();
+
+ void RemoveBlanks(char *s1);
+ void RemoveQuotes(char *s1);
+ int LoadImage(char *Filename);
+ int LoadFont(char *Font);
+ tColor ParseColor(char *Color) { return (tColor)strtoul(Color, NULL, 16); }
+ tGeom ParseGeom(char *Geom);
+ int ParseInt(char *Int) { return (int)strtoul(Int, NULL, 0); }
+ bool Check(void);
+ bool AddElement(const char *name, eElementType type);
+
+public:
+ static cYaepgTheme *Instance(void);
+ static void Destroy(void);
+ bool Load(char *Theme);
+ static void Themes(char ***_themes, int *_numThemes);
+ tThemeElement Element(const char *name) { return themeMap[std::string(name)]; }
+};
+
+cYaepgTheme *cYaepgTheme::instance = NULL;
+
+cYaepgTheme::cYaepgTheme(void)
+{
+ themeImages.clear();
+ themeFonts.clear();
+ fontMap.clear();
+
+ AddElement("bgImage", THEME_IMAGE);
+ AddElement("gridEventFont", THEME_FONT);
+ AddElement("gridChanFont", THEME_FONT);
+ AddElement("gridTimeFont", THEME_FONT);
+ AddElement("gridDateFont", THEME_FONT);
+ AddElement("eventTitleFont", THEME_FONT);
+ AddElement("eventTimeFont", THEME_FONT);
+ AddElement("eventDescFont", THEME_FONT);
+ AddElement("eventDateFont", THEME_FONT);
+ AddElement("gridEventColor", THEME_COLOR);
+ AddElement("gridSelFg", THEME_COLOR);
+ AddElement("gridSelBg", THEME_COLOR);
+ AddElement("gridSepColor", THEME_COLOR);
+ AddElement("gridChanColor", THEME_COLOR);
+ AddElement("gridTimeColor", THEME_COLOR);
+ AddElement("gridDateColor", THEME_COLOR);
+ AddElement("eventTitleColor", THEME_COLOR);
+ AddElement("eventTimeColor", THEME_COLOR);
+ AddElement("eventDescColor", THEME_COLOR);
+ AddElement("eventDateColor", THEME_COLOR);
+ AddElement("tlineBoxColor", THEME_COLOR);
+ AddElement("gridEventGeom", THEME_GEOM);
+ AddElement("gridChanGeom", THEME_GEOM);
+ AddElement("gridTimeGeom", THEME_GEOM);
+ AddElement("gridDateGeom", THEME_GEOM);
+ AddElement("eventTitleGeom", THEME_GEOM);
+ AddElement("eventTimeGeom", THEME_GEOM);
+ AddElement("eventDescGeom", THEME_GEOM);
+ AddElement("eventDateGeom", THEME_GEOM);
+ AddElement("tlineLocGeom", THEME_GEOM);
+ AddElement("tlineBoxGeom", THEME_GEOM);
+ AddElement("vidWinGeom", THEME_GEOM);
+ AddElement("helpGeom", THEME_GEOM);
+ AddElement("gridHorizSpace", THEME_IVAL);
+ AddElement("gridNumChans", THEME_IVAL);
+ AddElement("leftArrowWidth", THEME_IVAL);
+ AddElement("rightArrowWidth", THEME_IVAL);
+ AddElement("textBorder", THEME_IVAL);
+ AddElement("textSpace", THEME_IVAL);
+
+ AddElement("recDlgImage", THEME_IMAGE);
+ AddElement("recDlgGeom", THEME_GEOM);
+ AddElement("recDlgColor", THEME_COLOR);
+ AddElement("recDlgFont", THEME_FONT);
+ AddElement("recTitleGeom", THEME_GEOM);
+ AddElement("recTimeGeom", THEME_GEOM);
+ AddElement("recStartGeom", THEME_GEOM);
+ AddElement("recEndGeom", THEME_GEOM);
+ AddElement("recFreqGeom", THEME_GEOM);
+ AddElement("recStInpGeom", THEME_GEOM);
+ AddElement("recEnInpGeom", THEME_GEOM);
+ AddElement("recFrInpGeom", THEME_GEOM);
+
+ AddElement("msgBgImage", THEME_IMAGE);
+ AddElement("msgBoxFont", THEME_FONT);
+ AddElement("msgBoxGeom", THEME_GEOM);
+ AddElement("msgBoxColor", THEME_COLOR);
+}
+
+cYaepgTheme *
+cYaepgTheme::Instance(void)
+{
+ if (instance == NULL) {
+ instance = new cYaepgTheme;
+ }
+ return instance;
+}
+
+void
+cYaepgTheme::Destroy(void)
+{
+ cYaepgTheme *theme = Instance();
+
+ if (theme != NULL) {
+ std::vector< cBitmap *>::iterator it1;
+ for (it1 = theme->themeImages.begin();
+ it1 != theme->themeImages.end();
+ it1++) {
+ delete *it1;
+ }
+ std::vector< cFont *>::iterator it2;
+ for (it2 = theme->themeFonts.begin();
+ it2 != theme->themeFonts.end();
+ it2++) {
+ delete *it2;
+ }
+ instance = NULL;
+ }
+}
+
+void
+cYaepgTheme::Themes(char ***_themes, int *_numThemes)
+{
+ DIR *dir;
+ struct dirent *dp;
+ char **themes = NULL;
+ int numThemes = 0;
+
+ *_numThemes = 0;
+ *_themes = NULL;
+
+ dir = opendir(cPlugin::ConfigDirectory("yaepghd"));
+ if (dir == NULL) {
+ perror("opendir");
+ return;
+ }
+
+ while ((dp = readdir(dir)) != NULL) {
+ char *ext = strrchr(dp->d_name, '.');
+ if (ext == NULL) {
+ continue;
+ }
+ if (strcmp(ext + 1, "theme") != 0) {
+ continue;
+ }
+
+ *ext = '\0';
+ themes = (char **) realloc(themes, sizeof(char *) * (numThemes + 1));
+ themes[numThemes++] = strdup(dp->d_name);
+ }
+ *_themes = themes;
+ *_numThemes = numThemes;
+
+ return;
+}
+
+
+bool
+cYaepgTheme::AddElement(const char *name, eElementType type)
+{
+ std::string elemName(name);
+
+ if (themeMap.find(elemName) != themeMap.end()) {
+ YAEPG_ERROR("Duplicate theme element definition '%s'", elemName.c_str());
+ return false;
+ }
+ if (type < THEME_ELEM_FIRST || type > THEME_ELEM_LAST) {
+ YAEPG_ERROR("Invalid theme element type %d", type);
+ return false;
+ }
+ themeMap[elemName].type = type;
+ themeMap[elemName].init = false;
+
+ return true;
+}
+
+bool
+cYaepgTheme::Load(char *Theme)
+{
+ char themeFile[128], lineBuf[128], *s, *key, *val;
+ FILE *fp;
+
+ YAEPG_INFO("Loading theme: %s", Theme);
+
+ snprintf(themeFile, sizeof(themeFile), "%s/%s.theme",
+ cPlugin::ConfigDirectory("yaepghd"), Theme);
+
+ fp = fopen(themeFile, "r");
+ if (fp == NULL) {
+ YAEPG_ERROR("Could not open teme file: %s", Theme);
+ return false;
+ }
+
+ while ((s = fgets(lineBuf, sizeof(lineBuf), fp)) != NULL) {
+ /* Remove all whitespace and trailing \n */
+ RemoveBlanks(s);
+
+ /* Ignore comments and empty lines */
+ if (*s == '#' || *s == '\0') {
+ continue;
+ }
+
+ /* Split the key/value pair */
+ key = s;
+ val = strchr(s, '=');
+ if (val == NULL) {
+ continue;
+ }
+ *val++ = '\0';
+
+ /* If the value has quotes remove them */
+ RemoveQuotes(val);
+
+ if (themeMap.find(std::string(key)) == themeMap.end()) {
+ YAEPG_ERROR("Unknown key value '%s'", key);
+ continue;
+ }
+
+ tThemeElement &e = themeMap[std::string(key)];
+ int bmpIndex, fntIndex;
+
+ /* Call the appropriate parsing function based on the type */
+ switch (e.type) {
+ case THEME_IMAGE:
+ bmpIndex = LoadImage(val);
+ if (bmpIndex == -1) {
+ YAEPG_ERROR("Error loading image '%s = %s'", key, val);
+ fclose(fp);
+ return false;
+ }
+ e.u.bmp = themeImages[bmpIndex];
+ break;
+ case THEME_FONT:
+ fntIndex = LoadFont(val);
+ if (fntIndex == -1) {
+ YAEPG_ERROR("Error loading font '%s = %s'", key, val);
+ fclose(fp);
+ return false;
+ }
+ e.u.font = themeFonts[fntIndex];
+ break;
+ case THEME_COLOR:
+ e.u.color = ParseColor(val);
+ break;
+ case THEME_GEOM:
+ e.u.geom = ParseGeom(val);
+ break;
+ case THEME_IVAL:
+ e.u.ival = ParseInt(val);
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+ e.init = true;
+ }
+
+ return true;
+}
+
+void
+cYaepgTheme::RemoveBlanks(char *s1)
+{
+ char *s2 = s1;
+ int inQuote = 0;
+
+ while (*s1 != '\0') {
+ if (*s1 == '"') {
+ inQuote ^= 1;
+ }
+ if (inQuote || !isspace(*s1)) {
+ *s2++ = *s1;
+ }
+ s1++;
+ }
+ *s2 = '\0';
+ if (inQuote) {
+ YAEPG_ERROR("Umatched quote %s", s1);
+ }
+}
+
+void
+cYaepgTheme::RemoveQuotes(char *s1)
+{
+ char *s2 = s1;
+
+ while (*s1 != '\0') {
+ if (*s1 != '"') {
+ *s2++ = *s1;
+ }
+ s1++;
+ }
+ *s2 = '\0';
+}
+
+int
+cYaepgTheme::LoadImage(char *Filename)
+{
+ std::vector< Magick::Image > images;
+ char fullFilePath[128];
+ cBitmap *bmp = NULL;
+ int index = -1;
+
+ snprintf(fullFilePath, sizeof(fullFilePath), "%s/%s",
+ cPlugin::ConfigDirectory("yaepghd"), Filename);
+
+ YAEPG_INFO("Loading image '%s'", fullFilePath);
+
+ try {
+ int w, h;
+ readImages(&images, fullFilePath);
+ if (images.size() == 0) {
+ YAEPG_ERROR("Couldn't load %s", fullFilePath);
+ return -1;
+ }
+ if (images.size() > 1) {
+ YAEPG_ERROR("Animated images not supported %s", fullFilePath);
+ return -1;
+ }
+ w = images[0].columns();
+ h = images[0].rows();
+
+ bmp = new cBitmap(w, h, images[0].depth());
+
+ const Magick::PixelPacket *pix = images[0].getConstPixels(0, 0, w, h);
+ for (int iy = 0; iy < h; ++iy) {
+ for (int ix = 0; ix < w; ++ix) {
+ tColor col = (~(pix->opacity * 255 / MaxRGB) << 24) |
+ ((pix->red * 255 / MaxRGB) << 16) |
+ ((pix->green * 255 / MaxRGB) << 8) |
+ (pix->blue * 255 / MaxRGB);
+ bmp->DrawPixel(ix, iy, col);
+ ++pix;
+ }
+ }
+ index = themeImages.size();
+ themeImages.push_back(bmp);
+ } catch (Magick::Exception &e) {
+ YAEPG_ERROR("Couldn't load %s: %s", fullFilePath, e.what());
+ delete bmp;
+ return -1;
+ } catch (...) {
+ YAEPG_ERROR("Couldn't load %s: Unknown exception caught", fullFilePath);
+ delete bmp;
+ return -1;
+ }
+
+ return index;
+}
+
+int
+cYaepgTheme::LoadFont(char *Font)
+{
+ cFont *newFont;
+ std::string fontString(Font);
+ char *fontName, *fontSize;
+ int fontIndex;
+
+ /* Have we already loaded this font ? */
+ if (fontMap.find(fontString) != fontMap.end()) {
+ return fontMap.find(fontString)->second;
+ }
+
+ /* Split the name/size fields */
+ fontName = Font;
+ fontSize = strchr(fontName, ';');
+ if (fontSize == NULL) {
+ YAEPG_ERROR("Invalid font, missing size [<font>;<size>] %s", Font);
+ }
+ *fontSize++ = '\0';
+
+ /* Add the font to fontMap and fontVector */
+ YAEPG_INFO("Loading font '%s'", fontName);
+ newFont = cFont::CreateFont(fontName, (int)strtoul(fontSize, NULL, 10));
+ if (newFont == NULL) {
+ ASSERT(0);
+ YAEPG_ERROR("Could not load font %s", Font);
+ return -1;
+ }
+ fontIndex = themeFonts.size();
+ themeFonts.push_back(newFont);
+ fontMap[fontString] = fontIndex;
+
+ return fontIndex;
+}
+
+tGeom
+cYaepgTheme::ParseGeom(char *Geom)
+{
+ tGeom g = { 0, 0, 0, 0 };
+ char *val, *d;
+
+ val = Geom;
+ d = strchr(val, ',');
+ if (d == NULL) {
+ YAEPG_ERROR("Invalid geometry %s", val);
+ return g;
+ }
+ *d++ = '\0';
+ g.x = strtoul(val, NULL, 0);
+
+ val = d;
+ d = strchr(val, ',');
+ if (d == NULL) {
+ YAEPG_ERROR("Invalid geometry %s", val);
+ return g;
+ }
+ *d++ = '\0';
+ g.y = strtoul(val, NULL, 0);
+
+ val = d;
+ d = strchr(val, ',');
+ if (d == NULL) {
+ YAEPG_ERROR("Invalid geometry %s", val);
+ return g;
+ }
+ *d++ = '\0';
+ g.w = strtoul(val, NULL, 0);
+ g.h = strtoul(d, NULL, 0);
+
+ return g;
+}
+
+/*
+ *****************************************************************************
+ * cYaepgTextBox
+ *****************************************************************************
+ */
+enum eTextFlags {
+ TBOX_VALIGN_LEFT = 0x00000001,
+ TBOX_VALIGN_CENTER = 0x00000002,
+ TBOX_VALIGN_RIGHT = 0x00000004,
+ TBOX_VALIGN_FLAGS = 0x00000007,
+ TBOX_HALIGN_TOP = 0x00000008,
+ TBOX_HALIGN_CENTER = 0x00000010,
+ TBOX_HALIGN_BOTTOM = 0x00000020,
+ TBOX_HALIGN_FLAGS = 0x00000038,
+ TBOX_WRAP = 0x00000040,
+ TBOX_ARROW_LEFT = 0x00000080,
+ TBOX_ARROW_RIGHT = 0x00000100
+};
+
+class cYaepgTextBox {
+private:
+ struct sTextLine {
+ std::string text;
+ tGeom geom;
+ };
+
+ std::string text;
+ cFont *font;
+ tColor fgColor;
+ tColor bgColor;
+ cBitmap *bgImage;
+ cBitmap *bitmap;
+ eTextFlags flags;
+ tGeom geom;
+ std::vector< sTextLine > fmtText;
+
+public:
+ cYaepgTextBox(void);
+ ~cYaepgTextBox() { delete bitmap; }
+ void Text(const char *_text) { text.assign(_text); }
+ void Font(cFont *_font) { font = _font; }
+ void Flags(eTextFlags _flags) { flags = _flags; }
+ eTextFlags Flags(void) { return flags; }
+ void SetFlags(eTextFlags _flags) { flags = (eTextFlags)(flags | _flags); }
+ void FgColor(tColor color) { fgColor = color; }
+ void BgColor(tColor color) { bgColor = color; }
+ void BgImage(cBitmap *bmp) { bgImage = bmp; }
+ void X(int _x) { geom.x = _x; }
+ void Y(int _y) { geom.y = _y; }
+ void W(int _w) { geom.w = _w; }
+ void H(int _h) { geom.h = _h; }
+ int X(void) { return geom.x; }
+ int Y(void) { return geom.y; }
+ int W(void) { return geom.w; }
+ int H(void) { return geom.h; }
+ void Generate(void);
+ void Draw(cBitmap *bmp);
+};
+
+cYaepgTextBox::cYaepgTextBox(void) :
+ text(""),
+ font(NULL),
+ fgColor(clrTransparent),
+ bgColor(clrTransparent),
+ bgImage(NULL),
+ bitmap(NULL),
+ flags((eTextFlags)0)
+{
+ geom.x = 0;
+ geom.y = 0;
+ geom.w = 0;
+ geom.h = 0;
+}
+
+void
+cYaepgTextBox::Generate(void)
+{
+ /* Calulate width available for text */
+ int boxWidth = geom.w - (2 * TEXT_BORDER);
+ if (boxWidth <= 0) {
+ YAEPG_INFO("Box too small for text (%d %d %d)",
+ geom.w, boxWidth, TEXT_BORDER);
+ fmtText.clear();
+ return;
+ }
+
+ /* Calculate how many lines of text we can fit into the box */
+ int numLines = geom.h / (font->Height() + TEXT_SPACE);
+ if (numLines == 0) {
+ numLines = 1;
+ }
+
+ /*
+ * Allocate a temporary string for parsing. Add 4 chars to make space for
+ * adding "..." if the line does not fit in the box.
+ */
+ char *tokText = (char *) malloc(strlen(text.c_str()) + 4);
+ memset(tokText, 0, strlen(text.c_str()) + 4);
+ strcpy(tokText, text.c_str());
+
+ /* Remove trailing spaces */
+ char *s = tokText + strlen(tokText) - 1;
+ while (strlen(tokText) && *s == ' ') {
+ *s-- = '\0';
+ }
+
+ /* Break text up into lines */
+ char *line, *nextLine = tokText;
+
+ fmtText.clear();
+ nextLine = tokText;
+ if ((flags & TBOX_WRAP) && (numLines > 1)) {
+ char *d, *od;
+
+ do {
+ line = nextLine;
+ nextLine = NULL;
+ d = NULL;
+
+ /* Move the NUL char back one word at a time */
+ while (font->Width(line) > boxWidth) {
+ od = d;
+ d = strrchr(line, ' ');
+ if (od != NULL) {
+ *od = ' ';
+ }
+ if (d == NULL) {
+ break;
+ }
+ *d = '\0';
+ nextLine = d + 1;
+ }
+
+ /* Remove initial spaces */
+ while (*line == ' ') {
+ line++;
+ }
+
+ fmtText.resize(fmtText.size() + 1);
+ fmtText.back().text.assign(line);
+ } while (nextLine && ((int)fmtText.size() < (numLines - 1)));
+ }
+
+ if (nextLine) {
+ fmtText.resize(fmtText.size() + 1);
+ fmtText.back().text.assign(nextLine);
+ }
+
+ /* The code above does not format the last line */
+ line = (char *) malloc(strlen(fmtText.back().text.c_str()) + 4);
+ strcpy(line, fmtText.back().text.c_str());
+ if (font->Width(line) > boxWidth) {
+ strcpy(line + (strlen(line) - 1), "...");
+ while (font->Width(line) > boxWidth) {
+ switch (strlen(line)) {
+ case 0:
+ YAEPG_INFO("Zero length string");
+ goto out;
+ default:
+ line[strlen(line) - 4] = '.';
+ case 1:
+ case 2:
+ case 3:
+ line[strlen(line) - 1] = '\0';
+ break;
+ }
+ }
+ }
+
+out:
+ fmtText.back().text.assign(line);
+ free(line);
+
+ /* Figure out the initial y offset */
+ int yOff = 0, yDelta, boxHeight;
+
+ boxHeight = (fmtText.size() * font->Height()) +
+ ((fmtText.size() - 1) * TEXT_SPACE);
+ yDelta = font->Height() + TEXT_SPACE;
+ switch (flags & TBOX_HALIGN_FLAGS) {
+ case TBOX_HALIGN_TOP:
+ yOff = geom.y + TEXT_BORDER;
+ break;
+ case TBOX_HALIGN_CENTER:
+ yOff = geom.y + (geom.h / 2) - (boxHeight / 2);
+ break;
+ case TBOX_HALIGN_BOTTOM:
+ yOff = geom.y + geom.h - TEXT_BORDER - boxHeight;
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ /* Fill in the x/y coordinates for each line */
+ for (int i = 0; i < (int)fmtText.size(); i++) {
+ switch (flags & TBOX_VALIGN_FLAGS) {
+ case TBOX_VALIGN_LEFT:
+ fmtText[i].geom.x = geom.x + TEXT_BORDER;
+ break;
+ case TBOX_VALIGN_CENTER:
+ fmtText[i].geom.x = geom.x + (geom.w / 2) -
+ (font->Width(fmtText[i].text.c_str()) / 2);
+ break;
+ case TBOX_VALIGN_RIGHT:
+ fmtText[i].geom.x = geom.x + geom.w - TEXT_BORDER -
+ font->Width(fmtText[i].text.c_str());
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+ fmtText[i].geom.y = yOff;
+ yOff += yDelta;
+ }
+
+ free(tokText);
+
+ return;
+}
+
+void
+cYaepgTextBox::Draw(cBitmap *bmp)
+{
+ YAEPG_INFO("Drawing text box (%d)", fmtText.size());
+
+ for (int i = 0; i < (int)fmtText.size(); i++) {
+ /* Fill in background color */
+ if (bgColor != clrTransparent) {
+ bmp->DrawRectangle(geom.x, geom.y,
+ geom.x + (geom.w - 1), geom.y + (geom.h - 1),
+ bgColor);
+ }
+
+ /* Draw the text */
+ YAEPG_INFO("Drawing text '%s' at (%d %d color #%08X",
+ fmtText[i].text.c_str(), fmtText[i].geom.x,
+ fmtText[i].geom.y, fgColor);
+ YAEPG_INFO("Text widht %d box widht %d",
+ font->Width(fmtText[i].text.c_str()), geom.w);
+
+ bmp->DrawText(fmtText[i].geom.x, fmtText[i].geom.y,
+ fmtText[i].text.c_str(), fgColor, bgColor, font);
+ }
+}
+
+/*
+ *****************************************************************************
+ * cYaepgGrid
+ *****************************************************************************
+ */
+enum eCursorDir {
+ DIR_UP,
+ DIR_DOWN,
+ DIR_LEFT,
+ DIR_RIGHT
+};
+
+class cYaepgGrid {
+private:
+ class cNoInfoEvent : public cEvent {
+ private:
+ time_t startTime;
+
+ public:
+ cNoInfoEvent(time_t);
+ };
+
+ struct tYaepgEvent {
+ const cEvent *event;
+ cYaepgTextBox box;
+ };
+
+ cYaepgTextBox leftBox;
+ cYaepgTextBox rightBox;
+
+ tGeom geom;
+ int startTime;
+ int horizSpace;
+ float gridRowHeight;
+ float gridPixPerMin;
+ std::vector< cChannel * > &chanVec;
+ std::vector< std::vector< tYaepgEvent > > events;
+ std::vector< cYaepgTextBox > leftArrows;
+ std::vector< cYaepgTextBox > rightArrows;
+ std::vector< const cEvent * > noInfoEvents;
+ int curX;
+ int curY;
+
+ void FixCursor(void);
+
+public:
+ cYaepgGrid(std::vector< cChannel * > &chans, int time);
+ ~cYaepgGrid();
+ void UpdateTime(time_t newTime) { startTime = newTime; Generate(); }
+ void UpdateChans(std::vector< cChannel * > &chans) { chanVec = chans; Generate(); }
+ bool MoveCursor(eCursorDir dir);
+ const cEvent *Event(void) { return events[curY][curX].event; }
+ void Row(int row);
+ void Col(int col);
+ int Row(void) { return curY; }
+ int Col(void) { return curX; }
+ void Generate(void);
+ void Draw(cBitmap *bmp);
+};
+
+cYaepgGrid::cNoInfoEvent::cNoInfoEvent(time_t startTime) :
+ cEvent(0)
+{
+ SetStartTime(startTime);
+ SetDuration(9000);
+ SetTitle(tr(tr("No Info")));
+ SetDescription(tr(tr("No Info")));
+}
+
+cYaepgGrid::cYaepgGrid(std::vector< cChannel *> &chans, int time) :
+ startTime(time),
+ chanVec(chans),
+ curX(0),
+ curY(0)
+{
+ noInfoEvents.clear();
+ geom = GRID_EVENT_GEOM;
+ horizSpace = GRID_HORIZ_SPACE;
+ gridRowHeight = (float)(geom.h - ((chanVec.size() - 1) * horizSpace)) /
+ (float)chanVec.size();
+ gridPixPerMin = (float)geom.w / (float)90;
+ leftArrows.resize(chanVec.size());
+ rightArrows.resize(chanVec.size());
+ Generate();
+}
+
+cYaepgGrid::~cYaepgGrid()
+{
+ std::vector< const cEvent *>::iterator it;
+
+ for (it = noInfoEvents.begin();
+ it != noInfoEvents.end();
+ it ++) {
+ delete *it;
+ }
+ noInfoEvents.clear();
+}
+
+void
+cYaepgGrid::FixCursor(void)
+{
+ if (curY < 0) {
+ curY = 0;
+ }
+ if (curY >= (int)events.size()) {
+ curY = events.size() - 1;
+ }
+
+ if (curX < 0) {
+ curX = 0;
+ }
+ if (curX >= (int)events[curY].size()) {
+ curX = events[curY].size() - 1;
+ }
+}
+
+void
+cYaepgGrid::Row(int row)
+{
+ curY = row;
+ FixCursor();
+}
+
+void
+cYaepgGrid::Col(int col)
+{
+ curX = col;
+ FixCursor();
+}
+
+void
+cYaepgGrid::Generate(void)
+{
+ YAEPG_INFO("Generating grid");
+
+ const cSchedule *curSched;
+ const cEvent *curEvent;
+ time_t curTime, endTime;
+ time_t evStart, evDuration;
+ eTextFlags evFlags;
+ time_t gridStart;
+
+ gridStart = startTime - (startTime % 1800);
+ events.clear();
+ events.resize(chanVec.size());
+ cSchedulesLock SchedulesLock;
+ const cSchedules* Schedules = cSchedules::Schedules(SchedulesLock);
+ for (int i = 0; i < (int)chanVec.size(); i++) {
+ curSched = Schedules->GetSchedule(chanVec[i]->GetChannelID());
+ curTime = gridStart;
+ endTime = curTime + 5400;
+ int j = 0;
+
+ while (curTime < endTime) {
+ events[i].resize(events[i].size() + 1);
+ if (curSched != NULL) {
+ curEvent = curSched->GetEventAround(curTime);
+ if ((curEvent != NULL) &&
+ (curEvent->StartTime() + curEvent->Duration()) <= curTime) {
+ curEvent = NULL;
+ }
+ } else {
+ curEvent = NULL;
+ }
+
+ if (curEvent == NULL) {
+ curEvent = new cNoInfoEvent(curTime);
+ noInfoEvents.push_back(curEvent);
+ }
+
+ evFlags = (eTextFlags)0;
+ evStart = curEvent->StartTime();
+ evDuration = curEvent->Duration();
+ if (evStart < gridStart) {
+ evFlags = (eTextFlags)(evFlags | TBOX_ARROW_LEFT);
+ evStart = gridStart;
+ evDuration -= gridStart - curEvent->StartTime();
+ }
+ if ((evStart + evDuration) > endTime) {
+ evFlags = (eTextFlags)(evFlags | TBOX_ARROW_RIGHT);
+ evDuration = endTime - evStart;
+ }
+
+ ASSERT(evDuration <= 5400);
+ ASSERT(evStart >= curTime);
+ ASSERT(evStart + evDuration <= endTime);
+
+ events[i][j].event = curEvent;
+ events[i][j].box.Text(curEvent->Title());
+ events[i][j].box.Font(GRID_EVENT_FONT);
+ events[i][j].box.FgColor(GRID_EVENT_COLOR);
+ events[i][j].box.BgColor(clrTransparent);
+ events[i][j].box.Flags((eTextFlags)(evFlags | TBOX_VALIGN_LEFT | TBOX_HALIGN_CENTER));
+ events[i][j].box.X(geom.x + ROUND((float)((evStart - gridStart) / 60) * gridPixPerMin));
+ events[i][j].box.Y(geom.y + ROUND(((float)i * (gridRowHeight + (float)horizSpace))));
+ events[i][j].box.W(ROUND((float)(evDuration / 60) * gridPixPerMin));
+ events[i][j].box.H(ROUND(gridRowHeight));
+ events[i][j].box.Generate();
+
+ YAEPG_INFO("Event [%d][%d] (%d %d, %d %d) '%s'", i, j,
+ events[i][j].box.X(), events[i][j].box.Y(),
+ events[i][j].box.W(), events[i][j].box.H(),
+ curEvent->Title());
+
+ curTime = curEvent->StartTime() + curEvent->Duration();
+ j++;
+ }
+
+ /*
+ * Generate the arrows
+ * XXX Most of the arrow initialization could be done once when the grid is constructed
+ */
+ leftArrows[i].Text("<");
+ leftArrows[i].Font(GRID_EVENT_FONT);
+ leftArrows[i].FgColor(GRID_EVENT_COLOR);
+ leftArrows[i].BgColor(clrTransparent);
+ leftArrows[i].Flags((eTextFlags)(TBOX_VALIGN_RIGHT | TBOX_HALIGN_CENTER));
+ leftArrows[i].X(geom.x - LEFT_ARROW_WIDTH);
+ leftArrows[i].Y(geom.y + ROUND(((float)i * (gridRowHeight + (float)horizSpace))));
+ leftArrows[i].W(LEFT_ARROW_WIDTH);
+ leftArrows[i].H(ROUND(gridRowHeight));
+ leftArrows[i].Generate();
+
+ rightArrows[i].Text(">");
+ rightArrows[i].Font(GRID_EVENT_FONT);
+ rightArrows[i].FgColor(GRID_EVENT_COLOR);
+ rightArrows[i].BgColor(clrTransparent);
+ rightArrows[i].Flags((eTextFlags)(TBOX_VALIGN_LEFT | TBOX_HALIGN_CENTER));
+ rightArrows[i].X(geom.x + geom.w);
+ rightArrows[i].Y(geom.y + ROUND(((float)i * (gridRowHeight + (float)horizSpace))));
+ rightArrows[i].W(RIGHT_ARROW_WIDTH);
+ rightArrows[i].H(ROUND(gridRowHeight));
+ rightArrows[i].Generate();
+ }
+
+ FixCursor();
+}
+
+bool
+cYaepgGrid::MoveCursor(eCursorDir dir)
+{
+ switch (dir) {
+ case DIR_UP:
+ if (curY == 0) {
+ return false;
+ }
+ curY--;
+ break;
+ case DIR_DOWN:
+ if (curY == (int)(events.size() - 1)) {
+ return false;
+ }
+ curY++;
+ break;
+ case DIR_LEFT:
+ if (curX == 0) {
+ return false;
+ }
+ curX--;
+ break;
+ case DIR_RIGHT:
+ if (curX == (int)(events[curY].size() - 1)) {
+ return false;
+ }
+ curX++;
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ if (curX >= (int)events[curY].size()) {
+ curX = events[curY].size() - 1;
+ }
+
+ ASSERT(curY >= 0 && curY < (int)events.size());
+ ASSERT(curX >= 0 && curX < (int)events[curY].size());
+
+ return true;
+}
+
+void
+cYaepgGrid::Draw(cBitmap *bmp)
+{
+ YAEPG_INFO("Drawing grid at (%d %d)", geom.x, geom.y);
+
+ for (int i = 0; i < (int)events.size(); i++) {
+ for (int j = 0; j < (int)events[i].size(); j++) {
+ /* Is this the currently selected event */
+ if (i == curY && j == curX) {
+ events[i][j].box.FgColor(GRID_SEL_FG);
+ events[i][j].box.BgColor(GRID_SEL_BG);
+
+ /* Check to see if the arrow is "selected" */
+ if (j == 0) {
+ leftArrows[i].FgColor(GRID_SEL_FG);
+ leftArrows[i].BgColor(GRID_SEL_BG);
+ } else {
+ leftArrows[i].FgColor(GRID_EVENT_COLOR);
+ leftArrows[i].BgColor(clrTransparent);
+ }
+ if (j == (int)(events[i].size() - 1)) {
+ rightArrows[i].FgColor(GRID_SEL_FG);
+ rightArrows[i].BgColor(GRID_SEL_BG);
+ } else {
+ rightArrows[i].FgColor(GRID_EVENT_COLOR);
+ rightArrows[i].BgColor(clrTransparent);
+ }
+ } else {
+ events[i][j].box.FgColor(GRID_EVENT_COLOR);
+ events[i][j].box.BgColor(clrTransparent);
+ leftArrows[i].FgColor(GRID_EVENT_COLOR);
+ leftArrows[i].BgColor(clrTransparent);
+ rightArrows[i].FgColor(GRID_EVENT_COLOR);
+ rightArrows[i].BgColor(clrTransparent);
+ }
+ events[i][j].box.Draw(bmp);
+
+ /* Draw the arrow boxes */
+ if (events[i][j].box.Flags() & TBOX_ARROW_LEFT) {
+ leftArrows[i].Text("<");
+ } else {
+ leftArrows[i].Text("");
+ }
+ leftArrows[i].Generate();
+ leftArrows[i].Draw(bmp);
+
+ if (events[i][j].box.Flags() & TBOX_ARROW_RIGHT) {
+ rightArrows[i].Text(">");
+ } else {
+ rightArrows[i].Text("");
+ rightArrows[i].BgColor(clrTransparent);
+ }
+ rightArrows[i].Generate();
+ rightArrows[i].Draw(bmp);
+
+ /* Draw a separator if there is no right arrow */
+ if ((events[i][j].box.Flags() & TBOX_ARROW_RIGHT) == 0) {
+ YAEPG_INFO("Drawing separator at (%d %d, %d %d)",
+ events[i][j].box.X() + events[i][j].box.W() - 1,
+ events[i][j].box.Y(),
+ events[i][j].box.X() + events[i][j].box.W(),
+ events[i][j].box.Y() + ROUND(gridRowHeight));
+
+ bmp->DrawRectangle(events[i][j].box.X() + events[i][j].box.W() - 1,
+ events[i][j].box.Y(),
+ events[i][j].box.X() + events[i][j].box.W(),
+ events[i][j].box.Y() + ROUND(gridRowHeight),
+ GRID_SEP_COLOR);
+ }
+ } /* for j < events[i].size() */
+ } /* for i < events.size() */
+}
+
+bool
+cYaepgTheme::Check(void)
+{
+ std::map< std::string, tThemeElement >::iterator it;
+ bool valid = true;
+
+ for (it = themeMap.begin(); it != themeMap.end(); it++) {
+ if (it->second.init == false) {
+ YAEPG_ERROR("%s not defined!", it->first.c_str());
+ valid = false;
+ }
+ }
+
+ return valid;
+}
+
+/*
+ *****************************************************************************
+ * cYaepgGridChans
+ *****************************************************************************
+ */
+class cYaepgGridChans {
+private:
+ struct tYaepgChan {
+ cChannel *c;
+ cYaepgTextBox numBox;
+ cYaepgTextBox nameBox;
+ };
+
+ tGeom geom;
+ std::vector< cChannel * > &chanVec;
+ std::vector< tYaepgChan > chanInfo;
+ float chanRowHeight;
+ int horizSpace;
+
+public:
+ cYaepgGridChans(std::vector< cChannel * > &chans);
+ void UpdateChans(std::vector< cChannel * > &chans) { chanVec = chans; Generate(); }
+ void Generate(void);
+ void Draw(cBitmap *bmp);
+};
+
+cYaepgGridChans::cYaepgGridChans(std::vector< cChannel * > &chans) :
+ chanVec(chans)
+{
+ geom = GRID_CHAN_GEOM;
+ horizSpace = GRID_HORIZ_SPACE;
+ chanRowHeight = (float)(geom.h - ((chanVec.size() - 1) * horizSpace)) /
+ (float)chanVec.size();
+ chanInfo.resize(GRID_NUM_CHANS);
+ Generate();
+}
+
+void
+cYaepgGridChans::Generate(void)
+{
+ char numStr[16];
+
+ for (int i = 0; i < GRID_NUM_CHANS; i++) {
+ snprintf(numStr, sizeof(numStr), "%d", chanVec[i]->Number());
+ chanInfo[i].numBox.Text(numStr);
+ chanInfo[i].numBox.Font(GRID_CHAN_FONT);
+ chanInfo[i].numBox.FgColor(GRID_CHAN_COLOR);
+ chanInfo[i].numBox.BgColor(clrTransparent);
+ chanInfo[i].numBox.Flags((eTextFlags)(TBOX_VALIGN_LEFT | TBOX_HALIGN_CENTER));
+ chanInfo[i].numBox.X(geom.x);
+ chanInfo[i].numBox.Y(geom.y + ROUND((float)i * (chanRowHeight + (float)horizSpace)));
+ chanInfo[i].numBox.W(geom.w / 2);
+ chanInfo[i].numBox.H(ROUND(chanRowHeight));
+ chanInfo[i].numBox.Generate();
+ chanInfo[i].nameBox.Text(chanVec[i]->Name());
+ chanInfo[i].nameBox.Font(GRID_CHAN_FONT);
+ chanInfo[i].nameBox.FgColor(GRID_CHAN_COLOR);
+ chanInfo[i].nameBox.BgColor(clrTransparent);
+ chanInfo[i].nameBox.Flags((eTextFlags)(TBOX_VALIGN_LEFT | TBOX_HALIGN_CENTER));
+ chanInfo[i].nameBox.X(geom.x + (geom.w / 2));
+ chanInfo[i].nameBox.Y(geom.y + ROUND((float)i * (chanRowHeight + (float)horizSpace)));
+ chanInfo[i].nameBox.W(geom.w / 2);
+ chanInfo[i].nameBox.H(ROUND(chanRowHeight));
+ chanInfo[i].nameBox.Generate();
+
+ YAEPG_INFO("Chan [%d] (%d %d, %d %d) '%s %s'", i,
+ chanInfo[i].numBox.X(), chanInfo[i].numBox.Y(),
+ chanInfo[i].numBox.W() + chanInfo[i].nameBox.W(),
+ chanInfo[i].numBox.H() + chanInfo[i].nameBox.H(),
+ numStr, chanVec[i]->Name());
+ }
+}
+
+void
+cYaepgGridChans::Draw(cBitmap *bmp)
+{
+ YAEPG_INFO("Drawing grid channels at (%d %d)", geom.x, geom.y);
+
+ for (int i = 0; i < (int)chanInfo.size(); i++) {
+ chanInfo[i].numBox.Draw(bmp);
+ chanInfo[i].nameBox.Draw(bmp);
+ }
+}
+
+/*
+ *****************************************************************************
+ * cYaepgGridTime
+ *****************************************************************************
+ */
+class cYaepgGridTime {
+private:
+ time_t startTime;
+ std::vector< cYaepgTextBox > times;
+ tGeom geom;
+
+public:
+ cYaepgGridTime(time_t _startTime);
+ void UpdateTime(time_t _startTime) { startTime = _startTime; Generate(); }
+ void Generate(void);
+ void Draw(cBitmap *bmp);
+};
+
+cYaepgGridTime::cYaepgGridTime(time_t _startTime) :
+ startTime(_startTime)
+{
+ geom = GRID_TIME_GEOM;
+ times.resize(3);
+ Generate();
+}
+
+void
+cYaepgGridTime::Generate(void)
+{
+ time_t curTime = startTime;
+ struct tm locTime;
+ char timeStr[32];
+ int timeWidth;
+
+ timeWidth = geom.w / 3;
+ for (int i = 0; i < 3; i++) {
+ localtime_r(&curTime, &locTime);
+ locTime.tm_min = (locTime.tm_min >= 30) ? 30 : 0;
+ if (iTimeFormat == TIME_24HR) {
+ snprintf(timeStr, sizeof(timeStr), "%02d:%02d",
+ locTime.tm_hour, locTime.tm_min);
+ } else {
+ snprintf(timeStr, sizeof(timeStr), "%d:%02d%s",
+ FMT_12HR(locTime.tm_hour), locTime.tm_min,
+ FMT_AMPM(locTime.tm_hour));
+ }
+ times[i].Text(timeStr);
+ times[i].Font(GRID_TIME_FONT);
+ times[i].FgColor(GRID_TIME_COLOR);
+ times[i].BgColor(clrTransparent);
+ times[i].Flags((eTextFlags)(TBOX_VALIGN_LEFT | TBOX_HALIGN_CENTER));
+ times[i].X(geom.x + (i * timeWidth));
+ times[i].Y(geom.y);
+ times[i].W(timeWidth);
+ times[i].H(geom.h);
+ times[i].Generate();
+ curTime += 1800;
+ }
+}
+
+void
+cYaepgGridTime::Draw(cBitmap *bmp)
+{
+ YAEPG_INFO("Drawing grid times at (%d %d)", geom.x, geom.y);
+
+ for (int i = 0; i < (int)times.size(); i++) {
+ times[i].Draw(bmp);
+ }
+}
+
+/*
+ *****************************************************************************
+ * cYaepgGridDate
+ *****************************************************************************
+ */
+class cYaepgGridDate {
+private:
+ tGeom geom;
+ time_t t;
+ char chanStr[16];
+ char dateStr[16];
+ cYaepgTextBox box;
+
+public:
+ cYaepgGridDate(time_t _t);
+ void UpdateTime(time_t _t);
+ void UpdateChan(int chanNum);
+ void Generate(void);
+ void Draw(cBitmap *bmp);
+};
+
+cYaepgGridDate::cYaepgGridDate(time_t _t)
+{
+ geom = GRID_DATE_GEOM;
+ chanStr[0] = '\0';
+ UpdateTime(_t);
+}
+
+void
+cYaepgGridDate::UpdateTime(time_t _t)
+{
+ struct tm locTime;
+
+ t = _t;
+ localtime_r(&t, &locTime);
+ snprintf(dateStr, sizeof(dateStr), "%s %d/%d",
+ tr(numToDay[locTime.tm_wday]), locTime.tm_mon, locTime.tm_mday);
+ Generate();
+}
+
+void
+cYaepgGridDate::UpdateChan(int chanNum)
+{
+ if (chanNum == 0) {
+ chanStr[0] = '\0';
+ } else {
+ snprintf(chanStr, sizeof(chanStr), "%d-", chanNum);
+ }
+ Generate();
+}
+
+void
+cYaepgGridDate::Generate(void)
+{
+ box.Text(strlen(chanStr) ? chanStr : dateStr);
+ box.Font(GRID_DATE_FONT);
+ box.FgColor(GRID_DATE_COLOR);
+ box.BgColor(clrTransparent);
+ box.Flags((eTextFlags)(TBOX_VALIGN_LEFT | TBOX_HALIGN_CENTER));
+ box.X(geom.x);
+ box.Y(geom.y);
+ box.W(geom.w);
+ box.H(geom.h);
+ box.Generate();
+}
+
+void
+cYaepgGridDate::Draw(cBitmap *bmp)
+{
+ YAEPG_INFO("Drawing grid date at (%d %d)", geom.x, geom.y);
+
+ box.Draw(bmp);
+}
+
+/*
+ *****************************************************************************
+ * cYaepgEventTitle
+ *****************************************************************************
+ */
+class cYaepgEventTitle {
+private:
+ tGeom geom;
+ const cEvent *event;
+ cYaepgTextBox box;
+
+public:
+ cYaepgEventTitle(const cEvent *_event);
+ void UpdateEvent(const cEvent *_event) { event = _event; Generate(); }
+ void Generate(void);
+ void Draw(cBitmap *bmp);
+};
+
+cYaepgEventTitle::cYaepgEventTitle(const cEvent *_event) :
+ event(_event)
+{
+ geom = EVENT_TITLE_GEOM;
+ Generate();
+}
+
+void
+cYaepgEventTitle::Generate(void)
+{
+ box.Text(event->Title());
+ box.Font(EVENT_TITLE_FONT);
+ box.FgColor(EVENT_TITLE_COLOR);
+ box.BgColor(clrTransparent);
+ box.Flags((eTextFlags)(TBOX_VALIGN_LEFT | TBOX_HALIGN_BOTTOM | TBOX_WRAP));
+ box.X(geom.x);
+ box.Y(geom.y);
+ box.W(geom.w);
+ box.H(geom.h);
+ box.Generate();
+}
+
+void
+cYaepgEventTitle::Draw(cBitmap *bmp)
+{
+ YAEPG_INFO("Drawing event title at (%d %d)", geom.x, geom.y);
+
+ box.Draw(bmp);
+}
+
+/*
+ *****************************************************************************
+ * cYaepgEventTime
+ *****************************************************************************
+ */
+class cYaepgEventTime {
+private:
+ const cEvent *event;
+ cYaepgTextBox box;
+ tGeom geom;
+
+public:
+ cYaepgEventTime(const cEvent *_event);
+ void UpdateEvent(const cEvent *_event) { event = _event; Generate(); }
+ void Generate(void);
+ void Draw(cBitmap *bmp);
+};
+
+cYaepgEventTime::cYaepgEventTime(const cEvent *_event) :
+ event(_event)
+{
+ geom = EVENT_TIME_GEOM;
+ Generate();
+}
+
+void
+cYaepgEventTime::Generate(void)
+{
+ struct tm locStart, locEnd;
+ time_t t;
+ char timeStr[32];
+
+ t = event->StartTime();
+ localtime_r(&t, &locStart);
+ t += event->Duration();
+ localtime_r(&t, &locEnd);
+ if (iTimeFormat == TIME_24HR) {
+ snprintf(timeStr, sizeof(timeStr), "%02d:%02d - %02d:%02d",
+ locStart.tm_hour, locStart.tm_min,
+ locEnd.tm_hour, locEnd.tm_min);
+ } else {
+ snprintf(timeStr, sizeof(timeStr), "%d:%02d%s - %d:%02d%s",
+ FMT_12HR(locStart.tm_hour), locStart.tm_min,
+ FMT_AMPM(locStart.tm_hour),
+ FMT_12HR(locEnd.tm_hour), locEnd.tm_min,
+ FMT_AMPM(locEnd.tm_hour));
+ }
+
+ box.Text(timeStr);
+ box.Font(EVENT_TIME_FONT);
+ box.FgColor(EVENT_TIME_COLOR);
+ box.BgColor(clrTransparent);
+ box.Flags((eTextFlags)(TBOX_VALIGN_LEFT | TBOX_HALIGN_CENTER));
+ box.X(geom.x);
+ box.Y(geom.y);
+ box.W(geom.w);
+ box.H(geom.h);
+ box.Generate();
+}
+
+void
+cYaepgEventTime::Draw(cBitmap *bmp)
+{
+ YAEPG_INFO("Drawing event time at (%d %d)", geom.x, geom.y);
+
+ box.Draw(bmp);
+}
+
+/*
+ *****************************************************************************
+ * cYaepgEventDesc
+ *****************************************************************************
+ */
+class cYaepgEventDesc {
+private:
+ const cEvent *event;
+ cYaepgTextBox box;
+ tGeom geom;
+
+public:
+ cYaepgEventDesc(const cEvent *_event);
+ void UpdateEvent(const cEvent *_event) { event = _event; Generate(); }
+ void Generate(void);
+ void Draw(cBitmap *bmp);
+};
+
+cYaepgEventDesc::cYaepgEventDesc(const cEvent *_event) :
+ event(_event)
+{
+ geom = EVENT_DESC_GEOM;
+ Generate();
+}
+
+void
+cYaepgEventDesc::Generate(void)
+{
+ box.Text(event->Description() ? event->Description() : "");
+ box.Font(EVENT_DESC_FONT);
+ box.FgColor(EVENT_DESC_COLOR);
+ box.BgColor(clrTransparent);
+ box.Flags((eTextFlags)(TBOX_VALIGN_LEFT | TBOX_HALIGN_TOP | TBOX_WRAP));
+ box.X(geom.x);
+ box.Y(geom.y);
+ box.W(geom.w);
+ box.H(geom.h);
+ box.Generate();
+}
+
+void
+cYaepgEventDesc::Draw(cBitmap *bmp)
+{
+ YAEPG_INFO("Drawing event description at (%d %d)", geom.x, geom.y);
+
+ box.Draw(bmp);
+}
+
+/*
+ *****************************************************************************
+ * cYaepgEventDate
+ *****************************************************************************
+ */
+class cYaepgEventDate {
+private:
+ cYaepgTextBox box;
+ tGeom geom;
+
+public:
+ cYaepgEventDate(void);
+ void Update(void) { Generate(); }
+ void Generate(void);
+ void Draw(cBitmap *bmp);
+};
+
+cYaepgEventDate::cYaepgEventDate(void)
+{
+ geom = EVENT_DATE_GEOM;
+ Generate();
+}
+
+void
+cYaepgEventDate::Generate(void)
+{
+ char timeStr[128];
+ struct tm locTime;
+
+ time_t t = time(NULL);
+ localtime_r(&t, &locTime);
+ if (iTimeFormat == TIME_24HR) {
+ snprintf(timeStr, sizeof(timeStr), "%s %02d:%02d",
+ tr(numToDay[locTime.tm_wday]), locTime.tm_hour, locTime.tm_min);
+ } else {
+ snprintf(timeStr, sizeof(timeStr), "%s %d:%02d%s",
+ tr(numToDay[locTime.tm_wday]),
+ FMT_12HR(locTime.tm_hour), locTime.tm_min,
+ FMT_AMPM(locTime.tm_hour));
+ }
+ box.Text(timeStr);
+ box.Font(EVENT_DATE_FONT);
+ box.FgColor(EVENT_DATE_COLOR);
+ box.BgColor(clrTransparent);
+ box.Flags((eTextFlags)(TBOX_VALIGN_LEFT | TBOX_HALIGN_CENTER));
+ box.X(geom.x);
+ box.Y(geom.y);
+ box.W(geom.w);
+ box.H(geom.h);
+ box.Generate();
+}
+
+void
+cYaepgEventDate::Draw(cBitmap *bmp)
+{
+ YAEPG_INFO("Drawing event date at (%d %d)", geom.x, geom.y);
+
+ box.Draw(bmp);
+}
+
+/*
+ *****************************************************************************
+ * cYaepgTimeLine
+ *****************************************************************************
+ */
+class cYaepgTimeLine {
+private:
+ tGeom locGeom;
+ tGeom boxGeom;
+ tColor boxColor;
+ time_t startTime;
+ float pixPerMin;
+ int xOff;
+ bool hidden;
+
+public:
+ cYaepgTimeLine(time_t _startTime);
+ void UpdateTime(time_t _startTime);
+ void Generate(void);
+ void Draw(cBitmap *bmp);
+};
+
+cYaepgTimeLine::cYaepgTimeLine(time_t _startTime) :
+ hidden(true)
+{
+ locGeom = TLINE_LOC_GEOM;
+ boxGeom = TLINE_BOX_GEOM;
+ boxColor = TLINE_BOX_COLOR;
+ pixPerMin = (float)locGeom.w / (float)90;
+ UpdateTime(_startTime);
+}
+
+void
+cYaepgTimeLine::UpdateTime(time_t _startTime)
+{
+ startTime = _startTime - (_startTime % 1800);
+ Generate();
+}
+
+void
+cYaepgTimeLine::Generate(void)
+{
+ if (startTime > time(NULL)) {
+ hidden = true;
+ } else {
+ int minOff = (time(NULL) - startTime) / 60;
+ ASSERT(minOff <= 30);
+ xOff = locGeom.x + ROUND((float)minOff * pixPerMin) - (boxGeom.w / 2);
+ hidden = false;
+ YAEPG_INFO("Time line minOff %d pixPerMin %d xOff %d",
+ minOff, ROUND(pixPerMin), xOff);
+ }
+}
+
+void
+cYaepgTimeLine::Draw(cBitmap *bmp)
+{
+ YAEPG_INFO("Drawing time line at (%d %d) (%d %d) %d",
+ xOff, locGeom.y,
+ xOff + boxGeom.w, locGeom.y + (boxGeom.h - 1),
+ hidden);
+
+ if (!hidden) {
+ bmp->DrawRectangle(xOff, locGeom.y,
+ xOff + boxGeom.w, locGeom.y + (boxGeom.h - 1),
+ boxColor);
+ }
+}
+
+/*
+ *****************************************************************************
+ * cYaepgHelpBar
+ *****************************************************************************
+ */
+class cYaepgHelpBar {
+private:
+ struct {
+ int x1;
+ int x2;
+ int y1;
+ int y2;
+ tColor color;
+ } dots[4];
+ int dotDiam;
+ cYaepgTextBox boxes[4];
+ static const char *helpStrs[4];
+ static const tColor dotColors[4];
+ tGeom geom;
+
+public:
+ cYaepgHelpBar(void);
+ void Draw(cBitmap *bmp);
+};
+
+const char *cYaepgHelpBar::helpStrs[4] = {
+ "-12 Hours",
+ "+12 Hours",
+ "Tune Channel",
+ "Screenshot"
+};
+
+const tColor cYaepgHelpBar::dotColors[4] = {
+ (tColor)0xFFFF0000,
+ (tColor)0xFF00FF00,
+ (tColor)0xFFFFFF00,
+ (tColor)0xFF0000FF
+};
+
+cYaepgHelpBar::cYaepgHelpBar(void) :
+ dotDiam(10)
+{
+ geom = HELP_BAR_GEOM;
+ int boxWidth = geom.w / 4;
+ for (int i = 0; i < 4; i++) {
+ boxes[i].Text(helpStrs[i]);
+ boxes[i].Font(GRID_EVENT_FONT);
+ boxes[i].FgColor(GRID_EVENT_COLOR);
+ boxes[i].BgColor(clrTransparent);
+ boxes[i].Flags((eTextFlags)(TBOX_VALIGN_LEFT | TBOX_HALIGN_CENTER));
+ boxes[i].X(geom.x + (i * boxWidth) + dotDiam);
+ boxes[i].Y(geom.y);
+ boxes[i].W(boxWidth - dotDiam);
+ boxes[i].H(geom.h);
+ boxes[i].Generate();
+
+ dots[i].x1 = geom.x + (i * boxWidth);
+ dots[i].y1 = geom.y + ((geom.h - dotDiam) / 2);
+ dots[i].x2 = dots[i].x1 + dotDiam;
+ dots[i].y2 = dots[i].y1 + dotDiam;
+ dots[i].color = dotColors[i];
+ }
+}
+
+void
+cYaepgHelpBar::Draw(cBitmap *bmp)
+{
+ if (geom.w < (4 * dotDiam)) {
+ return;
+ }
+
+ for (int i = 0; i < 4; i++) {
+ bmp->DrawEllipse(dots[i].x1, dots[i].y1,
+ dots[i].x2, dots[i].y2,
+ dots[i].color);
+ boxes[i].Draw(bmp);
+ }
+}
+
+/*
+ *****************************************************************************
+ * cYaepgInputTime
+ *****************************************************************************
+ */
+class cYaepgInputTime : public cYaepgTextBox {
+private:
+ time_t t;
+
+public:
+ cYaepgInputTime(void) : t(0) {}
+ void UpdateTime(time_t _t);
+ eOSState ProcessKey(eKeys key);
+};
+
+void
+cYaepgInputTime::UpdateTime(time_t _t)
+{
+ struct tm locTime;
+ char timeStr[32];
+
+ t = _t;
+ localtime_r(&t, &locTime);
+ if (iTimeFormat == TIME_24HR) {
+ snprintf(timeStr, sizeof(timeStr), "%02d:%02d",
+ locTime.tm_hour, locTime.tm_min);
+ } else {
+ snprintf(timeStr, sizeof(timeStr), "%d:%02d%s",
+ FMT_12HR(locTime.tm_hour), locTime.tm_min,
+ FMT_AMPM(locTime.tm_hour));
+ }
+ Text(timeStr);
+ Generate();
+}
+
+eOSState
+cYaepgInputTime::ProcessKey(eKeys key)
+{
+ eOSState state = osContinue;
+
+ switch (key & ~k_Repeat) {
+ case kLeft:
+ UpdateTime(t - 60);
+ break;
+ case kRight:
+ UpdateTime(t + 60);
+ break;
+ default:
+ state = osUnknown;
+ break;
+ }
+
+ return state;
+}
+
+/*
+ *****************************************************************************
+ * cYaepgInputStra
+ *****************************************************************************
+ */
+class cYaepgInputStra : public cYaepgTextBox {
+private:
+ std::vector< std::string > stra;
+ int index;
+
+ void UpdateIndex(int _index);
+
+public:
+ cYaepgInputStra(void) : index(0) {}
+ void UpdateStra(char **_stra);
+ eOSState ProcessKey(eKeys key);
+};
+
+void
+cYaepgInputStra::UpdateStra(char **_stra)
+{
+ int i = 0;
+
+ stra.clear();
+ while (_stra[i] != NULL) {
+ stra.push_back(std::string(_stra[i++]));
+ }
+ UpdateIndex(0);
+}
+
+void
+cYaepgInputStra::UpdateIndex(int _index)
+{
+ if (_index < 0) {
+ index = 0;
+ }
+ if (_index >= (int)stra.size()) {
+ index = (int)stra.size() - 1;
+ }
+ Text(stra[index].c_str());
+ Generate();
+}
+
+eOSState
+cYaepgInputStra::ProcessKey(eKeys key)
+{
+ eOSState state = osContinue;
+
+ switch (key & ~k_Repeat) {
+ case kLeft:
+ UpdateIndex(index - 1);
+ break;
+ case kRight:
+ UpdateIndex(index + 1);
+ break;
+ default:
+ state = osUnknown;
+ break;
+ }
+
+ return state;
+}
+
+/*
+ *****************************************************************************
+ * cYaepgRecDlg
+ *****************************************************************************
+ */
+class cYaepgRecDlg {
+private:
+ static const char *freqStra[4];
+ char freqs[4][32];
+ const cEvent *event;
+ tGeom geom;
+ cYaepgTextBox titleBox;
+ cYaepgTextBox timeBox;
+ cYaepgTextBox startBox;
+ cYaepgTextBox endBox;
+ cYaepgTextBox freqBox;
+ cYaepgInputTime startInput;
+ cYaepgInputTime endInput;
+ cYaepgInputStra freqInput;
+ int curY;
+
+public:
+ cYaepgRecDlg(void);
+ void UpdateEvent(const cEvent *_event);
+ eOSState ProcessKey(eKeys key);
+ void Draw(cBitmap *bmp);
+};
+
+const char *cYaepgRecDlg::freqStra[4] = {
+ "Once",
+ "Every",
+ "Mon-Fri",
+ "Sun-Sat",
+};
+
+cYaepgRecDlg::cYaepgRecDlg(void) :
+ event(NULL),
+ curY(0)
+{
+ geom = REC_DLG_GEOM;
+
+ /*
+ * The record dialog box widget locations are relative to the record dialog
+ * box location.
+ */
+ titleBox.Text("");
+ titleBox.Font(REC_DLG_FONT);
+ titleBox.FgColor(REC_DLG_COLOR);
+ titleBox.BgColor(clrTransparent);
+ titleBox.Flags((eTextFlags)(TBOX_VALIGN_LEFT | TBOX_HALIGN_CENTER));
+ titleBox.X(REC_DLG_GEOM.x + REC_TITLE_GEOM.x);
+ titleBox.Y(REC_DLG_GEOM.y + REC_TITLE_GEOM.y);
+ titleBox.W(REC_TITLE_GEOM.w);
+ titleBox.H(REC_TITLE_GEOM.h);
+
+ timeBox.Text("");
+ timeBox.Font(REC_DLG_FONT);
+ timeBox.FgColor(REC_DLG_COLOR);
+ timeBox.BgColor(clrTransparent);
+ timeBox.Flags((eTextFlags)(TBOX_VALIGN_LEFT | TBOX_HALIGN_CENTER));
+ timeBox.X(REC_DLG_GEOM.x + REC_TIME_GEOM.x);
+ timeBox.Y(REC_DLG_GEOM.y + REC_TIME_GEOM.y);
+ timeBox.W(REC_TIME_GEOM.w);
+ timeBox.H(REC_TIME_GEOM.h);
+
+ startBox.Text(tr("Start"));
+ startBox.Font(REC_DLG_FONT);
+ startBox.FgColor(REC_DLG_COLOR);
+ startBox.BgColor(clrTransparent);
+ startBox.Flags((eTextFlags)(TBOX_VALIGN_LEFT | TBOX_HALIGN_CENTER));
+ startBox.X(REC_DLG_GEOM.x + REC_START_GEOM.x);
+ startBox.Y(REC_DLG_GEOM.y + REC_START_GEOM.y);
+ startBox.W(REC_START_GEOM.w);
+ startBox.H(REC_START_GEOM.h);
+
+ endBox.Text(tr("Stop"));
+ endBox.Font(REC_DLG_FONT);
+ endBox.FgColor(REC_DLG_COLOR);
+ endBox.BgColor(clrTransparent);
+ endBox.Flags((eTextFlags)(TBOX_VALIGN_LEFT | TBOX_HALIGN_CENTER));
+ endBox.X(REC_DLG_GEOM.x + REC_END_GEOM.x);
+ endBox.Y(REC_DLG_GEOM.y + REC_END_GEOM.y);
+ endBox.W(REC_END_GEOM.w);
+ endBox.H(REC_END_GEOM.h);
+
+ freqBox.Text(tr("Frequency"));
+ freqBox.Font(REC_DLG_FONT);
+ freqBox.FgColor(REC_DLG_COLOR);
+ freqBox.BgColor(clrTransparent);
+ freqBox.Flags((eTextFlags)(TBOX_VALIGN_LEFT | TBOX_HALIGN_CENTER));
+ freqBox.X(REC_DLG_GEOM.x + REC_FREQ_GEOM.x);
+ freqBox.Y(REC_DLG_GEOM.y + REC_FREQ_GEOM.y);
+ freqBox.W(REC_FREQ_GEOM.w);
+ freqBox.H(REC_FREQ_GEOM.h);
+
+ startInput.Text("");
+ startInput.Font(REC_DLG_FONT);
+ startInput.FgColor(REC_DLG_COLOR);
+ startInput.BgColor(clrTransparent);
+ startInput.Flags((eTextFlags)(TBOX_VALIGN_RIGHT | TBOX_HALIGN_CENTER));
+ startInput.X(REC_DLG_GEOM.x + REC_STINP_GEOM.x);
+ startInput.Y(REC_DLG_GEOM.y + REC_STINP_GEOM.y);
+ startInput.W(REC_STINP_GEOM.w);
+ startInput.H(REC_STINP_GEOM.h);
+
+ endInput.Text("");
+ endInput.Font(REC_DLG_FONT);
+ endInput.FgColor(REC_DLG_COLOR);
+ endInput.BgColor(clrTransparent);
+ endInput.Flags((eTextFlags)(TBOX_VALIGN_RIGHT | TBOX_HALIGN_CENTER));
+ endInput.X(REC_DLG_GEOM.x + REC_ENINP_GEOM.x);
+ endInput.Y(REC_DLG_GEOM.y + REC_ENINP_GEOM.y);
+ endInput.W(REC_ENINP_GEOM.w);
+ endInput.H(REC_ENINP_GEOM.h);
+
+ freqInput.Text("");
+ freqInput.Font(REC_DLG_FONT);
+ freqInput.FgColor(REC_DLG_COLOR);
+ freqInput.BgColor(clrTransparent);
+ freqInput.Flags((eTextFlags)(TBOX_VALIGN_RIGHT | TBOX_HALIGN_CENTER));
+ freqInput.X(REC_DLG_GEOM.x + REC_FRINP_GEOM.x);
+ freqInput.Y(REC_DLG_GEOM.y + REC_FRINP_GEOM.y);
+ freqInput.W(REC_FRINP_GEOM.w);
+ freqInput.H(REC_FRINP_GEOM.h);
+
+ for (int i = 0; i < 4; i++) {
+ strcpy(freqs[i], tr(freqStra[i]));
+ }
+ freqs[5][0] = '\0';
+}
+
+eOSState
+cYaepgRecDlg::ProcessKey(eKeys key)
+{
+ eOSState state = osUnknown;
+
+ /* First let the input boxes process the key */
+ switch (curY) {
+ case 0:
+ state = startInput.ProcessKey(key);
+ break;
+ case 1:
+ state = endInput.ProcessKey(key);
+ break;
+ case 2:
+ state = freqInput.ProcessKey(key);
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ /* Key was not consumed by one of the input boxes */
+ if (state == osUnknown) {
+ switch (key & ~k_Repeat) {
+ case kUp:
+ break;
+ case kRight:
+ break;
+ default:
+ state = osUnknown;
+ break;
+ }
+ }
+
+ return state;
+}
+
+void
+cYaepgRecDlg::UpdateEvent(const cEvent *_event)
+{
+ event = _event;
+
+ /* Update the event title */
+ titleBox.Text(event->Title());
+ titleBox.Generate();
+
+ /* Update the start/end time */
+ struct tm locStart, locEnd;
+ time_t t;
+ char timeStr[32];
+
+ t = event->StartTime();
+ localtime_r(&t, &locStart);
+ t += event->Duration();
+ localtime_r(&t, &locEnd);
+ if (iTimeFormat == TIME_24HR) {
+ snprintf(timeStr, sizeof(timeStr), "%02d:%02d - %02d:%02d",
+ locStart.tm_hour, locStart.tm_min,
+ locEnd.tm_hour, locEnd.tm_min);
+ } else {
+ snprintf(timeStr, sizeof(timeStr), "%d:%02d%s - %d:%02d%s",
+ FMT_12HR(locStart.tm_hour), locStart.tm_min,
+ FMT_AMPM(locStart.tm_hour),
+ FMT_12HR(locEnd.tm_hour), locEnd.tm_min,
+ FMT_AMPM(locEnd.tm_hour));
+ }
+ timeBox.Text(timeStr);
+ timeBox.Generate();
+
+ /* Fill in initial values for start/end input */
+ startInput.UpdateTime(event->StartTime() - (Setup.MarginStart * 60));
+ endInput.UpdateTime(event->StartTime() + _event->Duration() + (Setup.MarginStart * 60));
+
+ /* Update the frequency string array */
+ struct tm locTime;
+ t = event->StartTime();
+ localtime_r(&t, &locTime);
+ freqs[1][5] = ' ';
+ strcpy(&freqs[1][6], tr(numToDay[locTime.tm_wday]));
+ freqInput.UpdateStra((char **)freqs);
+}
+
+void
+cYaepgRecDlg::Draw(cBitmap *bmp)
+{
+ bmp->DrawBitmap(geom.x, geom.y, *REC_DLG_IMG);
+ titleBox.Draw(bmp);
+ timeBox.Draw(bmp);
+ startBox.Draw(bmp);
+ endBox.Draw(bmp);
+ freqBox.Draw(bmp);
+
+ startInput.Draw(bmp);
+ endInput.Draw(bmp);
+ freqInput.Draw(bmp);
+}
+
+/*
+ *****************************************************************************
+ * cYaepgMsg
+ *****************************************************************************
+ */
+class cYaepgMsg {
+private:
+ cYaepgTextBox msgBox;
+ tGeom geom;
+
+public:
+ cYaepgMsg(void);
+ void UpdateMsg(char *msg);
+ void Draw(cBitmap *bmp);
+};
+
+cYaepgMsg::cYaepgMsg(void)
+{
+ geom = MSG_BOX_GEOM;
+ msgBox.Text("");
+ msgBox.Font(MSG_BOX_FONT);
+ msgBox.FgColor(MSG_BOX_COLOR);
+ msgBox.BgColor(clrTransparent);
+ msgBox.Flags((eTextFlags)(TBOX_VALIGN_CENTER | TBOX_HALIGN_CENTER));
+ msgBox.X(geom.x);
+ msgBox.Y(geom.y);
+ msgBox.W(MSG_BG_IMG->Width());
+ msgBox.H(MSG_BG_IMG->Height());
+}
+
+void
+cYaepgMsg::UpdateMsg(char *msg)
+{
+ msgBox.Text(msg);
+ msgBox.Generate();
+}
+
+void
+cYaepgMsg::Draw(cBitmap *bmp)
+{
+ bmp->DrawBitmap(geom.x, geom.y, *MSG_BG_IMG);
+ msgBox.Draw(bmp);
+}
+
+#ifdef REEL_EHD
+/*
+ *****************************************************************************
+ * cReelVidWin
+ *
+ * For the Reel eHD card I use a telnet connectin to issue fbset comands on
+ * the card to adjust the video plane. This is quite buggy, the eHD card
+ * doesn't seem to like changing channels while the video plane is scaled and
+ * many resolutions for HD streams end up with a distoted image.
+ *
+ *****************************************************************************
+ */
+#define REEL_CURL_CMD "fbset -mmp %d %d -rmp %d %d\n"
+#define VIDPLANE_HORI 1920
+#define VIDPLANE_VERT 1080
+
+class cReelVidWin {
+private:
+ pthread_t thrd;
+ FILE *readFp, *writeFp;
+
+ static void *CurlThread(void *arg);
+ static size_t CurlWrite(void *ptr, size_t size, size_t nmemb, void *stream);
+ void SendCmd(const char *cmd);
+
+public:
+ cReelVidWin(void);
+ ~cReelVidWin();
+ bool Init(void);
+ void Update(int x, int y, int w, int h);
+ void Open(tGeom geom);
+ void Close(void);
+};
+
+cReelVidWin::cReelVidWin(void) :
+ readFp(NULL),
+ writeFp(NULL)
+{
+}
+
+cReelVidWin::~cReelVidWin()
+{
+ Close();
+ SendCmd("exit\n");
+ (void) pthread_join(thrd, NULL);
+ (void) close(fileno(readFp));
+ (void) close(fileno(writeFp));
+ (void) fclose(readFp);
+ (void) fclose(writeFp);
+}
+
+size_t
+cReelVidWin::CurlWrite(void *ptr, size_t size, size_t nmemb, void *stream)
+{
+ return (size * nmemb);
+}
+
+void *
+cReelVidWin::CurlThread(void *arg)
+{
+ CURL *hdl;
+ CURLcode status;
+ FILE *fp = (FILE *)arg;
+
+ hdl = curl_easy_init();
+ if (hdl == NULL) {
+ YAEPG_ERROR("curl_easy_init");
+ return NULL;
+ }
+
+ status = curl_easy_setopt(hdl, CURLOPT_URL, "telnet://192.168.99.129/");
+ if (status != CURLE_OK) {
+ YAEPG_ERROR("curl_easy_setopt");
+ return NULL;
+ }
+
+ /* Set the read FILE * to the read end of the pipe */
+ status = curl_easy_setopt(hdl, CURLOPT_READDATA, fp);
+ if (status != CURLE_OK) {
+ YAEPG_ERROR("curl_easy_setopt");
+ return NULL;
+ }
+
+ /* Setup our own write function so we can discard output */
+ status = curl_easy_setopt(hdl, CURLOPT_WRITEFUNCTION,
+ (curl_write_callback)&CurlWrite);
+ if (status != CURLE_OK) {
+ YAEPG_ERROR("curl_easy_setopt");
+ return NULL;
+ }
+
+ /* Connect to eHD card, this call blocks until the connection is closed */
+ status = curl_easy_perform(hdl);
+ if (status != CURLE_OK) {
+ YAEPG_ERROR("curl_easy_perform");
+ return NULL;
+ }
+
+ YAEPG_INFO("Thread exit!");
+ return NULL;
+}
+
+void
+cReelVidWin::SendCmd(const char *cmd)
+{
+ char errStr[128];
+
+ YAEPG_INFO("curl cmd '%s'", cmd);
+ if (fwrite(cmd, 1, strlen(cmd), writeFp) != strlen(cmd)) {
+ snprintf(errStr, sizeof(errStr), "fwrite: %s", strerror(errno));
+ YAEPG_ERROR(errStr);
+ }
+}
+
+bool
+cReelVidWin::Init(void)
+{
+ int pfds[2], error;
+ char errStr[128];
+
+ /* Init the curl library */
+ curl_global_init(CURL_GLOBAL_ALL);
+
+ /* Create a pipe to communciate with the telnet connection */
+ if (pipe(pfds) == -1) {
+ snprintf(errStr, sizeof(errStr), "pipe: %s", strerror(errno));
+ YAEPG_ERROR(errStr);
+ return false;
+ }
+ readFp = fdopen(pfds[0], "r");
+ if (readFp == NULL) {
+ snprintf(errStr, sizeof(errStr), "fdopen: %s", strerror(errno));
+ YAEPG_ERROR(errStr);
+ return false;
+ }
+ writeFp = fdopen(pfds[1], "w");
+ if (writeFp == NULL) {
+ snprintf(errStr, sizeof(errStr), "fdopen: %s", strerror(errno));
+ YAEPG_ERROR(errStr);
+ return false;
+ }
+
+ /* Make the write side of the pipe unbufferd */
+ if (setvbuf(writeFp, NULL, _IONBF, 0) == -1) {
+ snprintf(errStr, sizeof(errStr), "setvbuf: %s", strerror(errno));
+ YAEPG_ERROR(errStr);
+ return false;
+ }
+
+ /* Create a thread to handle the telnet connection */
+ error = pthread_create(&thrd, NULL, (void *(*)(void *))&CurlThread, readFp);
+ if (error) {
+ snprintf(errStr, sizeof(errStr), "pthread_create: %s", strerror(error));
+ YAEPG_ERROR(errStr);
+ return false;
+ }
+
+ return true;
+}
+
+void
+cReelVidWin::Open(tGeom geom)
+{
+ tGeom scaled;
+ char cmd[128];
+
+ /*
+ * For the Reel eHD card we set up the video window by executing an "fbset"
+ * command on the eHD card. The video window geometry is specified using
+ * coordinates of the OSD plane but the fbset command works on the video
+ * plane. We need to map the geometry from the OSD plane to the video plane.
+ */
+ scaled.x = ROUND((float)VID_WIN_GEOM.x * ((float)VIDPLANE_HORI / 720.0f));
+ scaled.y = ROUND((float)VID_WIN_GEOM.y * ((float)VIDPLANE_VERT / 576.0f));
+ scaled.w = ROUND((float)VID_WIN_GEOM.w * ((float)VIDPLANE_HORI / 720.0f));
+ scaled.h = ROUND((float)VID_WIN_GEOM.h * ((float)VIDPLANE_VERT / 576.0f));
+
+ /*
+ * For some read if we set the video plane width to < 577 while in 1080i mode
+ * we end up with a blue screen. To prevent this adjust the width to be >=
+ * 577 even though it may not be what the user wants. Not sure if this holds
+ * true while video is running in 720p.
+ */
+ scaled.w = MAX(scaled.w, 577);
+ scaled.h = MAX(scaled.h, 325);
+
+ snprintf(cmd, sizeof(cmd), REEL_CURL_CMD,
+ scaled.x, scaled.y, scaled.w, scaled.h);
+ SendCmd(cmd);
+}
+
+void
+cReelVidWin::Close(void)
+{
+ char cmd[128];
+ snprintf(cmd, sizeof(cmd), REEL_CURL_CMD,
+ 0, 0, VIDPLANE_HORI, VIDPLANE_VERT);
+ YAEPG_INFO("closing video window");
+ SendCmd(cmd);
+}
+
+static cReelVidWin *reelVidWin = NULL;
+#endif
+
+/*
+ *****************************************************************************
+ * cYaepgHd
+ *****************************************************************************
+ */
+class cYaepghd : public cOsdObject {
+private:
+ cYaepgTheme *theme;
+ cOsd *osd;
+ time_t startTime;
+ tArea mainWin;
+ cBitmap *mainBmp;
+
+ std::vector< cChannel * > chanVec;
+ const cEvent *event;
+ uint64_t lastInput;
+ int directChan;
+ bool needsRedraw;
+
+ cYaepgGrid *gridEvents;
+ cYaepgGridChans *gridChans;
+ cYaepgGridTime *gridTime;
+ cYaepgGridDate *gridDate;
+ cYaepgEventTitle *eventTitle;
+ cYaepgEventTime *eventTime;
+ cYaepgEventDesc *eventDesc;
+ cYaepgEventDate *eventDate;
+ cYaepgTimeLine *timeLine;
+ cYaepgHelpBar *helpBar;
+
+public:
+ cYaepghd(void);
+ ~cYaepghd();
+ virtual void Show(void);
+ virtual eOSState ProcessKey(eKeys key);
+ void SetTime(time_t newTime);
+ void UpdateChans(cChannel *c);
+ void UpdateChans(int change);
+ void UpdateTime(int change);
+ void UpdateEvent(const cEvent *newEvent);
+ void MoveCursor(eCursorDir dir);
+ void SwitchToCurrentChannel(bool closeVidWin = false);
+ void Draw(void);
+};
+
+cYaepghd::cYaepghd(void) :
+ theme(NULL),
+ osd(NULL),
+ startTime((time_t)0),
+ mainBmp(NULL),
+ event(NULL),
+ lastInput((uint64_t)0),
+ directChan(0),
+ needsRedraw(false),
+ gridEvents(NULL),
+ gridChans(NULL),
+ gridTime(NULL),
+ gridDate(NULL),
+ eventTitle(NULL),
+ eventTime(NULL),
+ eventDesc(NULL),
+ eventDate(NULL),
+ timeLine(NULL),
+ helpBar(NULL)
+{
+ memset(&mainWin, 0, sizeof(mainWin));
+ chanVec.clear();
+}
+
+cYaepghd::~cYaepghd()
+{
+ delete osd;
+ delete mainBmp;
+ delete gridEvents;
+ delete gridChans;
+ delete gridTime;
+ delete gridDate;
+ delete timeLine;
+ delete eventTitle;
+ delete eventTime;
+ delete eventDesc;
+ delete eventDate;
+ delete helpBar;
+#ifdef REEL_EHD
+ reelVidWin->Close();
+#endif
+}
+
+void
+cYaepghd::Show(void)
+{
+ /* Create new OSD object */
+ osd = cOsdProvider::NewOsd(0, 0);
+ if (osd == NULL) {
+ YAEPG_ERROR("NewOsd returned NULL!");
+ return;
+ }
+
+ /* Load the theme */
+ theme = cYaepgTheme::Instance();
+ if (theme->Load(sThemeName) == false) {
+ YAEPG_ERROR("Error loading theme %s", sThemeName);
+ return;
+ }
+
+ /* Create the main window and bitmap for drawing the EPG */
+ YAEPG_INFO("Main window (%d %d)", BG_IMAGE->Width(), BG_IMAGE->Height());
+ mainWin.x1 = 0;
+ mainWin.y1 = 0;
+ mainWin.x2 = BG_IMAGE->Width() - 1;
+ mainWin.y2 = BG_IMAGE->Height() - 1;
+ mainWin.bpp = BG_IMAGE->Bpp();
+ mainBmp = new cBitmap(BG_IMAGE->Width(),
+ BG_IMAGE->Height(),
+ BG_IMAGE->Bpp());
+ osd->SetAreas(&mainWin, 1);
+
+ /* Set up the video window parameters */
+ if (VID_WIN_GEOM.w != 0 && VID_WIN_GEOM.h != 0) {
+ osd->vidWin.x1 = VID_WIN_GEOM.x;
+ osd->vidWin.y1 = VID_WIN_GEOM.y;
+ osd->vidWin.x2 = VID_WIN_GEOM.x + VID_WIN_GEOM.w;
+ osd->vidWin.y2 = VID_WIN_GEOM.y + VID_WIN_GEOM.h;
+ osd->vidWin.bpp = 12;
+ }
+
+#ifdef REEL_EHD
+ reelVidWin->Open(VID_WIN_GEOM);
+#endif
+
+ /* Create all the EPG widgets based on current channel/time */
+ UpdateChans(Channels.GetByNumber(cDevice::CurrentChannel()));
+
+ time_t t = time(NULL);
+ gridEvents = new cYaepgGrid(chanVec, t);
+ gridChans = new cYaepgGridChans(chanVec);
+ gridTime = new cYaepgGridTime(t);
+ gridDate = new cYaepgGridDate(t);
+ timeLine = new cYaepgTimeLine(t);
+ const cEvent *e = gridEvents->Event();
+ eventTitle = new cYaepgEventTitle(e);
+ eventTime = new cYaepgEventTime(e);
+ eventDesc = new cYaepgEventDesc(e);
+ eventDate = new cYaepgEventDate();
+ helpBar = new cYaepgHelpBar();
+
+ Draw();
+}
+
+eOSState
+cYaepghd::ProcessKey(eKeys key)
+{
+ eOSState state = cOsdObject::ProcessKey(key);
+
+ needsRedraw = false;
+ if (state == osUnknown) {
+ switch (key & ~k_Repeat) {
+ case kBack:
+ state = osEnd;
+ break;
+ case kBlue:
+ cDevice::PrimaryDevice()->GrabImageFile("img.jpg", true, 256, -1, -1);
+ state = osContinue;
+ break;
+ case kLeft:
+ MoveCursor(DIR_LEFT);
+ needsRedraw = true;
+ state = osContinue;
+ break;
+ case kRight:
+ MoveCursor(DIR_RIGHT);
+ needsRedraw = true;
+ state = osContinue;
+ break;
+ case kUp:
+ MoveCursor(DIR_UP);
+ needsRedraw = true;
+ if (iChannelChange == CHANNEL_CHANGE_AUTO_INEPG) {
+ SwitchToCurrentChannel();
+ }
+ state = osContinue;
+ break;
+ case kDown:
+ MoveCursor(DIR_DOWN);
+ needsRedraw = true;
+ if (iChannelChange == CHANNEL_CHANGE_AUTO_INEPG) {
+ SwitchToCurrentChannel();
+ }
+ state = osContinue;
+ break;
+ case kOk:
+ SwitchToCurrentChannel(true);
+ if (iChannelChange == CHANNEL_CHANGE_MANUAL_INEPG)
+ state = osContinue;
+ else
+ state = osEnd;
+ break;
+ case kRed:
+ case kChanUp:
+ UpdateChans((iChannelOrder == CHANNEL_ORDER_UP ? 1 : -1) * GRID_NUM_CHANS * 1);
+ needsRedraw = true;
+ state = osContinue;
+ break;
+ case kGreen:
+ SwitchToCurrentChannel();
+ state = osContinue;
+ break;
+ case kYellow:
+ case kChanDn:
+ UpdateChans((iChannelOrder == CHANNEL_ORDER_UP ? 1 : -1) * GRID_NUM_CHANS * -1);
+ needsRedraw = true;
+ state = osContinue;
+ break;
+ case k0 ... k9:
+ if (directChan || (key != k0)) {
+ directChan = ((directChan * 10) + ((key & ~k_Repeat) - k0)) % 100000;
+ gridDate->UpdateChan(directChan);
+ lastInput = cTimeMs::Now();
+ needsRedraw = true;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* Channel input timeout */
+ if (directChan && (cTimeMs::Now() - lastInput) > 1000) {
+ YAEPG_INFO("Direct input timed out, channel %d", directChan);
+
+ /* Look for a channel close to what the user entered */
+ cChannel *chan = NULL;
+ for (int i = 0; i < 500; i++) {
+ if ((chan = Channels.GetByNumber(directChan + i)) != NULL) {
+ break;
+ }
+ if ((chan = Channels.GetByNumber(directChan - i)) != NULL) {
+ break;
+ }
+ }
+ if (chan != NULL) {
+ UpdateChans(chan);
+ }
+
+ /* Reset the direct input value */
+ directChan = 0;
+ gridDate->UpdateChan(0);
+
+ /* Position the cursor on the channel entered */
+ gridEvents->Row(0);
+
+ needsRedraw = true;
+ }
+
+ /* Uptate the grid time */
+ time_t now = time(NULL);
+ struct tm locNow, locStart;
+ localtime_r(&now, &locNow);
+ localtime_r(&startTime, &locStart);
+ if (locNow.tm_min != locStart.tm_min) {
+ needsRedraw = true;
+ if (now > startTime) {
+ SetTime(now);
+ }
+ }
+
+ /* Redraw the screen if needed */
+ if (needsRedraw) {
+ Draw();
+ }
+
+ return state;
+}
+
+void
+cYaepghd::UpdateChans(cChannel *c)
+{
+ chanVec.resize(GRID_NUM_CHANS);
+ chanVec[0] = c;
+ for (int i = 1; i < GRID_NUM_CHANS; i++) {
+ if (iChannelOrder == CHANNEL_ORDER_UP) {
+ while ((c = (cChannel *)c->Prev()) && (c->GroupSep()));
+ if (c == NULL)
+ c = Channels.First();
+ } else {
+ while ((c = (cChannel *)c->Next()) && (c->GroupSep()));
+ if (c == NULL)
+ c = Channels.Last();
+ }
+ chanVec[i] = c;
+ }
+
+ /* On first update, widgets haven't been created yet */
+ if (gridEvents == NULL) {
+ return;
+ }
+
+ gridEvents->UpdateChans(chanVec);
+ gridChans->UpdateChans(chanVec);
+ UpdateEvent(gridEvents->Event());
+}
+
+void
+cYaepghd::UpdateChans(int change)
+{
+ cChannel *c = chanVec[0];
+
+ YAEPG_INFO("Scrolling %d, current channel %d", change, c->Number());
+
+ if (change > 0) {
+ for (int i = 0; i < change; i++) {
+ while ((c = (cChannel *)c->Next()) && (c->GroupSep())) {
+ ;
+ }
+ if (c == NULL) {
+ c = Channels.First();
+ }
+ }
+ } else if (change < 0) {
+ for (int i = 0; i > change; i--) {
+ while ((c = (cChannel *)c->Prev()) && (c->GroupSep())) {
+ ;
+ }
+ if (c == NULL) {
+ c = Channels.Last();
+ }
+ }
+ }
+
+ YAEPG_INFO("New channel %d", c->Number());
+
+ UpdateChans(c);
+}
+
+void
+cYaepghd::SetTime(time_t newTime)
+{
+ startTime = newTime;
+
+ gridEvents->UpdateTime(startTime);
+ gridTime->UpdateTime(startTime);
+ gridDate->UpdateTime(startTime);
+ timeLine->UpdateTime(startTime);
+ eventDate->Update();
+ UpdateEvent(gridEvents->Event());
+}
+
+void
+cYaepghd::UpdateTime(int change)
+{
+ startTime += change;
+ if (startTime < time(NULL)) {
+ startTime = time(NULL);
+ }
+
+ gridEvents->UpdateTime(startTime);
+ gridTime->UpdateTime(startTime);
+ gridDate->UpdateTime(startTime);
+ timeLine->UpdateTime(startTime);
+ eventDate->Update();
+ UpdateEvent(gridEvents->Event());
+}
+
+void
+cYaepghd::UpdateEvent(const cEvent *newEvent)
+{
+ YAEPG_INFO("Updating event widgets");
+
+ if (event == newEvent) {
+ return;
+ }
+ event = newEvent;
+ eventTitle->UpdateEvent(event);
+ eventTime->UpdateEvent(event);
+ eventDesc->UpdateEvent(event);
+}
+
+void
+cYaepghd::SwitchToCurrentChannel(bool closeVidWin)
+{
+ const cChannel *gridChan = chanVec[gridEvents->Row()];
+
+ if (gridChan && (gridChan->Number() != cDevice::CurrentChannel())) {
+ /*
+ * The "Channel not availaible message" will cause vdr to crash. Do a
+ * "lower level" channel switch to avoid the error message.
+ *
+ * XXX Is this still true ? XXX
+ */
+ eSetChannelResult ret;
+
+ /*
+ * The eHD card doesn't seem to like changing channels while the video
+ * plane is sacled down. To get around this problem we close/reopen the
+ * video window across channel changes.
+ */
+#ifdef REEL_EHD
+ reelVidWin->Close();
+ usleep(100000); /* 1/10 of a second */
+#endif
+
+ ret = cDevice::PrimaryDevice()->SetChannel(gridChan, true);
+ if (ret != scrOk) {
+ fprintf(stderr, "SetChannel(): %d\n", ret);
+ }
+
+#ifdef REEL_EHD
+ if (closeVidWin == false) {
+ reelVidWin->Open(VID_WIN_GEOM);
+ }
+#endif
+ }
+ return;
+}
+
+void
+cYaepghd::MoveCursor(eCursorDir dir)
+{
+ if (gridEvents->MoveCursor(dir)) {
+ UpdateEvent(gridEvents->Event());
+ return;
+ }
+
+ /* Need to scroll */
+ switch (dir) {
+ case DIR_UP:
+ UpdateChans(1 * (iChannelOrder == CHANNEL_ORDER_UP ? 1 : -1));
+ break;
+ case DIR_DOWN:
+ UpdateChans(-1 * (iChannelOrder == CHANNEL_ORDER_UP ? 1 : -1));
+ break;
+ case DIR_LEFT:
+ UpdateTime(-3600);
+ break;
+ case DIR_RIGHT:
+ UpdateTime(3600);
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+}
+
+void
+cYaepghd::Draw(void)
+{
+ mainBmp->DrawBitmap(0, 0, *BG_IMAGE);
+
+ gridEvents->Draw(mainBmp);
+ gridChans->Draw(mainBmp);
+ gridTime->Draw(mainBmp);
+ gridDate->Draw(mainBmp);
+ timeLine->Draw(mainBmp);
+ eventTitle->Draw(mainBmp);
+ eventTime->Draw(mainBmp);
+ eventDesc->Draw(mainBmp);
+ eventDate->Draw(mainBmp);
+ helpBar->Draw(mainBmp);
+
+ osd->DrawBitmap(0, 0, *mainBmp);
+ osd->Flush();
+}
+
+/*
+ *****************************************************************************
+ * cMenuSetupYaepg
+ *****************************************************************************
+ */
+class cMenuSetupYaepg : public cMenuSetupPage {
+private:
+ int iNewHideMenuEntry;
+ int iNewChannelChange;
+ int iNewTimeFormat;
+ int iNewChannelOrder;
+ int iNewThemeIndex;
+
+protected:
+ virtual void Store(void);
+
+public:
+ cMenuSetupYaepg(void);
+};
+
+void cMenuSetupYaepg::Store(void)
+{
+ iHideMenuEntry = iNewHideMenuEntry;
+ iChannelChange = iNewChannelChange;
+ iTimeFormat = iNewTimeFormat;
+ iChannelOrder = iNewChannelOrder;
+
+ SetupStore("HideMenuEntry", iHideMenuEntry);
+ SetupStore("ChannelChange", iChannelChange);
+ SetupStore("TimeFormat", iTimeFormat);
+ SetupStore("ChannelOrder", iChannelOrder);
+ SetupStore("Theme", sThemeName);
+}
+
+cMenuSetupYaepg::cMenuSetupYaepg(void)
+{
+ char **themes;
+ int numThemes;
+
+ cYaepgTheme::Themes(&themes, &numThemes);
+ iNewThemeIndex = 0;
+ if (sThemeName) {
+ for (int i = 0; i < numThemes; i++) {
+ if (strcmp(sThemeName, themes[i]) == 0) {
+ iNewThemeIndex = i;
+ }
+ }
+ } else {
+ iNewThemeIndex = 0;
+ }
+
+ iNewHideMenuEntry = iHideMenuEntry;
+ iNewChannelChange = iChannelChange;
+ iNewTimeFormat = iTimeFormat;
+ iNewChannelOrder = iChannelOrder;
+
+ Add(new cMenuEditBoolItem (tr("Hide mainmenu entry"), &iNewHideMenuEntry));
+ Add(new cMenuEditStraItem (tr("Channel Change"), &iNewChannelChange, 3, CH_CHANGE_MODES));
+ Add(new cMenuEditStraItem (tr("Time format"), &iNewTimeFormat, 2, TIME_FORMATS));
+ Add(new cMenuEditStraItem (tr("Channel Order"), &iNewChannelOrder, 2, CH_ORDER_FORMATS));
+ Add(new cMenuEditStraItem (tr("Theme"), &iNewThemeIndex, numThemes, themes));
+
+ for (int i = 0; i < numThemes; i++) {
+ free(themes[i]);
+ }
+ free(themes);
+}
+
+/*
+ *****************************************************************************
+ * cPluginYaepghd
+ *****************************************************************************
+ */
+static const char *VERSION = "0.0.1";
+static const char *DESCRIPTION = "HD version of yaepg";
+static const char *MAINMENUENTRY = "Yaepghd";
+
+class cPluginYaepghd : public cPlugin {
+private:
+ cYaepghd *yaepg;
+
+public:
+ cPluginYaepghd(void);
+ virtual ~cPluginYaepghd();
+ virtual const char *Version(void) { return VERSION; }
+ virtual const char *Description(void) { return DESCRIPTION; }
+ virtual const char *CommandLineHelp(void);
+ virtual bool ProcessArgs(int argc, char *argv[]);
+ virtual bool Initialize(void);
+ virtual bool Start(void);
+ virtual void Stop(void);
+ virtual void Housekeeping(void);
+ virtual void MainThreadHook(void);
+ virtual cString Active(void);
+ virtual time_t WakeupTime(void);
+ virtual const char *MainMenuEntry(void) { return MAINMENUENTRY; }
+ virtual cOsdObject *MainMenuAction(void);
+ virtual cMenuSetupPage *SetupMenu(void);
+ virtual bool SetupParse(const char *Name, const char *Value);
+ virtual bool Service(const char *Id, void *Data = NULL);
+ virtual const char **SVDRPHelpPages(void);
+ virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
+};
+
+cPluginYaepghd::cPluginYaepghd(void)
+{
+ // Initialize any member variables here.
+ // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
+ // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
+}
+
+cPluginYaepghd::~cPluginYaepghd()
+{
+ // Clean up after yourself!
+}
+
+const char *
+cPluginYaepghd::CommandLineHelp(void)
+{
+ // Return a string that describes all known command line options.
+ return NULL;
+}
+
+bool
+cPluginYaepghd::ProcessArgs(int argc, char *argv[])
+{
+ // Implement command line argument processing here if applicable.
+ return true;
+}
+
+bool
+cPluginYaepghd::Initialize(void)
+{
+ // Initialize any background activities the plugin shall perform.
+ return true;
+}
+
+bool
+cPluginYaepghd::Start(void)
+{
+ // Start any background activities the plugin shall perform.
+#ifdef REEL_EHD
+ reelVidWin = new cReelVidWin;
+ reelVidWin->Init();
+#endif
+ return true;
+}
+
+void
+cPluginYaepghd::Stop(void)
+{
+ // Stop any background activities the plugin is performing.
+#ifdef REEL_EHD
+ delete reelVidWin;
+#endif
+}
+
+void
+cPluginYaepghd::Housekeeping(void)
+{
+ // Perform any cleanup or other regular tasks.
+}
+
+void
+cPluginYaepghd::MainThreadHook(void)
+{
+ // Perform actions in the context of the main program thread.
+ // WARNING: Use with great care - see PLUGINS.html!
+}
+
+cString
+cPluginYaepghd::Active(void)
+{
+ // Return a message string if shutdown should be postponed
+ return NULL;
+}
+
+time_t
+cPluginYaepghd::WakeupTime(void)
+{
+ // Return custom wakeup time for shutdown script
+ return 0;
+}
+
+cOsdObject *
+cPluginYaepghd::MainMenuAction(void)
+{
+ yaepg = new cYaepghd;
+ return yaepg;
+}
+
+cMenuSetupPage *
+cPluginYaepghd::SetupMenu(void)
+{
+ // Return a setup menu in case the plugin supports one.
+ return new cMenuSetupYaepg;
+}
+
+bool
+cPluginYaepghd::SetupParse(const char *Name, const char *Value)
+{
+ // Parse your own setup parameters and store their values.
+ if (!strcasecmp(Name, "HideMenuEntry")) { iHideMenuEntry = atoi(Value); }
+ else if (!strcasecmp(Name, "ChannelChange")) { iChannelChange = atoi(Value); }
+ else if (!strcasecmp(Name, "TimeFormat")) { iTimeFormat = atoi(Value); }
+ else if (!strcasecmp(Name, "ChannelOrder")) { iChannelOrder = atoi(Value); }
+ else if (!strcasecmp(Name, "Theme")) { Utf8Strn0Cpy(sThemeName, Value, MaxThemeName); }
+ else { return false; }
+
+ return true;
+}
+
+bool
+cPluginYaepghd::Service(const char *Id, void *Data)
+{
+ // Handle custom service requests from other plugins
+ return false;
+}
+
+const char **
+cPluginYaepghd::SVDRPHelpPages(void)
+{
+ // Return help text for SVDRP commands this plugin implements
+ return NULL;
+}
+
+cString
+cPluginYaepghd::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
+{
+ // Process SVDRP commands this plugin implements
+ return NULL;
+}
+
+VDRPLUGINCREATOR(cPluginYaepghd); // Don't touch this!