summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlouis <louis.braun@gmx.de>2013-01-17 13:16:44 +0100
committerlouis <louis.braun@gmx.de>2013-01-17 13:16:44 +0100
commit47c3fea545a1b4607deda1e7d2fa51cbcf89a656 (patch)
tree4109469360bfb71ce467c240a33d0738ad44c18e
downloadvdr-plugin-tvguide-47c3fea545a1b4607deda1e7d2fa51cbcf89a656.tar.gz
vdr-plugin-tvguide-47c3fea545a1b4607deda1e7d2fa51cbcf89a656.tar.bz2
Initial push tvguide 0.0.1
-rw-r--r--COPYING340
-rw-r--r--HISTORY6
-rw-r--r--Makefile122
-rw-r--r--Makefile-smaller-VDR1.7.34115
-rw-r--r--README132
-rw-r--r--channelcolumn.c274
-rw-r--r--channelcolumn.h41
-rw-r--r--config.c176
-rw-r--r--config.h67
-rw-r--r--detailview.c186
-rw-r--r--detailview.h39
-rw-r--r--epggrid.c146
-rw-r--r--epggrid.h44
-rw-r--r--footer.c59
-rw-r--r--footer.h23
-rw-r--r--imageloader.c78
-rw-r--r--imageloader.h23
-rw-r--r--messagebox.c114
-rw-r--r--messagebox.h31
-rw-r--r--osdmanager.c52
-rwxr-xr-xpo/de_DE.po139
-rw-r--r--setup.c209
-rw-r--r--setup.h54
-rw-r--r--styledpixmap.c125
-rw-r--r--styledpixmap.h35
-rw-r--r--themes/tvguide-darkblue.theme28
-rw-r--r--themes/tvguide-default.theme27
-rw-r--r--timeline.c115
-rw-r--r--timeline.h22
-rw-r--r--timer.c106
-rw-r--r--timer.h32
-rw-r--r--tvguide.c194
-rw-r--r--tvguideosd.c541
-rw-r--r--tvguideosd.h45
34 files changed, 3740 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..b1cf711
--- /dev/null
+++ b/HISTORY
@@ -0,0 +1,6 @@
+VDR Plugin 'tvguide' Revision History
+-------------------------------------
+
+2012-08-12: Version 0.0.1
+
+- Initial revision.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..7c47228
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,122 @@
+#
+# 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.
+#
+PLUGIN = tvguide
+
+### 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 directory environment:
+
+# Use package data if installed...otherwise assume we're under the VDR source directory:
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+LIBDIR = $(DESTDIR)$(call PKGCFG,libdir)
+LOCDIR = $(DESTDIR)$(call PKGCFG,locdir)
+PLGCFG = $(call PKGCFG,plgcfg)
+CONFDIR= $(DESTDIR)$(call PKGCFG,configdir)
+TMPDIR ?= /tmp
+
+### The compiler options:
+export CFLAGS = $(call PKGCFG,cflags)
+export CXXFLAGS = $(call PKGCFG,cxxflags)
+
+### Allow user defined options to overwrite defaults:
+-include $(PLGCFG)
+
+APIVERSION = $(call PKGCFG,apiversion)
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### The name of the shared object file:
+
+SOFILE = libvdr-$(PLUGIN).so
+
+### Includes and Defines (add further entries here):
+
+INCLUDES +=
+INCLUDES += -I/usr/include/ImageMagick
+
+DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).o
+
+### The main target:
+
+all: $(SOFILE) 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
+I18Npo = $(wildcard $(PODIR)/*.po)
+I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file))))
+I18Nmsgs = $(addprefix $(LOCDIR)/, $(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 --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='<see README>' -o $@ `ls $^`
+
+%.po: $(I18Npot)
+ msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $<
+ @touch $@
+
+$(I18Nmsgs): $(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
+ install -D -m644 $< $@
+
+.PHONY: i18n
+i18n: $(I18Nmo) $(I18Npot)
+
+install-i18n: $(I18Nmsgs)
+
+### Targets:
+$(SOFILE): $(OBJS)
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) -lMagick++ -o $@
+
+install-lib: $(SOFILE)
+ install -D $^ $(LIBDIR)/$^.$(APIVERSION)
+
+install-themes:
+ @mkdir -p $(CONFDIR)/themes
+ cp themes/* $(CONFDIR)/themes
+
+install: install-lib install-i18n install-themes
+
+dist: $(I18Npo) 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 $(PODIR)/*.mo $(PODIR)/*.pot
+ @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~
diff --git a/Makefile-smaller-VDR1.7.34 b/Makefile-smaller-VDR1.7.34
new file mode 100644
index 0000000..49b84cc
--- /dev/null
+++ b/Makefile-smaller-VDR1.7.34
@@ -0,0 +1,115 @@
+#
+# 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 = tvguide
+
+### 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 ?= -g -O3 -Wall -Woverloaded-virtual -Wno-parentheses
+
+### The directory environment:
+
+VDRDIR ?= ../../..
+LIBDIR ?= ../../lib
+TMPDIR ?= /tmp
+
+### Make sure that necessary options are included:
+
+include $(VDRDIR)/Make.global
+
+### 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
+INCLUDES += -I/usr/include/ImageMagick
+
+DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).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 --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --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) $(LDFLAGS) -shared $(OBJS) -lMagick++ -o $@
+ @cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
+
+dist: $(I18Npo) 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..1003a21
--- /dev/null
+++ b/README
@@ -0,0 +1,132 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Louis Braun <louis DOT braun AT gmx DOT de>
+
+Project's homepage: URL
+
+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.
+
+Requirements
+------------
+
+- VDR version >= 1.7.17 (TrueColor OSD is mandatorily needed)
+
+- Installed ImageMagick for showing png/jpg Channel Logos and EPG Images
+
+
+Description
+-----------
+
+"TvGuide" is a highly customizable 2D EPG viewer plugin.
+
+Installation
+------------
+
+After "normal" Plugin installation copy the themes from
+
+/put/your/path/here/VDR/PLUGINS/src/tvguide/themes/
+
+to
+
+VDRCONF/themes/
+
+Options
+-------
+
+-l path, --logodir=path
+ Path to the logos (Default: <vdrconfdir>/plugins/tvguide/channellogos/).
+
+-i path, --epgimages=path
+ Path to the epgimages (Default: <vdrconfdir>/plugins/tvguide/epgimages/).
+
+
+Usage
+-----
+
+Remote Control Keys:
+
+Up/Down/Left/Right: Navigation in the EPG grid
+Ok: Detailed EPG View of the selected grid
+ 2nd Ok closes the detailed view
+Red: Set recording timer for the currently selected grid
+Green / Yellow: Jump (default) five channels back / forward
+Blue: Switch to currently selected channel
+1 / 3: Big jump (default 3h) back / forward in time
+4 / 6: huge jump (default 24h) back / forward in time
+7 / 9: jump to previous / next prime time (8pm)
+Exit: closes plugin
+
+
+Setup Options
+-------------
+
+* General:
+
+- Number of Channels / Columns:
+ Number of columns per screen (max. 8)
+ Keep in mind that the True Color OSD displays 64 Pixmaps in maximum, and each EPG
+ entry is a dedicated Pixmap. So if this value is too large, maybe not all EPG
+ information is shown on the screen.
+
+- Channel Jump (Keys Green / Yellow):
+ Number of channels to jump back / forward, counted from the currently selected
+ channel (channel to which the selected EPG entry belongs)
+
+- Time to display vertically in minutes
+ With this value the number of minutes per screen is determinated. The value is
+ an approximately value, because rounded values are used during calculation.
+ int((OSD Height - Header Height - Footer Height) / value)
+ --> Number of Pixel for one minute
+
+- Big Step (Keys 1 / 3) in hours
+ Hours to jump vertically with keys 1 / 3
+
+- Huge Step (Keys 4 / 6) in hours
+ Hours to jump vertically with keys 4 / 6
+
+- Time Format (12h/24h)
+ Switching between 12h and 24h time format
+
+* Screen Layout:
+
+- Theme
+ Used Theme, theme files have to be placed accordingly
+
+- Width of left Time Column
+ Width of almost left column in Pixel
+
+- Height of Header (Channel Logos)
+ Height of header row in Pixel
+
+- Height of Footer (Buttons)
+ Height of footer with color buttons in Pixel
+
+- Show Channel Logos
+ show / hide channel logos, if logos are shown:
+ - Logo Extension
+ jpg / png
+ - Logo width
+ in Pixel
+ - Logo height
+ in Pixel
+
+- Show EPG Images
+ show / hide EPG images, if images are shown:
+ - EPG Image width
+ in Pixel
+ - EPG Image height
+ in Pixel
+
+* Fonts and Fontsizes:
+
+- Font:
+ Used Font, all Fonts installed on your system are shown
+- various font sizes:
+ Size in Pixel used for described purpose
+
diff --git a/channelcolumn.c b/channelcolumn.c
new file mode 100644
index 0000000..de5962b
--- /dev/null
+++ b/channelcolumn.c
@@ -0,0 +1,274 @@
+#include "channelcolumn.h"
+
+cChannelColumn::cChannelColumn(int num, cChannel *channel, cMyTime *myTime) {
+ this->channel = channel;
+ this->num = num;
+ this->myTime = myTime;
+ hasTimer = channel->HasTimer();
+}
+
+cChannelColumn::~cChannelColumn(void) {
+ grids.Clear();
+}
+
+void cChannelColumn::clearGrids() {
+ grids.Clear();
+}
+
+void cChannelColumn::createHeader() {
+ color = theme.Color(clrHeader);
+ colorBlending = theme.Color(clrHeaderBlending);
+ caller = cString::sprintf("channelcolumn %s", channel->Name());
+ pixmap = osdManager.requestPixmap(2, cRect(tvguideConfig.timeColWidth + num*tvguideConfig.colWidth, 0, tvguideConfig.colWidth, tvguideConfig.headerHeight),
+ cRect::Null, *caller);
+ if (!pixmap) {
+ return;
+ }
+ drawBackground();
+ cTextWrapper tw;
+ cString headerText = cString::sprintf("%d - %s", channel->Number(), channel->Name());
+ tw.Set(*headerText, tvguideConfig.FontHeader, tvguideConfig.colWidth - 8);
+ int lines = tw.Lines();
+ int lineHeight = tvguideConfig.FontHeader->Height();
+ int yStart = (tvguideConfig.headerHeight - lines*lineHeight)/2 + 8;
+
+ if (!tvguideConfig.hideChannelLogos) {
+ cImageLoader imgLoader;
+ if (imgLoader.LoadLogo(channel->Name())) {
+ cImage logo = imgLoader.GetImage();
+ int logoX = (tvguideConfig.colWidth - tvguideConfig.logoWidth)/2;
+ pixmap->DrawImage(cPoint(logoX, 5), logo);
+ }
+ yStart = tvguideConfig.logoHeight + 8;
+ }
+ for (int i=0; i<lines; i++) {
+ int textWidth = tvguideConfig.FontHeader->Width(tw.GetLine(i));
+ int xText = (tvguideConfig.colWidth - textWidth) / 2;
+ if (xText < 0)
+ xText = 0;
+ pixmap->DrawText(cPoint(xText, yStart + i*lineHeight), tw.GetLine(i), theme.Color(clrFontHeader), clrTransparent, tvguideConfig.FontHeader);
+ }
+ drawBorder();
+}
+
+void cChannelColumn::drawHeader() {
+ pixmap->SetViewPort(cRect(tvguideConfig.timeColWidth + num*tvguideConfig.colWidth, 0, tvguideConfig.colWidth, tvguideConfig.headerHeight));
+}
+
+bool cChannelColumn::readGrids() {
+ schedules = cSchedules::Schedules(schedulesLock);
+ const cSchedule *Schedule = NULL;
+ Schedule = schedules->GetSchedule(channel);
+ if (!Schedule) {
+ return false;
+ }
+ bool eventFound = false;
+ const cEvent *event = Schedule->GetEventAround(myTime->GetStart());
+ if (event != NULL) {
+ eventFound = true;
+ } else {
+ for (int i=1; i<6; i++) {
+ event = Schedule->GetEventAround(myTime->GetStart()+i*5*60);
+ if (event) {
+ eventFound = true;
+ break;
+ }
+ }
+ }
+ if (eventFound) {
+ bool col = true;
+ for (; event; event = Schedule->Events()->Next(event)) {
+ cEpgGrid *grid = new cEpgGrid(this, event);
+ grid->setText();
+ grid->SetColor(col);
+ col = !col;
+ grids.Add(grid);
+ if (event->EndTime() > myTime->GetStop()) {
+ break;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+
+}
+
+void cChannelColumn::drawGrids() {
+ for (cEpgGrid *grid = grids.First(); grid; grid = grids.Next(grid)) {
+ grid->SetViewportHeight();
+ grid->PositionPixmap();
+ grid->Draw();
+ }
+}
+
+int cChannelColumn::getX() {
+ return tvguideConfig.timeColWidth + num*tvguideConfig.colWidth;
+}
+
+cEpgGrid * cChannelColumn::getActive() {
+ cMyTime t;
+ t.Now();
+ for (cEpgGrid *grid = grids.First(); grid; grid = grids.Next(grid)) {
+ if (grid->isActiveInitial(t.Get()))
+ return grid;
+ }
+ return grids.First();
+}
+
+cEpgGrid * cChannelColumn::getNext(cEpgGrid *activeGrid) {
+ if (activeGrid == NULL)
+ return NULL;
+ cEpgGrid *next = grids.Next(activeGrid);
+ if (next)
+ return next;
+ return NULL;
+}
+
+cEpgGrid * cChannelColumn::getPrev(cEpgGrid *activeGrid) {
+ if (activeGrid == NULL)
+ return NULL;
+ cEpgGrid *prev = grids.Prev(activeGrid);
+ if (prev)
+ return prev;
+ return NULL;
+}
+
+cEpgGrid * cChannelColumn::getNeighbor(cEpgGrid *activeGrid) {
+ if (!activeGrid)
+ return NULL;
+ cEpgGrid *neighbor = NULL;
+ int overlap = 0;
+ int overlapNew = 0;
+ cEpgGrid *grid = NULL;
+ grid = grids.First();
+ if (grid) {
+ for (; grid; grid = grids.Next(grid)) {
+ if ( (grid->StartTime() == activeGrid->StartTime()) ) {
+ neighbor = grid;
+ break;
+ }
+ overlapNew = activeGrid->calcOverlap(grid);
+ if (overlapNew > overlap) {
+ neighbor = grid;
+ overlap = overlapNew;
+ }
+ }
+ }
+ if (!neighbor)
+ neighbor = grids.First();
+ return neighbor;
+}
+
+void cChannelColumn::AddNewGridsAtStart() {
+ cEpgGrid *firstGrid = NULL;
+ firstGrid = grids.First();
+ if (firstGrid == NULL) {
+ //no epg, completely new.
+ schedules = cSchedules::Schedules(schedulesLock);
+ const cSchedule *Schedule = NULL;
+ Schedule = schedules->GetSchedule(channel);
+ if (!Schedule)
+ return;
+ const cEvent *event = Schedule->GetEventAround(myTime->GetStart());
+ if (!event)
+ return;
+ cEpgGrid *grid = new cEpgGrid(this, event);
+ grid->setText();
+ grid->SetColor(true);
+ grids.Ins(grid, grids.First());
+ return;
+ } else {
+ //if first event is long enough, nothing to do.
+ if (firstGrid->StartTime() <= myTime->GetStart()) {
+ return;
+ }
+ //if not, i have to add new ones to the list
+ schedules = cSchedules::Schedules(schedulesLock);
+ const cSchedule *Schedule = NULL;
+ Schedule = schedules->GetSchedule(channel);
+ if (!Schedule)
+ return;
+ bool col = !(firstGrid->IsColor1());
+ for (const cEvent *event = Schedule->GetEventAround(firstGrid->StartTime()-60); event; event = Schedule->Events()->Prev(event)) {
+ if (!event)
+ return;
+ cEpgGrid *grid = new cEpgGrid(this, event);
+ grid->setText();
+ grid->SetColor(col);
+ col = !col;
+ grids.Ins(grid, firstGrid);
+ firstGrid = grid;
+ if (event->StartTime() <= myTime->GetStart()) {
+ break;
+ }
+ }
+ }
+}
+
+void cChannelColumn::AddNewGridsAtEnd() {
+ cEpgGrid *lastGrid = NULL;
+ lastGrid = grids.Last();
+ if (lastGrid == NULL)
+ return;
+ //if last event is long enough, nothing to do.
+ if (lastGrid->EndTime() > myTime->GetStop()) {
+ return;
+ }
+ //if not, i have to add new ones to the list
+ schedules = cSchedules::Schedules(schedulesLock);
+ const cSchedule *Schedule = NULL;
+ Schedule = schedules->GetSchedule(channel);
+ if (!Schedule)
+ return;
+ bool col = !(lastGrid->IsColor1());
+ for (const cEvent *event = Schedule->GetEventAround(lastGrid->EndTime()+60); event; event = Schedule->Events()->Next(event)) {
+ if (!event)
+ return;
+ cEpgGrid *grid = new cEpgGrid(this, event);
+ grid->setText();
+ grid->SetColor(col);
+ col = !col;
+ grids.Add(grid);
+ if (event->EndTime() > myTime->GetStop()) {
+ break;
+ }
+ }
+}
+
+void cChannelColumn::ClearOutdatedStart() {
+ bool goOn = true;
+ cEpgGrid *firstGrid = NULL;
+ while (goOn) {
+ firstGrid = grids.First();
+ if ((firstGrid != NULL)&&(firstGrid->EndTime() < myTime->GetStart())) {
+ grids.Del(firstGrid);
+ firstGrid = NULL;
+ } else {
+ goOn = false;
+ }
+ }
+}
+
+void cChannelColumn::ClearOutdatedEnd() {
+ bool goOn = true;
+ cEpgGrid *lastGrid = NULL;
+ while (goOn) {
+ lastGrid = grids.Last();
+ if ((lastGrid != NULL)&&(lastGrid->StartTime() > myTime->GetStop())) {
+ grids.Del(lastGrid);
+ lastGrid = NULL;
+ } else {
+ goOn = false;
+ }
+ }
+}
+
+void cChannelColumn::dumpGrids() {
+ esyslog("------Channel %s ---------", channel->Name());
+
+ for (cEpgGrid *grid = grids.First(); grid; grid = grids.Next(grid)) {
+ grid->debug();
+ }
+
+}
diff --git a/channelcolumn.h b/channelcolumn.h
new file mode 100644
index 0000000..28064d2
--- /dev/null
+++ b/channelcolumn.h
@@ -0,0 +1,41 @@
+#ifndef __TVGUIDE_CHANNELCOLUMN_H
+#define __TVGUIDE_CHANNELCOLUMN_H
+
+class cEpgGrid;
+// --- cChannelColumn -------------------------------------------------------------
+
+class cChannelColumn : public cListObject, public cStyledPixmap {
+friend class cEpgGrid;
+private:
+ cMyTime *myTime;
+ int num;
+ cChannel *channel;
+ cList<cEpgGrid> grids;
+ cSchedulesLock schedulesLock;
+ const cSchedules *schedules;
+ bool hasTimer;
+public:
+ cChannelColumn(int num, cChannel *channel, cMyTime *myTime);
+ virtual ~cChannelColumn(void);
+ void createHeader();
+ void drawHeader();
+ bool readGrids();
+ void drawGrids();
+ int getX();
+ cChannel * getChannel() {return channel;}
+ cEpgGrid * getActive();
+ cEpgGrid * getNext(cEpgGrid *activeGrid);
+ cEpgGrid * getPrev(cEpgGrid *activeGrid);
+ cEpgGrid * getNeighbor(cEpgGrid *activeGrid);
+ void AddNewGridsAtStart();
+ void AddNewGridsAtEnd();
+ void ClearOutdatedStart();
+ void ClearOutdatedEnd();
+ int GetNum() {return num;};
+ void SetNum(int num) {this->num = num;};
+ void setTimer() {hasTimer = true;};
+ void clearGrids();
+ void dumpGrids();
+};
+
+#endif //__TVGUIDE_CHANNELCOLUMN_H \ No newline at end of file
diff --git a/config.c b/config.c
new file mode 100644
index 0000000..229f8aa
--- /dev/null
+++ b/config.c
@@ -0,0 +1,176 @@
+#include "config.h"
+
+enum {
+ e12Hours,
+ e24Hours
+};
+
+cTvguideConfig::cTvguideConfig() {
+ osdWidth = 0;
+ osdHeight = 0;
+ colWidth = 0;
+ channelCols = 5;
+ displayTime = 160;
+ minuteHeight = 0;
+ timeColWidth = 120;
+ headerHeight = 120;
+ footerHeight = 80;
+ stepMinutes = 30;
+ bigStepHours = 3;
+ hugeStepHours = 24;
+ jumpChannels = 5;
+ hideChannelLogos = 0;
+ logoWidth = 130;
+ logoHeight = 73;
+ logoExtension = 0;
+ hideEpgImages = 0;
+ epgImageWidth = 315;
+ epgImageHeight = 240;
+ fontIndex = 0;
+ fontNameDefault = "VDRSymbols Sans:Book";
+ fontHeaderSize = 33;
+ fontGridSize = 27;
+ fontGridSmallSize = 24;
+ fontTimeLineWeekdaySize = 40;
+ fontTimeLineDateSize = 33;
+ fontTimeLineTimeSize = 0;
+ fontTimeLineTimeSizeDef12 = 24;
+ fontTimeLineTimeSizeDef24 = 33;
+ fontButtonSize = 33;
+ fontDetailViewSize = 33;
+ fontDetailHeaderSize = 40;
+ fontMessageBoxSize = 33;
+ fontMessageBoxLargeSize = 40;
+
+
+ FontHeader = NULL;
+ FontGrid = NULL;
+ FontGridSmall = NULL;
+ FontTimeLineWeekday = NULL;
+ FontTimeLineDate = NULL;
+ FontTimeLineTime = NULL;
+ FontButton = NULL;
+ FontDetailView = NULL;
+ FontDetailHeader = NULL;
+ FontMessageBox = NULL;
+ FontMessageBoxLarge = NULL;
+
+ timeFormat = 1;
+ themeIndex = 0;
+ useBlending = 1;
+ roundedCorners = 0;
+}
+
+cTvguideConfig::~cTvguideConfig() {
+ delete FontHeader;
+ delete FontGrid;
+ delete FontGridSmall;
+ delete FontTimeLineWeekday;
+ delete FontTimeLineDate;
+ delete FontTimeLineTime;
+ delete FontButton;
+ delete FontDetailView;
+ delete FontDetailHeader;
+ delete FontMessageBox;
+ delete FontMessageBoxLarge;
+}
+
+void cTvguideConfig::setDynamicValues(int width, int height) {
+ osdWidth = width;
+ osdHeight = height;
+ colWidth = (osdWidth - timeColWidth) / channelCols;
+ minuteHeight = (osdHeight - headerHeight - footerHeight) / displayTime;
+
+ if (!fontTimeLineTimeSize) {
+ if (timeFormat == e12Hours) {
+ fontTimeLineTimeSize = fontTimeLineTimeSizeDef12;
+ } else if (timeFormat == e24Hours) {
+ fontTimeLineTimeSize = fontTimeLineTimeSizeDef24;
+ }
+ } else if ((fontTimeLineTimeSize == fontTimeLineTimeSizeDef12) && (timeFormat == e24Hours)) {
+ fontTimeLineTimeSize = fontTimeLineTimeSizeDef24;
+ } else if ((fontTimeLineTimeSize == fontTimeLineTimeSizeDef24) && (timeFormat == e12Hours)) {
+ fontTimeLineTimeSize = fontTimeLineTimeSizeDef12;
+ }
+ cString fontname;
+ if (fontIndex == 0) {
+ fontname = fontNameDefault;
+ } else {
+ cStringList availableFonts;
+ cFont::GetAvailableFontNames(&availableFonts);
+ if (availableFonts[fontIndex-1]) {
+ fontname = availableFonts[fontIndex-1];
+ } else
+ fontname = fontNameDefault;
+ }
+ cFont *test = NULL;
+ test = cFont::CreateFont(*fontname, fontHeaderSize);
+ if (!test) {
+ fontname = DefaultFontSml;
+ }
+ delete test;
+ FontHeader = cFont::CreateFont(*fontname, fontHeaderSize);
+ FontGrid = cFont::CreateFont(*fontname, fontGridSize);
+ FontGridSmall = cFont::CreateFont(*fontname, fontGridSmallSize);
+ FontTimeLineWeekday = cFont::CreateFont(*fontname, fontTimeLineWeekdaySize);
+ FontTimeLineDate = cFont::CreateFont(*fontname, fontTimeLineDateSize);
+ FontTimeLineTime = cFont::CreateFont(*fontname, fontTimeLineTimeSize);
+ FontButton = cFont::CreateFont(*fontname, fontButtonSize);
+ FontDetailView = cFont::CreateFont(*fontname, fontDetailViewSize);
+ FontDetailHeader = cFont::CreateFont(*fontname, fontDetailHeaderSize);
+ FontMessageBox = cFont::CreateFont(*fontname, fontMessageBoxSize);
+ FontMessageBoxLarge = cFont::CreateFont(*fontname, fontMessageBoxLargeSize);
+}
+
+void cTvguideConfig::SetLogoPath(cString path) {
+ logoPath = path;
+}
+
+void cTvguideConfig::SetImagesPath(cString path) {
+ epgImagePath = path;
+}
+
+void cTvguideConfig::loadTheme() {
+ cThemes themes;
+ themes.Load(*cString("tvguide"));
+ const char *FileName = themes.FileName(themeIndex);
+ if (access(FileName, F_OK) == 0) {
+ ::theme.Load(FileName);
+ }
+}
+
+bool cTvguideConfig::SetupParse(const char *Name, const char *Value) {
+ if (strcmp(Name, "timeFormat") == 0) timeFormat = atoi(Value);
+ else if (strcmp(Name, "themeIndex") == 0) themeIndex = atoi(Value);
+ else if (strcmp(Name, "useBlending") == 0) useBlending = atoi(Value);
+ else if (strcmp(Name, "roundedCorners") == 0) roundedCorners = atoi(Value);
+ else if (strcmp(Name, "channelCols") == 0) channelCols = atoi(Value);
+ else if (strcmp(Name, "displayTime") == 0) displayTime = atoi(Value);
+ else if (strcmp(Name, "hideChannelLogos") == 0) hideChannelLogos = atoi(Value);
+ else if (strcmp(Name, "logoExtension") == 0) logoExtension = atoi(Value);
+ else if (strcmp(Name, "logoWidth") == 0) logoWidth = atoi(Value);
+ else if (strcmp(Name, "logoHeight") == 0) logoHeight = atoi(Value);
+ else if (strcmp(Name, "bigStepHours") == 0) bigStepHours = atoi(Value);
+ else if (strcmp(Name, "hugeStepHours") == 0) hugeStepHours = atoi(Value);
+ else if (strcmp(Name, "jumpChannels") == 0) jumpChannels = atoi(Value);
+ else if (strcmp(Name, "hideEpgImages") == 0) hideEpgImages = atoi(Value);
+ else if (strcmp(Name, "epgImageWidth") == 0) epgImageWidth = atoi(Value);
+ else if (strcmp(Name, "epgImageHeight") == 0) epgImageHeight = atoi(Value);
+ else if (strcmp(Name, "timeColWidth") == 0) timeColWidth = atoi(Value);
+ else if (strcmp(Name, "headerHeight") == 0) headerHeight = atoi(Value);
+ else if (strcmp(Name, "footerHeight") == 0) footerHeight = atoi(Value);
+ else if (strcmp(Name, "fontIndex") == 0) fontIndex = atoi(Value);
+ else if (strcmp(Name, "fontHeaderSize") == 0) fontHeaderSize = atoi(Value);
+ else if (strcmp(Name, "fontGridSize") == 0) fontGridSize = atoi(Value);
+ else if (strcmp(Name, "fontGridSmallSize") == 0) fontGridSmallSize = atoi(Value);
+ else if (strcmp(Name, "fontTimeLineWeekdaySize") == 0) fontTimeLineWeekdaySize = atoi(Value);
+ else if (strcmp(Name, "fontTimeLineDateSize") == 0) fontTimeLineDateSize = atoi(Value);
+ else if (strcmp(Name, "fontTimeLineTimeSize") == 0) fontTimeLineTimeSize = atoi(Value);
+ else if (strcmp(Name, "fontButtonSize") == 0) fontButtonSize = atoi(Value);
+ else if (strcmp(Name, "fontDetailViewSize") == 0) fontDetailViewSize = atoi(Value);
+ else if (strcmp(Name, "fontDetailHeaderSize") == 0) fontDetailHeaderSize = atoi(Value);
+ else if (strcmp(Name, "fontMessageBoxSize") == 0) fontMessageBoxSize = atoi(Value);
+ else if (strcmp(Name, "fontMessageBoxLargeSize") == 0) fontMessageBoxLargeSize = atoi(Value);
+ else return false;
+ return true;
+} \ No newline at end of file
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..0e184c8
--- /dev/null
+++ b/config.h
@@ -0,0 +1,67 @@
+#ifndef __TVGUIDE_CONFIG_H
+#define __TVGUIDE_CONFIG_H
+
+class cTvguideConfig {
+ public:
+ cTvguideConfig();
+ ~cTvguideConfig();
+ void SetLogoPath(cString path);
+ void SetImagesPath(cString path);
+ int osdWidth;
+ int osdHeight;
+ int colWidth;
+ int channelCols;
+ int displayTime;
+ int minuteHeight;
+ int timeColWidth;
+ int headerHeight;
+ int footerHeight;
+ int stepMinutes;
+ int bigStepHours;
+ int hugeStepHours;
+ int jumpChannels;
+ int hideChannelLogos;
+ int logoWidth;
+ int logoHeight;
+ cString logoPath;
+ int logoExtension;
+ int hideEpgImages;
+ int epgImageWidth;
+ int epgImageHeight;
+ cString epgImagePath;
+ int fontIndex;
+ const char *fontNameDefault;
+ int fontHeaderSize;
+ int fontGridSize;
+ int fontGridSmallSize;
+ int fontTimeLineWeekdaySize;
+ int fontTimeLineDateSize;
+ int fontTimeLineTimeSize;
+ int fontTimeLineTimeSizeDef12;
+ int fontTimeLineTimeSizeDef24;
+ int fontButtonSize;
+ int fontDetailViewSize;
+ int fontDetailHeaderSize;
+ int fontMessageBoxSize;
+ int fontMessageBoxLargeSize;
+ const cFont *FontHeader;
+ const cFont *FontGrid;
+ const cFont *FontGridSmall;
+ const cFont *FontTimeLineWeekday;
+ const cFont *FontTimeLineDate;
+ const cFont *FontTimeLineTime;
+ const cFont *FontButton;
+ const cFont *FontDetailView;
+ const cFont *FontDetailHeader;
+ const cFont *FontMessageBox;
+ const cFont *FontMessageBoxLarge;
+ int timeFormat;
+ int themeIndex;
+ int useBlending;
+ int roundedCorners;
+ void setDynamicValues(int width, int height);
+ bool SetupParse(const char *Name, const char *Value);
+ void loadTheme();
+};
+
+#endif //__TVGUIDE_CONFIG_H \ No newline at end of file
diff --git a/detailview.c b/detailview.c
new file mode 100644
index 0000000..2c791e4
--- /dev/null
+++ b/detailview.c
@@ -0,0 +1,186 @@
+#include "detailview.h"
+
+cDetailView::cDetailView(cEpgGrid *grid) {
+ this->grid = grid;
+ this->event = grid->GetEvent();
+ imgScrollBar = NULL;
+ FrameTime = 40; // ms
+ FadeTime = 500; // ms
+ borderWidth = 100; //px
+ headerHeight = max (80 + tvguideConfig.logoHeight + 3 * tvguideConfig.FontDetailHeader->Height(), // border + logo + 3 Lines
+ 80 + tvguideConfig.epgImageHeight);
+ description.Set(event->Description(), tvguideConfig.FontDetailView, tvguideConfig.osdWidth-2*borderWidth - 50 - 40);
+ contentScrollable = setContentDrawportHeight();
+ createPixmaps();
+}
+
+cDetailView::~cDetailView(void){
+ delete header;
+ osdManager.releasePixmap(content);
+ osdManager.releasePixmap(scrollBar);
+ osdManager.releasePixmap(footer);
+ delete imgScrollBar;
+}
+
+bool cDetailView::setContentDrawportHeight() {
+ int linesContent = description.Lines() + 1;
+ heightContent = linesContent * tvguideConfig.FontDetailView->Height();
+ if (heightContent > (tvguideConfig.osdHeight - 2 * borderWidth - headerHeight))
+ return true;
+ else
+ return false;
+}
+
+void cDetailView::createPixmaps() {
+ int scrollBarWidth = 50;
+
+ header = new cStyledPixmap(osdManager.requestPixmap(5, cRect(borderWidth, borderWidth, tvguideConfig.osdWidth - 2*borderWidth, headerHeight), cRect::Null, "detailViewHeader"), "detailViewHeader");
+ header->SetAlpha(0);
+ header->setColor(theme.Color(clrHeader), theme.Color(clrHeaderBlending));
+ content = osdManager.requestPixmap(5, cRect(borderWidth, borderWidth + headerHeight, tvguideConfig.osdWidth - 2*borderWidth - scrollBarWidth, tvguideConfig.osdHeight-2*borderWidth-headerHeight),
+ cRect(0,0, tvguideConfig.osdWidth - 2*borderWidth - scrollBarWidth, max(heightContent, tvguideConfig.osdHeight-2*borderWidth-headerHeight)));
+ content->SetAlpha(0);
+ header->setColor(theme.Color(clrHeader), theme.Color(clrHeaderBlending));
+
+ scrollBar = osdManager.requestPixmap(5, cRect(tvguideConfig.osdWidth-borderWidth-scrollBarWidth, borderWidth + headerHeight, scrollBarWidth, tvguideConfig.osdHeight-2*borderWidth-headerHeight));
+ scrollBar->SetAlpha(0);
+
+ footer = osdManager.requestPixmap(5, cRect(borderWidth, borderWidth + headerHeight + content->ViewPort().Height(), tvguideConfig.osdWidth - 2*borderWidth, 3));
+ footer->SetAlpha(0);
+ footer->Fill(clrWhite);
+}
+
+void cDetailView::drawHeader() {
+ header->drawBackground();
+ header->drawBoldBorder();
+
+ int lineHeight = tvguideConfig.FontDetailHeader->Height();
+ int offset = 30;
+ cImageLoader imgLoader;
+ if (tvguideConfig.hideChannelLogos) {
+ header->DrawText(cPoint(20, offset + 10), grid->column->getChannel()->Name(), theme.Color(clrFont), clrTransparent, tvguideConfig.FontDetailHeader);
+ offset += lineHeight + 10;
+ } else {
+ if (imgLoader.LoadLogo(grid->column->getChannel()->Name())) {
+ cImage logo = imgLoader.GetImage();
+ header->DrawImage(cPoint(20, 20), logo);
+ }
+ offset += tvguideConfig.logoHeight;
+ }
+
+ if (!tvguideConfig.hideEpgImages) {
+ if (imgLoader.LoadEPGImage(event->EventID())) {
+ cImage epgImage = imgLoader.GetImage();
+ int epgImageX = header->Width() - 30 - tvguideConfig.epgImageWidth;
+ int epgImageY = (header->Height() - 10 - tvguideConfig.epgImageHeight) / 2;
+ header->DrawRectangle(cRect(epgImageX-2, epgImageY-2, tvguideConfig.epgImageWidth + 4, tvguideConfig.epgImageHeight + 4), theme.Color(clrBorder));
+ header->DrawImage(cPoint(epgImageX, epgImageY), epgImage);
+ }
+ }
+
+ header->DrawText(cPoint(20, offset), event->Title(), theme.Color(clrFont), clrTransparent, tvguideConfig.FontDetailHeader);
+ cString datetime = cString::sprintf("%s, %s - %s (%d min)", *event->GetDateString(), *event->GetTimeString(), *event->GetEndTimeString(), event->Duration()/60);
+ header->DrawText(cPoint(20, offset + lineHeight), *datetime, theme.Color(clrFont), clrTransparent, tvguideConfig.FontDetailView);
+ header->DrawText(cPoint(20, offset + 2 * lineHeight), event->ShortText(), theme.Color(clrFont), clrTransparent, tvguideConfig.FontDetailView);
+}
+
+void cDetailView::drawContent() {
+ content->Fill(theme.Color(clrBorder));
+ content->DrawRectangle(cRect(2, 0, content->ViewPort().Width() - 2, content->DrawPort().Height()), theme.Color(clrBackground));
+
+ int textHeight = tvguideConfig.FontDetailView->Height();
+ int textLines = description.Lines();
+
+ for (int i=0; i<textLines; i++) {
+ content->DrawText(cPoint(20, 20 + i*textHeight), description.GetLine(i), theme.Color(clrFont), clrTransparent, tvguideConfig.FontDetailView);
+ }
+}
+
+void cDetailView::drawScrollbar() {
+ scrollBar->Fill(theme.Color(clrBorder));
+ double scrollBarOffset = 0.0;
+ if (contentScrollable) {
+ heightScrollbar = ( (double)scrollBar->ViewPort().Height() ) / (double)heightContent * ( (double)scrollBar->ViewPort().Height() );
+ scrollBarOffset = (-1.0)*(double)content->DrawPort().Point().Y() / (double)(content->DrawPort().Height() - (tvguideConfig.osdHeight-2*borderWidth-headerHeight));
+ scrollBarOffset *= ( (double)scrollBar->ViewPort().Height()-7.0 - heightScrollbar);
+ scrollBarOffset++;
+ } else {
+ heightScrollbar = scrollBar->ViewPort().Height();
+ }
+ scrollBar->DrawRectangle(cRect(3,0,scrollBar->ViewPort().Width()-6, scrollBar->ViewPort().Height()), theme.Color(clrBackground));
+ if (imgScrollBar == NULL) {
+ imgScrollBar = createScrollbar(scrollBar->ViewPort().Width()-10, heightScrollbar, theme.Color(clrHighlight), theme.Color(clrHighlightBlending));
+ }
+ scrollBar->DrawImage(cPoint(5, 2 + scrollBarOffset), *imgScrollBar);
+}
+
+void cDetailView::scrollUp() {
+ if (contentScrollable) {
+ int newDrawportHeight = content->DrawPort().Point().Y() + tvguideConfig.FontDetailView->Height();
+ content->SetDrawPortPoint(cPoint(0, min(newDrawportHeight,0)));
+ drawScrollbar();
+ }
+}
+
+void cDetailView::scrollDown() {
+ if (contentScrollable) {
+ int newDrawportHeight = content->DrawPort().Point().Y() - tvguideConfig.FontDetailView->Height();
+ int maxDrawportHeight = (content->DrawPort().Height() - (tvguideConfig.osdHeight-2*borderWidth-headerHeight));
+ content->SetDrawPortPoint(cPoint(0, max(newDrawportHeight,(-1)*maxDrawportHeight)));
+ drawScrollbar();
+ }
+}
+
+cImage *cDetailView::createScrollbar(int width, int height, tColor clrBgr, tColor clrBlend) {
+ cImage *image = new cImage(cSize(width, height));
+ image->Fill(clrBgr);
+ if (tvguideConfig.useBlending) {
+ int numSteps = 64;
+ int alphaStep = 0x03;
+ if (height < 30)
+ return image;
+ else if (height < 100) {
+ numSteps = 32;
+ alphaStep = 0x06;
+ }
+ int stepY = 0.5*height / numSteps;
+ if (stepY == 0)
+ stepY = 1;
+ int alpha = 0x40;
+ tColor clr;
+ for (int i = 0; i<numSteps; i++) {
+ clr = AlphaBlend(clrBgr, clrBlend, alpha);
+ for (int y = i*stepY; y < (i+1)*stepY; y++) {
+ for (int x=0; x<width; x++) {
+ image->SetPixel(cPoint(x,y), clr);
+ }
+ }
+ alpha += alphaStep;
+ }
+ }
+ return image;
+}
+
+void cDetailView::Action(void) {
+ drawHeader();
+ drawContent();
+ drawScrollbar();
+ uint64_t Start = cTimeMs::Now();
+ while (true) {
+ uint64_t Now = cTimeMs::Now();
+ cPixmap::Lock();
+ double t = min(double(Now - Start) / FadeTime, 1.0);
+ int Alpha = t * ALPHA_OPAQUE;
+ header->SetAlpha(Alpha);
+ content->SetAlpha(Alpha);
+ scrollBar->SetAlpha(Alpha);
+ footer->SetAlpha(Alpha);
+ osdManager.flush();
+ cPixmap::Unlock();
+ int Delta = cTimeMs::Now() - Now;
+ if (Delta < FrameTime)
+ cCondWait::SleepMs(FrameTime - Delta);
+ if ((Now - Start) > FadeTime)
+ break;
+ }
+} \ No newline at end of file
diff --git a/detailview.h b/detailview.h
new file mode 100644
index 0000000..c938eb2
--- /dev/null
+++ b/detailview.h
@@ -0,0 +1,39 @@
+#ifndef __TVGUIDE_DETAILVIEW_H
+#define __TVGUIDE_DETAILVIEW_H
+
+// --- cDetailView -------------------------------------------------------------
+
+class cEpgGrid;
+
+class cDetailView : public cThread {
+private:
+ cEpgGrid *grid;
+ cStyledPixmap *header;
+ cPixmap *content;
+ cPixmap *scrollBar;
+ cPixmap *footer;
+ const cEvent *event;
+ cImage *imgScrollBar;
+ int FrameTime;
+ int FadeTime;
+ cTextWrapper description;
+ int borderWidth;
+ int headerHeight;
+ bool setContentDrawportHeight();
+ int heightContent;
+ int heightScrollbar;
+ bool contentScrollable;
+ virtual void Action(void);
+ void drawHeader();
+ void drawContent();
+ void drawScrollbar();
+ cImage *createScrollbar(int width, int height, tColor clrBgr, tColor clrBlend);
+public:
+ cDetailView(cEpgGrid *grid);
+ virtual ~cDetailView(void);
+ void createPixmaps();
+ void scrollUp();
+ void scrollDown();
+};
+
+#endif //__TVGUIDE_DETAILVIEW_H \ No newline at end of file
diff --git a/epggrid.c b/epggrid.c
new file mode 100644
index 0000000..8c8109d
--- /dev/null
+++ b/epggrid.c
@@ -0,0 +1,146 @@
+#include "channelcolumn.h"
+#include "epggrid.h"
+
+cEpgGrid::cEpgGrid(cChannelColumn *c, const cEvent *event) {
+ this->event = event;
+ this->column = c;
+ text = new cTextWrapper();
+ extText = new cTextWrapper();
+ dirty = true;
+ active = false;
+ viewportHeight = 0;
+ borderWidth = 10;
+ hasTimer = false;
+ if (column->hasTimer)
+ hasTimer = event->HasTimer();
+}
+
+cEpgGrid::~cEpgGrid(void) {
+ delete text;
+ delete extText;
+}
+
+void cEpgGrid::SetViewportHeight() {
+ int viewportHeightOld = viewportHeight;
+ if ( column->myTime->GetStart() > event->StartTime() ) {
+ viewportHeight = (min(event->EndTime(), column->myTime->GetStop()) - column->myTime->GetStart()) /60;
+ } else if ( column->myTime->GetStop() < event->EndTime() ) {
+ viewportHeight = (column->myTime->GetStop() - event->StartTime()) /60;
+ } else {
+ viewportHeight = event->Duration() / 60;
+ }
+ if (viewportHeight != viewportHeightOld)
+ dirty = true;
+}
+
+void cEpgGrid::PositionPixmap() {
+ int x0 = column->getX();
+ int y0 = tvguideConfig.headerHeight;
+ if ( column->myTime->GetStart() < event->StartTime() ) {
+ y0 += (event->StartTime() - column->myTime->GetStart())/60*tvguideConfig.minuteHeight;
+ }
+
+ if (!pixmap) {
+ caller = cString::sprintf("epggrid %s %s", column->channel->Name(), event->Title());
+ pixmap = osdManager.requestPixmap(-1, cRect(x0, y0, tvguideConfig.colWidth, viewportHeight * tvguideConfig.minuteHeight),
+ cRect(0, 0, tvguideConfig.colWidth, event->Duration()/60*tvguideConfig.minuteHeight), *caller);
+ } else {
+ pixmap->SetViewPort(cRect(x0, y0, tvguideConfig.colWidth, viewportHeight * tvguideConfig.minuteHeight));
+ }
+}
+
+void cEpgGrid::setBackground() {
+ if (active) {
+ color = theme.Color(clrHighlight);
+ colorBlending = theme.Color(clrHighlightBlending);
+ } else {
+ if (isColor1) {
+ color = theme.Color(clrGrid1);
+ colorBlending = theme.Color(clrGrid1Blending);
+ } else {
+ color = theme.Color(clrGrid2);
+ colorBlending = theme.Color(clrGrid2Blending);
+ }
+ }
+}
+
+void cEpgGrid::Draw() {
+ if (!pixmap) {
+ return;
+ }
+ if (dirty) {
+ setBackground();
+ drawBackground();
+ drawText();
+ if (hasTimer)
+ DrawRecIcon();
+ drawBorder();
+ pixmap->SetLayer(1);
+ dirty = false;
+ }
+}
+
+void cEpgGrid::DrawRecIcon() {
+ cString recIconText("REC");
+ int width = tvguideConfig.FontGrid->Width(*recIconText)+2*borderWidth;
+ int height = tvguideConfig.FontGrid->Height()+10;
+ pixmap->DrawRectangle( cRect(pixmap->ViewPort().Width() - width - borderWidth, pixmap->ViewPort().Height() - height - borderWidth, width, height), theme.Color(clrButtonRed));
+ pixmap->DrawText(cPoint(pixmap->ViewPort().Width() - width, pixmap->ViewPort().Height() - height - borderWidth/2), *recIconText, theme.Color(clrFont), clrTransparent, tvguideConfig.FontGrid);
+}
+
+void cEpgGrid::setText() {
+ cString strText;
+ strText = cString::sprintf("%s - %s:\n%s", *(event->GetTimeString()), *(event->GetEndTimeString()), event->Title());
+ text->Set(*(strText), tvguideConfig.FontGrid, tvguideConfig.colWidth-2*borderWidth);
+ extText->Set(event->ShortText(), tvguideConfig.FontGridSmall, tvguideConfig.colWidth-2*borderWidth);
+}
+
+void cEpgGrid::drawText() {
+ int gridHeight = pixmap->ViewPort().Height();
+ if (gridHeight/tvguideConfig.minuteHeight < 6)
+ return;
+ int textHeight = tvguideConfig.FontGrid->Height();
+ int textLines = text->Lines();
+ for (int i=0; i<textLines; i++) {
+ pixmap->DrawText(cPoint(borderWidth, borderWidth + i*textHeight), text->GetLine(i), theme.Color(clrFont), clrTransparent, tvguideConfig.FontGrid);
+ }
+ int extTextLines = extText->Lines();
+ int offset = (textLines+1)*textHeight - 0.5*textHeight;
+ textHeight = tvguideConfig.FontGridSmall->Height();
+ if ((pixmap->ViewPort().Height()-textHeight-10) > offset) {
+ for (int i=0; i<extTextLines; i++) {
+ pixmap->DrawText(cPoint(borderWidth, borderWidth + offset + i*textHeight), extText->GetLine(i), theme.Color(clrFont), clrTransparent, tvguideConfig.FontGridSmall);
+ }
+ }
+}
+
+int cEpgGrid::calcOverlap(cEpgGrid *neighbor) {
+ int overlap = 0;
+ if (intersects(neighbor)) {
+ if ((event->StartTime() <= neighbor->StartTime()) && (event->EndTime() <= neighbor->EndTime())) {
+ overlap = event->EndTime() - neighbor->StartTime();
+ } else if ((event->StartTime() >= neighbor->StartTime()) && (event->EndTime() >= neighbor->EndTime())) {
+ overlap = neighbor->EndTime() - event->StartTime();
+ } else if ((event->StartTime() >= neighbor->StartTime()) && (event->EndTime() <= neighbor->EndTime())) {
+ overlap = event->Duration();
+ } else if ((event->StartTime() <= neighbor->StartTime()) && (event->EndTime() >= neighbor->EndTime())) {
+ overlap = neighbor->EndTime() - neighbor->StartTime();
+ }
+ }
+ return overlap;
+}
+
+bool cEpgGrid::intersects(cEpgGrid *neighbor) {
+ return ! ( (neighbor->EndTime() <= event->StartTime()) || (neighbor->StartTime() >= event->EndTime()) );
+}
+
+bool cEpgGrid::isActiveInitial(time_t t) {
+ if ((event->StartTime() < t) && (event->EndTime() > t))
+ return true;
+ else
+ return false;
+}
+
+void cEpgGrid::debug() {
+ esyslog("tvguide Grid: %s, %s, viewportHeight: %d, Duration: %d", *(event->GetTimeString()), event->Title(), viewportHeight, event->Duration()/60);
+} \ No newline at end of file
diff --git a/epggrid.h b/epggrid.h
new file mode 100644
index 0000000..00160dc
--- /dev/null
+++ b/epggrid.h
@@ -0,0 +1,44 @@
+#ifndef __TVGUIDE_EPGGRID_H
+#define __TVGUIDE_EPGGRID_H
+
+// --- cEpgGrid -------------------------------------------------------------
+
+class cEpgGrid : public cListObject, public cStyledPixmap {
+private:
+ const cEvent *event;
+ cTextWrapper *text;
+ cTextWrapper *extText;
+ int viewportHeight;
+ int borderWidth;
+ void drawText();
+ void setBackground();
+ bool isColor1;
+ bool active;
+ bool dirty;
+ bool intersects(cEpgGrid *neighbor);
+ bool hasTimer;
+ void DrawRecIcon();
+public:
+ cEpgGrid(cChannelColumn *c, const cEvent *event);
+ virtual ~cEpgGrid(void);
+ cChannelColumn *column;
+ void SetViewportHeight();
+ void PositionPixmap();
+ void setText();
+ void Draw();
+ void SetDirty() {dirty = true;};
+ void SetActive() {dirty = true; active = true;};
+ void SetInActive() {dirty = true; active = false;};
+ void SetColor(bool color) {isColor1 = color;};
+ bool IsColor1() {return isColor1;};
+ int GetViewportHeight() {return viewportHeight;};
+ const cEvent *GetEvent() {return event;};
+ bool isActiveInitial(time_t t);
+ time_t StartTime() { return event->StartTime(); };
+ time_t EndTime() { return event->EndTime(); };
+ int calcOverlap(cEpgGrid *neighbor);
+ void setTimer() {hasTimer = true;};
+ void debug();
+};
+
+#endif //__TVGUIDE_EPGGRID_H \ No newline at end of file
diff --git a/footer.c b/footer.c
new file mode 100644
index 0000000..19ee958
--- /dev/null
+++ b/footer.c
@@ -0,0 +1,59 @@
+#include "footer.h"
+
+cFooter::cFooter() {
+ int buttonHeight= tvguideConfig.footerHeight - 20;
+ textY = (buttonHeight - tvguideConfig.FontButton->Height())/2;
+ int distanceX = 20;
+ buttonWidth = (tvguideConfig.osdWidth - tvguideConfig.timeColWidth-5*distanceX)/4;
+ int startX = tvguideConfig.timeColWidth + distanceX;
+ int Y = tvguideConfig.osdHeight - tvguideConfig.footerHeight + (tvguideConfig.footerHeight - buttonHeight)/2;
+
+ buttonRed = new cStyledPixmap(osdManager.requestPixmap(3, cRect(startX, Y, buttonWidth, buttonHeight), cRect::Null, "btnRed"), "btnRed");
+ buttonGreen = new cStyledPixmap(osdManager.requestPixmap(3, cRect(startX + buttonWidth + distanceX, Y, buttonWidth, buttonHeight), cRect::Null, "btnGreen"), "btnGreen");
+ buttonYellow = new cStyledPixmap(osdManager.requestPixmap(3, cRect(startX + 2*(buttonWidth + distanceX), Y, buttonWidth, buttonHeight), cRect::Null, "btnYellow"), "btnYellow");
+ buttonBlue = new cStyledPixmap(osdManager.requestPixmap(3, cRect(startX + 3*(buttonWidth + distanceX), Y, buttonWidth, buttonHeight), cRect::Null, "btnBlue"), "btnBlue");
+}
+
+cFooter::~cFooter(void) {
+ delete buttonRed;
+ delete buttonGreen;
+ delete buttonYellow;
+ delete buttonBlue;
+}
+
+void cFooter::drawRedButton() {
+ buttonRed->setColor(theme.Color(clrButtonRed), theme.Color(clrButtonRedBlending));
+ buttonRed->drawBackground();
+ buttonRed->drawBorder();
+ cString text(tr("Set Timer"));
+ int width = tvguideConfig.FontButton->Width(*(text));
+ buttonRed->DrawText(cPoint((buttonWidth-width)/2, textY), *(text), theme.Color(clrFontButtons), clrTransparent, tvguideConfig.FontButton);
+
+}
+
+void cFooter::drawGreenButton() {
+ buttonGreen->setColor(theme.Color(clrButtonGreen), theme.Color(clrButtonGreenBlending));
+ buttonGreen->drawBackground();
+ buttonGreen->drawBorder();
+ cString text = cString::sprintf("%d %s", tvguideConfig.jumpChannels, tr("Channels back"));
+ int width = tvguideConfig.FontButton->Width(*text);
+ buttonGreen->DrawText(cPoint((buttonWidth-width)/2, textY), *text, theme.Color(clrFontButtons), clrTransparent, tvguideConfig.FontButton);
+}
+
+void cFooter::drawYellowButton() {
+ buttonYellow->setColor(theme.Color(clrButtonYellow), theme.Color(clrButtonYellowBlending));
+ buttonYellow->drawBackground();
+ buttonYellow->drawBorder();
+ cString text = cString::sprintf("%d %s", tvguideConfig.jumpChannels, tr("Channels forward"));
+ int width = tvguideConfig.FontButton->Width(*text);
+ buttonYellow->DrawText(cPoint((buttonWidth-width)/2, textY), *text, theme.Color(clrFontButtons), clrTransparent, tvguideConfig.FontButton);
+}
+
+void cFooter::drawBlueButton() {
+ buttonBlue->setColor(theme.Color(clrButtonBlue), theme.Color(clrButtonBlueBlending));
+ buttonBlue->drawBackground();
+ buttonBlue->drawBorder();
+ cString text(tr("Switch to Channel"));
+ int width = tvguideConfig.FontButton->Width(*(text));
+ buttonBlue->DrawText(cPoint((buttonWidth-width)/2, textY), *(text), theme.Color(clrFontButtons), clrTransparent, tvguideConfig.FontButton);
+}
diff --git a/footer.h b/footer.h
new file mode 100644
index 0000000..9cf6045
--- /dev/null
+++ b/footer.h
@@ -0,0 +1,23 @@
+#ifndef __TVGUIDE_FOOTER_H
+#define __TVGUIDE_FOOTER_H
+
+// --- cFooter -------------------------------------------------------------
+
+class cFooter {
+private:
+ cStyledPixmap *buttonRed;
+ cStyledPixmap *buttonGreen;
+ cStyledPixmap *buttonYellow;
+ cStyledPixmap *buttonBlue;
+ int textY;
+ int buttonWidth;
+public:
+ cFooter();
+ virtual ~cFooter(void);
+ void drawRedButton();
+ void drawGreenButton();
+ void drawYellowButton();
+ void drawBlueButton();
+};
+
+#endif //__TVGUIDE_FOOTER_H \ No newline at end of file
diff --git a/imageloader.c b/imageloader.c
new file mode 100644
index 0000000..015f805
--- /dev/null
+++ b/imageloader.c
@@ -0,0 +1,78 @@
+#include "imageloader.h"
+#include <math.h>
+
+using namespace Magick;
+
+cImageLoader::cImageLoader() {
+}
+
+cImageLoader::~cImageLoader() {
+}
+
+bool cImageLoader::LoadLogo(const char *logo)
+{
+ try
+ {
+ int width = tvguideConfig.logoWidth;
+ int height = tvguideConfig.logoHeight;
+ cString extension;
+ if (tvguideConfig.logoExtension == 0) {
+ extension = "png";
+ } else if (tvguideConfig.logoExtension == 1) {
+ extension = "jpg";
+ }
+ cString Filename = cString::sprintf("%s%s.%s", *tvguideConfig.logoPath, logo, *extension);
+ osdImage.read(*Filename);
+
+ if (height != 0 || width != 0) {
+ osdImage.sample( Geometry(width, height));
+
+ }
+ return true;
+ }
+ catch (...)
+ {
+ return false;
+ }
+}
+
+bool cImageLoader::LoadEPGImage(int eventID)
+{
+ try
+ {
+ int width = tvguideConfig.epgImageWidth;
+ int height = tvguideConfig.epgImageHeight;
+ cString Filename = cString::sprintf("%s%d.jpg", *tvguideConfig.epgImagePath, eventID);
+ osdImage.read(*Filename);
+
+ if (height != 0 || width != 0)
+ osdImage.sample( Geometry(width, height));
+
+ return true;
+ }
+ catch (...)
+ {
+ return false;
+ }
+}
+
+
+cImage cImageLoader::GetImage()
+{
+ int w, h;
+ w = osdImage.columns();
+ h = osdImage.rows();
+ cImage image (cSize(w, h));
+ const PixelPacket *pixels = osdImage.getConstPixels(0, 0, w, h);
+ for (int iy = 0; iy < h; ++iy) {
+ for (int ix = 0; ix < w; ++ix) {
+ tColor col = (~int(pixels->opacity * 255 / MaxRGB) << 24)
+ | (int(pixels->green * 255 / MaxRGB) << 8)
+ | (int(pixels->red * 255 / MaxRGB) << 16)
+ | (int(pixels->blue * 255 / MaxRGB) );
+ image.SetPixel(cPoint(ix, iy), col);
+ ++pixels;
+ }
+ }
+ return image;
+}
diff --git a/imageloader.h b/imageloader.h
new file mode 100644
index 0000000..c122758
--- /dev/null
+++ b/imageloader.h
@@ -0,0 +1,23 @@
+#ifndef _TVGUIDE_IMAGELOADER_H
+#define _TVGUIDE_IMAGELOADER_H
+
+#define X_DISPLAY_MISSING
+
+#include <vdr/osd.h>
+#include <vdr/skins.h>
+#include <Magick++.h>
+
+using namespace Magick;
+
+class cImageLoader {
+public:
+ cImageLoader();
+ ~cImageLoader();
+ cImage GetImage();
+ bool LoadLogo(const char *logo);
+ bool LoadEPGImage(int eventID);
+private:
+ Image osdImage;
+};
+
+#endif //_TVGUIDE_IMAGELOADER_H
diff --git a/messagebox.c b/messagebox.c
new file mode 100644
index 0000000..3f727d4
--- /dev/null
+++ b/messagebox.c
@@ -0,0 +1,114 @@
+#include "messagebox.h"
+
+cMessageBoxThread::cMessageBoxThread(cPixmap *content, int displayTime) {
+ this->content = content;
+ FrameTime = 30; // ms
+ FadeTime = 200; // ms
+ this->displayTime = displayTime;
+}
+
+cMessageBoxThread::~cMessageBoxThread(void) {
+ Cancel(0);
+}
+
+void cMessageBoxThread::Action(void) {
+ uint64_t Start = cTimeMs::Now();
+ while (Running()) {
+ uint64_t Now = cTimeMs::Now();
+ cPixmap::Lock();
+ double t = min(double(Now - Start) / FadeTime, 1.0);
+ int Alpha = t * ALPHA_OPAQUE;
+ if (content) {
+ content->SetAlpha(Alpha);
+ osdManager.flush();
+ }
+ cPixmap::Unlock();
+ int Delta = cTimeMs::Now() - Now;
+ if (Delta < FrameTime)
+ cCondWait::SleepMs(FrameTime - Delta);
+ if ((Now - Start) > FadeTime)
+ break;
+ }
+ cCondWait::SleepMs(displayTime - 2*FadeTime);
+ Start = cTimeMs::Now();
+ while (Running()) {
+ uint64_t Now = cTimeMs::Now();
+ cPixmap::Lock();
+ double t = min(double(Now - Start) / FadeTime, 1.0);
+ int Alpha = (1-t) * ALPHA_OPAQUE;
+ if (content) {
+ content->SetAlpha(Alpha);
+ osdManager.flush();
+ }
+ cPixmap::Unlock();
+ int Delta = cTimeMs::Now() - Now;
+ if (Delta < FrameTime)
+ cCondWait::SleepMs(FrameTime - Delta);
+ if ((Now - Start) > FadeTime)
+ break;
+ }
+ osdManager.flush();
+}
+
+//--cMessageBox-------------------------------------------------------------
+cMutex cMessageBox::mutex;
+cMessageBoxThread *cMessageBox::msgboxThread = NULL;
+cPixmap *cMessageBox::content = NULL;
+
+bool cMessageBox::Start(int displayTime, cString msg) {
+ cMutexLock MutexLock(&mutex);
+ int width = (tvguideConfig.osdWidth - 600)/2;
+ if (!content) {
+ int height = 400;
+ content = osdManager.requestPixmap(5, cRect((tvguideConfig.osdWidth - width)/2,
+ (tvguideConfig.osdHeight- height)/2,
+ width, height),
+ cRect::Null, "msgbox");
+ }
+ if (msgboxThread) {
+ delete msgboxThread;
+ msgboxThread = NULL;
+ }
+ if (!msgboxThread) {
+ msgboxThread = new cMessageBoxThread(content, displayTime);
+ cTextWrapper message;
+ message.Set(msg, tvguideConfig.FontMessageBox, width - 40);
+ int textHeight = tvguideConfig.FontMessageBox->Height();
+ int textLines = message.Lines();
+ int height = textLines * (textHeight+20);
+ cPixmap::Lock();
+ content->SetViewPort(cRect((tvguideConfig.osdWidth - width)/2,(tvguideConfig.osdHeight- height)/2, width, height));
+ content->SetAlpha(0);
+ content->Fill(theme.Color(clrBorder));
+ content->DrawRectangle(cRect(2,2,width-4, height-4), theme.Color(clrBackground));
+ int textWidth = 0;
+ for (int i=0; i<textLines; i++) {
+ textWidth = tvguideConfig.FontMessageBox->Width(message.GetLine(i));
+ content->DrawText(cPoint((width - textWidth)/2, 20 + i*textHeight), message.GetLine(i), theme.Color(clrFont), clrTransparent, tvguideConfig.FontMessageBox);
+ }
+ cPixmap::Unlock();
+ msgboxThread->Start();
+ return true;
+ }
+ return false;
+}
+
+void cMessageBox::Stop(void) {
+ cMutexLock MutexLock(&mutex);
+ if (msgboxThread) {
+ delete msgboxThread;
+ msgboxThread = NULL;
+ }
+}
+
+void cMessageBox::Destroy(void) {
+ cMutexLock MutexLock(&mutex);
+ if (msgboxThread) {
+ delete msgboxThread;
+ msgboxThread = NULL;
+ }
+ if (content) {
+ osdManager.releasePixmap(content, "msgboxDestroy");
+ content = NULL;
+ }
+}
diff --git a/messagebox.h b/messagebox.h
new file mode 100644
index 0000000..8b9ab23
--- /dev/null
+++ b/messagebox.h
@@ -0,0 +1,31 @@
+#ifndef __TVGUIDE_MESSAGEBOX_H
+#define __TVGUIDE_MESSAGEBOX_H
+
+class cMessageBoxThreadPool;
+
+// --- cMessageBox -------------------------------------------------------------
+
+class cMessageBoxThread : public cThread {
+private:
+ cPixmap *content;
+ int FadeTime;
+ int FrameTime;
+ int displayTime;
+ virtual void Action(void);
+public:
+ cMessageBoxThread(cPixmap *content, int displayTime);
+ virtual ~cMessageBoxThread(void);
+};
+
+class cMessageBox {
+private:
+ static cMutex mutex;
+ static cMessageBoxThread *msgboxThread;
+ static cPixmap *content;
+public:
+ static bool Start(int displayTime, cString msg);
+ static void Stop(void);
+ static void Destroy(void);
+};
+
+#endif //__TVGUIDE_MESSAGEBOX_H \ No newline at end of file
diff --git a/osdmanager.c b/osdmanager.c
new file mode 100644
index 0000000..fb0ec6e
--- /dev/null
+++ b/osdmanager.c
@@ -0,0 +1,52 @@
+#ifndef __TVGUIDE_OSDMANAGER_H
+#define __TVGUIDE_OSDMANAGER_H
+
+class cOsdManager {
+ private:
+ cOsd *osd;
+ int activePixmaps;
+ public:
+ cOsdManager(void);
+ bool setOsd();
+ void setBackground();
+ void flush() {osd->Flush();};
+ cPixmap *requestPixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort = cRect::Null, const char *caller = "anonymous");
+ void releasePixmap(cPixmap *pixmap, const char *caller = "anonymous");
+ void deleteOsd() {delete osd;};
+ int Width() { return osd->Width(); };
+ int Height() { return osd->Height(); };
+};
+
+#endif //__TVGUIDE_OSDMANAGER_H
+
+cOsdManager::cOsdManager(void) {
+ activePixmaps = 0;
+}
+
+bool cOsdManager::setOsd() {
+ osd = cOsdProvider::NewOsd(cOsd::OsdLeft(), cOsd::OsdTop());
+ if (osd) {
+ tArea Area = { 0, 0, cOsd::OsdWidth(), cOsd::OsdHeight(), 32 };
+ if (osd->SetAreas(&Area, 1) == oeOk) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void cOsdManager::setBackground() {
+ osd->DrawRectangle(0, 0, cOsd::OsdWidth(), cOsd::OsdHeight(), theme.Color(clrBackgroundOSD));
+}
+cPixmap *cOsdManager::requestPixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort, const char *caller) {
+ if (activePixmaps >= 64)
+ return NULL;
+ activePixmaps++;
+ //esyslog("tvguide: Pixmap angefordert von %s, verwendet: %d", caller, activePixmaps);
+ return osd->CreatePixmap(Layer, ViewPort, DrawPort);
+ }
+
+void cOsdManager::releasePixmap(cPixmap *pixmap, const char *caller) {
+ activePixmaps--;
+ //esyslog("tvguide: Pixmap geloescht von %s, verwendet: %d", caller, activePixmaps);
+ osd->DestroyPixmap(pixmap);
+} \ No newline at end of file
diff --git a/po/de_DE.po b/po/de_DE.po
new file mode 100755
index 0000000..bdf0cd8
--- /dev/null
+++ b/po/de_DE.po
@@ -0,0 +1,139 @@
+# VDR plugin language source file.
+msgid ""
+msgstr ""
+"Project-Id-Version: vdr-tvguide 0.0.1\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2012-12-27 15:27+0100\n"
+"PO-Revision-Date: 2012-08-25 17:49+0200\n"
+"Last-Translator: Horst\n"
+"Language-Team: \n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Set Timer"
+msgstr "Aufnehmen"
+
+msgid "Channels back"
+msgstr "Kanäle zurück"
+
+msgid "Channels forward"
+msgstr "Kanäle vor"
+
+msgid "Switch to Channel"
+msgstr "Umschalten"
+
+msgid "General Settings"
+msgstr "Allgemeine Einstellungen"
+
+msgid "Screen Presentation"
+msgstr "Anzeigeoptionen"
+
+msgid "Fonts and Fontsizes"
+msgstr "Schriften und Schriftgrößen"
+
+msgid "Number of Channels / Columns"
+msgstr "Anzahl der Kanäle bzw. Spalten"
+
+msgid "Channels to Jump (Keys Green / Yellow)"
+msgstr "Kanalsprung (Tasten Grün / Gelb)"
+
+msgid "Time to display vertically in minutes"
+msgstr "Vertikal angezeigte Zeit (in Minuten)"
+
+msgid "Big Step (Keys 1 / 3) in hours"
+msgstr "Großer Sprung (Tasten 1 / 3) in Stunden"
+
+msgid "Huge Step (Keys 4 / 6) in hours"
+msgstr "Sehr großer Sprung (Tasten 4 / 6) in Stunden"
+
+msgid "Time Format (12h/24h)"
+msgstr "Zeitformat (12h/24h)"
+
+msgid "Theme"
+msgstr "Theme"
+
+msgid "Use color gradients"
+msgstr "Farbverläufe verwenden"
+
+msgid "Rounded Corners"
+msgstr "Abgerundete Ecken"
+
+msgid "Width of Timeline"
+msgstr "Breite der Zeitleiste"
+
+msgid "Height of Header"
+msgstr "Höhe des Headers"
+
+msgid "Height of Footer"
+msgstr "Höhe des Footers"
+
+msgid "Show Channel Logos"
+msgstr "Kanallogos anzeigen"
+
+msgid "Logo Path used"
+msgstr "Benutzer Pfad für Kanallogos"
+
+msgid "Logo Extension"
+msgstr "Logo Extension"
+
+msgid "Logo width"
+msgstr "Breite der Logos"
+
+msgid "Logo height"
+msgstr "Höhe der Logos"
+
+msgid "Show EPG Images"
+msgstr "EPG Bilder anzeigen"
+
+msgid "EPG Images Path used"
+msgstr "benutzer EPG Bilder Pfad"
+
+msgid "EPG Image width"
+msgstr "Breite der EPG Bilder"
+
+msgid "EPG Image height"
+msgstr "Höhe der EPG Bilder"
+
+msgid "Font"
+msgstr "Schriftart"
+
+msgid "Channel Header Font Size"
+msgstr "Kanal Header Schriftgröße"
+
+msgid "Grid Font Size"
+msgstr "Grid Schriftgröße"
+
+msgid "Grid Font Small Size"
+msgstr "Grid kleine Schriftgröße"
+
+msgid "Timeline Weekday Font Size"
+msgstr "Zeitleiste Wochentag Schriftgröße"
+
+msgid "Timeline Date Font Size"
+msgstr "Zeitleiste Datum Schriftgröße"
+
+msgid "Timeline Time Font Size"
+msgstr "Zeitleiste Zeit Schriftgröße"
+
+msgid "Button Font Size"
+msgstr "Button Schriftgröße"
+
+msgid "Detail EPG View Font Size"
+msgstr "Detailierte EPG Ansicht Schriftgröße"
+
+msgid "Detail EPG View Header Font Size"
+msgstr "Detailierte EPG Ansicht Header Schriftgröße"
+
+msgid "Message Font Size"
+msgstr "Nachrichten Schriftgröße"
+
+msgid "Message Large Font Size"
+msgstr "Nachrichten große Schriftgröße"
+
+msgid "Timer not set! There is already a timer for this item."
+msgstr "Timer wurde nicht gesetzt! Es existiert bereits ein Timer für diese Sendung"
+
+msgid "Timer set"
+msgstr "Timer gesetzt"
diff --git a/setup.c b/setup.c
new file mode 100644
index 0000000..57d2168
--- /dev/null
+++ b/setup.c
@@ -0,0 +1,209 @@
+#include "setup.h"
+
+cTvguideSetup::cTvguideSetup() {
+ tmpTvguideConfig = tvguideConfig;
+ Setup();
+}
+
+cTvguideSetup::~cTvguideSetup() {
+}
+
+
+void cTvguideSetup::Setup(void) {
+ int currentItem = Current();
+ Clear();
+
+ Add(new cOsdItem(tr("General Settings")));
+ Add(new cOsdItem(tr("Screen Presentation")));
+ Add(new cOsdItem(tr("Fonts and Fontsizes")));
+
+ SetCurrent(Get(currentItem));
+ Display();
+}
+
+eOSState cTvguideSetup::ProcessKey(eKeys Key) {
+ bool hadSubMenu = HasSubMenu();
+ eOSState state = cMenuSetupPage::ProcessKey(Key);
+ if (hadSubMenu && Key == kOk)
+ Store();
+
+ if (!hadSubMenu && (state == osUnknown || Key == kOk)) {
+ if ((Key == kOk && !hadSubMenu)) {
+ const char* ItemText = Get(Current())->Text();
+ if (strcmp(ItemText, tr("General Settings")) == 0)
+ state = AddSubMenu(new cMenuSetupGeneral(&tmpTvguideConfig));
+ if (strcmp(ItemText, tr("Screen Presentation")) == 0)
+ state = AddSubMenu(new cMenuSetupScreenLayout(&tmpTvguideConfig));
+ if (strcmp(ItemText, tr("Fonts and Fontsizes")) == 0)
+ state = AddSubMenu(new cMenuSetupFont(&tmpTvguideConfig));
+ }
+ }
+ return state;
+}
+
+void cTvguideSetup::Store(void) {
+
+ tvguideConfig = tmpTvguideConfig;
+
+ SetupStore("themeIndex", tvguideConfig.themeIndex);
+ SetupStore("useBlending", tvguideConfig.useBlending);
+ SetupStore("roundedCorners", tvguideConfig.roundedCorners);
+ SetupStore("timeFormat", tvguideConfig.timeFormat);
+ SetupStore("channelCols", tvguideConfig.channelCols);
+ SetupStore("displayTime", tvguideConfig.displayTime);
+ SetupStore("bigStepHours", tvguideConfig.bigStepHours);
+ SetupStore("hugeStepHours", tvguideConfig.hugeStepHours);
+ SetupStore("jumpChannels", tvguideConfig.jumpChannels);
+ SetupStore("hideChannelLogos", tvguideConfig.hideChannelLogos);
+ SetupStore("logoExtension", tvguideConfig.logoExtension);
+ SetupStore("logoWidth", tvguideConfig.logoWidth);
+ SetupStore("logoHeight", tvguideConfig.logoHeight);
+ SetupStore("hideEpgImages", tvguideConfig.hideEpgImages);
+ SetupStore("epgImageWidth", tvguideConfig.epgImageWidth);
+ SetupStore("epgImageHeight", tvguideConfig.epgImageHeight);
+ SetupStore("epgImageHeight", tvguideConfig.epgImageHeight);
+ SetupStore("timeColWidth", tvguideConfig.timeColWidth);
+ SetupStore("headerHeight", tvguideConfig.headerHeight);
+ SetupStore("footerHeight", tvguideConfig.footerHeight);
+ SetupStore("fontIndex", tvguideConfig.fontIndex);
+ SetupStore("fontHeaderSize", tvguideConfig.fontHeaderSize);
+ SetupStore("fontGridSize", tvguideConfig.fontGridSize);
+ SetupStore("fontGridSmallSize", tvguideConfig.fontGridSmallSize);
+ SetupStore("fontTimeLineWeekdaySize", tvguideConfig.fontTimeLineWeekdaySize);
+ SetupStore("fontTimeLineDateSize", tvguideConfig.fontTimeLineDateSize);
+ SetupStore("fontTimeLineTimeSize", tvguideConfig.fontTimeLineTimeSize);
+ SetupStore("fontButtonSize", tvguideConfig.fontButtonSize);
+ SetupStore("fontDetailViewSize", tvguideConfig.fontDetailViewSize);
+ SetupStore("fontDetailHeaderSize", tvguideConfig.fontDetailHeaderSize);
+ SetupStore("fontMessageBoxSize", tvguideConfig.fontMessageBoxSize);
+ SetupStore("fontMessageBoxLargeSize", tvguideConfig.fontMessageBoxLargeSize);
+}
+
+cMenuSetupSubMenu::cMenuSetupSubMenu(const char* Title, cTvguideConfig* data) : cOsdMenu(Title, 30) {
+ tmpTvguideConfig = data;
+}
+
+cOsdItem *cMenuSetupSubMenu::InfoItem(const char *label, const char *value) {
+ cOsdItem *item;
+ item = new cOsdItem(cString::sprintf("%s: %s", label, value));
+ item->SetSelectable(false);
+ return item;
+}
+
+eOSState cMenuSetupSubMenu::ProcessKey(eKeys Key) {
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ if (state == osUnknown) {
+ switch (Key) {
+ case kOk:
+ return osBack;
+ default:
+ break;
+ }
+ }
+ return state;
+}
+
+//------------------------------------------------------------------------------------------------------------------
+
+cMenuSetupGeneral::cMenuSetupGeneral(cTvguideConfig* data) : cMenuSetupSubMenu(tr("General Settings"), data) {
+ timeFormatItems[0] = "12h";
+ timeFormatItems[1] = "24h";
+
+ Set();
+}
+
+void cMenuSetupGeneral::Set(void) {
+ int currentItem = Current();
+ Clear();
+
+ Add(new cMenuEditIntItem(tr("Number of Channels / Columns"), &tmpTvguideConfig->channelCols, 3, 8));
+ Add(new cMenuEditIntItem(tr("Channels to Jump (Keys Green / Yellow)"), &tmpTvguideConfig->jumpChannels, 2, 30));
+ Add(new cMenuEditIntItem(tr("Time to display vertically in minutes"), &tmpTvguideConfig->displayTime, 120, 320));
+ Add(new cMenuEditIntItem(tr("Big Step (Keys 1 / 3) in hours"), &tmpTvguideConfig->bigStepHours, 1, 12));
+ Add(new cMenuEditIntItem(tr("Huge Step (Keys 4 / 6) in hours"), &tmpTvguideConfig->hugeStepHours, 13, 48));
+ Add(new cMenuEditStraItem(tr("Time Format (12h/24h)"), &tmpTvguideConfig->timeFormat, 2, timeFormatItems));
+
+ SetCurrent(Get(currentItem));
+ Display();
+}
+
+//------------------------------------------------------------------------------------------------------------------
+
+cMenuSetupScreenLayout::cMenuSetupScreenLayout(cTvguideConfig* data) : cMenuSetupSubMenu(tr("Screen Presentation"), data) {
+ themes.Load(*cString("tvguide"));
+ hideChannelLogosItems[0] = trVDR("yes");
+ hideChannelLogosItems[1] = trVDR("no");
+ logoExtensionItems[0] = "png";
+ logoExtensionItems[1] = "jpg";
+ Set();
+}
+
+void cMenuSetupScreenLayout::Set(void) {
+ int currentItem = Current();
+ Clear();
+ if (themes.NumThemes())
+ Add(new cMenuEditStraItem(tr("Theme"), &tmpTvguideConfig->themeIndex, themes.NumThemes(), themes.Descriptions()));
+ Add(new cMenuEditBoolItem(tr("Use color gradients"), &tmpTvguideConfig->useBlending));
+ Add(new cMenuEditBoolItem(tr("Rounded Corners"), &tmpTvguideConfig->roundedCorners));
+ Add(new cMenuEditIntItem(tr("Width of Timeline"), &tmpTvguideConfig->timeColWidth, 50, 300));
+ Add(new cMenuEditIntItem(tr("Height of Header"), &tmpTvguideConfig->headerHeight, 50, 300));
+ Add(new cMenuEditIntItem(tr("Height of Footer"), &tmpTvguideConfig->footerHeight, 50, 300));
+
+ const char *indent = " ";
+ Add(new cMenuEditStraItem(tr("Show Channel Logos"), &tmpTvguideConfig->hideChannelLogos, 2, hideChannelLogosItems));
+ if (!tmpTvguideConfig->hideChannelLogos) {
+ Add(InfoItem(tr("Logo Path used"), *tvguideConfig.logoPath));
+ Add(new cMenuEditStraItem(*cString::sprintf("%s%s", indent, tr("Logo Extension")), &tmpTvguideConfig->logoExtension, 2, logoExtensionItems));
+ Add(new cMenuEditIntItem(*cString::sprintf("%s%s", indent, tr("Logo width")), &tmpTvguideConfig->logoWidth, 0, 350));
+ Add(new cMenuEditIntItem(*cString::sprintf("%s%s", indent, tr("Logo height")), &tmpTvguideConfig->logoHeight, 0, 250));
+ }
+
+ Add(new cMenuEditStraItem(tr("Show EPG Images"), &tmpTvguideConfig->hideEpgImages, 2, hideChannelLogosItems));
+ if (!tmpTvguideConfig->hideEpgImages) {
+ Add(InfoItem(tr("EPG Images Path used"), *tvguideConfig.epgImagePath));
+ Add(new cMenuEditIntItem(*cString::sprintf("%s%s", indent, tr("EPG Image width")), &tmpTvguideConfig->epgImageWidth, 0, 800));
+ Add(new cMenuEditIntItem(*cString::sprintf("%s%s", indent, tr("EPG Image height")), &tmpTvguideConfig->epgImageHeight, 0, 800));
+ }
+
+ SetCurrent(Get(currentItem));
+ Display();
+}
+
+eOSState cMenuSetupScreenLayout::ProcessKey(eKeys Key) {
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ if (Key == kOk) {
+ state = osBack;
+ } else if (Key != kNone) {
+ Set();
+ }
+ return state;
+}
+
+//------------------------------------------------------------------------------------------------------------------
+
+cMenuSetupFont::cMenuSetupFont(cTvguideConfig* data) : cMenuSetupSubMenu(tr("Fonts and Fontsizes"), data) {
+ cFont::GetAvailableFontNames(&fontNames);
+ fontNames.Insert(strdup(tvguideConfig.fontNameDefault));
+ Set();
+}
+
+void cMenuSetupFont::Set(void) {
+ int currentItem = Current();
+ Clear();
+
+ Add(new cMenuEditStraItem(tr("Font"), &tmpTvguideConfig->fontIndex, fontNames.Size(), &fontNames[0]));
+ Add(new cMenuEditIntItem(tr("Channel Header Font Size"), &tmpTvguideConfig->fontHeaderSize, 10, 70));
+ Add(new cMenuEditIntItem(tr("Grid Font Size"), &tmpTvguideConfig->fontGridSize, 10, 70));
+ Add(new cMenuEditIntItem(tr("Grid Font Small Size"), &tmpTvguideConfig->fontGridSmallSize, 10, 70));
+ Add(new cMenuEditIntItem(tr("Timeline Weekday Font Size"), &tmpTvguideConfig->fontTimeLineWeekdaySize, 10, 70));
+ Add(new cMenuEditIntItem(tr("Timeline Date Font Size"), &tmpTvguideConfig->fontTimeLineDateSize, 10, 70));
+ Add(new cMenuEditIntItem(tr("Timeline Time Font Size"), &tmpTvguideConfig->fontTimeLineTimeSize, 10, 70));
+ Add(new cMenuEditIntItem(tr("Button Font Size"), &tmpTvguideConfig->fontButtonSize, 10, 70));
+ Add(new cMenuEditIntItem(tr("Detail EPG View Font Size"), &tmpTvguideConfig->fontDetailViewSize, 10, 70));
+ Add(new cMenuEditIntItem(tr("Detail EPG View Header Font Size"), &tmpTvguideConfig->fontDetailHeaderSize, 10, 70));
+ Add(new cMenuEditIntItem(tr("Message Font Size"), &tmpTvguideConfig->fontMessageBoxSize, 10, 70));
+ Add(new cMenuEditIntItem(tr("Message Large Font Size"), &tmpTvguideConfig->fontMessageBoxLargeSize, 10, 70));
+
+ SetCurrent(Get(currentItem));
+ Display();
+} \ No newline at end of file
diff --git a/setup.h b/setup.h
new file mode 100644
index 0000000..91fad7f
--- /dev/null
+++ b/setup.h
@@ -0,0 +1,54 @@
+#ifndef __TVGUIDE_SETUP_H
+#define __TVGUIDE_SETUP_H
+
+class cTvguideSetup : public cMenuSetupPage {
+ public:
+ cTvguideSetup(void);
+ virtual ~cTvguideSetup();
+ private:
+ cTvguideConfig tmpTvguideConfig;
+ void Setup(void);
+ protected:
+ virtual eOSState ProcessKey(eKeys Key);
+ virtual void Store(void);
+
+};
+
+class cMenuSetupSubMenu : public cOsdMenu {
+ protected:
+ cTvguideConfig *tmpTvguideConfig;
+ virtual eOSState ProcessKey(eKeys Key);
+ virtual void Set(void) = 0;
+ cOsdItem *InfoItem(const char *label, const char *value);
+ public:
+ cMenuSetupSubMenu(const char *Title, cTvguideConfig *data);
+};
+
+class cMenuSetupGeneral : public cMenuSetupSubMenu {
+ protected:
+ const char * timeFormatItems[2];
+ void Set(void);
+ public:
+ cMenuSetupGeneral(cTvguideConfig *data);
+};
+
+class cMenuSetupScreenLayout : public cMenuSetupSubMenu {
+ protected:
+ virtual eOSState ProcessKey(eKeys Key);
+ cThemes themes;
+ const char * hideChannelLogosItems[2];
+ const char * logoExtensionItems[2];
+ void Set(void);
+ public:
+ cMenuSetupScreenLayout(cTvguideConfig *data);
+};
+
+class cMenuSetupFont : public cMenuSetupSubMenu {
+ protected:
+ cStringList fontNames;
+ void Set(void);
+ public:
+ cMenuSetupFont(cTvguideConfig *data);
+};
+
+#endif //__TVGUIDE_SETUP_H \ No newline at end of file
diff --git a/styledpixmap.c b/styledpixmap.c
new file mode 100644
index 0000000..2569f53
--- /dev/null
+++ b/styledpixmap.c
@@ -0,0 +1,125 @@
+#include "styledpixmap.h"
+
+cStyledPixmap::cStyledPixmap(void) {
+ pixmap = NULL;
+ caller = NULL;
+}
+
+cStyledPixmap::cStyledPixmap(cPixmap *pixmap, cString caller) {
+ this->pixmap = pixmap;
+ this->caller = caller;
+}
+
+cStyledPixmap::~cStyledPixmap(void) {
+ if (pixmap)
+ osdManager.releasePixmap(pixmap, *caller);
+}
+
+void cStyledPixmap::setPixmap(cPixmap *pixmap) {
+ if (pixmap) {
+ this->pixmap = pixmap;
+ }
+}
+
+void cStyledPixmap::drawBackground() {
+ if (!tvguideConfig.useBlending) {
+ pixmap->Fill(color);
+ } else {
+ drawBlendedBackground();
+ }
+}
+
+void cStyledPixmap::drawBlendedBackground() {
+ int width = pixmap->ViewPort().Width();
+ int height = pixmap->ViewPort().Height();
+ pixmap->Fill(color);
+ int numSteps = 64;
+ int alphaStep = 0x04;
+ if (height < 30)
+ return;
+ else if (height < 100) {
+ numSteps = 32;
+ alphaStep = 0x08;
+ }
+ int stepY = 0.5*height / numSteps;
+ if (stepY == 0) stepY = 1;
+ int alpha = 0x00;
+ tColor clr;
+ for (int i = 0; i<numSteps; i++) {
+ clr = AlphaBlend(color, colorBlending, alpha);
+ pixmap->DrawRectangle(cRect(0,i*stepY,width,stepY), clr);
+ alpha += alphaStep;
+ }
+}
+
+void cStyledPixmap::drawBorder() {
+ int width = pixmap->ViewPort().Width();
+ int height = pixmap->ViewPort().Height();
+
+ drawDefaultBorder(width, height);
+ if (tvguideConfig.roundedCorners) {
+ int borderRadius = 12;
+ drawRoundedCorners(width, height, borderRadius);
+ }
+}
+
+void cStyledPixmap::drawDefaultBorder(int width, int height) {
+ pixmap->DrawRectangle(cRect(0,0,width,2), theme.Color(clrBackground)); //top
+ pixmap->DrawRectangle(cRect(0,0,2,height), theme.Color(clrBackground)); //left
+ pixmap->DrawRectangle(cRect(0,height-2,width,2), theme.Color(clrBackground)); //bottom
+ pixmap->DrawRectangle(cRect(width-2,0,2,height), theme.Color(clrBackground)); //right
+
+ pixmap->DrawRectangle(cRect(2,2,width-4,1), theme.Color(clrBorder)); //top
+ pixmap->DrawRectangle(cRect(2,2,1,height-4), theme.Color(clrBorder)); //left
+ pixmap->DrawRectangle(cRect(2,height-3,width-4,1), theme.Color(clrBorder)); //bottom
+ pixmap->DrawRectangle(cRect(width-3,2,1,height-4), theme.Color(clrBorder)); //right
+}
+
+void cStyledPixmap::drawBoldBorder() {
+ int width = pixmap->ViewPort().Width();
+ int height = pixmap->ViewPort().Height();
+ pixmap->DrawRectangle(cRect(0,0,width,2), theme.Color(clrBorder)); //top
+ pixmap->DrawRectangle(cRect(0,0,2,height), theme.Color(clrBorder)); //left
+ pixmap->DrawRectangle(cRect(0,height-2,width,2), theme.Color(clrBorder)); //bottom
+ pixmap->DrawRectangle(cRect(width-2,0,2,height), theme.Color(clrBorder)); //right
+}
+
+void cStyledPixmap::drawRoundedCorners(int width, int height, int radius) {
+ pixmap->DrawEllipse(cRect(2,2,radius,radius), theme.Color(clrBorder), -2);
+ pixmap->DrawEllipse(cRect(1,1,radius,radius), theme.Color(clrBackground), -2);
+
+ pixmap->DrawEllipse(cRect(width-radius - 2,2,radius,radius), theme.Color(clrBorder), -1);
+ pixmap->DrawEllipse(cRect(width-radius - 1,1,radius,radius), theme.Color(clrBackground), -1);
+
+ if( height > 2*radius) {
+ pixmap->DrawEllipse(cRect(2,height-radius - 2,radius,radius), theme.Color(clrBorder), -3);
+ pixmap->DrawEllipse(cRect(1,height-radius - 1,radius,radius), theme.Color(clrBackground), -3);
+
+ pixmap->DrawEllipse(cRect(width-radius - 2,height-radius - 2,radius,radius), theme.Color(clrBorder), -4);
+ pixmap->DrawEllipse(cRect(width-radius - 1,height-radius - 1,radius,radius), theme.Color(clrBackground), -4);
+ }
+}
+
+void cStyledPixmap::drawVerticalLine(int x, int yStart, int yStop, tColor col) {
+ for (int y = yStart; y <= yStop; y++) {
+ pixmap->DrawPixel(cPoint(x,y), col);
+ }
+}
+
+void cStyledPixmap::drawHorizontalLine(int y, int xStart, int xStop, tColor col) {
+ for (int x = xStart; x <= xStop; x++) {
+ pixmap->DrawPixel(cPoint(x,y), col);
+ }
+}
+
+void cStyledPixmap::DrawText(const cPoint &Point, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font) {
+ pixmap->DrawText(Point, s, ColorFg, ColorBg, Font);
+}
+
+void cStyledPixmap::DrawImage(const cPoint &Point, const cImage &Image) {
+ pixmap->DrawImage(Point, Image);
+}
+
+void cStyledPixmap::DrawRectangle(const cRect &Rect, tColor Color) {
+ pixmap->DrawRectangle(Rect,Color);
+}
diff --git a/styledpixmap.h b/styledpixmap.h
new file mode 100644
index 0000000..a751032
--- /dev/null
+++ b/styledpixmap.h
@@ -0,0 +1,35 @@
+#ifndef __TVGUIDE_STYLEDPIXMAP_H
+#define __TVGUIDE_STYLEDPIXMAP_H
+
+// --- cStyledPixmap -------------------------------------------------------------
+
+class cStyledPixmap {
+private:
+ void drawVerticalLine(int x, int yStart, int yStop, tColor col);
+ void drawHorizontalLine(int y, int xStart, int xStop, tColor col);
+protected:
+ cPixmap *pixmap;
+ cString caller;
+ tColor color;
+ tColor colorBlending;
+ void setPixmap(cPixmap *pixmap);
+public:
+ cStyledPixmap(void);
+ cStyledPixmap(cPixmap *pixmap, cString caller);
+ virtual ~cStyledPixmap(void);
+ void drawBackground();
+ void drawBlendedBackground();
+ void drawBorder();
+ void drawBoldBorder();
+ void drawDefaultBorder(int width, int height);
+ void drawRoundedCorners(int width, int height, int radius);
+ void setColor(tColor color, tColor colorBlending) {this->color = color; this->colorBlending = colorBlending;};
+ void SetAlpha(int alpha) {pixmap->SetAlpha(alpha);};
+ void DrawText(const cPoint &Point, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font);
+ void DrawImage(const cPoint &Point, const cImage &Image);
+ void DrawRectangle(const cRect &Rect, tColor Color);
+ int Width() {return pixmap->ViewPort().Width();};
+ int Height() {return pixmap->ViewPort().Height();};
+};
+
+#endif //__TVGUIDE_STYLEDPIXMAP_H \ No newline at end of file
diff --git a/themes/tvguide-darkblue.theme b/themes/tvguide-darkblue.theme
new file mode 100644
index 0000000..4040458
--- /dev/null
+++ b/themes/tvguide-darkblue.theme
@@ -0,0 +1,28 @@
+Description = DarkBlue
+
+clrBackground = FF000000
+clrBackgroundOSD = FF000000
+clrGrid1 = FF0E53A7
+clrGrid1Blending = FF000000
+clrGrid2 = FF071871
+clrGrid2Blending = FF000000
+clrHighlight = FF7A6D6D
+clrHighlightBlending = FF1C1919
+clrFont = FFFFFFFF
+clrFontHeader = FF000000
+clrFontButtons = FFFFFFFF
+clrHeader = FF8B9194
+clrHeaderBlending = FF000000
+clrBorder = FFFFFFFF
+clrTimeline1 = FFFFFFFF
+clrTimeline1Blending = FF828282
+clrTimeline2 = FF000000
+clrTimeline2Blending = FF3F3F3F
+clrButtonRed = FFD42627
+clrButtonRedBlending = FF000000
+clrButtonGreen = FF004F00
+clrButtonGreenBlending = FF000000
+clrButtonYellow = FFFFA300
+clrButtonYellowBlending = FF000000
+clrButtonBlue = FF0000FE
+clrButtonBlueBlending = FF000000
diff --git a/themes/tvguide-default.theme b/themes/tvguide-default.theme
new file mode 100644
index 0000000..e1e5794
--- /dev/null
+++ b/themes/tvguide-default.theme
@@ -0,0 +1,27 @@
+Description = Default
+clrBackground = FF000000
+clrBackgroundOSD = FF000000
+clrGrid1 = FF404749
+clrGrid1Blending = FF000000
+clrGrid2 = FF20293F
+clrGrid2Blending = FF000000
+clrHighlight = FFFF4D00
+clrHighlightBlending = FF000000
+clrFont = FFFFFFFF
+clrFontHeader = FFFFFFFF
+clrFontButtons = FFFFFFFF
+clrHeader = FF000000
+clrHeaderBlending = FFE0E0E0
+clrBorder = FFFFFFFF
+clrTimeline1 = FFFFFFFF
+clrTimeline1Blending = FF828282
+clrTimeline2 = FF000000
+clrTimeline2Blending = FF3F3F3F
+clrButtonRed = FFD42627
+clrButtonRedBlending = FFE0E0E0
+clrButtonGreen = FF004F00
+clrButtonGreenBlending = FFE0E0E0
+clrButtonYellow = FFFFA300
+clrButtonYellowBlending = FFE0E0E0
+clrButtonBlue = FF0000FE
+clrButtonBlueBlending = FFE0E0E0
diff --git a/timeline.c b/timeline.c
new file mode 100644
index 0000000..e2275fd
--- /dev/null
+++ b/timeline.c
@@ -0,0 +1,115 @@
+#include "timeline.h"
+
+cTimeLine::cTimeLine(cMyTime *myTime) {
+ this->myTime = myTime;
+ dateViewer = new cStyledPixmap(osdManager.requestPixmap(3, cRect(0, 0, tvguideConfig.timeColWidth, tvguideConfig.headerHeight), cRect::Null, "dateViewer"), "dateViewer");
+ timeline = osdManager.requestPixmap(2, cRect(0, tvguideConfig.headerHeight, tvguideConfig.timeColWidth, tvguideConfig.osdHeight - tvguideConfig.headerHeight - tvguideConfig.footerHeight),
+ cRect(0,0, tvguideConfig.timeColWidth, 1440*tvguideConfig.minuteHeight), "timeline");
+ clock = new cStyledPixmap(osdManager.requestPixmap(3, cRect(0, tvguideConfig.osdHeight-tvguideConfig.footerHeight, tvguideConfig.timeColWidth, tvguideConfig.footerHeight-9), cRect::Null, "timeViewer"), "timeViewer");
+}
+
+cTimeLine::~cTimeLine(void) {
+ delete dateViewer;
+ osdManager.releasePixmap(timeline);
+ delete clock;
+}
+
+void cTimeLine::drawDateViewer() {
+ cString weekDay = myTime->GetWeekday();
+ cString date = myTime->GetDate();
+
+ int textHeight = tvguideConfig.FontTimeLineWeekday->Height();
+ int weekdayWidth = tvguideConfig.FontTimeLineWeekday->Width(*weekDay);
+ int dateWidth = tvguideConfig.FontTimeLineDate->Width(*date);
+
+ dateViewer->setColor(theme.Color(clrHeader), theme.Color(clrHeaderBlending));
+ dateViewer->drawBackground();
+ dateViewer->drawBorder();
+ dateViewer->DrawText(cPoint((tvguideConfig.timeColWidth-weekdayWidth)/2, (tvguideConfig.headerHeight-2*textHeight)/2), *weekDay, theme.Color(clrFontHeader), clrTransparent, tvguideConfig.FontTimeLineWeekday);
+ dateViewer->DrawText(cPoint((tvguideConfig.timeColWidth-dateWidth)/2, (tvguideConfig.headerHeight-2*textHeight)/2 + textHeight + 5), *date, theme.Color(clrFontHeader), clrTransparent, tvguideConfig.FontTimeLineDate);
+}
+
+void cTimeLine::drawTimeline() {
+ timeline->SetTile(true);
+ timeline->Fill(theme.Color(clrBackground));
+ tColor colorFont;
+
+ const cImage *img1 = createBackgroundImage(tvguideConfig.timeColWidth-4, tvguideConfig.minuteHeight*30, theme.Color(clrTimeline1), theme.Color(clrTimeline1Blending));
+ const cImage *img2 = createBackgroundImage(tvguideConfig.timeColWidth-4, tvguideConfig.minuteHeight*30, theme.Color(clrTimeline2), theme.Color(clrTimeline2Blending));
+ const cImage *img = NULL;
+
+ int textWidth, posY;
+ char timetext[10];
+
+ for (int i=0; i<48; i++) {
+ if (i%2==0) {
+ img = img1;
+ colorFont = theme.Color(clrTimeline2);
+ if (tvguideConfig.timeFormat == e12Hours) {
+ if (i == 0)
+ sprintf(timetext, "12:00 PM");
+ else if (i/2 < 13)
+ sprintf(timetext, "%d:00 AM", i/2);
+ else
+ sprintf(timetext, "%d:00 PM", i/2-12);
+ } else {
+ sprintf(timetext, "%d:00", i/2);
+ }
+ } else {
+ img = img2;
+ colorFont = theme.Color(clrTimeline1);
+ if (tvguideConfig.timeFormat == e12Hours) {
+ if (i == 1)
+ sprintf(timetext, "12:30 PM");
+ else if (i/2 < 13)
+ sprintf(timetext, "%d:30 AM", i/2);
+ else
+ sprintf(timetext, "%d:30 PM", i/2-12);
+ } else {
+ sprintf(timetext, "%d:30", i/2);
+ }
+ }
+ posY = i*tvguideConfig.minuteHeight*30;
+ timeline->DrawImage(cPoint(2, posY), *img);
+ textWidth = tvguideConfig.FontTimeLineTime->Width(timetext);
+ timeline->DrawText(cPoint((tvguideConfig.timeColWidth-textWidth)/2, posY + 5), timetext, colorFont, clrTransparent, tvguideConfig.FontTimeLineTime);
+ }
+ setTimeline();
+ delete img1;
+ delete img2;
+}
+
+cImage *cTimeLine::createBackgroundImage(int width, int height, tColor clrBgr, tColor clrBlend) {
+ cImage *image = new cImage(cSize(width, height));
+ image->Fill(clrBgr);
+ if (tvguideConfig.useBlending) {
+ int stepY = 0.5*height / 64;
+ int alpha = 0x00;
+ tColor clr;
+ for (int i = 0; i<64; i++) {
+ clr = AlphaBlend(clrBgr, clrBlend, alpha);
+ for (int y = i*stepY; y < (i+1)*stepY; y++) {
+ for (int x=0; x<width; x++) {
+ image->SetPixel(cPoint(x,y), clr);
+ }
+ }
+ alpha += 0x04;
+ }
+ }
+ return image;
+}
+
+void cTimeLine::setTimeline() {
+ int offset = myTime->GetTimelineOffset();
+ timeline->SetDrawPortPoint(cPoint(0, -offset*tvguideConfig.minuteHeight));
+}
+
+void cTimeLine::drawClock() {
+ cString currentTime = myTime->GetCurrentTime();
+ int textHeight = tvguideConfig.FontTimeLineTime->Height();
+ int clockWidth = tvguideConfig.FontTimeLineTime->Width(*currentTime);
+ clock->setColor(theme.Color(clrHeader), theme.Color(clrHeaderBlending));
+ clock->drawBackground();
+ clock->drawBorder();
+ clock->DrawText(cPoint((tvguideConfig.timeColWidth-clockWidth)/2, (tvguideConfig.footerHeight-textHeight)/2), *currentTime, theme.Color(clrFontHeader), clrTransparent, tvguideConfig.FontTimeLineTime);
+} \ No newline at end of file
diff --git a/timeline.h b/timeline.h
new file mode 100644
index 0000000..e534fa5
--- /dev/null
+++ b/timeline.h
@@ -0,0 +1,22 @@
+#ifndef __TVGUIDE_TIMELINE_H
+#define __TVGUIDE_TIMELINE_H
+
+// --- cTimeLine -------------------------------------------------------------
+
+class cTimeLine {
+private:
+ cMyTime *myTime;
+ cStyledPixmap *dateViewer;
+ cPixmap *timeline;
+ cStyledPixmap *clock;
+ cImage *createBackgroundImage(int width, int height, tColor clrBgr, tColor clrBlend);
+public:
+ cTimeLine(cMyTime *myTime);
+ virtual ~cTimeLine(void);
+ void drawDateViewer();
+ void drawTimeline();
+ void setTimeline();
+ void drawClock();
+};
+
+#endif //__TVGUIDE_TIMELINE_H \ No newline at end of file
diff --git a/timer.c b/timer.c
new file mode 100644
index 0000000..9ec6681
--- /dev/null
+++ b/timer.c
@@ -0,0 +1,106 @@
+#include "timer.h"
+
+cMyTime::~cMyTime(void) {
+}
+
+void cMyTime::Now() {
+ t = time(0);
+ tStart = t;
+ tStart = GetRounded();
+ tStop = tStart + (tvguideConfig.osdHeight - tvguideConfig.headerHeight - tvguideConfig.footerHeight)/tvguideConfig.minuteHeight*60;
+}
+
+void cMyTime::AddStep(int step) {
+ tStart += step*60;
+ tStop += step*60;
+}
+
+bool cMyTime::DelStep(int step) {
+ if ((tStart - step*60)+30*60 < t) {
+ return true;
+ }
+ tStart -= step*60;
+ tStop -= step*60;
+ return false;
+}
+
+void cMyTime::SetTime(time_t newTime) {
+ tStart = newTime;
+ tStop = tStart + (tvguideConfig.osdHeight - tvguideConfig.headerHeight - tvguideConfig.footerHeight)/tvguideConfig.minuteHeight*60;
+}
+
+time_t cMyTime::getPrevPrimetime(time_t current) {
+ tm *st = localtime(&current);
+ if (st->tm_hour < 21) {
+ current -= 24 * 60* 60;
+ st = localtime(&current);
+ }
+ st->tm_hour = 20;
+ st->tm_min = 0;
+ time_t primeTime = mktime(st);
+ return primeTime;
+}
+
+time_t cMyTime::getNextPrimetime(time_t current){
+ tm *st = localtime(&current);
+ if (st->tm_hour > 19) {
+ current += 24 * 60* 60;
+ st = localtime(&current);
+ }
+ st->tm_hour = 20;
+ st->tm_min = 0;
+ time_t primeTime = mktime(st);
+ return primeTime;
+}
+
+bool cMyTime::tooFarInPast(time_t current) {
+ if (current < t) {
+ return true;
+ }
+ return false;
+}
+
+cString cMyTime::GetCurrentTime() {
+ char buf[25];
+ t = time(0);
+ tm *st = localtime(&t);
+ //snprintf(text, sizeof(text), "%d:%02d", st->tm_hour, st->tm_min);
+ if (tvguideConfig.timeFormat == e12Hours) {
+ strftime(buf, sizeof(buf), "%I:%M %p", st);
+ } else if (tvguideConfig.timeFormat == e24Hours)
+ strftime(buf, sizeof(buf), "%H:%M", st);
+ return buf;
+
+}
+
+cString cMyTime::GetDate() {
+ char text[6];
+ tm *st = localtime(&tStart);
+ snprintf(text, sizeof(text), "%d.%d", st->tm_mday, st->tm_mon+1);
+ return text;
+}
+
+cString cMyTime::GetWeekday() {
+ return WeekDayName(tStart);
+}
+
+int cMyTime::GetTimelineOffset() {
+ tm *st = localtime(&tStart);
+ int offset = st->tm_hour*60;
+ offset += st->tm_min;
+ return offset;
+}
+
+time_t cMyTime::GetRounded() {
+ tm *rounded = localtime ( &tStart );
+ rounded->tm_sec = 0;
+ if (rounded->tm_min > 29)
+ rounded->tm_min = 30;
+ else
+ rounded->tm_min = 0;
+ return mktime(rounded);
+}
+
+void cMyTime::debug() {
+ esyslog("t: %s, tStart: %s, tStop: %s", *TimeString(t), *TimeString(tStart), *TimeString(tStop));
+}
diff --git a/timer.h b/timer.h
new file mode 100644
index 0000000..b2d9aa5
--- /dev/null
+++ b/timer.h
@@ -0,0 +1,32 @@
+#ifndef __TVGUIDE_TIMER_H
+#define __TVGUIDE_TIMER_H
+
+// --- cMyTime -------------------------------------------------------------
+
+class cMyTime {
+ private:
+ time_t t;
+ time_t tStart;
+ time_t tStop;
+ public:
+ cMyTime(){};
+ virtual ~cMyTime(void);
+ void Now();
+ void AddStep(int step);
+ bool DelStep(int step);
+ void SetTime(time_t newTime);
+ time_t Get() {return t;};
+ time_t GetStart() {return tStart;};
+ time_t GetStop() {return tStop;};
+ cString GetCurrentTime();
+ cString GetDate();
+ cString GetWeekday();
+ time_t getPrevPrimetime(time_t current);
+ time_t getNextPrimetime(time_t current);
+ bool tooFarInPast(time_t current);
+ int GetTimelineOffset();
+ time_t GetRounded();
+ void debug();
+};
+
+#endif //__TVGUIDE_TIMER_H \ No newline at end of file
diff --git a/tvguide.c b/tvguide.c
new file mode 100644
index 0000000..d513a62
--- /dev/null
+++ b/tvguide.c
@@ -0,0 +1,194 @@
+/*
+ * tvguide.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include <time.h>
+#include <getopt.h>
+#include <vdr/osd.h>
+#include <vdr/plugin.h>
+#include <vdr/device.h>
+
+#include "tvguideosd.c"
+
+#if defined(APIVERSNUM) && APIVERSNUM < 10717
+#error "VDR-1.7.17 API version or greater is required!"
+#endif
+
+
+
+static const char *VERSION = "0.0.1";
+static const char *DESCRIPTION = "A fancy 2d EPG Viewer";
+static const char *MAINMENUENTRY = "Tvguide";
+
+class cPluginTvguide : public cPlugin {
+private:
+ bool logoPathSet;
+ bool imagesPathSet;
+public:
+ cPluginTvguide(void);
+ virtual ~cPluginTvguide();
+ 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);
+ };
+
+cPluginTvguide::cPluginTvguide(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!
+ logoPathSet = false;
+ imagesPathSet = false;
+}
+
+cPluginTvguide::~cPluginTvguide()
+{
+ // Clean up after yourself!
+}
+
+const char *cPluginTvguide::CommandLineHelp(void)
+{
+ // Return a string that describes all known command line options.
+ return
+ " -i <IMAGESDIR>, --epgimages=<IMAGESDIR> Set directory where epgimages are stored\n"
+ " -l <LOGODIR>, --logodir=<LOGODIR> Set directory where logos are stored.\n";
+}
+
+bool cPluginTvguide::ProcessArgs(int argc, char *argv[])
+{
+ // Implement command line argument processing here if applicable.
+ static const struct option long_options[] = {
+ { "epgimages", required_argument, NULL, 'i' },
+ { "logopath", required_argument, NULL, 'l' },
+ { 0, 0, 0, 0 }
+ };
+
+ int c;
+ cString *path = NULL;
+ while ((c = getopt_long(argc, argv, "i:f:l:", long_options, NULL)) != -1) {
+ switch (c) {
+ case 'i':
+ path = new cString(optarg);
+ tvguideConfig.SetImagesPath(*path);
+ imagesPathSet = true;
+ break;
+ case 'l':
+ path = new cString(optarg);
+ tvguideConfig.SetLogoPath(*path);
+ logoPathSet = true;
+ break;
+ default:
+ return false;
+ }
+ if (path)
+ delete path;
+ }
+ return true;
+}
+
+bool cPluginTvguide::Initialize(void)
+{
+ // Initialize any background activities the plugin shall perform.
+ return true;
+}
+
+bool cPluginTvguide::Start(void)
+{
+ if (!logoPathSet) {
+ cString path = cString::sprintf("%s/channellogos/", cPlugin::ConfigDirectory(PLUGIN_NAME_I18N));
+ tvguideConfig.SetLogoPath(path);
+ logoPathSet = true;
+ }
+
+ if (!imagesPathSet) {
+ cString path = cString::sprintf("%s/epgimages/", cPlugin::ConfigDirectory(PLUGIN_NAME_I18N));
+ tvguideConfig.SetImagesPath(path);
+ logoPathSet = true;
+ }
+ return true;
+}
+
+void cPluginTvguide::Stop(void)
+{
+ // Stop any background activities the plugin is performing.
+}
+
+void cPluginTvguide::Housekeeping(void)
+{
+ // Perform any cleanup or other regular tasks.
+}
+
+void cPluginTvguide::MainThreadHook(void)
+{
+ // Perform actions in the context of the main program thread.
+ // WARNING: Use with great care - see PLUGINS.html!
+}
+
+cString cPluginTvguide::Active(void)
+{
+ // Return a message string if shutdown should be postponed
+ return NULL;
+}
+
+time_t cPluginTvguide::WakeupTime(void)
+{
+ // Return custom wakeup time for shutdown script
+ return 0;
+}
+
+cOsdObject *cPluginTvguide::MainMenuAction(void)
+{
+ // Perform the action when selected from the main VDR menu.
+ return new cTvGuideOsd;
+}
+
+cMenuSetupPage *cPluginTvguide::SetupMenu(void)
+{
+ // Return a setup menu in case the plugin supports one.
+ return new cTvguideSetup();
+}
+
+bool cPluginTvguide::SetupParse(const char *Name, const char *Value)
+{
+ // Parse your own setup parameters and store their values.
+ return tvguideConfig.SetupParse(Name, Value);
+}
+
+bool cPluginTvguide::Service(const char *Id, void *Data)
+{
+ // Handle custom service requests from other plugins
+ return false;
+}
+
+const char **cPluginTvguide::SVDRPHelpPages(void)
+{
+ // Return help text for SVDRP commands this plugin implements
+ return NULL;
+}
+
+cString cPluginTvguide::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
+{
+ // Process SVDRP commands this plugin implements
+ return NULL;
+}
+
+VDRPLUGINCREATOR(cPluginTvguide); // Don't touch this!
diff --git a/tvguideosd.c b/tvguideosd.c
new file mode 100644
index 0000000..8fca65a
--- /dev/null
+++ b/tvguideosd.c
@@ -0,0 +1,541 @@
+// --- Theme -------------------------------------------------------------
+static cTheme theme;
+
+THEME_CLR(theme, clrBackgroundOSD, clrBlack);
+THEME_CLR(theme, clrBackground, clrBlack);
+THEME_CLR(theme, clrGrid1, 0xFF404749);
+THEME_CLR(theme, clrGrid1Blending, 0xFF000000);
+THEME_CLR(theme, clrGrid2, 0xFF20293F);
+THEME_CLR(theme, clrGrid2Blending, 0xFF000000);
+THEME_CLR(theme, clrHighlight, 0xFFFF4D00);
+THEME_CLR(theme, clrHighlightBlending, 0xFF000000);
+THEME_CLR(theme, clrFont, clrWhite);
+THEME_CLR(theme, clrFontHeader, clrWhite);
+THEME_CLR(theme, clrFontButtons, clrWhite);
+THEME_CLR(theme, clrHeader, clrBlack);
+THEME_CLR(theme, clrHeaderBlending, 0xFFE0E0E0);
+THEME_CLR(theme, clrBorder, clrWhite);
+THEME_CLR(theme, clrTimeline1, clrWhite);
+THEME_CLR(theme, clrTimeline1Blending, 0xFF828282);
+THEME_CLR(theme, clrTimeline2, clrBlack);
+THEME_CLR(theme, clrTimeline2Blending, 0xFF3F3F3F);
+THEME_CLR(theme, clrButtonRed, 0xFFD42627);
+THEME_CLR(theme, clrButtonRedBlending, 0xFFE0E0E0);
+THEME_CLR(theme, clrButtonGreen, 0xFF004F00);
+THEME_CLR(theme, clrButtonGreenBlending, 0xFFE0E0E0);
+THEME_CLR(theme, clrButtonYellow, 0xFFffa300);
+THEME_CLR(theme, clrButtonYellowBlending, 0xFFE0E0E0);
+THEME_CLR(theme, clrButtonBlue, 0xFF0000fe);
+THEME_CLR(theme, clrButtonBlueBlending, 0xFFE0E0E0);
+
+#include "config.c"
+cTvguideConfig tvguideConfig;
+
+#include "osdmanager.c"
+cOsdManager osdManager;
+
+#include "setup.c"
+#include "imageloader.c"
+#include "styledpixmap.c"
+#include "timer.c"
+#include "messagebox.c"
+#include "timeline.c"
+#include "epggrid.c"
+#include "detailview.c"
+#include "channelcolumn.c"
+#include "footer.c"
+
+#include "tvguideosd.h"
+#include <stdlib.h>
+
+cTvGuideOsd::cTvGuideOsd(void) {
+ detailView = NULL;
+ detailViewActive = false;
+ timeLine = NULL;
+}
+
+cTvGuideOsd::~cTvGuideOsd() {
+ delete myTime;
+ columns.Clear();
+ if (detailView)
+ delete detailView;
+ delete timeLine;
+ delete footer;
+ cMessageBox::Destroy();
+ osdManager.deleteOsd();
+}
+
+void cTvGuideOsd::Show(void) {
+ int start = cTimeMs::Now();
+ bool ok = false;
+ ok = osdManager.setOsd();
+ if (ok) {
+ tvguideConfig.setDynamicValues(osdManager.Width(), osdManager.Height());
+ tvguideConfig.loadTheme();
+ osdManager.setBackground();
+ myTime = new cMyTime();
+ myTime->Now();
+ drawOsd();
+ }
+ esyslog("tvguide: Rendering took %d ms", int(cTimeMs::Now()-start));
+}
+
+void cTvGuideOsd::drawOsd() {
+ cPixmap::Lock();
+ cChannel *startChannel = Channels.GetByNumber(cDevice::CurrentChannel());
+ timeLine = new cTimeLine(myTime);
+ timeLine->drawDateViewer();
+ timeLine->drawTimeline();
+ timeLine->drawClock();
+ footer = new cFooter();
+ footer->drawRedButton();
+ footer->drawGreenButton();
+ footer->drawYellowButton();
+ footer->drawBlueButton();
+ osdManager.flush();
+ readChannels(startChannel);
+ drawGridsChannelJump();
+ osdManager.flush();
+ cPixmap::Unlock();
+}
+
+void cTvGuideOsd::readChannels(cChannel *channelStart) {
+ int i=0;
+ columns.Clear();
+ if (!channelStart)
+ return;
+ for (cChannel *channel = channelStart; channel; channel = Channels.Next(channel)) {
+ if (!channel->GroupSep()) {
+ cChannelColumn *column = new cChannelColumn(i, channel, myTime);
+ if (column->readGrids()) {
+ columns.Add(column);
+ i++;
+ } else {
+ delete column;
+ }
+ }
+ if (i == tvguideConfig.channelCols)
+ break;
+ }
+}
+
+bool cTvGuideOsd::readChannelsReverse(cChannel *channelStart) {
+ bool doUpdate = false;
+ int i = tvguideConfig.channelCols;
+ if (!channelStart)
+ return false;
+ for (cChannel *channel = Channels.Prev(channelStart); channel; channel = Channels.Prev(channel)) {
+ if (!channel->GroupSep()) {
+ cChannelColumn *column = new cChannelColumn(i-1, channel, myTime);
+ if (column->readGrids()) {
+ if (i == tvguideConfig.channelCols) {
+ columns.Clear();
+ doUpdate = true;
+ }
+ columns.Ins(column, columns.First());
+ i--;
+ } else {
+ delete column;
+ }
+ }
+ if (i == 0)
+ break;
+ }
+ return doUpdate;
+}
+
+void cTvGuideOsd::drawGridsChannelJump() {
+ if (columns.Count() == 0)
+ return;
+ activeGrid = columns.First()->getActive();
+ if (activeGrid)
+ activeGrid->SetActive();
+ for (cChannelColumn *column = columns.First(); column; column = columns.Next(column)) {
+ column->createHeader();
+ column->drawGrids();
+ }
+}
+
+void cTvGuideOsd::drawGridsTimeJump() {
+ if (columns.Count() == 0)
+ return;
+ cChannelColumn *colActive = NULL;
+ if (activeGrid) {
+ colActive = activeGrid->column;
+ } else {
+ colActive = columns.First();
+ }
+ for (cChannelColumn *column = columns.First(); column; column = columns.Next(column)) {
+ column->clearGrids();
+ column->readGrids();
+ column->drawGrids();
+ }
+ activeGrid = colActive->getActive();
+ if (activeGrid) {
+ activeGrid->SetActive();
+ activeGrid->Draw();
+ }
+}
+
+void cTvGuideOsd::setNextActiveGrid(cEpgGrid *next) {
+ if (!next || !activeGrid) {
+ return;
+ }
+ activeGrid->SetInActive();
+ activeGrid->Draw();
+ activeGrid = next;
+ activeGrid->SetActive();
+ activeGrid->Draw();
+}
+
+void cTvGuideOsd::processKeyUp() {
+ if (detailViewActive) {
+ detailView->scrollUp();
+ } else {
+ if (activeGrid == NULL) {
+ ScrollBack();
+ //Search for new active Grid
+ cEpgGrid *actGrid = NULL;
+ for (cChannelColumn *column = columns.First(); column; column = columns.Next(column)) {
+ actGrid = column->getActive();
+ if (actGrid) {
+ activeGrid = actGrid;
+ activeGrid->SetActive();
+ activeGrid->Draw();
+ break;
+ }
+ }
+ } else if (activeGrid->StartTime() <= myTime->GetStart()) {
+ activeGrid->debug();
+ ScrollBack();
+ } else {
+ cEpgGrid *prev = NULL;
+ prev = activeGrid->column->getPrev(activeGrid);
+ if (prev) {
+ setNextActiveGrid(prev);
+ } else {
+ ScrollBack();
+ prev = activeGrid->column->getPrev(activeGrid);
+ if (prev) {
+ setNextActiveGrid(prev);
+ }
+ }
+ }
+ }
+ osdManager.flush();
+}
+
+void cTvGuideOsd::processKeyDown() {
+ if (detailViewActive) {
+ detailView->scrollDown();
+ } else {
+ if (activeGrid == NULL) {
+ ScrollForward();
+ } else if (activeGrid->EndTime() > myTime->GetStop()) {
+ ScrollForward();
+ } else {
+ cEpgGrid *next = NULL;
+ next = activeGrid->column->getNext(activeGrid);
+ if (next) {
+ setNextActiveGrid(next);
+ } else {
+ ScrollForward();
+ next = activeGrid->column->getNext(activeGrid);
+ if (next) {
+ setNextActiveGrid(next);
+ }
+ }
+ }
+ }
+ osdManager.flush();
+}
+
+void cTvGuideOsd::ScrollForward() {
+ myTime->AddStep(tvguideConfig.stepMinutes);
+ timeLine->drawDateViewer();
+ timeLine->drawClock();
+ timeLine->setTimeline();
+ for (cChannelColumn *column = columns.First(); column; column = columns.Next(column)) {
+ column->AddNewGridsAtEnd();
+ column->ClearOutdatedStart();
+ column->drawGrids();
+ }
+}
+
+void cTvGuideOsd::ScrollBack() {
+ bool tooFarInPast = myTime->DelStep(tvguideConfig.stepMinutes);
+ if (tooFarInPast)
+ return;
+ timeLine->drawDateViewer();
+ timeLine->drawClock();
+ timeLine->setTimeline();
+ for (cChannelColumn *column = columns.First(); column; column = columns.Next(column)) {
+ column->AddNewGridsAtStart();
+ column->ClearOutdatedEnd();
+ column->drawGrids();
+ }
+}
+
+void cTvGuideOsd::processKeyLeft() {
+ if (detailViewActive)
+ return;
+ if (activeGrid == NULL)
+ return;
+ cChannelColumn *colLeft = columns.Prev(activeGrid->column);
+ if (!colLeft) {
+ cChannel *channelLeft = activeGrid->column->getChannel();
+ while (channelLeft = Channels.Prev(channelLeft)) {
+ if (!channelLeft->GroupSep()) {
+ colLeft = new cChannelColumn(0, channelLeft, myTime);
+ if (colLeft->readGrids()) {
+ break;
+ } else {
+ delete colLeft;
+ colLeft = NULL;
+ }
+ }
+ }
+ if (colLeft) {
+ if (columns.Count() == tvguideConfig.channelCols) {
+ cChannelColumn *cLast = columns.Last();
+ columns.Del(cLast);
+ }
+ for (cChannelColumn *column = columns.First(); column; column = columns.Next(column)) {
+ column->SetNum(column->GetNum() + 1);
+ column->drawHeader();
+ column->drawGrids();
+ }
+ columns.Ins(colLeft, columns.First());
+ colLeft->createHeader();
+ colLeft->drawGrids();
+ }
+ }
+
+ if (colLeft) {
+ cEpgGrid *left = colLeft->getNeighbor(activeGrid);
+ if (left) {
+ setNextActiveGrid(left);
+ }
+ }
+ osdManager.flush();
+}
+
+void cTvGuideOsd::processKeyRight() {
+ if (detailViewActive)
+ return;
+ if (activeGrid == NULL)
+ return;
+ cChannelColumn *colRight = columns.Next(activeGrid->column);
+ if (!colRight) {
+ cChannel *channelRight = activeGrid->column->getChannel();
+ while (channelRight = Channels.Next(channelRight)) {
+ if (!channelRight->GroupSep()) {
+ colRight = new cChannelColumn(tvguideConfig.channelCols - 1, channelRight, myTime);
+ if (colRight->readGrids()) {
+ break;
+ } else {
+ delete colRight;
+ colRight = NULL;
+ }
+ }
+ }
+ if (colRight) {
+ if (columns.Count() == tvguideConfig.channelCols) {
+ cChannelColumn *cFirst = columns.First();
+ columns.Del(cFirst);
+ }
+ for (cChannelColumn *column = columns.First(); column; column = columns.Next(column)) {
+ column->SetNum(column->GetNum() - 1);
+ column->drawHeader();
+ column->drawGrids();
+ }
+ columns.Add(colRight);
+ colRight->createHeader();
+ colRight->drawGrids();
+ }
+ }
+ if (colRight) {
+ cEpgGrid *right = colRight->getNeighbor(activeGrid);
+ if (right) {
+ setNextActiveGrid(right);
+ }
+ }
+ osdManager.flush();
+}
+
+void cTvGuideOsd::processKeyOk() {
+ if (detailViewActive) {
+ delete detailView;
+ detailView = NULL;
+ detailViewActive = false;
+ osdManager.flush();
+ } else {
+ detailViewActive = true;
+ detailView = new cDetailView(activeGrid);
+ detailView->Start();
+ }
+}
+
+void cTvGuideOsd::processKeyRed() {
+ if (activeGrid == NULL)
+ return;
+ cTimer *timer = new cTimer(activeGrid->GetEvent());
+ cTimer *t = Timers.GetTimer(timer);
+ cString msg;
+ if (t) {
+ isyslog("timer %s already exists", *timer->ToDescr());
+ delete timer;
+ msg = cString::sprintf(tr("Timer not set! There is already a timer for this item."));
+ } else {
+ Timers.Add(timer);
+ Timers.SetModified();
+ msg = cString::sprintf("%s:\n%s (%s) %s - %s", tr("Timer set"), activeGrid->GetEvent()->Title(), timer->Channel()->Name(), *DayDateTime(timer->StartTime()), *TimeString(timer->StopTime()));
+ timer->SetEvent(activeGrid->GetEvent());
+ activeGrid->setTimer();
+ activeGrid->column->setTimer();
+ activeGrid->SetDirty();
+ activeGrid->Draw();
+ osdManager.flush();
+ isyslog("timer %s added (active)", *timer->ToDescr());
+ }
+ cMessageBox::Start(4000, msg);
+}
+
+void cTvGuideOsd::processKeyGreen() {
+ if (activeGrid == NULL)
+ return;
+ cChannel *currentChannel = activeGrid->column->getChannel();
+ bool doUpdate = readChannelsReverse(currentChannel);
+ if (doUpdate && (columns.Count() > 0)) {
+ drawGridsChannelJump();
+ }
+ osdManager.flush();
+}
+
+void cTvGuideOsd::processKeyYellow() {
+ if (activeGrid == NULL)
+ return;
+ cChannel *currentChannel = activeGrid->column->getChannel();
+ cChannel *next = NULL;
+ int i=0;
+ for (cChannel *channel = currentChannel; channel; channel = Channels.Next(channel)) {
+ if (!channel->GroupSep()) {
+ next = channel;
+ i++;
+ }
+ if (i == (tvguideConfig.jumpChannels+1))
+ break;
+ }
+ if (next) {
+ readChannels(next);
+ if (columns.Count() > 0) {
+ drawGridsChannelJump();
+ }
+ osdManager.flush();
+ }
+}
+
+eOSState cTvGuideOsd::processKeyBlue() {
+ if (activeGrid == NULL)
+ return osContinue;
+ cChannel *currentChannel = activeGrid->column->getChannel();
+ if (currentChannel) {
+ cDevice::PrimaryDevice()->SwitchChannel(currentChannel, true);
+ return osEnd;
+ }
+ return osContinue;
+}
+
+void cTvGuideOsd::processKey1() {
+ bool tooFarInPast = myTime->DelStep(tvguideConfig.bigStepHours*60);
+ if (tooFarInPast)
+ return;
+ drawGridsTimeJump();
+ timeLine->drawDateViewer();
+ timeLine->drawClock();
+ timeLine->setTimeline();
+ osdManager.flush();
+}
+
+void cTvGuideOsd::processKey3() {
+ myTime->AddStep(tvguideConfig.bigStepHours*60);
+ drawGridsTimeJump();
+ timeLine->drawDateViewer();
+ timeLine->drawClock();
+ timeLine->setTimeline();
+ osdManager.flush();
+}
+
+void cTvGuideOsd::processKey4() {
+ bool tooFarInPast = myTime->DelStep(tvguideConfig.hugeStepHours*60);
+ if (tooFarInPast)
+ return;
+ drawGridsTimeJump();
+ timeLine->drawDateViewer();
+ timeLine->drawClock();
+ timeLine->setTimeline();
+ osdManager.flush();
+}
+
+void cTvGuideOsd::processKey6() {
+ myTime->AddStep(tvguideConfig.hugeStepHours*60);
+ drawGridsTimeJump();
+ timeLine->drawDateViewer();
+ timeLine->drawClock();
+ timeLine->setTimeline();
+ osdManager.flush();
+}
+
+void cTvGuideOsd::processKey7() {
+ cMyTime *primeChecker = new cMyTime();
+ primeChecker->Now();
+ time_t prevPrime = primeChecker->getPrevPrimetime(myTime->GetStart());
+ if (primeChecker->tooFarInPast(prevPrime))
+ return;
+ myTime->SetTime(prevPrime);
+ drawGridsTimeJump();
+ timeLine->drawDateViewer();
+ timeLine->drawClock();
+ timeLine->setTimeline();
+ osdManager.flush();
+}
+
+void cTvGuideOsd::processKey9() {
+ cMyTime *primeChecker = new cMyTime();
+ time_t nextPrime = primeChecker->getNextPrimetime(myTime->GetStart());
+ myTime->SetTime(nextPrime);
+ drawGridsTimeJump();
+ timeLine->drawDateViewer();
+ timeLine->drawClock();
+ timeLine->setTimeline();
+ osdManager.flush();
+}
+
+eOSState cTvGuideOsd::ProcessKey(eKeys Key) {
+ eOSState state = cOsdObject::ProcessKey(Key);
+ if (state == osUnknown) {
+ cPixmap::Lock();
+ state = osContinue;
+ switch (Key & ~k_Repeat) {
+ case kUp: processKeyUp(); break;
+ case kDown: processKeyDown(); break;
+ case kLeft: processKeyLeft(); break;
+ case kRight: processKeyRight(); break;
+ case kRed: processKeyRed(); break;
+ case kGreen: processKeyGreen(); break;
+ case kYellow: processKeyYellow(); break;
+ case kBlue: state = processKeyBlue(); break;
+ case kOk: processKeyOk(); break;
+ case kBack: state=osEnd; break;
+ case k1: processKey1(); break;
+ case k3: processKey3(); break;
+ case k4: processKey4(); break;
+ case k6: processKey6(); break;
+ case k7: processKey7(); break;
+ case k9: processKey9(); break;
+ default: break;
+ }
+ cPixmap::Unlock();
+ }
+ return state;
+} \ No newline at end of file
diff --git a/tvguideosd.h b/tvguideosd.h
new file mode 100644
index 0000000..c619ec4
--- /dev/null
+++ b/tvguideosd.h
@@ -0,0 +1,45 @@
+#ifndef __TVGUIDE_TVGUIDEOSD_H
+#define __TVGUIDE_TVGUIDEOSD_H
+
+// --- cTvGuideOsd -------------------------------------------------------------
+
+class cTvGuideOsd : public cOsdObject {
+private:
+ cMyTime *myTime;
+ cList<cChannelColumn> columns;
+ cEpgGrid *activeGrid;
+ cDetailView *detailView;
+ cTimeLine *timeLine;
+ cFooter *footer;
+ bool detailViewActive;
+ void drawOsd();
+ void readChannels(cChannel *channelStart);
+ bool readChannelsReverse(cChannel *channelStart);
+ void drawGridsChannelJump();
+ void drawGridsTimeJump();
+ void processKeyUp();
+ void processKeyDown();
+ void processKeyLeft();
+ void processKeyRight();
+ void processKeyRed();
+ void processKeyGreen();
+ void processKeyYellow();
+ eOSState processKeyBlue();
+ void processKeyOk();
+ void processKey1();
+ void processKey3();
+ void processKey4();
+ void processKey6();
+ void processKey7();
+ void processKey9();
+ void setNextActiveGrid(cEpgGrid *next);
+ void ScrollForward();
+ void ScrollBack();
+public:
+ cTvGuideOsd(void);
+ virtual ~cTvGuideOsd(void);
+ virtual void Show(void);
+ virtual eOSState ProcessKey(eKeys Key);
+};
+
+#endif //__TVGUIDE_TVGUIDEOSD_H \ No newline at end of file