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