From 47c3fea545a1b4607deda1e7d2fa51cbcf89a656 Mon Sep 17 00:00:00 2001 From: louis Date: Thu, 17 Jan 2013 13:16:44 +0100 Subject: [PATCH] Initial push tvguide 0.0.1 --- COPYING | 340 +++++++++++++++++++++ HISTORY | 6 + Makefile | 122 ++++++++ Makefile-smaller-VDR1.7.34 | 115 ++++++++ README | 132 +++++++++ channelcolumn.c | 274 +++++++++++++++++ channelcolumn.h | 41 +++ config.c | 176 +++++++++++ config.h | 67 +++++ detailview.c | 186 ++++++++++++ detailview.h | 39 +++ epggrid.c | 146 +++++++++ epggrid.h | 44 +++ footer.c | 59 ++++ footer.h | 23 ++ imageloader.c | 78 +++++ imageloader.h | 23 ++ messagebox.c | 114 +++++++ messagebox.h | 31 ++ osdmanager.c | 52 ++++ po/de_DE.po | 139 +++++++++ setup.c | 209 +++++++++++++ setup.h | 54 ++++ styledpixmap.c | 125 ++++++++ styledpixmap.h | 35 +++ themes/tvguide-darkblue.theme | 28 ++ themes/tvguide-default.theme | 27 ++ timeline.c | 115 ++++++++ timeline.h | 22 ++ timer.c | 106 +++++++ timer.h | 32 ++ tvguide.c | 194 ++++++++++++ tvguideosd.c | 541 ++++++++++++++++++++++++++++++++++ tvguideosd.h | 45 +++ 34 files changed, 3740 insertions(+) create mode 100644 COPYING create mode 100644 HISTORY create mode 100644 Makefile create mode 100644 Makefile-smaller-VDR1.7.34 create mode 100644 README create mode 100644 channelcolumn.c create mode 100644 channelcolumn.h create mode 100644 config.c create mode 100644 config.h create mode 100644 detailview.c create mode 100644 detailview.h create mode 100644 epggrid.c create mode 100644 epggrid.h create mode 100644 footer.c create mode 100644 footer.h create mode 100644 imageloader.c create mode 100644 imageloader.h create mode 100644 messagebox.c create mode 100644 messagebox.h create mode 100644 osdmanager.c create mode 100755 po/de_DE.po create mode 100644 setup.c create mode 100644 setup.h create mode 100644 styledpixmap.c create mode 100644 styledpixmap.h create mode 100644 themes/tvguide-darkblue.theme create mode 100644 themes/tvguide-default.theme create mode 100644 timeline.c create mode 100644 timeline.h create mode 100644 timer.c create mode 100644 timer.h create mode 100644 tvguide.c create mode 100644 tvguideosd.c create mode 100644 tvguideosd.h 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. + + + Copyright (C) + + 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. + + , 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='' -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='' -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 + +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: /plugins/tvguide/channellogos/). + +-i path, --epgimages=path + Path to the epgimages (Default: /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; iWidth(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 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; iDrawText(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; iSetPixel(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; iDrawText(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; iDrawText(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 + +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 +#include +#include + +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; iWidth(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: \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; iDrawRectangle(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; xSetPixel(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(¤t); + if (st->tm_hour < 21) { + current -= 24 * 60* 60; + st = localtime(¤t); + } + 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(¤t); + if (st->tm_hour > 19) { + current += 24 * 60* 60; + st = localtime(¤t); + } + 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 +#include +#include +#include +#include + +#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 , --epgimages= Set directory where epgimages are stored\n" + " -l , --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 + +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 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