commit 47c3fea545a1b4607deda1e7d2fa51cbcf89a656 Author: louis Date: Thu Jan 17 13:16:44 2013 +0100 Initial push tvguide 0.0.1 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