diff options
author | Andreas Mair <amair.sob@googlemail.com> | 2007-02-14 12:24:25 +0100 |
---|---|---|
committer | Andreas Mair <amair.sob@googlemail.com> | 2007-02-14 12:24:25 +0100 |
commit | 2ced4d35c533969fdb0ee6fa963a2fef26c50b79 (patch) | |
tree | b49facd42724fda2ad7c294479373d8f0f08a527 | |
download | vdr-plugin-skinenigmang-2ced4d35c533969fdb0ee6fa963a2fef26c50b79.tar.gz vdr-plugin-skinenigmang-2ced4d35c533969fdb0ee6fa963a2fef26c50b79.tar.bz2 |
2007-02-14: Version 0.0.1v0.0.1
- Initial release.
43 files changed, 5703 insertions, 0 deletions
@@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General +Public License instead of this License. @@ -0,0 +1,6 @@ +VDR Skin 'EnigmaNG' Revision History +------------------------------------------ + +2007-02-14: Version 0.0.1 + +- Initial release. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..44c0e63 --- /dev/null +++ b/Makefile @@ -0,0 +1,111 @@ +# +# Makefile for a Video Disk Recorder plugin +# +# $Id$ + +# If you are using the epgsearch plugin and want to see the number of +# timer conflicts in the main menu's info area. +#HAVE_EPGSEARCH = 1 + +# This turns usage of logos in the main menu complete. This might also +# improve the performance of the menus. EXPERIMENTAL!!! +#NO_MENULOGO = 1 + +# Debugging on/off +#SKINENIGMA_DEBUG = 1 + +# Strip debug symbols? Set eg. to /bin/true if not +#STRIP = strip +STRIP = /bin/true + +# The official name of this plugin. +# This name will be used in the '-P...' option of VDR to load the plugin. +# By default the main source file also carries this name. +# +PLUGIN = skinenigmang + +### 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 + +### 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 + +DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' + +ifdef HAVE_EPGSEARCH +DEFINES += -DHAVE_EPGSEARCH +endif + +ifdef SKINENIGMA_DEBUG +DEFINES += -DDEBUG +endif + +ifdef NO_MENULOGO +DEFINES += -DNO_MENULOGO +endif + +### The object files (add further files here): + +OBJS = $(PLUGIN).o enigma.o config.o logo.o i18n.o tools.o status.o + +### 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) + +### Targets: + +all: libvdr-$(PLUGIN).so + +libvdr-$(PLUGIN).so: $(OBJS) + $(CXX) $(CXXFLAGS) -shared $(OBJS) -o $@ +ifndef SKINENIGMA_DEBUG + @$(STRIP) $@ +endif + @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* *~ @@ -0,0 +1,173 @@ +This is the "EnigmaNG" skin for the Video Disk Recorder (VDR). + +Written by: Andreas Mair (andreas AT vdr - developer DOT org) + +Project's homepage: http://andreas.vdr-developer.org + +Latest version available at: http://andreas.vdr-developer.org + +See the file COPYING for license information. + + +Requirements +------------ + +- VDR version >= 1.4.0. + +- Logo pack from http://andreas.vdr-developer.org/enigmang + + +Description +----------- + +"EnigmaNG" is a standalone skin based on the "Enigma" text2skin addon. + + +Compile time configuration +-------------------------- + +There are some configuration items you need to set before running +"make plugins" in "Make.config": + +HAVE_EPGSEARCH = 1 +If you are using the epgsearch plugin and want to see the number of +timer conflicts in the main menu's info area. + +NO_MENULOGO = 1 +This turns usage of logos in the main menu complete. This might also +improve the performance of the menus. EXPERIMENTAL!!! + + +Patches +------- + +In the patches directory you'll find patches that might be of interest +to you before compiling. Just check it out! +Every patch (*.diff) has a corresponding description (*.txt). Read it +for more information. + + +Installation +------------ + +> cd /put/your/path/here/VDR/PLUGINS/src +> tar -xzf /put/your/path/here/vdr-skinenigmang-X.Y.Z.tgz +> ln -s skinenigmang-X.Y.Z skinenigmang +> cd skinenigmang + +> cp -df themes/*.theme /path/to/vdrconf/themes/ +> cd /put/your/path/here/VDR +> make +> make plugins +> ./vdr -P skinenigmang + + +Update +------ + +That's the same procedure as "Installation" but you have to remove +some things before: + +> cd /put/your/path/here/VDR/PLUGINS/src +> rm -f skinenigmang +> rm -rf /path/to/vdrconf/plugins/skinenigmang + + +Usage +----- + +In VDR go to the OSD settings menu and select the skin "EnigmaNG" + + +Setup Options +------------- + +- Show auxiliary information top/bottom + Here you can set where you want to have the auxiliary information + when you open an event's or recording's info screen. This information + includes for a recording its channel, size, priority, lifetime, audio + languages and some values that selected plugins (epgsearch, VDRAdmin-AM, + pin) have set. For an event the audio languages are shown. + +- Show remaining/elapsed time + Here you can set if you want to read the number of minutes an event is + already running (preceded by "+") or the number of minutes an event will + run (preceded by "-") in the channel info. + +- Show symbols in lists + This replaces "T", "t", "V"... in the EPG views symbols. + +- Show progressbar + This replaces progressbars shown with pipes (e.g. [|| ]) by a graphical + progressbar. + +- Show symbols + Here you can enable or disable the large symbols shown in top right corner + of the main menu, the recordings info and the event info and the "background" + symbol of the mode symbol when replaying recordings. + +- Show info area in main menu + This info area is shown in the main menu and it will show the current/next + timers and the number of timer conflicts (if HAVE_EPGSEARCH has been defined). + +- Show marker in lists + If enabled a small circle is drawn in front of the selected item in lists. + +- Show VPS + Hide or show VPS information in channel info and event info. + + +- Show channel logos + Hide or show channel logos in channel info. + +- Identify channel by name/data + Here you can choose if the channel logo should be searched by the channel's + name (e.g. "YLETV2" -> "YLETV2.xpm") or by the channel's unique id (e.g. + "T-8438-4097-33.xpm"). + +- Channel logo cache size + The number of logos/symbols to keep in the cache. + + +Notes +----- + +- Only XPM format is supported for channel logos: 80x80 pixels and + 13 colors (6 colors on DXR3) or less recommended. + +- If you'll add new channels logos on the fly, remember to flush the + channel logo cache. + + +Credits +------- + +- Rolf Ahrenberg for the Soppalusikka skin where I've lent some code. + http://www.saunalahti.fi/~rahrenbe/vdr/soppalusikka/ + +- Andreas Burger for the original "Enigma" for Text2Skin and his Text2Skin patches. + http://brougs78.vdr-developer.org/ + http://www.vdr-portal.de/board/thread.php?threadid=28730 + +- Sascha Volkenandt for Text2Skin where I've lent some code. + http://linux.kompiliert.net/ + +- Speedbomb for the hqlogos. + http://www.speedbomb.de/hobby/myhtpc/index.php?view=logos + +- zulu @vdr-portal.de for some symbols. + http://www.vdr-portal.de/board/thread.php?postid=514251#post514251 + +- Thomas @vdr-portal.de for some logos. + http://senderlogos.vdr-developer.org/ + +- FrankJepsen @vdr-portal.de for some logos. + http://www.vdr-portal.de/board/thread.php?postid=531412#post531412 + +- chrisz @vdr-portal.de for some radio logos and original Enigma themes. + WomensLike: http://www.vdr-portal.de/board/thread.php?threadid=59651 + WineRed: http://www.vdr-portal.de/board/thread.php?threadid=59416 + AppleGreen: http://www.vdr-portal.de/board/thread.php?threadid=59427 + +- skiller2k @vdr-portal.de for Comedy Central logo. + http://www.vdr-portal.de/board/thread.php?postid=567887#post567887 diff --git a/common.h b/common.h new file mode 100644 index 0000000..0d23e78 --- /dev/null +++ b/common.h @@ -0,0 +1,22 @@ +/* + * common.h: The 'EnigmaNG' VDR skin + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef __SKINENIGMA_COMMON_H +#define __SKINENIGMA_COMMON_H + +#include <string> +#include <vdr/tools.h> + +#ifdef DEBUG +#define debug(x...) printf("EnigmaNG: " x); +#define error(x...) printf("EnigmaNG: " x); +#else +#define debug(x...) ; +#define error(x...) esyslog("EnigmaNG: " x); +#endif + +#endif // __SKINENIGMA_COMMON_H diff --git a/config.c b/config.c new file mode 100644 index 0000000..98d5f1e --- /dev/null +++ b/config.c @@ -0,0 +1,32 @@ +/* + * config.c: 'EnigmaNG' skin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include <stdlib.h> +#include "common.h" +#include "config.h" + +cEnigmaConfig EnigmaConfig; + +cEnigmaConfig::cEnigmaConfig() : showAuxInfo(1), showLogo(1), showVps(1), showListSymbols(1), + showSymbols(1), showProgressbar(1), cacheSize(50), + useChannelId(0), showInfo(1), showRemaining(0), showMarker(1) +{ + memset(logoDir, 0, sizeof(logoDir)); +} + +cEnigmaConfig::~cEnigmaConfig() +{ +} + +void cEnigmaConfig::SetLogoDir(const char *logodirP) +{ + if (logodirP) + { + debug("cEnigmaConfig::SetLogoDir(%s)\n", logodirP); + strncpy(logoDir, logodirP, sizeof(logoDir)); + } +} diff --git a/config.h b/config.h new file mode 100644 index 0000000..b1ec6b0 --- /dev/null +++ b/config.h @@ -0,0 +1,35 @@ +/* + * config.h: 'EnigmaNG' skin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef __SKINENIGMA_CONFIG_H +#define __SKINENIGMA_CONFIG_H + +struct cEnigmaConfig +{ +private: + char logoDir[255]; +public: + cEnigmaConfig(); + ~cEnigmaConfig(); + void SetLogoDir(const char *logodirP); + char *GetLogoDir(void) { return logoDir; } + int showAuxInfo; + int showLogo; + int showVps; + int showSymbols; + int showListSymbols; + int showProgressbar; + int cacheSize; + int useChannelId; + int showInfo; + int showRemaining; + int showMarker; +}; + +extern cEnigmaConfig EnigmaConfig; + +#endif // __SKINENIGMA_CONFIG_H diff --git a/enigma.c b/enigma.c new file mode 100644 index 0000000..ee977f7 --- /dev/null +++ b/enigma.c @@ -0,0 +1,2573 @@ +/* + * enigma.c: The 'EnigmaNG' VDR skin + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include <math.h> +#include <ctype.h> +#include <sstream> +#include <iomanip> + +#ifndef __STL_CONFIG_H +#define __STL_CONFIG_H +#endif + +#include "common.h" +#include "tools.h" +#include "config.h" +#include "logo.h" +#include "i18n.h" +#include "enigma.h" +#include "status.h" +#include <vdr/device.h> +#include <vdr/timers.h> +#include <vdr/menu.h> +#include <vdr/font.h> +#include <vdr/osd.h> +#include <vdr/themes.h> +#include <vdr/plugin.h> + +#ifdef HAVE_EPGSEARCH +#include "../epgsearch/services.h" +#endif + +using namespace std; + +#define MAX_AUDIO_BITMAPS 3 +#define MAX_SPEED_BITMAPS 10 +#define MAX_TRICKSPEED_BITMAPS 4 + + +#include "symbols/small/eventparttimer.xpm" +#include "symbols/small/eventtimer.xpm" +#include "symbols/small/eventvps.xpm" +#include "symbols/small/eventrunning.xpm" +#include "symbols/small/eventrecording.xpm" +#include "symbols/small/recordingnew.xpm" + +// small symbols (e.g. for channelinfo) +#include "symbols/small/audio.xpm" +#include "symbols/small/audioleft.xpm" +#include "symbols/small/audioright.xpm" +#include "symbols/small/dolbydigital.xpm" +#include "symbols/small/encrypted.xpm" +#include "symbols/small/teletext.xpm" +#include "symbols/small/vps.xpm" +#include "symbols/small/radio.xpm" +#include "symbols/small/recording.xpm" +#include "symbols/small/timer.xpm" +#include "symbols/small/run.xpm" + +static cBitmap bmEventPartTimer(eventparttimer_xpm); +static cBitmap bmEventTimer(eventtimer_xpm); +static cBitmap bmEventVPS(eventvps_xpm); +static cBitmap bmEventRunning(eventrunning_xpm); +static cBitmap bmEventRecording(eventrecording_xpm); +static cBitmap bmRecordingNew(recordingnew_xpm); + +// channel info +static cBitmap bmAudio[MAX_AUDIO_BITMAPS] = { + cBitmap(audio_xpm), + cBitmap(audioleft_xpm), + cBitmap(audioright_xpm) +}; +static cBitmap bmDolbyDigital(dolbydigital_xpm); +static cBitmap bmEncrypted(encrypted_xpm); +static cBitmap bmRadio(radio_xpm); +static cBitmap bmRecording(recording_xpm); +static cBitmap bmTeletext(teletext_xpm); +static cBitmap bmVPS(vps_xpm); +static cBitmap bmRun(run_xpm); +static cBitmap bmTimer(timer_xpm); + +// audio menu +static const char *strAudio_large[MAX_AUDIO_BITMAPS] = { + "icons/audio/audioStereo", + "icons/audio/audioLeft", + "icons/audio/audioRight" +}; + +// replay +static const char *strSlowForward_large[MAX_TRICKSPEED_BITMAPS] = { + "icons/replay/slowForward", + "icons/replay/slowForward1", + "icons/replay/slowForward2", + "icons/replay/slowForward3" +}; +static const char *strFastForward_large[MAX_SPEED_BITMAPS] = { + "icons/replay/fastForward", + "icons/replay/fastForward1", + "icons/replay/fastForward2", + "icons/replay/fastForward3", + "icons/replay/fastForward4", + "icons/replay/fastForward5", + "icons/replay/fastForward6", + "icons/replay/fastForward7", + "icons/replay/fastForward8", + "icons/replay/fastForward9" +}; +static const char *strSlowRewind_large[MAX_TRICKSPEED_BITMAPS] = { + "icons/replay/slowRewind", + "icons/replay/slowRewind1", + "icons/replay/slowRewind2", + "icons/replay/slowRewind3" +}; +static const char *strFastRewind_large[MAX_SPEED_BITMAPS] = { + "icons/replay/fastRewind", + "icons/replay/fastRewind1", + "icons/replay/fastRewind2", + "icons/replay/fastRewind3", + "icons/replay/fastRewind4", + "icons/replay/fastRewind5", + "icons/replay/fastRewind6", + "icons/replay/fastRewind7", + "icons/replay/fastRewind8", + "icons/replay/fastRewind9" +}; + +static cTheme Theme; + +// Background colors +THEME_CLR(Theme, clrBackground, 0xE5DEE5FA); +THEME_CLR(Theme, clrAltBackground, 0xE5B2BBD6); +THEME_CLR(Theme, clrTitleBg, 0xE54158BC); +THEME_CLR(Theme, clrLogoBg, 0xE58EA4E3); +THEME_CLR(Theme, clrBottomBg, 0xE51D2F7D); +THEME_CLR(Theme, clrBotProgBarBg, 0xFF808080); +THEME_CLR(Theme, clrBotProgBarFg, 0xFFFFFFFF); +// Text colors +THEME_CLR(Theme, clrMenuTxtFg, 0xFF000000); +THEME_CLR(Theme, clrTitleFg, 0xFFFFFFFF); +THEME_CLR(Theme, clrTitleShadow, 0xFF000000); +// Symbols +THEME_CLR(Theme, clrSymbolActive, 0xFFC4C400); +THEME_CLR(Theme, clrSymbolInactive, 0xFF808080); +THEME_CLR(Theme, clrSymbolRecord, 0xFFC40000); +THEME_CLR(Theme, clrSymbolTimerActive, 0xFF0000C4); +THEME_CLR(Theme, clrSymbolVpsActive, 0xFFC4C400); +THEME_CLR(Theme, clrSymbolRecActive, 0xFFC40000); +THEME_CLR(Theme, clrSymbolRunActive, 0xFF00C400); +// Help buttons +THEME_CLR(Theme, clrButtonRedFg, 0xFFFFFFFF); +THEME_CLR(Theme, clrButtonRedBg, 0xE5C40000); +THEME_CLR(Theme, clrButtonGreenFg, 0xFFFFFFFF); +THEME_CLR(Theme, clrButtonGreenBg, 0xE500C400); +THEME_CLR(Theme, clrButtonYellowFg, 0xFFFFFFFF); +THEME_CLR(Theme, clrButtonYellowBg, 0xE5C4C400); +THEME_CLR(Theme, clrButtonBlueFg, 0xFFFFFFFF); +THEME_CLR(Theme, clrButtonBlueBg, 0xE50000C4); +// Messages +THEME_CLR(Theme, clrMessageBorder, 0xE5FF0000); +THEME_CLR(Theme, clrMessageStatusFg, 0xFF000000); +THEME_CLR(Theme, clrMessageStatusBg, 0xE5D7FFD1); +THEME_CLR(Theme, clrMessageInfoFg, 0xFF000000); +THEME_CLR(Theme, clrMessageInfoBg, 0xE5D1DDFF); +THEME_CLR(Theme, clrMessageWarningFg, 0xFF000000); +THEME_CLR(Theme, clrMessageWarningBg, 0xE5F9FFD1); +THEME_CLR(Theme, clrMessageErrorFg, 0xFF000000); +THEME_CLR(Theme, clrMessageErrorBg, 0xE5FFD1D1); +// Volume bar +THEME_CLR(Theme, clrVolumeBar, 0xFF33CC33); +THEME_CLR(Theme, clrVolumeBarMute, 0xFFFF0000); +// Menu list items +THEME_CLR(Theme, clrMenuItemCurrentFg, 0xFF000000); +THEME_CLR(Theme, clrMenuHighlight, 0xE5B2BBD6); +THEME_CLR(Theme, clrMenuItemSelectableFg, 0xFF000000); +THEME_CLR(Theme, clrMenuItemNotSelectableFg, 0xE54158BC); +// title/subtitle +THEME_CLR(Theme, clrMenuEventTitle, 0xFF000000); +THEME_CLR(Theme, clrMenuEventShortText, 0xFF1D2F7D); +// Scrollbar +THEME_CLR(Theme, clrMenuScrollbarTotal, 0xE5B2BBD6); +THEME_CLR(Theme, clrMenuScrollbarShown, 0xFF000000); +// Replay OSD +THEME_CLR(Theme, clrReplayCurrent, 0xFF1D2F7D); +THEME_CLR(Theme, clrReplayTotal, 0xFF1D2F7D); +THEME_CLR(Theme, clrReplayModeJump, 0xFF1D2F7D); +THEME_CLR(Theme, clrReplayBarAreaBg, 0xE5DEE5FA); +THEME_CLR(Theme, clrReplayProgressSeen, 0xFF8EA4E3); +THEME_CLR(Theme, clrReplayProgressRest, 0xE5DEE5FA); +THEME_CLR(Theme, clrReplayProgressSelected, 0xFF4158BC); +THEME_CLR(Theme, clrReplayProgressMark, 0xFF4158BC); +THEME_CLR(Theme, clrReplayProgressCurrent, 0xFFFF0000); + +#define TinyGap 1 +#define SmallGap 2 +#define Gap 4 +#define BigGap 8 +#define Roundness 10 +#define MessageHeight (2 * SmallGap + cFont::GetFont(fontOsd)->Height() + 2 * SmallGap) + +#define TitleDecoGap SmallGap +#define TitleDecoGap2 SmallGap +#define TitleDecoHeight SmallGap +#define TitleDeco (TitleDecoGap + TitleDecoHeight + TitleDecoGap2) +#define LogoDecoGap SmallGap +#define LogoDecoGap2 Gap +#define LogoDecoWidth SmallGap +#define MarkerGap 6 + +/* LogoHeight must be maximum of + * - the height needed for titlebar, event title & subtitle (see cSkinEnigmaDisplayMenu::SetEvent() & cSkinEnigmaDisplayMenu::SetRecording()) + * - the height of the message OSD + */ +#define LogoHeight max(cFont::GetFont(fontOsd)->Height() * 3 + TitleDeco + SmallGap, cFont::GetFont(fontOsd)->Height() * 2 + TitleDeco + SmallGap * 2 + MessageHeight) +#define LogoWidth LogoHeight + +// --- cSkinEnigmaDisplayChannel -------------------------------------------- + +class cSkinEnigmaDisplayChannel:public cSkinDisplayChannel { +private: + cOsd *osd; + bool fShowLogo; + + int xLogoLeft, xLogoRight, yLogoTop, yLogoBottom, xLogoDecoLeft, xLogoDecoRight, xLogoPos, yLogoPos; + int xTitleLeft, xTitleRight, yTitleTop, yTitleBottom, yTitleDecoTop, yTitleDecoBottom; + int xEventNowLeft, xEventNowRight, yEventNowTop, yEventNowBottom; + int xEventNextLeft, xEventNextRight, yEventNextTop, yEventNextBottom; + int xBottomLeft, xBottomRight, yBottomTop, yBottomBottom; + int xMessageLeft, xMessageRight, yMessageTop, yMessageBottom; + + int lineHeightSml; + int lineHeightOsd; + + bool HasChannelTimerRecording(const cChannel *Channel); + void DrawAreas(void); + void DrawGroupInfo(const cChannel *Channel, int Number); + void DrawChannelInfo(const cChannel *Channel, int Number); + cString GetChannelName(const cChannel *Channel); + cString GetChannelNumber(const cChannel *Channel, int Number); +public: + cSkinEnigmaDisplayChannel(bool WithInfo); + virtual ~ cSkinEnigmaDisplayChannel(); + virtual void SetChannel(const cChannel *Channel, int Number); + virtual void SetEvents(const cEvent *Present, const cEvent *Following); + virtual void SetMessage(eMessageType Type, const char *Text); + virtual void Flush(void); +}; + +cSkinEnigmaDisplayChannel::cSkinEnigmaDisplayChannel(bool WithInfo) +{ + fShowLogo = EnigmaConfig.showLogo; + + lineHeightSml = cFont::GetFont(fontSml)->Height(); + lineHeightOsd = cFont::GetFont(fontOsd)->Height(); + + int LogoSize = max(lineHeightSml * 2 + lineHeightOsd * 2 + SmallGap, LogoHeight); + // title bar & logo area + xLogoLeft = 0; + xLogoRight = xLogoLeft + LogoSize; + xLogoDecoLeft = xLogoRight + LogoDecoGap; + xLogoDecoRight = xLogoDecoLeft + LogoDecoWidth; + xTitleLeft = (fShowLogo ? xLogoDecoRight + LogoDecoGap2 : 0); + xTitleRight = xTitleLeft + ((Setup.OSDWidth - xTitleLeft) & ~0x07); // width must be multiple of 8 + yTitleTop = 0; + yTitleBottom = yTitleTop + lineHeightOsd; + yTitleDecoTop = yTitleBottom + TitleDecoGap; + yTitleDecoBottom = yTitleDecoTop + TitleDecoHeight; + yLogoTop = yTitleDecoBottom + TitleDecoGap2; + yLogoBottom = yLogoTop + LogoSize; + xLogoPos = xLogoLeft + (LogoSize - LogoHeight) / 2; + yLogoPos = yLogoTop + (LogoSize - LogoHeight) / 2; + // current event area + xEventNowLeft = xTitleLeft; + xEventNowRight = xTitleRight; + yEventNowTop = yLogoTop; + yEventNowBottom = yEventNowTop + lineHeightOsd + lineHeightSml; + // next event area + xEventNextLeft = xEventNowLeft; + xEventNextRight = xEventNowRight; + yEventNextTop = yEventNowBottom + SmallGap; + yEventNextBottom = yEventNextTop + lineHeightOsd + lineHeightSml; + // progress bar area + xBottomLeft = xTitleLeft; + xBottomRight = xTitleRight; + yBottomTop = yEventNextBottom + SmallGap; + yBottomBottom = yBottomTop + lineHeightOsd; + // message area + xMessageLeft = xTitleLeft; + xMessageRight = xTitleRight; + yMessageTop = yLogoTop + (LogoSize - 2 * SmallGap - lineHeightSml - 2 * SmallGap) / 2; + yMessageBottom = yMessageTop + MessageHeight; + + // create osd + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - yBottomBottom); + tArea Areas[] = { {0, 0, xBottomRight - 1, yBottomBottom - 1, fShowLogo ? 8 : 4} }; + if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea) == oeOk)) { + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + // clear all + osd->DrawRectangle(0, 0, osd->Width(), osd->Height(), clrTransparent); + } else { + if (fShowLogo) { + tArea Areas[] = { {xLogoLeft, yLogoTop, xLogoDecoRight - 1, yLogoBottom - 1, 4}, + {xTitleLeft, yTitleTop, xTitleRight - 1, yTitleDecoBottom - 1, 2}, + {xEventNowLeft, yEventNowTop, xEventNowRight - 1, yEventNextBottom - 1, 4}, + {xBottomLeft, yBottomTop, xBottomRight - 1, yBottomBottom - 1, 4} + }; + eOsdError rc = osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)); + if (rc == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else + error("cSkinEnigmaDisplayChannel: CanHandleAreas() returned %d\n", rc); + } else { + tArea Areas[] = { {xTitleLeft, yTitleTop, xTitleRight - 1, yTitleDecoBottom - 1, 2}, + {xEventNowLeft, yEventNowTop, xEventNowRight - 1, yEventNextBottom - 1, 4}, + {xBottomLeft, yBottomTop, xBottomRight - 1, yBottomBottom - 1, 4} + }; + eOsdError rc = osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)); + if (rc == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else + error("cSkinEnigmaDisplayChannel: CanHandleAreas() returned %d\n", rc); + } + } +} + +cSkinEnigmaDisplayChannel::~cSkinEnigmaDisplayChannel() +{ + delete osd; +} + +bool cSkinEnigmaDisplayChannel::HasChannelTimerRecording(const cChannel *Channel) +{ + // try to find current channel from timers + for (cTimer * t = Timers.First(); t; t = Timers.Next(t)) { + if ((t->Channel() == Channel) && t->Recording()) + return true; + } + return false; +} + +void cSkinEnigmaDisplayChannel::DrawAreas(void) +{ + // draw titlebar + osd->DrawRectangle(xTitleLeft, yTitleTop, xTitleRight - 1, yTitleBottom - 1, + Theme.Color(clrTitleBg)); + osd->DrawRectangle(xTitleLeft, yTitleDecoTop, xTitleRight - 1, + yTitleDecoBottom - 1, Theme.Color(clrTitleBg)); + // draw rounded left corner of title bar + osd->DrawEllipse(xTitleLeft, yTitleTop, xTitleLeft + Roundness - 1, + yTitleTop + Roundness - 1, clrTransparent, -2); + // draw rounded right corner of title bar + osd->DrawEllipse(xTitleRight - Roundness, yTitleTop, xTitleRight - 1, + yTitleTop + Roundness - 1, clrTransparent, -1); + // draw current event area + osd->DrawRectangle(xEventNowLeft, yEventNowTop, xEventNowRight - 1, + yEventNowBottom - 1, Theme.Color(clrBackground)); + // draw next event area + osd->DrawRectangle(xEventNextLeft, yEventNextTop, xEventNextRight - 1, + yEventNextBottom - 1, Theme.Color(clrAltBackground)); + // draw progress bar area + osd->DrawRectangle(xBottomLeft, yBottomTop, xBottomRight - 1, + yBottomBottom - 1, Theme.Color(clrBottomBg)); + osd->DrawEllipse(xBottomLeft, yBottomBottom - Roundness, + xBottomLeft + Roundness, yBottomBottom - 1, clrTransparent, + -3); + osd->DrawEllipse(xBottomRight - Roundness, yBottomBottom - Roundness, + xBottomRight - 1, yBottomBottom - 1, clrTransparent, -4); +} + +void cSkinEnigmaDisplayChannel::DrawGroupInfo(const cChannel *Channel, int Number) +{ + DrawAreas(); + + const cFont *font = cFont::GetFont(fontOsd); + int xName = xTitleLeft + Roundness + font->Width("0000-") + Gap; + // draw channel group name + osd->DrawText(xName + 3, yTitleTop + 3, GetChannelName(Channel), + Theme.Color(clrTitleShadow), clrTransparent, font, + xTitleRight - xTitleLeft - 3, lineHeightOsd - 3); + osd->DrawText(xName, yTitleTop, GetChannelName(Channel), + Theme.Color(clrTitleFg), clrTransparent, font, + xTitleRight - xTitleLeft, lineHeightOsd); +} + +void cSkinEnigmaDisplayChannel::DrawChannelInfo(const cChannel *Channel, int Number) +{ + DrawAreas(); + + const cFont *font = cFont::GetFont(fontOsd); + int xNumber = xTitleLeft + Roundness; + int xName = xNumber + font->Width("0000-") + Gap; + + // draw channel number + osd->DrawText(xNumber + 3, yTitleTop + 3, GetChannelNumber(Channel, Number), + Theme.Color(clrTitleShadow), clrTransparent, font, + xName - xNumber - Gap - 3, yTitleBottom - yTitleTop - 3); + osd->DrawText(xNumber, yTitleTop, GetChannelNumber(Channel, Number), + Theme.Color(clrTitleFg), clrTransparent, font, + xName - xNumber - Gap, yTitleBottom - yTitleTop); + // draw channel name + osd->DrawText(xName + 3, yTitleTop + 3, GetChannelName(Channel), + Theme.Color(clrTitleShadow), clrTransparent, font, + xTitleRight - Roundness - xName - 3, + yTitleBottom - yTitleTop - 3); + osd->DrawText(xName, yTitleTop, GetChannelName(Channel), + Theme.Color(clrTitleFg), clrTransparent, font, + xTitleRight - Roundness - xName, yTitleBottom - yTitleTop); + // draw symbols + // right edge of logo + int xs = xBottomRight - Roundness; + // bottom edge of logo + int ys = yBottomBottom - SmallGap; + bool isvps = false; + if (EnigmaConfig.showVps) { + // check if vps + // get schedule + cSchedulesLock SchedulesLock; + const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock); + if (Schedules) { + const cSchedule *Schedule = Schedules->GetSchedule(Channel); + if (Schedule) { + // get present event + const cEvent *Event = Schedule->GetPresentEvent(); + // check if present event has vps + if (Event && Event->Vps()) { + isvps = true; + } + } + } + } + char *strFile = NULL; + asprintf(&strFile, "flags/%s", Channel->Alang(0)); + if (EnigmaLogoCache.Load(strFile)) { + xs -= (EnigmaLogoCache.Get().Width() + SmallGap); + osd->DrawBitmap(xs, ys - EnigmaLogoCache.Get().Height(), EnigmaLogoCache.Get()); + } + free(strFile); + // draw audio symbol according to current audio channel + int AudioMode = cDevice::PrimaryDevice()->GetAudioChannel(); + if (!(AudioMode >= 0 && AudioMode < MAX_AUDIO_BITMAPS)) + AudioMode = 0; + xs -= (bmAudio[AudioMode].Width() + SmallGap); + osd->DrawBitmap(xs, ys - bmAudio[AudioMode].Height(), bmAudio[AudioMode], + Theme.Color(clrBottomBg), Theme.Color(Channel->Apid(1) ? clrSymbolActive : clrSymbolInactive)); + // draw dolby digital symbol + xs -= (bmDolbyDigital.Width() + SmallGap); + osd->DrawBitmap(xs, ys - bmDolbyDigital.Height(), bmDolbyDigital, + Theme.Color(clrBottomBg), Theme.Color(Channel->Dpid(0) ? clrSymbolActive : clrSymbolInactive)); + if (EnigmaConfig.showVps) { + // draw vps symbol + xs -= (bmVPS.Width() + SmallGap); + osd->DrawBitmap(xs, ys - bmVPS.Height(), bmVPS, + Theme.Color(clrBottomBg), Theme.Color(isvps ? clrSymbolActive : clrSymbolInactive)); + } + // draw radio symbol if no video PID; otherwise draw teletext symbol + if (Channel->Vpid()) { + xs -= (bmTeletext.Width() + SmallGap); + osd->DrawBitmap(xs, ys - bmTeletext.Height(), bmTeletext, + Theme.Color(clrBottomBg), Theme.Color(Channel->Tpid() ? clrSymbolActive : clrSymbolInactive)); + } else { + xs -= (bmRadio.Width() + SmallGap); + osd->DrawBitmap(xs, ys - bmRadio.Height(), bmRadio, + Theme.Color(clrBottomBg), Theme.Color(Channel->Apid(0) ? clrSymbolActive : clrSymbolInactive)); + } + // draw encryption symbol + xs -= (bmEncrypted.Width() + SmallGap); + osd->DrawBitmap(xs, ys - bmEncrypted.Height(), bmEncrypted, + Theme.Color(clrBottomBg), Theme.Color(Channel->Ca() ? clrSymbolActive : clrSymbolInactive)); + // draw recording symbol + xs -= (bmRecording.Width() + SmallGap); + if (cRecordControls::Active()) + osd->DrawBitmap(xs, ys - bmRecording.Height(), bmRecording, + Theme.Color(clrBottomBg), + Theme.Color(HasChannelTimerRecording(Channel) ? clrSymbolRecord : clrSymbolActive)); + else + osd->DrawBitmap(xs, ys - bmRecording.Height(), bmRecording, + Theme.Color(clrBottomBg), Theme.Color(clrSymbolInactive)); +} + +cString cSkinEnigmaDisplayChannel::GetChannelName(const cChannel *Channel) +{ + char buffer[256]; + // check if channel exists + if (Channel) { + snprintf(buffer, sizeof(buffer), "%s", Channel->Name()); + } else { + snprintf(buffer, sizeof(buffer), "%s", tr("*** Invalid Channel ***")); + } + return buffer; +} + +cString cSkinEnigmaDisplayChannel::GetChannelNumber(const cChannel *Channel, int Number) +{ + char buffer[256]; + // check if real channel exists + if (Channel && !Channel->GroupSep()) { + snprintf(buffer, sizeof(buffer), "%d%s", Channel->Number(), Number ? "-" : ""); + } else if (Number) { + // no channel but number + snprintf(buffer, sizeof(buffer), "%d-", Number); + } else { + // no channel and no number + snprintf(buffer, sizeof(buffer), " "); + } + return buffer; +} + +void cSkinEnigmaDisplayChannel::SetChannel(const cChannel *Channel, int Number) +{ + if (Channel) { + // clear all + osd->DrawRectangle(0, 0, osd->Width(), osd->Height(), clrTransparent); + + if (fShowLogo) { + // draw logo area + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + osd->DrawRectangle(xLogoDecoLeft, yLogoTop, xLogoDecoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + const char *logoname = EnigmaConfig.useChannelId ? *Channel->GetChannelID().ToString() : Channel->Name(); + char *filename = (char *)malloc(strlen(logoname) + 20 /* should be enough for folder */); + if (filename == NULL) return; + strcpy(filename, "hqlogos/"); + strcat(filename, logoname); + bool fFoundLogo = false; + if (!(fFoundLogo = EnigmaLogoCache.Load(filename))) { + strcpy(filename, "logos/"); + strcat(filename, logoname); + if (!(fFoundLogo = EnigmaLogoCache.Load(filename))) { + fFoundLogo = EnigmaLogoCache.Load("hqlogos/no_logo"); //TODO? different default logo for channel/group? + } + } + free(filename); + + if (fFoundLogo) { + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - ChannelLogoWidth) / 2, + yLogoTop + (yLogoBottom - yLogoTop - ChannelLogoHeight) / 2, + EnigmaLogoCache.Get(), EnigmaLogoCache.Get().Color(1), + Theme.Color(clrLogoBg), true); + } + } + + if (Channel->GroupSep()) + DrawGroupInfo(Channel, Number); + else + DrawChannelInfo(Channel, Number); + } +} + +void cSkinEnigmaDisplayChannel::SetEvents(const cEvent *Present, + const cEvent *Following) +{ + const cFont *lFontOsd = cFont::GetFont(fontOsd); + const cFont *lFontSml = cFont::GetFont(fontSml); + + int xTimeLeft = xEventNowLeft + Gap; + int xTimeWidth = lFontOsd->Width("00:00"); + int xDurationLeft = xEventNowRight - Gap - lFontOsd->Width("000'"); + int xDurationWidth = xEventNowRight - Gap - xDurationLeft; + int xTextLeft = xTimeLeft + xTimeWidth + BigGap; + int xTextWidth = xDurationLeft - xTextLeft - BigGap; + + // check epg datas + const cEvent *e = Present; // Current event + if (e) { + char sLen[4]; + int total = e->Duration(); + snprintf(sLen, sizeof(sLen), "%d'", total / 60); + // draw start time + osd->DrawText(xTimeLeft, yEventNowTop, e->GetTimeString(), + Theme.Color(clrMenuEventTitle), Theme.Color(clrBackground), + lFontOsd, xTimeWidth, lineHeightOsd); + // draw title + osd->DrawText(xTextLeft, yEventNowTop, e->Title(), + Theme.Color(clrMenuEventTitle), Theme.Color(clrBackground), + lFontOsd, xTextWidth, lineHeightOsd); + // draw duration + osd->DrawText(xDurationLeft, yEventNowTop, sLen, + Theme.Color(clrMenuEventTitle), Theme.Color(clrBackground), + lFontOsd, xDurationWidth, lineHeightOsd, taRight); + if (e->HasTimer()) { + if (e->IsRunning()) + osd->DrawBitmap(xTimeLeft + (xTimeWidth - bmRecording.Width()) / 2, + yEventNowTop + lineHeightOsd, bmRecording, + Theme.Color(clrSymbolRecActive), + Theme.Color(clrBackground)); + else + osd->DrawBitmap(xTimeLeft + (xTimeWidth - bmTimer.Width()) / 2, + yEventNowTop + lineHeightOsd, bmTimer, + Theme.Color(clrSymbolTimerActive), + Theme.Color(clrBackground)); + } + // draw shorttext + osd->DrawText(xTextLeft, yEventNowTop + lineHeightOsd, e->ShortText(), + Theme.Color(clrMenuEventShortText), + Theme.Color(clrBackground), lFontSml, xTextWidth, lineHeightSml); + int now = (time(NULL) - e->StartTime()); + if ((now < total) && ((now / 60) > 0)) { + char sNow[5]; + if (EnigmaConfig.showRemaining) { + snprintf(sNow, sizeof(sNow), "-%d'", (total - now) / 60); + } else { + snprintf(sNow, sizeof(sNow), "+%d'", now / 60); + } + osd->DrawText(xDurationLeft, yEventNowTop + lineHeightOsd, sNow, + Theme.Color(clrMenuEventShortText), + Theme.Color(clrBackground), lFontSml, xDurationWidth, + lineHeightSml, taRight); + } + // draw timebar + int xBarWidth = 124; + int xBarLeft = xBottomLeft + Roundness; + int x = xBarLeft + SmallGap + (int)(roundf((float)(now) / (float)(total) * (float)(xBarWidth - Gap - SmallGap))); + x = min(x, xBottomLeft + Gap + xBarWidth - SmallGap - 1); + osd->DrawRectangle(xBarLeft, yBottomTop + SmallGap + SmallGap, + xBottomLeft + Gap + xBarWidth - 1, + yBottomBottom - SmallGap - 1, + Theme.Color(clrBotProgBarBg)); + osd->DrawRectangle(xBarLeft + SmallGap, + yBottomTop + SmallGap + SmallGap + SmallGap, x, + yBottomBottom - SmallGap - SmallGap - 1, + Theme.Color(clrBotProgBarFg)); + } + + e = Following; // Next event + if (e) { + char sLen[5]; + snprintf(sLen, 5, "%d'", e->Duration() / 60); + // draw start time + osd->DrawText(xTimeLeft, yEventNextTop, e->GetTimeString(), + Theme.Color(clrMenuEventTitle), Theme.Color(clrAltBackground), + lFontOsd, xTimeWidth, lineHeightOsd); + // draw title + osd->DrawText(xTextLeft, yEventNextTop, e->Title(), + Theme.Color(clrMenuEventTitle), Theme.Color(clrAltBackground), + lFontOsd, xTextWidth, lineHeightOsd); + // draw duration + osd->DrawText(xDurationLeft, yEventNextTop, sLen, + Theme.Color(clrMenuEventTitle), Theme.Color(clrAltBackground), + lFontOsd, xDurationWidth, lineHeightOsd, taRight); + if (e->HasTimer()) + osd->DrawBitmap(xTimeLeft + (xTimeWidth - bmTimer.Width()) / 2, + yEventNextTop + lineHeightOsd, bmTimer, + Theme.Color(clrSymbolTimerActive), Theme.Color(clrAltBackground)); + // draw shorttext + osd->DrawText(xTextLeft, yEventNextTop + lineHeightOsd, e->ShortText(), + Theme.Color(clrMenuEventShortText), + Theme.Color(clrAltBackground), lFontSml, xTextWidth, lineHeightSml); + } +} + +void cSkinEnigmaDisplayChannel::SetMessage(eMessageType Type, const char *Text) +{ + // check if message + if (Text) { + // save osd region + osd->SaveRegion(xMessageLeft, yMessageTop, xMessageRight - 1, yMessageBottom - 1); + // draw message + osd->DrawRectangle(xMessageLeft, yMessageTop, xMessageRight - 1, + yMessageBottom - 1, clrTransparent); + osd->DrawRectangle(xMessageLeft, yMessageTop + SmallGap, xMessageRight - 1, + yMessageBottom - SmallGap - 1, + Theme.Color(clrMessageBorder)); + osd->DrawText(xMessageLeft + Gap, yMessageTop + 2 * SmallGap, Text, + Theme.Color(clrMessageStatusFg + 2 * Type), + Theme.Color(clrMessageStatusBg + 2 * Type), + cFont::GetFont(fontOsd), + xMessageRight - Gap - xMessageLeft - Gap - 1, + yMessageBottom - 2 * SmallGap - yMessageTop - 2 * SmallGap, + taCenter); + } else { + // restore saved osd region + osd->RestoreRegion(); + } +} + +void cSkinEnigmaDisplayChannel::Flush(void) +{ + // update date string + cString date = DayDateTime(); + const cFont *font = cFont::GetFont(fontOsd); + int w = font->Width(date); + osd->DrawText(xTitleRight - Roundness - w, yTitleTop, date, + Theme.Color(clrTitleFg), Theme.Color(clrTitleBg), + font, w, yTitleBottom - yTitleTop, taCenter); + osd->Flush(); +} + +// --- cSkinEnigmaDisplayMenu ----------------------------------------------- + +class cSkinEnigmaDisplayMenu:public cSkinDisplayMenu { +private: + cOsd *osd; + + char *strTitle; + bool isMainMenu; + bool fShowLogo; + bool fShowInfo; + + int xBodyLeft, xBodyRight, yBodyTop, yBodyBottom; + int xTitleLeft, xTitleRight, yTitleTop, yTitleBottom, yTitleDecoTop, yTitleDecoBottom; + int xButtonsLeft, xButtonsRight, yButtonsTop, yButtonsBottom; + int xMessageLeft, xMessageRight, yMessageTop, yMessageBottom; + int xDateLeft, xDateRight, yDateTop, yDateBottom; + int xLogoLeft, xLogoRight, yLogoTop, yLogoBottom; + int xInfoLeft, xInfoRight, yInfoTop, yInfoBottom; + + int lineHeightOsd; + int lineHeightSml; + + void SetScrollbar(void); + void SetupAreas(void); + void DrawTitle(const char *Title); + int DrawFlag(int x, int y, const tComponent *p); + const char *GetPluginMainMenuName(const char *plugin); +public: + cSkinEnigmaDisplayMenu(); + virtual ~cSkinEnigmaDisplayMenu(); + virtual void Scroll(bool Up, bool Page); + virtual int MaxItems(void); + virtual void Clear(void); + virtual void SetTitle(const char *Title); + virtual void SetButtons(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL); + virtual void SetMessage(eMessageType Type, const char *Text); + virtual void SetItem(const char *Text, int Index, bool Current, bool Selectable); + virtual void SetEvent(const cEvent *Event); + virtual void SetRecording(const cRecording *Recording); + virtual void SetText(const char *Text, bool FixedFont); + virtual int GetTextAreaWidth(void) const; + virtual const cFont *GetTextAreaFont(bool FixedFont) const; + virtual void Flush(void); +}; + +cSkinEnigmaDisplayMenu::cSkinEnigmaDisplayMenu(void) +{ + osd = NULL; + strTitle = NULL; + isMainMenu = true; +#ifdef NO_MENULOGO + fShowLogo = false; +#else + fShowLogo = EnigmaConfig.showSymbols; +#endif + fShowInfo = EnigmaConfig.showInfo; + + lineHeightSml = cFont::GetFont(fontSml)->Height(); + lineHeightOsd = cFont::GetFont(fontOsd)->Height(); + + int RightColWidth = (144 + LogoWidth) & ~0x07; // must be multiple of 8 + + // title bar + xTitleLeft = 0; + xTitleRight = Setup.OSDWidth - RightColWidth; + yTitleTop = 0; + yTitleBottom = lineHeightOsd; + yTitleDecoTop = yTitleBottom + TitleDecoGap; + yTitleDecoBottom = yTitleDecoTop + TitleDecoHeight; + // help buttons + xButtonsLeft = xTitleLeft; + xButtonsRight = Setup.OSDWidth; + yButtonsTop = Setup.OSDHeight - lineHeightOsd; + yButtonsBottom = Setup.OSDHeight; + // content area with items + xBodyLeft = xTitleLeft; + xBodyRight = xTitleRight; + yBodyTop = yTitleDecoBottom + TitleDecoGap2; + yBodyBottom = yButtonsTop - SmallGap; + // message area + xMessageLeft = xBodyLeft; + xMessageRight = Setup.OSDWidth; + yMessageBottom = yButtonsTop - SmallGap; + yMessageTop = yMessageBottom - MessageHeight + SmallGap; // No extra SmallGap at bottom as there's already the Gap above the buttons + // logo box + xLogoLeft = Setup.OSDWidth - LogoWidth; + xLogoRight = Setup.OSDWidth; + yLogoTop = yTitleTop; + yLogoBottom = yLogoTop + LogoHeight + SmallGap; + // info box + xInfoLeft = Setup.OSDWidth - RightColWidth; + xInfoRight = Setup.OSDWidth; + yInfoTop = yLogoBottom; + yInfoBottom = yBodyBottom; + // date box + xDateLeft = xTitleRight; + xDateRight = Setup.OSDWidth; + yDateTop = yTitleTop; + yDateBottom = yLogoBottom; + + // create osd + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop); + + tArea Areas[] = { {xTitleLeft, yTitleTop, xMessageRight - 1, yButtonsBottom - 1, 8} }; + if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk) { + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + } else { //TODO? single body area (-> no symbols in event/recording info) + tArea Areas[] = { {xTitleLeft, yTitleTop, xTitleRight - 1, yTitleDecoBottom - 1, 4}, //title area + {xBodyLeft, yBodyTop, xBodyRight - 1, yLogoBottom - 1, 2}, //body area (beside date/logo area) +#ifdef NO_MENULOGO + {xDateLeft, yDateTop, xDateRight - 1, yDateBottom - 1, 2}, //date +#else + {xDateLeft, yDateTop, xDateRight - 1, yInfoTop - 1, 4}, //date/logo area +#endif + {xBodyLeft, yInfoTop, xInfoRight - 1, yInfoTop + lineHeightOsd - 1, 4}, //area for symbols in event/recording info + {xBodyLeft, yInfoTop + lineHeightOsd, xInfoRight - 1, yMessageTop - 1, 2}, //body area (below date/logo area) + {xMessageLeft, yMessageTop, xMessageRight - 1, yButtonsBottom - 1, 4} //message/buttons area + }; + + eOsdError rc = osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)); + if (rc == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else { + error("cSkinEnigmaDisplayMenu: CanHandleAreas() [1] returned %d\n", rc); + return; + } + +#ifndef NO_MENULOGO + // set colors for info area + osd->GetBitmap(4)->Reset(); + osd->GetBitmap(4)->SetColor(0, Theme.Color(clrTransparent)); + osd->GetBitmap(4)->SetColor(1, Theme.Color(clrBackground)); + osd->GetBitmap(4)->SetColor(2, Theme.Color(clrAltBackground)); +#endif + } + // clear all + osd->DrawRectangle(0, 0, osd->Width(), osd->Height(), clrTransparent); + + SetupAreas(); +} + +void cSkinEnigmaDisplayMenu::SetupAreas(void) +{ + debug("cSkinEnigmaDisplayMenu::SetupAreas\n"); + + DrawTitle(strTitle); + + // draw date area + osd->DrawRectangle(xDateLeft, yDateTop, xDateRight - 1, yDateBottom - 1, + fShowLogo ? Theme.Color(clrLogoBg) : Theme.Color(clrBackground)); + + // draw info area + if (fShowInfo) { + osd->DrawRectangle(xInfoLeft, fShowLogo ? yInfoTop : yBodyTop, xInfoRight - 1, yInfoBottom - 1, Theme.Color(clrAltBackground)); + + const cFont *font = cFont::GetFont(fontOsd); + int h = font->Height(); + int x = xInfoLeft + Gap; + int y = fShowLogo ? yInfoTop : yBodyTop; + int w = xInfoRight - x; + int yMaxHeight = yInfoBottom; + +#ifdef HAVE_EPGSEARCH + cPlugin *p = cPluginManager::GetPlugin("epgsearch"); + if (p) { + Epgsearch_lastconflictinfo_v1_0 *serviceData = new Epgsearch_lastconflictinfo_v1_0; + if (serviceData) { + serviceData->nextConflict = 0; + serviceData->relevantConflicts = 0; + serviceData->totalConflicts = 0; + + p->Service("Epgsearch-lastconflictinfo-v1.0", serviceData); + if (serviceData->relevantConflicts > 0) { + int yWarning = yMaxHeight - 3 * h; // Needed space for warning + yMaxHeight = yWarning; + osd->DrawRectangle(xInfoLeft, yWarning, xInfoRight - 1, yWarning + SmallGap + 1, Theme.Color(clrBackground)); + yWarning += 1 + h / 2; + osd->DrawText(xInfoLeft, yWarning, tr("WARNING"), Theme.Color(clrMenuItemSelectableFg), Theme.Color(clrAltBackground), font, w, h, taCenter); + yWarning += h + SmallGap; + + char *info; + asprintf(&info, "%d %s", serviceData->relevantConflicts, (serviceData->relevantConflicts == 1 ? tr("Timer conflict") : tr("Timer conflicts"))); + osd->DrawText(x, yWarning, info, Theme.Color(clrMenuItemSelectableFg), Theme.Color(clrAltBackground), font, w, h, taCenter); + yWarning += h; + free(info); + } + delete serviceData; + } + } +#endif //HAVE_EPGSEARCH + + if (Timers.GetNextActiveTimer()) { + // Show next active timers + y += h / 2; + osd->DrawText(x, y, tr("TIMERS"), Theme.Color(clrMenuItemSelectableFg), Theme.Color(clrAltBackground), font, w, h, taCenter); + y += h + h / 2; + EnigmaStatus.UpdateActiveTimers(); + + for (tTimer *timer = EnigmaStatus.mTimers.First(); timer; timer = EnigmaStatus.mTimers.Next(timer)) { + if (y + 2 * h + SmallGap + 1 + h / 2 > yMaxHeight) + break; // don't overwrite warning or help buttons + + if (timer->isRecording) { + osd->DrawBitmap(x, y + (lineHeightOsd - bmRecording.Height()) / 2, bmRecording, Theme.Color(clrMenuItemSelectableFg), Theme.Color(clrAltBackground)); //TODO: draw red + } + osd->DrawText(x + (timer->isRecording ? (bmRecording.Width() + Gap) : 0), y, timer->title.c_str(), Theme.Color(clrMenuItemSelectableFg), Theme.Color(clrAltBackground), font, w, h); + y += h; + char* info = NULL; + asprintf(&info, "%d. %s / %s", timer->startDay, *TimeString(timer->startTime), timer->channelName.c_str()); + osd->DrawText(x, y, info, Theme.Color(clrMenuItemSelectableFg), Theme.Color(clrAltBackground), font, w, h); + free(info); + y += h; + osd->DrawRectangle(x, y + SmallGap, xInfoRight - Gap - 1, y + SmallGap + 1, Theme.Color(clrMenuItemSelectableFg)); + y += SmallGap + 1 + h / 2; + } + } + + } else { // !fShowInfo + osd->DrawRectangle(xInfoLeft, fShowLogo ? yInfoTop : yBodyTop, xInfoRight - 1, yInfoBottom - 1, Theme.Color(clrBackground)); + } +} + +cSkinEnigmaDisplayMenu::~cSkinEnigmaDisplayMenu() +{ + free(strTitle); + delete osd; +} + +void cSkinEnigmaDisplayMenu::SetScrollbar(void) +{ + // check if scrollbar is needed + if (textScroller.CanScroll()) { + int h = lineHeightOsd; + int w = lineHeightOsd; + int yt = textScroller.Top(); + int yb = yt + textScroller.Height(); + int st = yt + h + Gap; + int sb = yb - h - Gap; + int tt = st + (sb - st) * textScroller.Offset() / textScroller.Total(); + int tb = tt + (sb - st) * textScroller.Shown() / textScroller.Total(); + int xl = textScroller.Width() + SmallGap; + // arrow up + osd->DrawRectangle(xl, yt, xl + w, yt + SmallGap, + textScroller.CanScrollUp() ? Theme.Color(clrMenuScrollbarShown) : Theme.Color(clrAltBackground)); + osd->DrawRectangle(xl + w - SmallGap, yt + SmallGap, xl + w, yt + h, + textScroller.CanScrollUp() ? Theme.Color(clrMenuScrollbarShown) : Theme.Color(clrAltBackground)); + // draw background of scrollbar + osd->DrawRectangle(xl + w - SmallGap, st, xl + w, sb, Theme.Color(clrMenuScrollbarTotal)); + // draw visible area of scrollbar + osd->DrawRectangle(xl + w - SmallGap, tt, xl + w, tb, Theme.Color(clrMenuScrollbarShown)); + // arrow down + osd->DrawRectangle(xl + w - SmallGap, yb - h, xl + w, yb - SmallGap, + textScroller.CanScrollDown() ? Theme.Color(clrMenuScrollbarShown) : Theme.Color(clrAltBackground)); + osd->DrawRectangle(xl, yb - SmallGap, xl + w, yb, + textScroller.CanScrollDown() ? Theme.Color(clrMenuScrollbarShown) : Theme.Color(clrAltBackground)); + } +} + +void cSkinEnigmaDisplayMenu::Scroll(bool Up, bool Page) +{ + cSkinDisplayMenu::Scroll(Up, Page); + SetScrollbar(); +} + +int cSkinEnigmaDisplayMenu::MaxItems(void) +{ + // max number of items + return (yBodyBottom - yBodyTop) / lineHeightOsd; +} + +void cSkinEnigmaDisplayMenu::Clear(void) +{ + debug("cSkinEnigmaDisplayMenu::Clear\n"); + textScroller.Reset(); + // clear items area + osd->DrawRectangle(xBodyLeft, yBodyTop, (fShowLogo || fShowInfo ? xBodyRight : xInfoRight) - 1, yBodyBottom - 1, Theme.Color(clrBackground)); +} + +void cSkinEnigmaDisplayMenu::SetTitle(const char *Title) +{ + debug("cSkinEnigmaDisplayMenu::SetTitle(%s)\n", Title); + + if (Title && strTitle) { + if (strcmp(Title, strTitle) != 0) { + free(strTitle); + strTitle = strdup(Title); + } + } else { + free(strTitle); + if (Title) + strTitle = strdup(Title); + else + strTitle = NULL; + } + + if (!(isMainMenu && Title && strncmp(tr("VDR"), Title, strlen(tr("VDR"))) == 0)) { + bool old_isMainMenu = isMainMenu; + bool old_fShowLogo = fShowLogo; + bool old_fShowInfo = fShowInfo; + + if (strTitle == NULL || strncmp(tr("VDR"), strTitle, strlen(tr("VDR"))) == 0) { + isMainMenu = true; +#ifdef NO_MENULOGO + fShowLogo = false; +#else + fShowLogo = EnigmaConfig.showSymbols; +#endif + fShowInfo = EnigmaConfig.showInfo; + } else { + isMainMenu = false; + fShowLogo = false; + fShowInfo = false; + } + + if (old_isMainMenu != isMainMenu + || old_fShowLogo != fShowLogo + || old_fShowInfo != fShowInfo) { + SetupAreas(); + } else { + DrawTitle(Title); + } + } else { + DrawTitle(Title); + } +} + +void cSkinEnigmaDisplayMenu::DrawTitle(const char *Title) +{ + // draw titlebar + osd->DrawRectangle(xTitleLeft, yTitleTop, xTitleRight - 1, yTitleBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawRectangle(xTitleLeft, yTitleDecoTop, xTitleRight - 1, yTitleDecoBottom - 1, Theme.Color(clrTitleBg)); + // draw rounded left corner of title bar + osd->DrawEllipse(xTitleLeft, yTitleTop, xTitleLeft + Roundness - 1, yTitleTop + Roundness - 1, clrTransparent, -2); + +// debug("DrawTitle(%s)\n", Title); + if (Title) { + // draw title with shadow + osd->DrawText(xTitleLeft + Roundness + 3, yTitleTop + 3, Title, + Theme.Color(clrTitleShadow), clrTransparent, + cFont::GetFont(fontOsd), + xTitleRight - xTitleLeft - Roundness - 3, + yTitleBottom - yTitleTop - 3); + osd->DrawText(xTitleLeft + Roundness, yTitleTop, Title, + Theme.Color(clrTitleFg), clrTransparent, + cFont::GetFont(fontOsd), + xTitleRight - xTitleLeft - Roundness, + yTitleBottom - yTitleTop); + } +} + +void cSkinEnigmaDisplayMenu::SetButtons(const char *Red, const char *Green, const char *Yellow, const char *Blue) +{ + debug("cSkinEnigmaDisplayMenu::SetButtons(%s, %s, %s, %s)\n", Red, Green, Yellow, Blue); + const cFont *font = cFont::GetFont(fontOsd); + int w = (xButtonsRight - xButtonsLeft) / 4; + int t3 = xButtonsLeft + xButtonsRight - xButtonsLeft - w; + int t2 = t3 - w; + int t1 = t2 - w; + int t0 = xButtonsLeft; + // clear background + osd->DrawRectangle(xButtonsLeft, yButtonsTop, xButtonsRight, yButtonsBottom, clrTransparent); + // draw color buttons + if (Red) + osd->DrawText(t0, yButtonsTop, Red, Theme.Color(clrButtonRedFg), + Theme.Color(clrButtonRedBg), font, t1 - t0 + 1, + yButtonsBottom - yButtonsTop, taCenter); + else { + osd->DrawRectangle(t0, yButtonsTop, Green ? t1 - 1 : t1, yButtonsBottom - 1, Theme.Color(clrBottomBg)); + osd->DrawEllipse(t0, yButtonsBottom - Roundness, t0 + Roundness - 1, yButtonsBottom - 1, clrTransparent, -3); + } + if (Green) + osd->DrawText(t1, yButtonsTop, Green, Theme.Color(clrButtonGreenFg), + Theme.Color(clrButtonGreenBg), font, w, + yButtonsBottom - yButtonsTop, taCenter); + else + osd->DrawRectangle(t1, yButtonsTop, Yellow ? t1 + w : t2 - 1, yButtonsBottom - 1, Theme.Color(clrBottomBg)); + + if (Yellow) + osd->DrawText(t2, yButtonsTop, Yellow, Theme.Color(clrButtonYellowFg), + Theme.Color(clrButtonYellowBg), font, w, + yButtonsBottom - yButtonsTop, taCenter); + else + osd->DrawRectangle(t2, yButtonsTop, Blue ? t2 + w : t3 - 1, yButtonsBottom - 1, Theme.Color(clrBottomBg)); + if (Blue) + osd->DrawText(t3, yButtonsTop, Blue, Theme.Color(clrButtonBlueFg), + Theme.Color(clrButtonBlueBg), font, w, + yButtonsBottom - yButtonsTop, taCenter); + else { + osd->DrawRectangle(t3, yButtonsTop, xButtonsRight, yButtonsBottom, Theme.Color(clrBottomBg)); + osd->DrawEllipse(xButtonsRight - Roundness, yButtonsBottom - Roundness, + xButtonsRight - 1, yButtonsBottom - 1, clrTransparent, -4); + } +} + +void cSkinEnigmaDisplayMenu::SetMessage(eMessageType Type, const char *Text) +{ + // check if message + if (Text) { + // save osd region + osd->SaveRegion(xMessageLeft, yMessageTop, xMessageRight - 1, yMessageBottom - 1); + // draw message + osd->DrawRectangle(xMessageLeft, yMessageTop, xMessageRight - 1, + yMessageBottom - 1, clrTransparent); + osd->DrawRectangle(xMessageLeft, yMessageTop + SmallGap, xMessageRight - 1, + yMessageBottom - 1, Theme.Color(clrMessageBorder)); + osd->DrawText(xMessageLeft + Gap, yMessageTop + 2 * SmallGap, Text, + Theme.Color(clrMessageStatusFg + 2 * Type), + Theme.Color(clrMessageStatusBg + 2 * Type), + cFont::GetFont(fontOsd), + xMessageRight - Gap - xMessageLeft - Gap - 1, + yMessageBottom - SmallGap - yMessageTop - 2 * SmallGap, + taCenter); + } else { + // restore saved osd region + osd->RestoreRegion(); + } +} + +void cSkinEnigmaDisplayMenu::SetItem(const char *Text, int Index, bool Current, bool Selectable) +{ + int xItemLeft = xBodyLeft + (EnigmaConfig.showMarker ? lineHeightOsd : SmallGap); + int xItemRight = (fShowLogo || fShowInfo ? xBodyRight : xInfoRight) - (EnigmaConfig.showMarker ? lineHeightOsd : SmallGap); + int numItems = (yBodyBottom - yBodyTop) / lineHeightOsd; + int top = yBodyTop + (yBodyBottom - yBodyTop - numItems * lineHeightOsd) / 2; +// debug("top=%d yBodyTop=%d yBodyBottom=%d lineHeightOsd=%d\n", top, yBodyTop, yBodyBottom, lineHeightOsd); + int y = top + Index * lineHeightOsd; + tColor ColorFg, ColorBg; + // select colors + if (Current) { + ColorFg = Theme.Color(clrMenuItemCurrentFg); + ColorBg = Theme.Color(clrMenuHighlight); + } else { + if (Selectable) { + ColorFg = Theme.Color(clrMenuItemSelectableFg); + ColorBg = Theme.Color(clrBackground); + } else { + ColorFg = Theme.Color(clrMenuItemNotSelectableFg); + ColorBg = Theme.Color(clrBackground); + } + } + const cFont *font = cFont::GetFont(fontOsd); + // this should prevent menu flickering + osd->DrawRectangle(xItemLeft, y + lineHeightOsd / 2, xItemLeft + 1, y + lineHeightOsd / 2 + 1, Theme.Color(clrBackground)); + if (EnigmaConfig.showMarker) { + osd->DrawRectangle(xBodyLeft, y, xItemLeft - 1, y + lineHeightOsd - 1, ColorBg); + osd->DrawEllipse(xBodyLeft + MarkerGap, y + MarkerGap, xBodyLeft + lineHeightOsd - MarkerGap, y + lineHeightOsd - MarkerGap, Current ? ColorFg : ColorBg); + osd->DrawRectangle(xItemRight, y, (fShowLogo || fShowInfo ? xBodyRight : xInfoRight) - 1, y + lineHeightOsd - 1, ColorBg); + } + // draw item + for (int i = 0; i < MaxTabs; i++) { + const char *s = GetTabbedText(Text, i); + if (s) { + char buffer[9]; + int xt = xItemLeft + Tab(i); + bool iseventinfo = false; + bool isnewrecording = false; + bool isprogressbar = false; + int now = 0, total = 0; + + // check if event info symbol: "tTV*" "R" + if (EnigmaConfig.showListSymbols) { + // check if event info characters + if (strlen(s) == 3 && strchr(" tTR", s[0]) + && strchr(" V", s[1]) && strchr(" *", s[2])) { + // update status + iseventinfo = true; + } + + // check if new recording: "01.01.06*", "10:10*" + if (!iseventinfo && + (strlen(s) == 6 && s[5] == '*' && s[2] == ':' && isdigit(*s) + && isdigit(*(s + 1)) && isdigit(*(s + 3)) && isdigit(*(s + 4))) + || (strlen(s) == 9 && s[8] == '*' && s[5] == '.' && s[2] == '.' + && isdigit(*s) && isdigit(*(s + 1)) && isdigit(*(s + 3)) + && isdigit(*(s + 4)) && isdigit(*(s + 6)) + && isdigit(*(s + 7)))) { + // update status + isnewrecording = true; + // make a copy + strncpy(buffer, s, strlen(s)); + // remove the '*' character + buffer[strlen(s) - 1] = '\0'; + } + } + + // check if progress bar: "[||||||| ]" + if (!iseventinfo && !isnewrecording && EnigmaConfig.showProgressbar && + (strlen(s) > 5 && s[0] == '[' && s[strlen(s) - 1] == ']')) { + const char *p = s + 1; + // update status + isprogressbar = true; + for (; *p != ']'; ++p) { + // check if progressbar characters + if (*p == ' ' || *p == '|') { + // update counters + ++total; + if (*p == '|') + ++now; + } else { + // wrong character detected; not a progressbar + isprogressbar = false; + break; + } + } + } + + if (iseventinfo) { + int evx = xt + Gap; + const char *p = s; + // draw background + osd->DrawRectangle(xt, y, xItemRight, y + lineHeightOsd - 1, ColorBg); + // draw symbols + for (; *p; ++p) { + switch (*p) { + case 't': + // partial timer event + osd->DrawBitmap(evx, y + (lineHeightOsd - bmEventPartTimer.Height()) / 2, bmEventPartTimer, ColorFg, ColorBg); + evx += bmEventPartTimer.Width(); + break; + case 'T': + // timer event + osd->DrawBitmap(evx, y + (lineHeightOsd - bmEventTimer.Height()) / 2, bmEventTimer, ColorFg, ColorBg); + evx += bmEventTimer.Width(); + break; + case 'R': + // recording event (epgsearch) + osd->DrawBitmap(evx, y + (lineHeightOsd - bmEventRecording.Height()) / 2, bmEventRecording, ColorFg, ColorBg); + evx += bmEventRecording.Width(); + break; + case 'V': + // vps event + osd->DrawBitmap(evx, y + (lineHeightOsd - bmEventVPS.Height()) / 2, bmEventVPS, ColorFg, ColorBg); + evx += bmEventVPS.Width(); + break; + case '*': + // running event + osd->DrawBitmap(evx, y + (lineHeightOsd - bmEventRunning.Height()) / 2, bmEventRunning, ColorFg, ColorBg); + evx += bmEventRunning.Width(); + break; + case ' ': + default: + // let's ignore space character + break; + } + } + } else if (isnewrecording) { + // draw text + osd->DrawText(xt, y, buffer, ColorFg, ColorBg, font, xItemRight - xt); + // draw symbol + osd->DrawBitmap(xt + font->Width(buffer), y + (lineHeightOsd - bmRecordingNew.Height()) / 2, bmRecordingNew, ColorFg, ColorBg); + } else if (isprogressbar) { + // define x coordinates of progressbar + int px0 = xt; + int px1 = (Selectable ? (Tab(i + 1) ? Tab(i + 1) : xItemRight) : xItemRight) - 1; + int px = px0 + (int) ((float) now * (float) (px1 - px0) / (float) total); + // define y coordinates of progressbar + int py0 = y + Gap; + int py1 = y + lineHeightOsd - Gap; + // draw background + osd->DrawRectangle(px0, y, px1, y + lineHeightOsd - 1, ColorBg); + // draw progressbar + osd->DrawRectangle(px0, py0, px, py1, ColorFg); + osd->DrawRectangle(px + 1, py0, px1, py0, ColorFg); + osd->DrawRectangle(px + 1, py1, px1, py1, ColorFg); + osd->DrawRectangle(px1, py0, px1, py1, ColorFg); + } else { + // draw text + osd->DrawText(xt, y, s, ColorFg, ColorBg, font, xItemRight - xt); + } + } + if (!Tab(i + 1)) + break; + } + //set editable width + SetEditableWidth(xItemRight - Tab(1)); + +#ifndef NO_MENULOGO +// debug("SetItem: (%s) %d %d %d\n", Text, Index, Current, Selectable); + if (Current && isMainMenu && fShowLogo && Text) { + char *ItemText, *ItemText2; + int n = strtoul(Text, &ItemText, 10); + if (n != 0) + ItemText2 = ItemText = skipspace(ItemText); + else + ItemText2 = skipspace(ItemText); + + //debug("SetItem: %d (%s) %d %d %d\n", n, ItemText, Index, Current, Selectable); + bool fFoundLogo = false; + if (strcmp(ItemText, tr("Schedule")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/schedule"); + else if (strcmp(ItemText, tr("Channels")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/channels"); + else if (strcmp(ItemText, tr("Timers")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("timerinfo")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/timers"); + else if (strcmp(ItemText, tr("Recordings")) == 0 + || strcmp(ItemText, tr("Recording info")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("extrecmenu")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/recordings"); + else if (strcmp(ItemText, tr("Setup")) == 0 + || strcmp(ItemText2, tr("Setup")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/setup"); + else if (strcmp(ItemText, tr("Commands")) == 0 + || strcmp(ItemText2, tr("Commands")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/commands"); + else if (strcmp(ItemText, tr(" Stop replaying")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/stop"); + else if (strcmp(ItemText, tr(" Cancel editing")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/cancel"); + else if (strcmp(ItemText, tr("Summary")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/summary"); + else if (strcmp(ItemText2, GetPluginMainMenuName("audiorecorder")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/audiorecorder"); + else if (strcmp(ItemText2, GetPluginMainMenuName("burn")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/burn"); + else if (strcmp(ItemText2, GetPluginMainMenuName("cdda")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/cdda"); + else if (strcmp(ItemText2, GetPluginMainMenuName("chanorg")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/chanorg"); + else if (strcmp(ItemText2, GetPluginMainMenuName("channelscan")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/channelscan"); + else if (strcmp(ItemText2, GetPluginMainMenuName("digicam")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/digicam"); + else if (strcmp(ItemText2, GetPluginMainMenuName("director")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/director"); + else if (strcmp(ItemText2, GetPluginMainMenuName("dvd")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/dvd"); + else if (strcmp(ItemText2, GetPluginMainMenuName("dvdselect")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("dvdswitch")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/dvdselect"); + else if (strcmp(ItemText2, GetPluginMainMenuName("dxr3")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("softdevice")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/device"); + else if (strcmp(ItemText2, GetPluginMainMenuName("epgsearch")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("epgsearchonly")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("conflictcheckonly")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("nordlichtsepg")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/epgsearch"); + else if (strcmp(ItemText2, GetPluginMainMenuName("externalplayer")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/externalplayer"); + else if (strcmp(ItemText2, GetPluginMainMenuName("femon")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/femon"); + else if (strcmp(ItemText2, GetPluginMainMenuName("filebrowser")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/filebrowser"); + else if (strcmp(ItemText2, GetPluginMainMenuName("fussball")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/fussball"); + else if (strcmp(ItemText2, GetPluginMainMenuName("games")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/games"); + else if (strcmp(ItemText2, GetPluginMainMenuName("image")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("osdimage")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/image"); + else if (strcmp(ItemText2, GetPluginMainMenuName("mp3")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("mp3ng")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("muggle")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/mp3"); + else if (strcmp(ItemText2, GetPluginMainMenuName("mplayer")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/mplayer"); + else if (strcmp(ItemText2, GetPluginMainMenuName("newsticker")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/newsticker"); + else if (strcmp(ItemText2, GetPluginMainMenuName("osdpip")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/osdpip"); + else if (strcmp(ItemText2, GetPluginMainMenuName("pin")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/pin"); + else if (strcmp(ItemText2, GetPluginMainMenuName("radio")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/radio"); + else if (strcmp(ItemText2, GetPluginMainMenuName("rotor")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/rotor"); + else if (strcmp(ItemText2, GetPluginMainMenuName("solitaire")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/solitaire"); + else if (strcmp(ItemText2, GetPluginMainMenuName("streamdev-client")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("streamdev-server")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/streamdev"); + else if (strcmp(ItemText2, GetPluginMainMenuName("sudoku")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/sudoku"); + else if (strcmp(ItemText2, GetPluginMainMenuName("teletext")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("osdteletext")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/teletext"); + else if (strcmp(ItemText2, GetPluginMainMenuName("tvonscreen")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/tvonscreen"); + else if (strcmp(ItemText2, GetPluginMainMenuName("vcd")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/vcd"); + else if (strcmp(ItemText2, GetPluginMainMenuName("vdrc")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/vdrc"); + else if (strcmp(ItemText2, GetPluginMainMenuName("vdrcd")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("mediad")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/vdrcd"); + else if (strcmp(ItemText2, GetPluginMainMenuName("vdrrip")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/vdrrip"); + else if (strcmp(ItemText2, GetPluginMainMenuName("weather")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("weatherng")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/weather"); + else if (strcmp(ItemText2, GetPluginMainMenuName("webepg")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/webepg"); + else if (strcmp(ItemText2, GetPluginMainMenuName("xineliboutput")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/xineliboutput"); + else + fFoundLogo = EnigmaLogoCache.Load("icons/menu/vdr"); + + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoTop - 1, Theme.Color(clrLogoBg)); + if (fFoundLogo) { + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - EnigmaLogoCache.Get().Width()) / 2, + yLogoTop + (yLogoBottom - yLogoTop - EnigmaLogoCache.Get().Height()) / 2, + EnigmaLogoCache.Get(), EnigmaLogoCache.Get().Color(1), + Theme.Color(clrLogoBg), false, true); + } + } +#endif +} + +const char *cSkinEnigmaDisplayMenu::GetPluginMainMenuName(const char *plugin) +{ + cPlugin *p = cPluginManager::GetPlugin(plugin); + if (p) { + const char *entry = p->MainMenuEntry(); + if (entry) + return entry; + } + return plugin; +} + +int cSkinEnigmaDisplayMenu::DrawFlag(int x, int y, const tComponent *p) +{ + if (p == NULL) + return 0; + + if ((p->stream == 2) && p->language) { + string flag("flags/"); + flag += p->language; + /*TODO + if (p->description) { + + } + */ + if (EnigmaLogoCache.Load(flag.c_str())) { + osd->DrawBitmap(x, y - EnigmaLogoCache.Get().Height(), EnigmaLogoCache.Get()); + return EnigmaLogoCache.Get().Width(); + } + } + + return 0; +} + +void cSkinEnigmaDisplayMenu::SetEvent(const cEvent *Event) +{ + // check if event + if (!Event) + return; + + isMainMenu = false; + fShowInfo = false; +#ifdef NO_MENULOGO + fShowLogo = false; +#else + fShowLogo = EnigmaConfig.showSymbols; +#endif + + const cFont *font = cFont::GetFont(fontOsd); + const cFont *smlfont = cFont::GetFont(fontSml); + int wsb = lineHeightOsd + 2 * SmallGap; //scrollbar width + + time_t now = time(NULL); + // draw recording date string + stringstream sstrDate; + sstrDate << *Event->GetDateString() + << " " << *Event->GetTimeString() + << " - " << *Event->GetEndTimeString() + << " ("; + if (now > Event->StartTime()) + sstrDate << (now - Event->StartTime()) / 60 << '/'; + sstrDate << Event->Duration() / 60 << tr("min") << ")"; + + int y = yDateBottom + (smlfont->Height() - bmVPS.Height()) / 2; + int xs = xDateRight - Gap; + // check if event has VPS + if (EnigmaConfig.showVps && Event->Vps()) { + // draw VPS symbol + if (Event->Vps() != Event->StartTime()) { + char vps[6]; + struct tm tm_r; + time_t t_vps = Event->Vps(); + strftime(vps, sizeof(vps), "%H:%M", localtime_r(&t_vps, &tm_r)); + xs -= smlfont->Width(vps); + osd->DrawText(xs, yDateBottom, vps, + Theme.Color(clrMenuTxtFg), Theme.Color(clrBackground), + smlfont, smlfont->Width(vps), smlfont->Height()); + xs -= TinyGap; + } + xs -= bmVPS.Width(); + osd->DrawBitmap(xs, y, bmVPS, Theme.Color(clrSymbolVpsActive), Theme.Color(clrSymbolInactive)); + xs -= Gap; + } + // check if event is running + if (Event->IsRunning()) { + // draw running symbol + xs -= (bmRun.Width() + Gap); + osd->DrawBitmap(xs, y, bmRun, Theme.Color(clrSymbolRunActive), Theme.Color(clrBackground)); + } + // check if event has timer + if (Event->HasTimer()) { + if (Event->IsRunning()) { + // draw recording symbol + xs -= (bmRecording.Width() + Gap); + osd->DrawBitmap(xs, y, bmRecording, Theme.Color(clrSymbolRecActive), Theme.Color(clrBackground)); + } else { + // draw timer symbol + xs -= (bmTimer.Width() + Gap); + osd->DrawBitmap(xs, y, bmTimer, Theme.Color(clrSymbolTimerActive), Theme.Color(clrBackground)); + } + } + string stringInfo; + const cComponents *Components = Event->Components(); + if (Components) { + stringstream sstrInfo; + for (int i = 0; i < Components->NumComponents(); i++) { + const tComponent *p = Components->Component(i); + if ((p->stream == 2) && p->language) { + if (p->description) { + sstrInfo << p->description + << " (" << p->language << "), "; + } else { + sstrInfo << p->language << ", "; + } +// DrawFlag(p); //TODO + } + } + // strip out the last delimiter + if (!sstrInfo.str().empty()) + stringInfo = tr("Languages"); + stringInfo += ": "; + stringInfo += sstrInfo.str().substr(0, sstrInfo.str().length() - 2); + } + int yHeadlineBottom = yDateBottom; + int xHeadlineRight = fShowLogo ? xDateLeft : xInfoRight; + // draw recording title + osd->DrawRectangle(xBodyLeft, yBodyTop, xHeadlineRight - 1, + yHeadlineBottom - 1, Theme.Color(clrAltBackground)); + int th = font->Height() + (Event->Description() && Event->ShortText() ? Gap + smlfont->Height() : 0); + y = yBodyTop + (yHeadlineBottom - yBodyTop - th) / 2; + osd->DrawText(xBodyLeft + Gap, y, Event->Title(), + Theme.Color(clrMenuEventTitle), Theme.Color(clrAltBackground), + font, xHeadlineRight - xBodyLeft - Gap - 1, font->Height()); + osd->DrawText(xBodyLeft + Gap, yHeadlineBottom, sstrDate.str().c_str(), + Theme.Color(clrMenuTxtFg), Theme.Color(clrBackground), + smlfont, xDateLeft - xBodyLeft - Gap - 1, smlfont->Height()); + + // draw recording short text and description + const char *strDescr = NULL; + if (isempty(Event->Description())) { + // check if short text + if (!isempty(Event->ShortText())) { + // draw short text as description, if no description available + strDescr = Event->ShortText(); + } + } else { + // check if short text + if (!isempty(Event->ShortText())) { + // draw below Event->Title + y += lineHeightOsd + Gap; + // draw short text + osd->DrawText(xBodyLeft + Gap, y, Event->ShortText(), + Theme.Color(clrMenuEventShortText), Theme.Color(clrAltBackground), + smlfont, xHeadlineRight - xBodyLeft - Gap - 1, lineHeightSml); + } + // draw description + strDescr = Event->Description(); + } + + string stringReruns; +#ifdef HAVE_EPGSEARCH + // try to find a rerun of the show using epgsearch-service + if (!isempty(Event->Title())) { + stringstream sstrReruns; + Epgsearch_searchresults_v1_0 data; + data.query = (char *)Event->Title(); + data.mode = 0; + data.channelNr = 0; + data.useTitle = true; + data.useSubTitle = false; + data.useDescription = false; + if (cPluginManager::CallFirstService("Epgsearch-searchresults-v1.0", &data)) { + cList<Epgsearch_searchresults_v1_0::cServiceSearchResult>* list = data.pResultList; + if (list) { + //TODO: current event is shown as rerun + sstrReruns << tr("RERUNS OF THIS SHOW") << ':' << endl; + int i = 0; + for (Epgsearch_searchresults_v1_0::cServiceSearchResult *r = list->First(); r && i < 5; r = list->Next(r)) { + i++; + sstrReruns << "- " + << *DayDateTime(r->event->StartTime()) + << " " << Channels.GetByChannelID(r->event->ChannelID())->ShortName(true) + << ": " << r->event->Title(); + if (!isempty(r->event->ShortText())) + sstrReruns << "~" << r->event->ShortText(); + sstrReruns << endl; + } + delete list; + } + } + stringReruns = sstrReruns.str(); + } +#endif // HAVE_EPGSEARCH + + const char *strFirst = NULL; + const char *strSecond = NULL; + const char *strThird = stringReruns.empty() ? NULL : stringReruns.c_str(); + if (EnigmaConfig.showAuxInfo) { + strFirst = strDescr; + strSecond = stringInfo.empty() ? NULL : stringInfo.c_str(); + } else { + strFirst = stringInfo.empty() ? NULL : stringInfo.c_str(); + strSecond = strDescr; + } + if (strFirst || strSecond || strSecond) { + y = yHeadlineBottom + SmallGap + 2 * smlfont->Height(); + char *mytext; + asprintf(&mytext, "%s%s%s%s%s", strFirst ? strFirst : "", + strSecond ? "\n\n" : "", strSecond ? strSecond : "", + (strFirst || strSecond) && strThird ? "\n\n" : "", strThird ? strThird : ""); + textScroller.Set(osd, xBodyLeft + Gap, y, + xInfoRight - xBodyLeft - wsb - Gap, yBodyBottom - Gap - y, + mytext, smlfont, Theme.Color(clrMenuTxtFg), + Theme.Color(clrBackground)); + SetScrollbar(); +// free(mytext); + } + +#ifndef NO_MENULOGO + if (fShowLogo) { + // draw logo + osd->DrawRectangle(xDateLeft + SmallGap, yDateTop, xDateRight - 1, yDateBottom - SmallGap - 1, Theme.Color(clrLogoBg)); + if (EnigmaLogoCache.Load("icons/menu/schedule")) + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - EnigmaLogoCache.Get().Width()) / 2, + yLogoTop + (yLogoBottom - yLogoTop - EnigmaLogoCache.Get().Height()) / 2, + EnigmaLogoCache.Get(), EnigmaLogoCache.Get().Color(1), Theme.Color(clrLogoBg)); + // draw borders + osd->DrawRectangle(xDateLeft, yDateTop, xDateLeft + SmallGap - 1, yDateBottom - 1, clrTransparent); + osd->DrawRectangle(xDateLeft, yLogoBottom - SmallGap, xDateRight, yLogoBottom - 1, clrTransparent); + } +#endif +} + +void cSkinEnigmaDisplayMenu::SetRecording(const cRecording *Recording) +{ + // check if recording + if (!Recording) + return; + + isMainMenu = false; + fShowInfo = false; +#ifdef NO_MENULOGO + fShowLogo = false; +#else + fShowLogo = EnigmaConfig.showSymbols; +#endif + + const cRecordingInfo *Info = Recording->Info(); + const cFont *font = cFont::GetFont(fontOsd); + const cFont *smlfont = cFont::GetFont(fontSml); + int wsb = lineHeightOsd + 2 * SmallGap; //scrollbar width + + // draw recording date string + stringstream sstrDate; + sstrDate << *DateString(Recording->start) + << " " << *TimeString(Recording->start); + + // draw additional information + stringstream sstrInfo; + int dirSize = DirSizeMB(Recording->FileName()); + cChannel *channel = Channels.GetByChannelID(((cRecordingInfo *)Info)->ChannelID()); + sstrInfo << tr("Channel") << ": " << (channel ? channel->Name() : "???") << endl + << tr("Size") << ": " << setprecision(3) << (dirSize > 1023 ? dirSize / 1024.0 : dirSize) << (dirSize > 1023 ? "GB\n" : "MB\n") + << tr("Priority") << ": " << Recording->priority << endl + << tr("Lifetime") << ": " << Recording->lifetime << endl; + if (Info->Aux()) { + sstrInfo << endl << tr("Auxiliary information") << ":\n" + << parseaux(Info->Aux()); + } + const cComponents *Components = Info->Components(); + if (Components) { + //TODO: show flaggs? + stringstream info; + for (int i = 0; i < Components->NumComponents(); i++) { + const tComponent *p = Components->Component(i); + if ((p->stream == 2) && p->language) { + if (p->description) { + info << p->description + << " (" << p->language << "), "; + } else { + info << p->language << ", "; + } + } + } + // strip out the last delimiter + if (!info.str().empty()) { + sstrInfo << tr("Languages") << ": " + << info.str().substr(0, info.str().length() - 2); + } + } + + int yHeadlineBottom = yDateBottom; + int xHeadlineRight = fShowLogo ? xDateLeft : xInfoRight; + // draw recording title + const char *Title = Info->Title(); + if (isempty(Title)) + Title = Recording->Name(); + osd->DrawRectangle(xBodyLeft, yBodyTop, xHeadlineRight - 1, yHeadlineBottom - 1, Theme.Color(clrAltBackground)); + int th = font->Height() + (Info->Description() && Info->ShortText() ? Gap + smlfont->Height() : 0); + int y = yBodyTop + (yHeadlineBottom - yBodyTop - th) / 2; + osd->DrawText(xBodyLeft + Gap, y, Title, + Theme.Color(clrMenuTxtFg), Theme.Color(clrAltBackground), + font, xHeadlineRight - xBodyLeft - Gap - 1, font->Height()); + osd->DrawText(xBodyLeft + Gap, yHeadlineBottom, sstrDate.str().c_str(), + Theme.Color(clrMenuTxtFg), Theme.Color(clrBackground), + smlfont, xHeadlineRight - xBodyLeft - Gap - 1, smlfont->Height()); + // draw recording short text and description + const char* strDescr = NULL; + if (isempty(Info->Description())) { + // check if short text + if (!isempty(Info->ShortText())) { + // draw short text as description, if no description available + strDescr = Info->ShortText(); + } + } else { + // check if short text + if (!isempty(Info->ShortText())) { + // draw below Title + y += lineHeightOsd + Gap; + // draw short text + osd->DrawText(xBodyLeft + Gap, y, Info->ShortText(), + Theme.Color(clrMenuEventShortText), Theme.Color(clrAltBackground), + smlfont, xHeadlineRight - xBodyLeft - Gap - 1, lineHeightSml); + } + // draw description + strDescr = Info->Description(); + } + + string stringInfo = sstrInfo.str(); + const char *strInfo = stringInfo.empty() ? NULL : stringInfo.c_str(); + if (strDescr || strInfo) { + y = yHeadlineBottom + 2 * smlfont->Height(); + char *mytext; + if (EnigmaConfig.showAuxInfo) + asprintf(&mytext, "%s%s%s", strDescr ? strDescr : "", strInfo && strDescr ? "\n\n" : "", strInfo ? strInfo : ""); + else + asprintf(&mytext, "%s%s%s", strInfo ? strInfo : "", strInfo && strDescr ? "\n\n" : "", strDescr ? strDescr : ""); + textScroller.Set(osd, xBodyLeft + Gap, y, + xInfoRight - xBodyLeft - Gap - wsb, + yBodyBottom - Gap - y, mytext, smlfont, + Theme.Color(clrMenuTxtFg), + Theme.Color(clrBackground)); + SetScrollbar(); + free(mytext); + } + +#ifndef NO_MENULOGO + if (fShowLogo) { + // draw logo + osd->DrawRectangle(xDateLeft + SmallGap, yDateTop, xDateRight - 1, yDateBottom - SmallGap - 1, Theme.Color(clrLogoBg)); + if (EnigmaLogoCache.Load("icons/menu/recordings")) + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - EnigmaLogoCache.Get().Width()) / 2, + yLogoTop + (yLogoBottom - yLogoTop - EnigmaLogoCache.Get().Height()) / 2, + EnigmaLogoCache.Get(), EnigmaLogoCache.Get().Color(1), Theme.Color(clrLogoBg)); + // draw borders + osd->DrawRectangle(xDateLeft, yDateTop, xDateLeft + SmallGap - 1, yDateBottom - 1, clrTransparent); + osd->DrawRectangle(xDateLeft, yLogoBottom - SmallGap, xDateRight, yLogoBottom - 1, clrTransparent); + } +#endif +} + +void cSkinEnigmaDisplayMenu::SetText(const char *Text, bool FixedFont) +{ + int wsb = lineHeightOsd + 2 * SmallGap; //scrollbar width + // draw text + textScroller.Set(osd, xBodyLeft + Gap, yBodyTop + Gap, + (fShowLogo || fShowInfo ? xBodyRight : xInfoRight) - 2 * Gap - wsb, + yBodyBottom - yBodyTop - 2 * Gap, Text, + GetTextAreaFont(FixedFont), + Theme.Color(clrMenuTxtFg), Theme.Color(clrBackground)); + SetScrollbar(); +} + +int cSkinEnigmaDisplayMenu::GetTextAreaWidth(void) const +{ + // max text area width + return (fShowLogo || fShowInfo ? xBodyRight : xInfoRight) - xBodyLeft; +} + +const cFont *cSkinEnigmaDisplayMenu::GetTextAreaFont(bool FixedFont) const +{ + // text area font + return cFont::GetFont(FixedFont ? fontFix : fontOsd); +} + +void cSkinEnigmaDisplayMenu::Flush(void) +{ +//debug("cSkinEnigmaDisplayMenu::Flush\n"); + if (fShowLogo) { + int x = xDateLeft + SmallGap; + int w = xLogoLeft - x; + const cFont *font = cFont::GetFont(fontOsd); + int ys = yDateTop + (yDateBottom - yDateTop - 3 * lineHeightOsd) / 2; + + char temp[32]; + struct tm tm_r; + time_t t = time(NULL); + tm *tm = localtime_r(&t, &tm_r); + + strftime(temp, sizeof(temp), "%A", tm); + osd->DrawText(x, ys, temp, Theme.Color(clrMenuTxtFg), + Theme.Color(clrLogoBg), font, w, + lineHeightOsd, taCenter); + ys += lineHeightOsd; + + strftime(temp, sizeof(temp), "%d.%m.%Y", tm); + osd->DrawText(x, ys, temp, Theme.Color(clrMenuTxtFg), + Theme.Color(clrLogoBg), font, w, + lineHeightOsd, taCenter); + ys += lineHeightOsd; + + cString time = TimeString(t); + osd->DrawText(x, ys, time, Theme.Color(clrMenuTxtFg), + Theme.Color(clrLogoBg), font, w, + lineHeightOsd, taCenter); + //draw borders + osd->DrawRectangle(xDateLeft, yDateTop, xDateLeft + SmallGap - 1, yLogoBottom - SmallGap - 1, clrTransparent); + osd->DrawRectangle(xDateLeft, yLogoBottom - SmallGap, xDateRight, yLogoBottom - 1, clrTransparent); + } else { + cString date = DayDateTime(); + const cFont *font = cFont::GetFont(fontOsd); + osd->DrawText(xDateLeft + SmallGap, yDateTop, date, Theme.Color(clrMenuTxtFg), + Theme.Color(clrLogoBg), font, xDateRight - xDateLeft - SmallGap, + yTitleDecoBottom - yDateTop, taCenter); + //draw borders + osd->DrawRectangle(xDateLeft, yDateTop, xDateLeft + SmallGap - 1, yTitleDecoBottom - 1, clrTransparent); + osd->DrawRectangle(xDateLeft, yTitleDecoBottom, xDateRight, yBodyTop - 1, clrTransparent); + } + osd->Flush(); +} + +// --- cSkinEnigmaDisplayReplay --------------------------------------------- + +class cSkinEnigmaDisplayReplay:public cSkinDisplayReplay { +private: + cOsd *osd; + + int xTitleLeft, xTitleRight, yTitleTop, yTitleBottom, yTitleDecoTop, yTitleDecoBottom; + int xLogoLeft, xLogoRight, yLogoTop, yLogoBottom; + int xProgressLeft, xProgressRight, yProgressTop, yProgressBottom; + int xTimeLeft, xTimeRight, yTimeTop, yTimeBottom; + int xBottomLeft, xBottomRight, yBottomTop, yBottomBottom; + int xMessageLeft, xMessageRight, yMessageTop, yMessageBottom; + + int lineHeight; + bool modeonly; + int nJumpWidth; +public: + cSkinEnigmaDisplayReplay(bool ModeOnly); + virtual ~ cSkinEnigmaDisplayReplay(); + virtual void SetTitle(const char *Title); + virtual void SetMode(bool Play, bool Forward, int Speed); + virtual void SetProgress(int Current, int Total); + virtual void SetCurrent(const char *Current); + virtual void SetTotal(const char *Total); + virtual void SetJump(const char *Jump); + virtual void SetMessage(eMessageType Type, const char *Text); + virtual void Flush(void); +}; + +cSkinEnigmaDisplayReplay::cSkinEnigmaDisplayReplay(bool ModeOnly) +{ + modeonly = ModeOnly; + nJumpWidth = 0; + lineHeight = cFont::GetFont(fontOsd)->Height(); + + xTitleLeft = 0; + xTitleRight = Setup.OSDWidth; + yTitleTop = 0; + yTitleBottom = yTitleTop + lineHeight; + yTitleDecoTop = yTitleBottom + TitleDecoGap; + yTitleDecoBottom = yTitleDecoTop + TitleDecoHeight; + xLogoLeft = xTitleLeft; + xLogoRight = xLogoLeft + LogoWidth; + yLogoTop = yTitleDecoBottom + TitleDecoGap2; + yLogoBottom = yLogoTop + LogoHeight; + xTimeLeft = xLogoRight + LogoDecoGap2; + xTimeRight = xTitleRight; + xTimeLeft += (xTimeRight - xTimeLeft) - ((xTimeRight - xTimeLeft) & ~0x07); + yTimeTop = yLogoBottom - SmallGap - lineHeight - SmallGap; + yTimeBottom = yLogoBottom; + xProgressLeft = xTimeLeft; + xProgressRight = xTitleRight; + yProgressTop = yLogoTop; + yProgressBottom = yTimeTop - SmallGap; + xBottomLeft = xTitleLeft; + xBottomRight = xTitleRight; + yBottomTop = yLogoBottom + SmallGap; + yBottomBottom = yBottomTop + lineHeight; + xMessageLeft = xProgressLeft; + xMessageRight = xProgressRight; + yMessageTop = yLogoTop + (LogoHeight - MessageHeight) / 2; + yMessageBottom = yMessageTop + MessageHeight; + + // create osd + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - yBottomBottom); + tArea Areas[] = { {xTitleLeft, yTitleTop, xBottomRight - 1, yBottomBottom - 1, 8} }; + if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk) { + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + } else { + tArea Areas[] = { {xTitleLeft, yTitleTop, xTitleRight - 1, yTitleDecoBottom - 1, 2}, + {xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, 4}, + {xProgressLeft, yProgressTop, xTimeRight - 1, yTimeBottom - 1, 4}, + {xBottomLeft, yBottomTop, xBottomRight - 1, yBottomBottom - 1, 2} + }; + int rc = osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)); + if (rc == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else + error("cSkinEnigmaDisplayReplay: CanHandleAreas() returned %d\n", rc); + } + // clear all + osd->DrawRectangle(0, 0, osd->Width(), osd->Height(), clrTransparent); + if (modeonly) { + // draw logo area + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, EnigmaConfig.showSymbols ? Theme.Color(clrLogoBg) : clrTransparent); + } else { + // draw title area + osd->DrawRectangle(xTitleLeft, yTitleTop, xTitleRight - 1, + yTitleBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawEllipse(xTitleLeft, yTitleTop, xTitleLeft + Roundness - 1, + yTitleTop + Roundness - 1, clrTransparent, -2); + osd->DrawEllipse(xTitleRight - Roundness, yTitleTop, xTitleRight, + yTitleTop + Roundness, clrTransparent, -1); + osd->DrawRectangle(xTitleLeft, yTitleDecoTop, xTitleRight - 1, + yTitleDecoBottom - 1, Theme.Color(clrTitleBg)); + // draw logo area + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + // draw progress bar area + osd->DrawRectangle(xProgressLeft, yProgressTop, xProgressRight - 1, + yProgressBottom - 1, Theme.Color(clrReplayBarAreaBg)); + // draw time area + osd->DrawRectangle(xTimeLeft, yTimeTop, xTimeRight - 1, yTimeBottom - 1, Theme.Color(clrAltBackground)); + // draw bottom area + osd->DrawRectangle(xBottomLeft, yBottomTop, xBottomRight - 1, + yBottomBottom - 1, Theme.Color(clrBottomBg)); + osd->DrawEllipse(xBottomLeft, yBottomBottom - Roundness, + xBottomLeft + Roundness - 1, yBottomBottom - 1, + clrTransparent, -3); + osd->DrawEllipse(xBottomRight - Roundness, yBottomBottom - Roundness, + xBottomRight - 1, yBottomBottom - 1, clrTransparent, -4); + } +} + +cSkinEnigmaDisplayReplay::~cSkinEnigmaDisplayReplay() +{ + delete osd; +} + +void cSkinEnigmaDisplayReplay::SetTitle(const char *Title) +{ + // draw title area + osd->DrawRectangle(xTitleLeft, yTitleTop, xTitleRight - 1, + yTitleBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawEllipse(xTitleLeft, yTitleTop, xTitleLeft + Roundness - 1, + yTitleTop + Roundness - 1, clrTransparent, -2); + osd->DrawEllipse(xTitleRight - Roundness, yTitleTop, xTitleRight, + yTitleTop + Roundness, clrTransparent, -1); + osd->DrawRectangle(xTitleLeft, yTitleDecoTop, xTitleRight - 1, + yTitleDecoBottom - 1, Theme.Color(clrTitleBg)); + + if (Title) { + debug("REPLAY TITLE: %s\n", Title); + // draw titlebar + osd->DrawText(xTitleLeft + Roundness + 3, yTitleTop + 3, Title, + Theme.Color(clrTitleShadow), clrTransparent, + cFont::GetFont(fontOsd), + xTitleRight - Roundness - xTitleLeft - Roundness - 3, + yTitleBottom - yTitleTop - 3); + osd->DrawText(xTitleLeft + Roundness, yTitleTop, Title, + Theme.Color(clrTitleFg), clrTransparent, + cFont::GetFont(fontOsd), + xTitleRight - Roundness - xTitleLeft - Roundness, + yTitleBottom - yTitleTop); + } +} + +void cSkinEnigmaDisplayReplay::SetMode(bool Play, bool Forward, int Speed) +{ + bool fFoundLogo = false; + if (Speed < -1) + Speed = -1; + + if (EnigmaConfig.showSymbols) { + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + + char *logo = NULL; + asprintf(&logo, "icons/menu/%s", EnigmaStatus.ReplayModeName()); + if (EnigmaLogoCache.Load(logo)) { + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - EnigmaLogoCache.Get().Width()) / 2, + yLogoTop + (yLogoBottom - yLogoTop - EnigmaLogoCache.Get().Height()) / 2, + EnigmaLogoCache.Get(), EnigmaLogoCache.Get().Color(1), + Theme.Color(clrLogoBg), true); + } + free(logo); + } else { + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, modeonly ? clrTransparent : Theme.Color(clrLogoBg)); + } + + if (Speed == -1) + fFoundLogo = EnigmaLogoCache.Load(Play ? "icons/replay/play" : "icons/replay/pause"); + else if (Play) { + if (Speed > MAX_SPEED_BITMAPS - 1) { + error("MAX SPEED %d > 9\n", Speed); + Speed = MAX_SPEED_BITMAPS - 1; + } + fFoundLogo = EnigmaLogoCache.Load(Forward ? strFastForward_large[Speed] : strFastRewind_large[Speed]); + } else { // trick speed + if (Speed > MAX_TRICKSPEED_BITMAPS - 1) { + error("MAX SPEED %d > 3\n", Speed); + Speed = MAX_TRICKSPEED_BITMAPS - 1; + } + fFoundLogo = EnigmaLogoCache.Load(Forward ? strSlowForward_large[Speed] : strSlowRewind_large[Speed]); + } + if (fFoundLogo) + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - EnigmaLogoCache.Get().Width()) / 2, + yLogoTop + (yLogoBottom - yLogoTop - EnigmaLogoCache.Get().Height()) / 2, + EnigmaLogoCache.Get(), EnigmaLogoCache.Get().Color(1), + Theme.Color(clrLogoBg), false, true); +} + +void cSkinEnigmaDisplayReplay::SetProgress(int Current, int Total) +{ + // create progressbar + cProgressBar pb(xProgressRight - xProgressLeft - 2 * BigGap, + yProgressBottom - yProgressTop - 2 * BigGap, Current, Total, + marks, Theme.Color(clrReplayProgressSeen), + Theme.Color(clrReplayProgressRest), + Theme.Color(clrReplayProgressSelected), + Theme.Color(clrReplayProgressMark), + Theme.Color(clrReplayProgressCurrent)); + // draw progressbar + osd->DrawBitmap(xProgressLeft + BigGap, yProgressTop + BigGap, pb); +} + +void cSkinEnigmaDisplayReplay::SetCurrent(const char *Current) +{ + if (!Current) + return; + + // draw current time + const cFont *font = cFont::GetFont(fontSml); + int w = font->Width(Current); + osd->DrawText(xTimeLeft + BigGap, yTimeTop, Current, + Theme.Color(clrReplayCurrent), Theme.Color(clrAltBackground), font, + w, yTimeBottom - yTimeTop, taLeft); +} + +void cSkinEnigmaDisplayReplay::SetTotal(const char *Total) +{ + if (!Total) + return; + + // draw total time + const cFont *font = cFont::GetFont(fontSml); + int w = font->Width(Total); + osd->DrawText(xTimeRight - BigGap - w, yTimeTop, Total, + Theme.Color(clrReplayTotal), Theme.Color(clrAltBackground), font, w, + yTimeBottom - yTimeTop, taRight); +} + +void cSkinEnigmaDisplayReplay::SetJump(const char *Jump) +{ + if (Jump) { + // draw jump prompt + const cFont *font = cFont::GetFont(fontSml); + nJumpWidth = font->Width(Jump); + osd->DrawText(xTimeLeft + (xTimeRight - xTimeLeft - nJumpWidth) / 2, + yTimeTop, Jump, Theme.Color(clrReplayModeJump), + Theme.Color(clrAltBackground), font, nJumpWidth, + yTimeBottom - yTimeTop, taLeft); + } else { + // erase old prompt + osd->DrawRectangle(xTimeLeft + (xTimeRight - xTimeLeft - nJumpWidth) / 2, + yTimeTop, + xTimeLeft + (xTimeRight - xTimeLeft - nJumpWidth) / 2 + + nJumpWidth - 1, yTimeBottom - 1, Theme.Color(clrAltBackground)); + } +} + +void cSkinEnigmaDisplayReplay::SetMessage(eMessageType Type, const char *Text) +{ + if (Text) { + // save current osd + osd->SaveRegion(xMessageLeft, yMessageTop, xMessageRight, yMessageBottom); + // draw message + osd->DrawRectangle(xMessageLeft, yMessageTop, xMessageRight - 1, + yMessageBottom - 1, clrTransparent); + osd->DrawRectangle(xMessageLeft, yMessageTop + SmallGap, xMessageRight - 1, + yMessageBottom - SmallGap - 1, + Theme.Color(clrMessageBorder)); + osd->DrawText(xMessageLeft + Gap, yMessageTop + 2 * SmallGap, Text, + Theme.Color(clrMessageStatusFg + 2 * Type), + Theme.Color(clrMessageStatusBg + 2 * Type), + cFont::GetFont(fontOsd), + xMessageRight - Gap - xMessageLeft - Gap - 1, + yMessageBottom - 2 * SmallGap - yMessageTop - 2 * SmallGap, + taCenter); + } else { + // restore saved osd + osd->RestoreRegion(); + } +} + +void cSkinEnigmaDisplayReplay::Flush(void) +{ + // update date + if (!modeonly) { + cString date = DayDateTime(); + osd->DrawText(xBottomLeft + Roundness, yBottomTop, date, + Theme.Color(clrTitleFg), Theme.Color(clrBottomBg), + cFont::GetFont(fontOsd), + xBottomRight - Roundness - xBottomLeft - Roundness - 1, + yBottomBottom - yBottomTop - 1, taRight); + } + osd->Flush(); +} + +// --- cSkinEnigmaDisplayVolume --------------------------------------------- + +class cSkinEnigmaDisplayVolume:public cSkinDisplayVolume { +private: + cOsd *osd; + + int xTitleLeft, xTitleRight, yTitleTop, yTitleBottom, yTitleDecoTop, yTitleDecoBottom; + int xLogoLeft, xLogoRight, yLogoTop, yLogoBottom, xLogoDecoLeft, xLogoDecoRight; + int xBodyLeft, xBodyRight, yBodyTop, yBodyBottom; + int xBottomLeft, xBottomRight, yBottomTop, yBottomBottom; + + int lineHeight; +public: + cSkinEnigmaDisplayVolume(); + virtual ~ cSkinEnigmaDisplayVolume(); + virtual void SetVolume(int Current, int Total, bool Mute); + virtual void Flush(void); +}; + +cSkinEnigmaDisplayVolume::cSkinEnigmaDisplayVolume() +{ + lineHeight = cFont::GetFont(fontOsd)->Height(); + + xLogoLeft = 0; + xLogoRight = LogoWidth; + xLogoDecoLeft = xLogoRight + LogoDecoGap; + xLogoDecoRight = xLogoDecoLeft + LogoDecoWidth; + yLogoTop = 0; + yLogoBottom = yLogoTop + LogoHeight; + xTitleLeft = xLogoDecoRight + LogoDecoGap2; + xTitleRight = Setup.OSDWidth; + yTitleTop = yLogoTop; + yTitleBottom = yTitleTop + lineHeight; + yTitleDecoTop = yTitleBottom + TitleDecoGap; + yTitleDecoBottom = yTitleDecoTop + TitleDecoHeight; + xBottomLeft = xTitleLeft; + xBottomRight = Setup.OSDWidth; + yBottomTop = yLogoBottom - lineHeight; + yBottomBottom = yLogoBottom; + xBodyLeft = xTitleLeft; + xBodyRight = xTitleRight; + yBodyTop = yTitleDecoBottom + TitleDecoGap2; + yBodyBottom = yBottomTop - SmallGap; + + // create osd + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - yBottomBottom); + tArea Areas[] = { {xLogoLeft, yLogoTop, xTitleRight - 1, yBottomBottom - 1, 8} }; + if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk) { + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + } else { + tArea Areas[] = { {xLogoLeft, yLogoTop, xLogoRight + LogoDecoGap + LogoDecoWidth - 1, yLogoBottom - 1, 4}, + {xTitleLeft, yTitleTop, xTitleRight - 1, yBottomBottom - 1, 4} + }; + int rc = osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)); + if (rc == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else + error("cSkinEnigmaDisplayVolume: CanHandleAreas() returned %d (%d, %d)\n", rc, oeWrongAlignment, oeOutOfMemory); + // set colors + osd->GetBitmap(1)->Reset(); + osd->GetBitmap(1)->SetColor(0, Theme.Color(clrTransparent)); + osd->GetBitmap(1)->SetColor(1, Theme.Color(clrBackground)); + osd->GetBitmap(1)->SetColor(2, Theme.Color(clrTitleBg)); + osd->GetBitmap(1)->SetColor(3, Theme.Color(clrBottomBg)); + osd->GetBitmap(1)->SetColor(4, Theme.Color(clrVolumeBar)); + osd->GetBitmap(1)->SetColor(5, Theme.Color(clrVolumeBarMute)); + osd->GetBitmap(1)->SetColor(6, Theme.Color(clrTitleFg)); + osd->GetBitmap(1)->SetColor(7, Theme.Color(clrTitleShadow)); + } + // clear all + osd->DrawRectangle(0, 0, osd->Width(), osd->Height(), clrTransparent); + // draw logo area + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + osd->DrawRectangle(xLogoDecoLeft, yLogoTop, xLogoDecoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + // draw title + osd->DrawRectangle(xTitleLeft, yTitleTop, xTitleRight - 1, yTitleBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawRectangle(xTitleLeft, yTitleDecoTop, xTitleRight - 1, yTitleDecoBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawEllipse(xTitleRight - Roundness, yTitleTop, xTitleRight - 1, yTitleTop + Roundness - 1, clrTransparent, -1); + // draw body area + osd->DrawRectangle(xBodyLeft, yBodyTop, xBodyRight - 1, yBodyBottom - 1, Theme.Color(clrBackground)); + // draw bottom area + osd->DrawRectangle(xBottomLeft, yBottomTop, xBottomRight - 1, yBottomBottom - 1, Theme.Color(clrBottomBg)); + osd->DrawEllipse(xBottomRight - Roundness, yBottomBottom - Roundness, xBottomRight - 1, yBottomBottom - 1, clrTransparent, -4); +} + +cSkinEnigmaDisplayVolume::~cSkinEnigmaDisplayVolume() +{ + delete osd; +} + +void cSkinEnigmaDisplayVolume::SetVolume(int Current, int Total, bool Mute) +{ + tColor ColorBar; + const char *Prompt; + const cFont *font = cFont::GetFont(fontOsd); + bool fFoundLogo = false; + + // select behaviour + if (Mute) { + ColorBar = Theme.Color(clrVolumeBarMute); + Prompt = tr("Mute"); + fFoundLogo = EnigmaLogoCache.Load("icons/volume/muteOn"); + } else { + ColorBar = Theme.Color(clrVolumeBar); + Prompt = tr("Volume"); + fFoundLogo = EnigmaLogoCache.Load("icons/volume/muteOff"); + } + // logo + if (fFoundLogo) + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - EnigmaLogoCache.Get().Width()) / 2, + yLogoTop + (yLogoBottom - yLogoTop - EnigmaLogoCache.Get().Height()) / 2, + EnigmaLogoCache.Get(),EnigmaLogoCache.Get().Color(1), + Theme.Color(clrLogoBg)); + // current volume + int vol = xBodyLeft + Gap + (xBodyRight - Gap - xBodyLeft - Gap) * Current / Total; + // draw titlebar + osd->DrawRectangle(xTitleLeft + Gap, yTitleTop, xTitleRight - Roundness - 1, + yTitleBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawText(xTitleLeft + Gap + 3, yTitleTop + 3, Prompt, + Theme.Color(clrTitleShadow), clrTransparent, font, + xTitleRight - xTitleLeft, lineHeight, taCenter); + osd->DrawText(xTitleLeft + Gap, yTitleTop, Prompt, + Theme.Color(clrTitleFg), clrTransparent, font, + xTitleRight - xTitleLeft, lineHeight, taCenter); + // draw volumebar + osd->DrawRectangle(xBodyLeft + Gap, yBodyTop + Gap, xBodyRight - Gap - 1, + yBodyBottom - Gap - 1, Theme.Color(clrBackground)); + osd->DrawRectangle(xBodyLeft + Gap, yBodyTop + Gap, vol - 1, + yBodyBottom - Gap - 1, ColorBar); +} + +void cSkinEnigmaDisplayVolume::Flush(void) +{ + cString date = DayDateTime(); + osd->DrawText(xBottomLeft, yBottomTop, date, Theme.Color(clrTitleFg), + Theme.Color(clrBottomBg), cFont::GetFont(fontOsd), + xBottomRight - Gap - xBottomLeft - 1, + yBottomBottom - yBottomTop - 1, taRight); + osd->Flush(); +} + +// --- cSkinEnigmaDisplayTracks --------------------------------------------- + +class cSkinEnigmaDisplayTracks:public cSkinDisplayTracks { +private: + cOsd *osd; + + int xTitleLeft, xTitleRight, yTitleTop, yTitleBottom, yTitleDecoTop, yTitleDecoBottom; + int xLogoLeft, xLogoRight, yLogoTop, yLogoBottom; + int xListLeft, xListRight, yListTop, yListBottom; + int xBottomLeft, xBottomRight, yBottomTop, yBottomBottom; + + int lineHeight; + int currentIndex; + + void SetItem(const char *Text, int Index, bool Current); +public: + cSkinEnigmaDisplayTracks(const char *Title, int NumTracks, + const char *const *Tracks); + virtual ~ cSkinEnigmaDisplayTracks(); + virtual void SetTrack(int Index, const char *const *Tracks); + virtual void SetAudioChannel(int AudioChannel); + virtual void Flush(void); +}; + +cSkinEnigmaDisplayTracks::cSkinEnigmaDisplayTracks(const char *Title, int NumTracks, const char *const *Tracks) +{ //TODO: honour enable/disable option "show symbols" + const cFont *font = cFont::GetFont(fontOsd); + lineHeight = font->Height(); + currentIndex = -1; + int ItemsWidth = 0; + for (int i = 0; i < NumTracks; i++) + ItemsWidth = max(ItemsWidth, font->Width(Tracks[i])); + ItemsWidth += (EnigmaConfig.showMarker ? lineHeight : SmallGap); + ItemsWidth = max(ItemsWidth, LogoWidth); + int width = LogoWidth + LogoDecoGap2 + ItemsWidth; + width = max(width, font->Width(DayDateTime()) + 2 * Roundness); + width = max(width, font->Width(Title) + 2 * Roundness); + + xTitleLeft = 0; + xTitleRight = Setup.OSDWidth; + int d = xTitleRight - xTitleLeft; + if (d > width) { + d = (d - width) & ~0x07; // must be multiple of 8 + xTitleRight -= d; + } + + yTitleTop = 0; + yTitleBottom = lineHeight; + yTitleDecoTop = yTitleBottom + TitleDecoGap; + yTitleDecoBottom = yTitleDecoTop + TitleDecoHeight; + xLogoLeft = xTitleLeft; + xLogoRight = xLogoLeft + LogoWidth; + yLogoTop = yTitleDecoBottom + TitleDecoGap2; + yLogoBottom = yLogoTop + LogoHeight; + xListLeft = xLogoRight + LogoDecoGap2; + xListRight = xTitleRight; + yListTop = yLogoTop; + yListBottom = yLogoBottom; + xBottomLeft = xTitleLeft; + xBottomRight = xTitleRight; + yBottomTop = yListBottom + SmallGap; + yBottomBottom = yBottomTop + lineHeight; + + // create osd + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - yBottomBottom); + tArea Areas[] = { {xTitleLeft, yTitleTop, xBottomRight - 1, yBottomBottom - 1, 8} }; + if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk) { + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + } else { + tArea Areas[] = { {xTitleLeft, yTitleTop, xTitleRight - 1, yTitleDecoBottom- 1, 2}, + {xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, 4}, + {xListLeft, yListTop, xListRight - 1, yListBottom - 1, 4}, + {xBottomLeft, yBottomTop, xBottomRight - 1, yBottomBottom - 1, 2} + }; + int rc = osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)); + if (rc == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else + error("cSkinEnigmaDisplayTracks: CanHandleAreas() returned %d\n", rc); + } + // clear all + osd->DrawRectangle(0, 0, osd->Width(), osd->Height(), clrTransparent); + // draw titlebar + osd->DrawRectangle(xTitleLeft, yTitleTop, xTitleRight - 1, yTitleBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawRectangle(xTitleLeft, yTitleDecoTop, xTitleRight - 1, yTitleDecoBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawText(xTitleLeft + Roundness + 3, yTitleTop + 3, Title, + Theme.Color(clrTitleShadow), clrTransparent, font, + xTitleRight - Roundness - xTitleLeft - Roundness, + lineHeight - 3, taCenter); + osd->DrawText(xTitleLeft + Roundness, yTitleTop, Title, + Theme.Color(clrTitleFg), clrTransparent, font, + xTitleRight - Roundness - xTitleLeft - Roundness, lineHeight, + taCenter); + // draw rounded left corner of titlebar + osd->DrawEllipse(xTitleLeft, yTitleTop, xTitleLeft + Roundness - 1, + yTitleTop + Roundness - 1, clrTransparent, -2); + // draw rounded right corner of titlebar + osd->DrawEllipse(xTitleRight - Roundness, yTitleTop, xTitleRight - 1, + yTitleTop + Roundness - 1, clrTransparent, -1); + // draw logo area + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + // draw list area + osd->DrawRectangle(xListLeft, yListTop, xListRight - 1, yListBottom - 1, Theme.Color(clrBackground)); + // draw bottom + osd->DrawRectangle(xBottomLeft, yBottomTop, xBottomRight - 1, yBottomBottom - 1, Theme.Color(clrBottomBg)); + osd->DrawEllipse(xBottomLeft, yBottomBottom - Roundness, + xBottomLeft + Roundness, yBottomBottom - 1, clrTransparent, -3); + osd->DrawEllipse(xBottomRight - Roundness, yBottomBottom - Roundness, + xBottomRight - 1, yBottomBottom - 1, clrTransparent, -4); + // fill up audio tracks + SetAudioChannel(cDevice::PrimaryDevice()->GetAudioChannel()); + for (int i = 0; i < NumTracks; i++) + SetItem(Tracks[i], i, false); +} + +cSkinEnigmaDisplayTracks::~cSkinEnigmaDisplayTracks() +{ + delete osd; +} + +void cSkinEnigmaDisplayTracks::SetItem(const char *Text, int Index, bool Current) +{ + int xItemLeft = xListLeft + (EnigmaConfig.showMarker ? lineHeight : SmallGap); + const cFont *font = cFont::GetFont(fontOsd); + int y = yListTop + Index * lineHeight; + tColor ColorFg, ColorBg; + if (Current) { + ColorFg = Theme.Color(clrMenuItemCurrentFg); + ColorBg = Theme.Color(clrMenuHighlight); + currentIndex = Index; + } else { + ColorFg = Theme.Color(clrMenuItemSelectableFg); + ColorBg = Theme.Color(clrBackground); + } + // draw track id + osd->DrawRectangle(xListLeft, y, xListRight, y + lineHeight, ColorBg); + if (EnigmaConfig.showMarker) { + osd->DrawEllipse(xListLeft + MarkerGap, y + MarkerGap, + xListLeft + lineHeight - MarkerGap, + y + lineHeight - MarkerGap, + Current ? ColorFg : ColorBg); + } + osd->DrawText(xItemLeft, y, Text, ColorFg, ColorBg, font, xListRight - xItemLeft - SmallGap, lineHeight); +} + +void cSkinEnigmaDisplayTracks::SetAudioChannel(int AudioChannel) +{ + // draw logo area + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + if (!(AudioChannel >= 0 && AudioChannel < MAX_AUDIO_BITMAPS)) + AudioChannel = 0; + if (EnigmaLogoCache.Load(strAudio_large[AudioChannel])) + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - EnigmaLogoCache.Get().Width()) / 2, + yLogoTop + (yLogoBottom - yLogoTop - EnigmaLogoCache.Get().Height()) / 2, + EnigmaLogoCache.Get(),EnigmaLogoCache.Get().Color(1), + Theme.Color(clrLogoBg)); +} + +void cSkinEnigmaDisplayTracks::SetTrack(int Index, const char *const *Tracks) +{ + if (currentIndex >= 0) + SetItem(Tracks[currentIndex], currentIndex, false); + SetItem(Tracks[Index], Index, true); +} + +void cSkinEnigmaDisplayTracks::Flush(void) +{ + cString date = DayDateTime(); + osd->DrawText(xBottomLeft + Roundness, yBottomTop, date, + Theme.Color(clrTitleFg), Theme.Color(clrBottomBg), + cFont::GetFont(fontOsd), + xBottomRight - Roundness - xBottomLeft - Roundness - 1, + yBottomBottom - yBottomTop - 1, taRight); + osd->Flush(); +} + +// --- cSkinEnigmaDisplayMessage -------------------------------------------- + +class cSkinEnigmaDisplayMessage:public cSkinDisplayMessage { +private: + cOsd *osd; + + int xTitleLeft, xTitleRight, yTitleTop, yTitleBottom, yTitleDecoTop, yTitleDecoBottom; + int xLogoLeft, xLogoRight, yLogoTop, yLogoBottom, xLogoDecoLeft, xLogoDecoRight; + int xMessageLeft, xMessageRight, yMessageTop, yMessageBottom; + int xBottomLeft, xBottomRight, yBottomTop, yBottomBottom; + + int lineHeight; +public: + cSkinEnigmaDisplayMessage(); + virtual ~ cSkinEnigmaDisplayMessage(); + virtual void SetMessage(eMessageType Type, const char *Text); + virtual void Flush(void); +}; + +cSkinEnigmaDisplayMessage::cSkinEnigmaDisplayMessage() +{ + lineHeight = cFont::GetFont(fontOsd)->Height(); + + xLogoLeft = 0; + xLogoRight = xLogoLeft + LogoWidth; + xLogoDecoLeft = xLogoRight + LogoDecoGap; + xLogoDecoRight = xLogoDecoLeft + LogoDecoWidth; + yLogoTop = 0; + yLogoBottom = yLogoTop + LogoHeight; + xTitleLeft = xLogoDecoRight + LogoDecoGap2; + xTitleRight = Setup.OSDWidth; + yTitleTop = yLogoTop; + yTitleBottom = yTitleTop + lineHeight; + yTitleDecoTop = yTitleBottom + TitleDecoGap; + yTitleDecoBottom = yTitleDecoTop + TitleDecoHeight; + xBottomLeft = xTitleLeft; + xBottomRight = xTitleRight; + yBottomTop = yLogoBottom - lineHeight; + yBottomBottom = yLogoBottom; + xMessageLeft = xTitleLeft; + xMessageRight = xTitleRight; + yMessageTop = yTitleDecoBottom + TitleDecoGap2; + yMessageBottom = yBottomTop - SmallGap; + + // create osd + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - yBottomBottom); + tArea Areas[] = { {xLogoLeft, yLogoTop, xBottomRight - 1, yBottomBottom - 1, 8} }; + if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk) { + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + } else { + tArea Areas[] = { {xLogoLeft, yLogoTop, xLogoRight + LogoDecoGap + LogoDecoWidth - 1, yLogoBottom - 1, 4}, + {xTitleLeft, yTitleTop, xBottomRight - 1, yBottomBottom - 1, 4} }; + int rc = osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)); + if (rc == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else + error("cSkinEnigmaDisplayMessage: CanHandleAreas() returned %d\n", rc); + } + // clear all + osd->DrawRectangle(0, 0, osd->Width(), osd->Height(), clrTransparent); +} + +cSkinEnigmaDisplayMessage::~cSkinEnigmaDisplayMessage() +{ + delete osd; +} + +void cSkinEnigmaDisplayMessage::SetMessage(eMessageType Type, const char *Text) +{ + // draw logo + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + osd->DrawRectangle(xLogoDecoLeft, yLogoTop, xLogoDecoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + if (EnigmaLogoCache.Load("icons/message/info")) + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - EnigmaLogoCache.Get().Width()) / 2, + yLogoTop + (yLogoBottom - yLogoTop - EnigmaLogoCache.Get().Height()) / 2, EnigmaLogoCache.Get(), + EnigmaLogoCache.Get().Color(1), Theme.Color(clrLogoBg)); + // draw title + osd->DrawRectangle(xTitleLeft, yTitleTop, xTitleRight - 1, yTitleBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawRectangle(xTitleLeft, yTitleDecoTop, xTitleRight - 1, yTitleDecoBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawEllipse(xTitleRight - Roundness, yTitleTop, xTitleRight - 1, yTitleTop + Roundness - 1, clrTransparent, -1); + // draw centered message text + osd->DrawRectangle(xMessageLeft, yMessageTop, xMessageRight - 1, yMessageBottom - 1, clrTransparent); + osd->DrawRectangle(xMessageLeft, yMessageTop, xMessageRight - 1, + yMessageBottom - 1, Theme.Color(clrMessageBorder)); + osd->DrawText(xMessageLeft, yMessageTop + 2 * SmallGap, Text, + Theme.Color(clrMessageStatusFg + 2 * Type), + Theme.Color(clrMessageStatusBg + 2 * Type), + cFont::GetFont(fontOsd), + xMessageRight - xMessageLeft, + yMessageBottom - 2 * SmallGap - yMessageTop - 2 * SmallGap, taCenter); + // draw bottom + osd->DrawRectangle(xBottomLeft, yBottomTop, xBottomRight - 1, yBottomBottom - 1, Theme.Color(clrBottomBg)); + osd->DrawEllipse(xBottomRight - Roundness, yBottomBottom - Roundness, + xBottomRight - 1, yBottomBottom - 1, clrTransparent, -4); +} + +void cSkinEnigmaDisplayMessage::Flush(void) +{ + osd->Flush(); +} + +// --- cSkinEnigma ---------------------------------------------------------- + +cSkinEnigma::cSkinEnigma() : cSkin("EnigmaNG", &::Theme) +{} + +const char *cSkinEnigma::Description(void) +{ + return tr("EnigmaNG"); +} + +cSkinDisplayChannel *cSkinEnigma::DisplayChannel(bool WithInfo) +{ + return new cSkinEnigmaDisplayChannel(WithInfo); +} + +cSkinDisplayMenu *cSkinEnigma::DisplayMenu(void) +{ + return new cSkinEnigmaDisplayMenu; +} + +cSkinDisplayReplay *cSkinEnigma::DisplayReplay(bool ModeOnly) +{ + return new cSkinEnigmaDisplayReplay(ModeOnly); +} + +cSkinDisplayVolume *cSkinEnigma::DisplayVolume(void) +{ + return new cSkinEnigmaDisplayVolume; +} + +cSkinDisplayTracks *cSkinEnigma::DisplayTracks(const char *Title, int NumTracks, const char *const *Tracks) +{ + return new cSkinEnigmaDisplayTracks(Title, NumTracks, Tracks); +} + +cSkinDisplayMessage *cSkinEnigma::DisplayMessage(void) +{ + return new cSkinEnigmaDisplayMessage; +} + +// vim:et:sw=2:ts=2: diff --git a/enigma.h b/enigma.h new file mode 100644 index 0000000..e9dc35a --- /dev/null +++ b/enigma.h @@ -0,0 +1,24 @@ +/* + * enigma.h: The 'EnigmaNG' VDR skin + * + */ + +#ifndef __ENIGMA_H +#define __ENIGMA_H + +#include <vdr/skins.h> +#include <vdr/skinclassic.h> + +class cSkinEnigma : public cSkin { +public: + cSkinEnigma(); + virtual const char *Description(void); + virtual cSkinDisplayChannel *DisplayChannel(bool WithInfo); + virtual cSkinDisplayMenu *DisplayMenu(void); + virtual cSkinDisplayReplay *DisplayReplay(bool ModeOnly); + virtual cSkinDisplayVolume *DisplayVolume(void); + virtual cSkinDisplayTracks *DisplayTracks(const char *Title, int NumTracks, const char * const *Tracks); + virtual cSkinDisplayMessage *DisplayMessage(void); + }; + +#endif //__ENIGMA_H @@ -0,0 +1,804 @@ +/* + * i18n.c: The 'EnigmaNG' VDR skin + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include "i18n.h" + +const tI18nPhrase Phrases[] = { + {"EnigmaNG skin", // English + "EnigmaNG Oberfläche", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Skin EnigmaNG", // Français + "", // Norsk + "EnigmaNG-ulkoasu", // suomi (Finnish) + "Skin EnigmaNG", // Polski + "Piel EnigmaNG", // Español + "", // ÅëëçíéêÜ (Greek) + "EnigmaNG-temat", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "EnigmaNG kest", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"EnigmaNG", // English + "EnigmaNG", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "EnigmaNG", // Français + "", // Norsk + "EnigmaNG", // suomi (Finnish) + "EnigmaNG", // Polski + "EnigmaNG", // Español + "", // ÅëëçíéêÜ (Greek) + "EnigmaNG", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "EnigmaNG", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"min", // English + "min", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "min", // Français + "", // Norsk + "min", // suomi (Finnish) + "min", // Polski + "min", // Español + "", // ÅëëçíéêÜ (Greek) + "min", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "min", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Stereo", // English + "Stereo", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Stéréo", // Français + "", // Norsk + "Stereo", // suomi (Finnish) + "Stereo", // Polski + "Estereo", // Español + "", // ÅëëçíéêÜ (Greek) + "Stereo", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "Stereo", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Left", // English + "Links", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Gauche", // Français + "", // Norsk + "Vasen", // suomi (Finnish) + "W lewo", // Polski + "Izquierda", // Español + "", // ÅëëçíéêÜ (Greek) + "Vänster", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "Vasak", // Eesti + "", // Danske + "", // Èesky (Czech) + }, + {"Right", // English + "Rechts", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Droite", // Français + "", // Norsk + "Oikea", // suomi (Finnish) + "W prawo", // Polski + "Derecha", // Español + "", // ÅëëçíéêÜ (Greek) + "Höger", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "Parem", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Mute", // English + "Stumm", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Muet", // Français + "", // Norsk + "Mykistetty", // suomi (Finnish) + "Wycisz", // Polski + "Silenciar", // Español + "", // ÅëëçíéêÜ (Greek) + "Dämpa", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "Hääletu", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Volume", // English + "Lautstärke", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Volume", // Français + "", // Norsk + "Äänenvoimakkuus", // suomi (Finnish) + "G³o¶no¶æ", // Polski + "Volumen", // Español + "", // ÅëëçíéêÜ (Greek) + "Volym", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "Helitugevus", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Show channel logos", // English + "Kanal-Logos anzeigen", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Afficher logo des chaînes", // Français + "", // Norsk + "Näytä kanavalogot", // suomi (Finnish) + "Pokazuj logo kana³u", // Polski + "Mostrar los logos de los canales", // Español + "", // ÅëëçíéêÜ (Greek) + "Visa kanallogotyper", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "Kanalilogo näitamine", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Identify channel by", // English + "Kanal-Identifikation durch", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Identifier chaîne par", // Français + "", // Norsk + "Tunnista kanava", // suomi (Finnish) + "Rozpoznaj kana³ po", // Polski + "Identificar el canal por...", // Español + "", // ÅëëçíéêÜ (Greek) + "Identifiera kanal med", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "Kanali tuvastamise meetod", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"name", // English + "Name", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Nom", // Français + "", // Norsk + "nimestä", // suomi (Finnish) + "nazwa", // Polski + "Nombre", // Español + "", // ÅëëçíéêÜ (Greek) + "namn", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "nimi", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"data", // English + "ID", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Donnée", // Français + "", // Norsk + "tiedoista", // suomi (Finnish) + "parametry", // Polski + "Datos", // Español + "", // ÅëëçíéêÜ (Greek) + "data", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "andmed", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Channel logo cache size", // English + "Größe des Logo-Caches", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Taille du cache des logo", // Français + "", // Norsk + "Välimuistin koko kanavalogoille", // suomi (Finnish) + "Rozmiar cache dla logo kana³u", // Polski + "Tamaño de la cache de los logos", // Español + "", // ÅëëçíéêÜ (Greek) + "Cachestorlek för logotyper", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "Kanalilogo vahemälu suurus", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Button$Flush cache", // English + "Cache leeren", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Supprimer cache", // Français + "", // Norsk + "Tyhjennä", // suomi (Finnish) + "Opró¿nij cache", // Polski + "Limpiar la cache", // Español + "", // ÅëëçíéêÜ (Greek) + "Töm cachen", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "Tühjenda", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Flushing channel logo cache...", // English + "Logo-Cache wird geleert...", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Suppression du cache...", // Français + "", // Norsk + "Tyhjennetään välimuistia...", // suomi (Finnish) + "Opró¿niam cache logo kana³u...", // Polski + "Limpiando la cache...", // Español + "", // ÅëëçíéêÜ (Greek) + "Tömmer cachen...", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "Kanalilogo vahemälu tühjendamine...", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Languages", // English + "Sprachen", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Langues", // Français + "", // Norsk + "Kielet", // suomi (Finnish) + "Jêzyki", // Polski + "Lenguajes", // Español + "", // ÅëëçíéêÜ (Greek) + "Språk", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "Keeled", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Auxiliary information", // English + "Zusatzinfo", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Information auxiliare", // Français + "", // Norsk + "Lisätiedot", // suomi (Finnish) + "Informacje pomocnicze", // Polski + "Información auxiliar", // Español + "", // ÅëëçíéêÜ (Greek) + "Extrainformation", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "Lisainfo", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Show auxiliary information", // English + "Zusatzinfo anzeigen", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Afficher information auxiliaire", // Français + "", // Norsk + "Näytä lisätiedot", // suomi (Finnish) + "Pokazuj informacje pomocnicze", // Polski + "Mostrar información auxiliar", // Español + "", // ÅëëçíéêÜ (Greek) + "Visa extrainformation", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "Lisainfo näitamine", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Show remaining/elapsed time", // English + "Zeige abgel./restl. Zeit", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "", // Français + "", // Norsk + "", // suomi (Finnish) + "", // Polski + "", // Español + "", // ÅëëçíéêÜ (Greek) + "", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"remaining", // English + "restliche", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "", // Français + "", // Norsk + "", // suomi (Finnish) + "", // Polski + "", // Español + "", // ÅëëçíéêÜ (Greek) + "", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"elapsed", // English + "abgelaufene", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "", // Français + "", // Norsk + "", // suomi (Finnish) + "", // Polski + "", // Español + "", // ÅëëçíéêÜ (Greek) + "", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Show info area in main menu", // English + "Infobereich im Hauptmenü", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "", // Français + "", // Norsk + "", // suomi (Finnish) + "", // Polski + "", // Español + "", // ÅëëçíéêÜ (Greek) + "", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Show marker in lists", // English + "Symbol vor Listeneinträgen", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "", // Français + "", // Norsk + "", // suomi (Finnish) + "", // Polski + "", // Español + "", // ÅëëçíéêÜ (Greek) + "", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Show progressbar", // English + "Fortschrittbalken anzeigen", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Afficher barre de progression", // Français + "", // Norsk + "Näytä aikajana", // suomi (Finnish) + "Pokazuj pasek postêpu", // Polski + "Mostrar barra de progreso", // Español + "", // ÅëëçíéêÜ (Greek) + "Visa framsteg", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "Edenemisriba näitamine", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Show symbols", // English + "Symbole anzeigen", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "Afficher symbole", // Français + "", // Norsk + "Näytä symbolit", // suomi (Finnish) + "Pokazuj symbole", // Polski + "Mostrar símbolos", // Español + "", // ÅëëçíéêÜ (Greek) + "Visa symboler", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "Sümbolite näitamine", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Show VPS", // English + "VPS anzeigen", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "", // Français + "", // Norsk + "", // suomi (Finnish) + "", // Polski + "", // Español + "", // ÅëëçíéêÜ (Greek) + "", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"WARNING", // English + "WARNUNG", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "", // Français + "", // Norsk + "", // suomi (Finnish) + "", // Polski + "", // Español + "", // ÅëëçíéêÜ (Greek) + "", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Timer conflict", // English + "Timerkonflikt", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "", // Français + "", // Norsk + "", // suomi (Finnish) + "", // Polski + "", // Español + "", // ÅëëçíéêÜ (Greek) + "", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Timer conflicts", // English + "Timerkonflikte", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "", // Français + "", // Norsk + "", // suomi (Finnish) + "", // Polski + "", // Español + "", // ÅëëçíéêÜ (Greek) + "", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"TIMERS", // English + "TIMER", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "", // Français + "", // Norsk + "", // suomi (Finnish) + "", // Polski + "", // Español + "", // ÅëëçíéêÜ (Greek) + "", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"RERUNS OF THIS SHOW", // English + "WIEDERHOLUNGEN", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "", // Français + "", // Norsk + "", // suomi (Finnish) + "", // Polski + "", // Español + "", // ÅëëçíéêÜ (Greek) + "", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"*** Invalid Channel ***", // English + "*** Ungültiger Kanal ***", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "", // Français + "", // Norsk + "", // suomi (Finnish) + "", // Polski + "", // Español + "", // ÅëëçíéêÜ (Greek) + "", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Size", // English + "Größe", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "", // Français + "", // Norsk + "", // suomi (Finnish) + "", // Polski + "", // Español + "", // ÅëëçíéêÜ (Greek) + "", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Mute", // English + "Stumm", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "", // Français + "", // Norsk + "", // suomi (Finnish) + "", // Polski + "", // Español + "", // ÅëëçíéêÜ (Greek) + "", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Volume", // English + "Lautstärke", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "", // Français + "", // Norsk + "", // suomi (Finnish) + "", // Polski + "", // Español + "", // ÅëëçíéêÜ (Greek) + "", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {"Show symbols in lists", // English + "Symbole in Listen", // Deutsch + "", // Slovenski + "", // Italiano + "", // Nederlands + "", // Português + "", // Français + "", // Norsk + "", // suomi (Finnish) + "", // Polski + "", // Español + "", // ÅëëçíéêÜ (Greek) + "", // Svenska + "", // Românã + "", // Magyar + "", // Català + "", // ÀãááÚØÙ (Russian) + "", // Hrvatski + "", // Eesti + "", // Dansk + "", // Èesky (Czech) + }, + {NULL} +}; @@ -0,0 +1,15 @@ +/* + * i18n.h: The 'EnigmaNG' VDR skin + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef __ENIGMA_I18N_H +#define __ENIGMA_I18N_H + +#include <vdr/i18n.h> + +extern const tI18nPhrase Phrases[]; + +#endif // __ENIGMA_I18N_H @@ -0,0 +1,139 @@ +/* + * logo.c: The 'EnigmaNG' VDR skin + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include "common.h" +#include "config.h" +#include "logo.h" +#include <vdr/tools.h> +#include <vdr/plugin.h> + +#undef debug +#define debug(x...) ; + +cEnigmaLogoCache EnigmaLogoCache(0); + +cEnigmaLogoCache::cEnigmaLogoCache(unsigned int cacheSizeP) :cacheSizeM(cacheSizeP), bitmapM(NULL) +{ } + +cEnigmaLogoCache::~cEnigmaLogoCache() +{ + // let's flush the cache + Flush(); +} + +bool cEnigmaLogoCache::Resize(unsigned int cacheSizeP) +{ + debug("cPluginSkinEnigma::Resize(%d)\n", cacheSizeP); + // flush cache only if it's smaller than before + if (cacheSizeP < cacheSizeM) { + Flush(); + } + // resize current cache + cacheSizeM = cacheSizeP; + return true; +} + +bool cEnigmaLogoCache::Load(const char *fileNameP) +{ + debug("cPluginSkinEnigma::Load(%s)\n", fileNameP); + // does the logo exist already in map + std::map < std::string, cBitmap * >::iterator i = cacheMapM.find(fileNameP); + if (i != cacheMapM.end()) { + // yes - cache hit! + debug("cPluginSkinEnigma::Load() CACHE HIT!\n"); + // check if logo really exist + if (i->second == NULL) { + debug("cPluginSkinEnigma::Load() EMPTY\n"); + // empty logo in cache + return false; + } + bitmapM = i->second; + } else { + // no - cache miss! + debug("cPluginSkinEnigma::Load() CACHE MISS!\n"); + // try to load xpm logo + LoadXpm(fileNameP); + // check if cache is active + if (cacheSizeM) { + // update map + if (cacheMapM.size() >= cacheSizeM) { + // cache full - remove first + debug("cPluginSkinEnigma::Load() DELETE\n"); + if (cacheMapM.begin()->second != NULL) { + // logo exists - delete it + cBitmap *bmp = cacheMapM.begin()->second; + DELETENULL(bmp); + } + // erase item + cacheMapM.erase(cacheMapM.begin()); + } + // insert logo into map + debug("cPluginSkinEnigma::Load() INSERT(%s)\n", fileNameP); + cacheMapM.insert(std::make_pair(fileNameP, bitmapM)); + } + // check if logo really exist + if (bitmapM == NULL) { + debug("cPluginSkinEnigma::Load() EMPTY\n"); + // empty logo in cache + return false; + } + } + return true; +} + +cBitmap & cEnigmaLogoCache::Get(void) +{ + return *bitmapM; +} + +bool cEnigmaLogoCache::LoadXpm(const char *fileNameP) +{ + struct stat stbuf; + char *filename; + cBitmap *bmp = new cBitmap(1, 1, 1); + + // create absolute filename + asprintf(&filename, "%s/%s.xpm", EnigmaConfig.GetLogoDir(), fileNameP); + debug("cPluginSkinEnigma::LoadXpm(%s)\n", filename); + // check validity + if ((stat(filename, &stbuf) == 0) && bmp->LoadXpm(filename) + /*TODO? && (bmp->Width() == ChannelLogoWidth) + && (bmp->Height() == ChannelLogoHeight)*/) { + debug("cPluginSkinEnigma::LoadXpm() LOGO FOUND\n"); + // assign bitmap + bitmapM = bmp; + free(filename); + return true; + } + // no xpm logo found - delete bitmap + printf("NOT FOUND: %s\n", filename); + debug("cPluginSkinEnigma::LoadXpm() LOGO NOT FOUND\n"); + delete bmp; + bitmapM = NULL; + free(filename); + return false; +} + +bool cEnigmaLogoCache::Flush(void) +{ + debug("cPluginSkinEnigma::Flush()\n"); + // check if map is empty + if (!cacheMapM.empty()) { + debug("cPluginSkinEnigma::Flush() NON-EMPTY\n"); + // delete bitmaps and clear map + for (std::map < std::string, cBitmap * >::iterator i = cacheMapM.begin(); + i != cacheMapM.end(); ++i) { + cBitmap *bmp = i->second; + if (bmp) + DELETENULL(bmp); + cacheMapM.erase(i); + } + // nullify bitmap pointer + bitmapM = NULL; + } + return true; +} @@ -0,0 +1,35 @@ +/* + * logo.h: The 'EnigmaNG' VDR skin + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef __SKINENIGMA_LOGO_H +#define __SKINENIGMA_LOGO_H + +#include <map> +#include <string> +#include <vdr/osd.h> + +#define ChannelLogoWidth 80 +#define ChannelLogoHeight 80 + +class cEnigmaLogoCache { +private: + unsigned int cacheSizeM; + cBitmap *bitmapM; + std::map<std::string, cBitmap*> cacheMapM; + bool LoadXpm(const char *fileNameP); +public: + cEnigmaLogoCache(unsigned int cacheSizeP); + ~cEnigmaLogoCache(); + bool Resize(unsigned int cacheSizeP); + bool Load(const char *fileNameP); + cBitmap& Get(void); + bool Flush(void); +}; + +extern cEnigmaLogoCache EnigmaLogoCache; + +#endif // __SKINENIGMA_LOGO_H diff --git a/patches/vdr-1.4.5-no_trans_found.diff b/patches/vdr-1.4.5-no_trans_found.diff new file mode 100644 index 0000000..8e890cc --- /dev/null +++ b/patches/vdr-1.4.5-no_trans_found.diff @@ -0,0 +1,11 @@ +--- i18n.c.orig 2007-01-31 11:54:21.000000000 +0100 ++++ i18n.c 2007-01-31 11:54:40.000000000 +0100 +@@ -6201,7 +6201,7 @@ + } + p = Phrases; + } +- esyslog("%s%sno translation found for '%s' in language %d (%s)", Plugin ? Plugin : "", Plugin ? ": " : "", s, Setup.OSDLanguage, Phrases[0][Setup.OSDLanguage]); ++//AM esyslog("%s%sno translation found for '%s' in language %d (%s)", Plugin ? Plugin : "", Plugin ? ": " : "", s, Setup.OSDLanguage, Phrases[0][Setup.OSDLanguage]); + } + const char *p = strchr(s, '$'); + return p ? p + 1 : s; diff --git a/patches/vdr-1.4.5-no_trans_found.txt b/patches/vdr-1.4.5-no_trans_found.txt new file mode 100644 index 0000000..279e6d4 --- /dev/null +++ b/patches/vdr-1.4.5-no_trans_found.txt @@ -0,0 +1,26 @@ +WHO +--- +Andreas Mair (andreas AT vdr DASH developer DOT org) + +WHAT +----- +This patch removes the "no translation found for" warnings in the VDR log. + +WHY +---- +This might be useful if you have enabled the symbols in the main menu +because in that case EnigmaNG has to compare every main menu entry with +a localized text to find out what symbol has to be shown. + +FOR +--- +This patch has been tested with vanilla VDR 1.4.5 but should apply to other +releases too. Anyway, it's not hard to fix reject... + +HOW +--- +Change to VDR source directory and run +vdr-1.4.5$ patch <PLUGINS/src/skinenigmang/patches/vdr-1.4.5-no_trans_found.diff + + +2007-01-31, Andreas Mair diff --git a/skinenigmang.c b/skinenigmang.c new file mode 100644 index 0000000..e2a6728 --- /dev/null +++ b/skinenigmang.c @@ -0,0 +1,275 @@ +/* + * enigma.c: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include "common.h" +#include "config.h" +#include "i18n.h" +#include "logo.h" +#include "enigma.h" +#include "status.h" +#include <getopt.h> +#include <vdr/plugin.h> + +#if defined(APIVERSNUM) && APIVERSNUM < 10400 +#error "VDR-1.4.0 API version or greater is required!" +#endif + +static const char VERSION[] = "0.0.1"; +static const char DESCRIPTION[] = "EnigmaNG skin"; + +class cPluginSkinEnigma : public cPlugin { +public: + cPluginSkinEnigma(void); + virtual ~cPluginSkinEnigma(); + virtual const char *Version(void) { + return VERSION; + } virtual const char *Description(void) { + return tr(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) { + return NULL; + } + virtual const char *MainMenuEntry(void) { + return NULL; + } + 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); +}; + +class cPluginSkinEnigmaSetup : public cMenuSetupPage { +private: + cEnigmaConfig data; + virtual void Setup(void); +protected: + virtual eOSState ProcessKey(eKeys Key); + virtual void Store(void); +public: + cPluginSkinEnigmaSetup(void); +}; + +cPluginSkinEnigma::cPluginSkinEnigma(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! + debug("cPluginSkinEnigma()\n"); +} + +cPluginSkinEnigma::~cPluginSkinEnigma() +{ + // clean up after yourself! +} + +const char *cPluginSkinEnigma::CommandLineHelp(void) +{ + // return a string that describes all known command line options. + return NULL; +} + +bool cPluginSkinEnigma::ProcessArgs(int argc, char *argv[]) +{ + // implement command line argument processing here if applicable. + return true; +} + +bool cPluginSkinEnigma::Initialize(void) +{ + // initialize any background activities the plugin shall perform. + debug("cPluginSkinEnigma::Initialize()\n"); + return true; +} + +bool cPluginSkinEnigma::Start(void) +{ + // start any background activities the plugin shall perform. + debug("cPluginSkinEnigma::Start()\n"); + RegisterI18n(Phrases); + // set logo directory + EnigmaConfig.SetLogoDir(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N)); + // resize logo cache + EnigmaLogoCache.Resize(EnigmaConfig.cacheSize); + // create status catcher + //new cEnigmaStatus; + // create skin + new cSkinEnigma; + return true; +} + +void cPluginSkinEnigma::Stop(void) +{ + // stop any background activities the plugin shall perform. + debug("cPluginSkinEnigma::Stop()\n"); +} + +void cPluginSkinEnigma::Housekeeping(void) +{ + // perform any cleanup or other regular tasks. +} + +cOsdObject *cPluginSkinEnigma::MainMenuAction(void) +{ + // perform the action when selected from the main VDR menu. + return NULL; +} + +cMenuSetupPage *cPluginSkinEnigma::SetupMenu(void) +{ + // return a setup menu in case the plugin supports one. + debug("cPluginSkinEnigma::SetupMenu()\n"); + return new cPluginSkinEnigmaSetup(); +} + +bool cPluginSkinEnigma::SetupParse(const char *Name, const char *Value) +{ + // parse your own setup parameters and store their values. + debug("cPluginSkinEnigma::SetupParse()\n"); + if (!strcasecmp(Name, "ShowAuxInfo")) + EnigmaConfig.showAuxInfo = atoi(Value); + else if (!strcasecmp(Name, "ShowProgressBar")) + EnigmaConfig.showProgressbar = atoi(Value); + else if (!strcasecmp(Name, "ShowRemaining")) + EnigmaConfig.showRemaining = atoi(Value); + else if (!strcasecmp(Name, "ShowListSymbols")) + EnigmaConfig.showListSymbols = atoi(Value); + else if (!strcasecmp(Name, "ShowSymbols")) + EnigmaConfig.showSymbols = atoi(Value); + else if (!strcasecmp(Name, "ShowLogo")) + EnigmaConfig.showLogo = atoi(Value); + else if (!strcasecmp(Name, "ShowInfo")) + EnigmaConfig.showInfo = atoi(Value); + else if (!strcasecmp(Name, "ShowMarker")) + EnigmaConfig.showMarker = atoi(Value); + else if (!strcasecmp(Name, "ShowVPS")) + EnigmaConfig.showVps = atoi(Value); + else if (!strcasecmp(Name, "CacheSize")) + EnigmaConfig.cacheSize = atoi(Value); + else if (!strcasecmp(Name, "UseChannelId")) + EnigmaConfig.useChannelId = atoi(Value); + else + return false; + + return true; +} + +bool cPluginSkinEnigma::Service(const char *Id, void *Data) +{ + // handle custom service requests from other plugins + return false; +} + +const char **cPluginSkinEnigma::SVDRPHelpPages(void) +{ + // return help text for SVDRP commands this plugin implements + return NULL; +} + +cString cPluginSkinEnigma::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode) +{ + // process SVDRP commands this plugin implements + return NULL; +} + +cPluginSkinEnigmaSetup::cPluginSkinEnigmaSetup(void) +{ + // create setup menu + debug("cPluginSkinEnigmaSetup()\n"); + data = EnigmaConfig; + Setup(); + SetHelp(tr("Button$Flush cache"), NULL, NULL, NULL); +} + +void cPluginSkinEnigmaSetup::Setup(void) +{ + // update setup display + int current = Current(); + + Clear(); + + Add(new cMenuEditBoolItem(tr("Show auxiliary information"), &data.showAuxInfo, + tr("top"), tr("bottom"))); + Add(new cMenuEditBoolItem(tr("Show remaining/elapsed time"), &data.showRemaining, + tr("elapsed"), tr("remaining"))); + Add(new cMenuEditBoolItem(tr("Show symbols in lists"), &data.showListSymbols, + tr("no"), tr("yes"))); + Add(new cMenuEditBoolItem(tr("Show progressbar"), &data.showProgressbar, + tr("no"), tr("yes"))); + Add(new cMenuEditBoolItem(tr("Show symbols"), &data.showSymbols, + tr("no"), tr("yes"))); + Add(new cMenuEditBoolItem(tr("Show info area in main menu"), &data.showInfo, + tr("no"), tr("yes"))); + Add(new cMenuEditBoolItem(tr("Show marker in lists"), &data.showMarker, + tr("no"), tr("yes"))); + Add(new cMenuEditBoolItem(tr("Show VPS"), &data.showVps, + tr("no"), tr("yes"))); + Add(new cMenuEditBoolItem(tr("Show channel logos"), &data.showLogo, + tr("no"), tr("yes"))); + if (data.showLogo) { + Add(new cMenuEditBoolItem(tr("Identify channel by"), &data.useChannelId, + tr("name"), tr("data"))); + } + if (data.showLogo || data.showSymbols) { + Add(new cMenuEditIntItem(tr("Channel logo cache size"), &data.cacheSize, + 0, 1000)); + } + + SetCurrent(Get(current)); + Display(); +} + +void cPluginSkinEnigmaSetup::Store(void) +{ + // store setup data + debug("cPluginSkinEnigmaSetup::Store()\n"); + EnigmaConfig = data; + SetupStore("ShowAuxInfo", EnigmaConfig.showAuxInfo); + SetupStore("ShowRemaining", EnigmaConfig.showRemaining); + SetupStore("ShowProgressBar", EnigmaConfig.showProgressbar); + SetupStore("ShowListSymbols", EnigmaConfig.showListSymbols); + SetupStore("ShowSymbols", EnigmaConfig.showSymbols); + SetupStore("ShowLogo", EnigmaConfig.showLogo); + SetupStore("ShowInfo", EnigmaConfig.showInfo); + SetupStore("ShowVPS", EnigmaConfig.showVps); + SetupStore("ShowMarker", EnigmaConfig.showMarker); + SetupStore("CacheSize", EnigmaConfig.cacheSize); + SetupStore("UseChannelId", EnigmaConfig.useChannelId); + // resize logo cache + EnigmaLogoCache.Resize(EnigmaConfig.cacheSize); +} + +eOSState cPluginSkinEnigmaSetup::ProcessKey(eKeys Key) +{ + // process key presses + int oldShowLogo = data.showLogo; + int oldShowSymbols = data.showSymbols; + + eOSState state = cMenuSetupPage::ProcessKey(Key); + if ((state == osUnknown) && (Key == kRed)) { + Skins.Message(mtInfo, tr("Flushing channel logo cache...")); + EnigmaLogoCache.Flush(); + Skins.Message(mtInfo, NULL); + state = osContinue; + } + if (Key != kNone && ((data.showLogo != oldShowLogo) || (data.showSymbols != oldShowSymbols))) { + Setup(); + } + + return state; +} + +VDRPLUGINCREATOR(cPluginSkinEnigma); // don't touch this! diff --git a/status.c b/status.c new file mode 100644 index 0000000..749aea2 --- /dev/null +++ b/status.c @@ -0,0 +1,116 @@ +/* + * status.c: The 'EnigmaNG' VDR skin. + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include "common.h" +#include "status.h" +#include <vdr/timers.h> +#include <vdr/plugin.h> +#include <vdr/menu.h> + +const char *ReplayNames[7] = + { "", "recordings", "mp3", "mplayer", "dvd", "vcd", "image" }; + +cEnigmaStatus EnigmaStatus; + +cEnigmaStatus::cEnigmaStatus(void) : mReplayMode(replayNone), + mReplayIsLoop(false), + mReplayIsShuffle(false) +{} + +void cEnigmaStatus::Replaying(const cControl * /*Control */ , const char *Name, + const char *FileName, bool On) +{ + debug("cEnigmaStatus::Replaying(%s)\n", Name); + + if (Name != NULL) { + mReplayMode = replayMPlayer; + if (strlen(Name) > 6 && Name[0] == '[' && Name[3] == ']' && Name[5] == '(') { + int i; + for (i = 6; Name[i]; ++i) { + if (Name[i] == ' ' && Name[i - 1] == ')') + break; + } + if (Name[i]) { // replaying mp3 + mReplayMode = replayMP3; + mReplayIsLoop = Name[1] == 'L'; + mReplayIsShuffle = Name[2] == 'S'; + } + } else if (FileName ? Recordings.GetByName(FileName) : NULL) { + mReplayMode = replayNormal; + } else if (strcmp(Name, "DVD") == 0) + mReplayMode = replayDVD; + else if (strcmp(Name, "VCD") == 0) + mReplayMode = replayVCD; + else if (access(Name, F_OK) == 0) + mReplayMode = replayMPlayer; + else if (strncmp(Name, "[image]", 7) == 0) + mReplayMode = replayImage; + else if (strlen(Name) > 7) { + int i, n; + for (i = 0, n = 0; Name[i]; ++i) { + if (Name[i] == ' ' && Name[i - 1] == ',' && ++n == 4) + break; + } + if (Name[i]) { // replaying DVD + mReplayMode = replayDVD; + } + } + } else { + mReplayMode = replayNone; + mReplayIsLoop = false; + mReplayIsShuffle = false; + } +} + +void cEnigmaStatus::Recording(const cDevice * Device, const char *Name, const char *FileName, bool On) +{ +} + +void cEnigmaStatus::OsdClear(void) +{ +} + +void cEnigmaStatus::OsdCurrentItem(const char *Text) +{ +} + +void cEnigmaStatus::OsdItem(const char *Text, int Index) +{ +} + +void cEnigmaStatus::UpdateActiveTimers(void) +{ + mTimers.Clear(); + Timers.IncBeingEdited(); + + for (cTimer * tim = Timers.First(); tim; tim = Timers.Next(tim)) { + if (tim->HasFlags(tfActive)) { + int i = 0; + cTimer dummy; + dummy = *tim; + + do { + mTimers.Add(new tTimer(&dummy)); + + if (!dummy.IsSingleEvent()) // add 4 additional rep. timer + { + int j = 0; + do { + j++; // just to avoid a endless loop + dummy.Skip(); + dummy.Matches(); // Refresh start- and end-time + } while (!dummy.DayMatches(dummy.StartTime()) && (j < 7)); + } + + i++; + } while (!dummy.IsSingleEvent() && i < 5); + } + } + + Timers.DecBeingEdited(); + mTimers.Sort(); +} diff --git a/status.h b/status.h new file mode 100644 index 0000000..9a6ad2a --- /dev/null +++ b/status.h @@ -0,0 +1,87 @@ +/* + * status.h: The 'EnigmaNG' VDR skin. + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef __SKINENIGMA_STATUS_H +#define __SKINENIGMA_STATUS_H + +#include "common.h" +#include <vdr/status.h> + +extern const char *ReplayNames[7]; + +struct tTimer : public cListObject { + time_t startTime; + time_t stopTime; + int startDay; + int channelNumber; + std::string channelName; + int priority; + bool isRecording; + std::string title; + + tTimer(cTimer *timer): startTime(timer->StartTime()), + stopTime(timer->StopTime()), + startDay(timer->GetMDay(startTime)), + channelNumber(timer->Channel()->Number()), + channelName(timer->Channel()->Name()), + priority(timer->Priority()), + isRecording(timer->Recording()), + title(timer->File()) + {} + + virtual int Compare(const cListObject & listObj) const { + tTimer *e = (tTimer *) & listObj; + int r = startTime - e->startTime; + if (r == 0) + r = e->priority - priority; + return r; + } + }; + +class cEnigmaStatus : public cStatus { +public: + enum eReplayMode { + replayNone, + replayNormal, + replayMP3, + replayMPlayer, + replayDVD, + replayVCD, + replayImage, + }; + + void UpdateActiveTimers(void); + cList<tTimer> mTimers; + +private: + eReplayMode mReplayMode; + bool mReplayIsLoop; + bool mReplayIsShuffle; + +protected: + virtual void Replaying(const cControl * Control, const char *Name, + const char *FileName, bool On); + virtual void Recording(const cDevice * Device, const char *Name, + const char *FileName, bool On); + virtual void OsdClear(void); + virtual void OsdCurrentItem(const char *Text); + virtual void OsdItem(const char *Text, int Index); + +public: + cEnigmaStatus(void); + + eReplayMode ReplayMode(void) const { + return mReplayMode; + } + const char* ReplayModeName(void) const { + return ReplayNames[mReplayMode]; + } +}; + +extern cEnigmaStatus EnigmaStatus; + +#endif // __SKINENIGMA_STATUS_H diff --git a/symbols/small/audio.xpm b/symbols/small/audio.xpm new file mode 100644 index 0000000..79b8609 --- /dev/null +++ b/symbols/small/audio.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * audio_xpm[] = { +"27 18 2 1", +" c None", +". c #B9C000", +" ", +" ", +" ....................... ", +" ....................... ", +" .. .. ", +" .. .... .... .. ", +" .. ...... ...... .. ", +" .. .. .. .. .. .. ", +" .. .. .. .. .. .. ", +" .. .. .. .. .. .. ", +" .. .. .. .. .. .. ", +" .. ...... ...... .. ", +" .. .... .... .. ", +" .. .. ", +" ....................... ", +" ....................... ", +" ", +" "}; diff --git a/symbols/small/audioleft.xpm b/symbols/small/audioleft.xpm new file mode 100644 index 0000000..282616a --- /dev/null +++ b/symbols/small/audioleft.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * audioleft_xpm[] = { +"27 18 2 1", +" c None", +". c #B9C000", +" ", +" ", +" ....................... ", +" ....................... ", +" .. .. ", +" .. .... .. ", +" .. ...... .. ", +" .. .. .. .. ", +" .. .. .. .. ", +" .. .. .. .. ", +" .. .. .. .. ", +" .. ...... .. ", +" .. .... .. ", +" .. .. ", +" ....................... ", +" ....................... ", +" ", +" "}; diff --git a/symbols/small/audioright.xpm b/symbols/small/audioright.xpm new file mode 100644 index 0000000..5f1cd04 --- /dev/null +++ b/symbols/small/audioright.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * audioright_xpm[] = { +"27 18 2 1", +" c None", +". c #B9C000", +" ", +" ", +" ....................... ", +" ....................... ", +" .. .. ", +" .. .... .. ", +" .. ...... .. ", +" .. .. .. .. ", +" .. .. .. .. ", +" .. .. .. .. ", +" .. .. .. .. ", +" .. ...... .. ", +" .. .... .. ", +" .. .. ", +" ....................... ", +" ....................... ", +" ", +" "}; diff --git a/symbols/small/dolbydigital.xpm b/symbols/small/dolbydigital.xpm new file mode 100644 index 0000000..c347169 --- /dev/null +++ b/symbols/small/dolbydigital.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * dolbydigital_xpm[] = { +"27 18 2 1", +" c None", +". c #B9C000", +" ", +" ", +" ........... ........... ", +" ........... ........... ", +" .. ..... ..... .. ", +" .. ... ... .. ", +" .. ... ... .. ", +" .. .. .. .. ", +" .. .. .. .. ", +" .. .. .. .. ", +" .. .. .. .. ", +" .. ... ... .. ", +" .. ... ... .. ", +" .. ..... ..... .. ", +" ........... ........... ", +" ........... ........... ", +" ", +" "}; diff --git a/symbols/small/encrypted.xpm b/symbols/small/encrypted.xpm new file mode 100644 index 0000000..73ca50c --- /dev/null +++ b/symbols/small/encrypted.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * encrypted_xpm[] = { +"27 18 2 1", +" c None", +". c #B9C000", +" ", +" ", +" ....................... ", +" ....................... ", +" .. .. ", +" .. .... .. ", +" .. ...... .. ", +" .. ............. .. .. ", +" .. ............. .. .. ", +" .. ..... ...... .. ", +" .. ....... .... .. ", +" .. ... ... .. ", +" .. .. ", +" .. .. ", +" ....................... ", +" ....................... ", +" ", +" "}; diff --git a/symbols/small/eventparttimer.xpm b/symbols/small/eventparttimer.xpm new file mode 100644 index 0000000..85d5c97 --- /dev/null +++ b/symbols/small/eventparttimer.xpm @@ -0,0 +1,27 @@ +/* XPM */ +static char * eventparttimer_xpm[] = { +"14 22 2 1", +". c #FFFFFF", +"+ c #000000", +"..............", +"..............", +"..............", +"......++......", +"......++......", +"..++......++..", +"..++......++..", +".........++...", +"........++....", +".......++.....", +"++....+++++.++", +"++....+++++.++", +"..............", +"..............", +"..............", +"..............", +"..............", +"..............", +"..............", +"..............", +"..............", +".............."}; diff --git a/symbols/small/eventparttimer.xpm.old b/symbols/small/eventparttimer.xpm.old new file mode 100644 index 0000000..d9df70e --- /dev/null +++ b/symbols/small/eventparttimer.xpm.old @@ -0,0 +1,27 @@ +/* XPM */ +static const char *const eventparttimer_xpm[] = { +"14 22 2 1", +". c #FFFFFF", +"+ c #000000", +"..............", +"..............", +"..............", +"..............", +"..............", +"......++......", +"..++..++..++..", +"..++.....+++..", +"........++....", +".......++.....", +".++...++++.++.", +".++...++++.++.", +"..............", +"..............", +"..++......++..", +"..++..++..++..", +"......++......", +"..............", +"..............", +"..............", +"..............", +".............."}; diff --git a/symbols/small/eventrecording.xpm b/symbols/small/eventrecording.xpm new file mode 100644 index 0000000..09a199b --- /dev/null +++ b/symbols/small/eventrecording.xpm @@ -0,0 +1,27 @@ +/* XPM */ +static const char *const eventrecording_xpm[] = { +"14 22 2 1", +". c #FFFFFF", +"+ c #000000", +"..............", +"..............", +"+++++.........", +"++++++........", +"++..++........", +"++..++........", +"++++++..++++..", +"+++++..++++++.", +"++++++.++..++.", +"++.+++.++.....", +"++..+++++.....", +"++.++++++.....", +"...++..++.....", +"...++..++..++.", +"...++++.+++++.", +"...++++.++++..", +"...++.........", +"...++.........", +"...+++++......", +"...+++++......", +"..............", +".............."}; diff --git a/symbols/small/eventrunning.xpm b/symbols/small/eventrunning.xpm new file mode 100644 index 0000000..6a4ed70 --- /dev/null +++ b/symbols/small/eventrunning.xpm @@ -0,0 +1,27 @@ +/* XPM */ +static const char *const eventrunning_xpm[] = { +"14 22 2 1", +". c #FFFFFF", +"+ c #000000", +"..............", +"..............", +"..+++.........", +".+++++........", +"++.++++.......", +"+++++++.......", +"...++++..++...", +".+++++..++++..", +"...++++++++++.", +".....++++..++.", +"..+...++++..+.", +".+++.++++++...", +"..+++++.++++..", +"...+++..+++++.", +"....+..++++++.", +"......+++.++..", +".....+++.+++..", +"......+++.+++.", +".......++..++.", +"........+...+.", +"..............", +".............."}; diff --git a/symbols/small/eventtimer.xpm b/symbols/small/eventtimer.xpm new file mode 100644 index 0000000..a76bbad --- /dev/null +++ b/symbols/small/eventtimer.xpm @@ -0,0 +1,27 @@ +/* XPM */ +static char * eventtimer_xpm[] = { +"14 22 2 1", +". c #FFFFFF", +"+ c #000000", +"..............", +"..............", +"..............", +"......++......", +"......++......", +"..++......++..", +"..++......++..", +".........++...", +"........++....", +".......++.....", +"++....+++++.++", +"++....+++++.++", +"..............", +"..............", +"..............", +"..++......++..", +"..++......++..", +"......++......", +"......++......", +"..............", +"..............", +".............."}; diff --git a/symbols/small/eventvps.xpm b/symbols/small/eventvps.xpm new file mode 100644 index 0000000..35a099f --- /dev/null +++ b/symbols/small/eventvps.xpm @@ -0,0 +1,27 @@ +/* XPM */ +static const char *const eventvps_xpm[] = { +"14 22 2 1", +". c #FFFFFF", +"+ c #000000", +"..............", +"..............", +"++...++.......", +"++...++.......", +"++...++.......", +"++...++..+++..", +"++...++.+++++.", +"++...+++++.++.", +"+++.+++++.....", +".+++++.+++....", +"..+++...+++...", +"...++++..+++..", +"..++++++..+++.", +"..++..++...++.", +"..++..+++..++.", +"..++++++++.++.", +"..+++++.+++++.", +"..++.....+++..", +"..++..........", +"..++..........", +"..............", +".............."}; diff --git a/symbols/small/radio.xpm b/symbols/small/radio.xpm new file mode 100644 index 0000000..329892e --- /dev/null +++ b/symbols/small/radio.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static const char *const radio_xpm[] = { +"27 18 2 1", +" c None", +"+ c #B9C000", +" ", +" +++++ ", +" +++++++++ ", +" +++++++++++ ", +" +++ ++++++ ", +" +++ ++++ ", +" +++ +++ ", +" +++ ++ ", +" +++ ", +" +++ ", +" +++ ", +" +++++++++ ", +" +++++++++++++ ", +" +++++++++++++++ ", +" +++++++++++++++ ", +" +++++++++++++ ", +" ++++++++++ ", +" "}; diff --git a/symbols/small/recording.xpm b/symbols/small/recording.xpm new file mode 100644 index 0000000..30c0198 --- /dev/null +++ b/symbols/small/recording.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * recording_xpm[] = { +"27 18 2 1", +" c None", +". c #B9C000", +" ", +" ", +" ....................... ", +" ....................... ", +" .. .. ", +" .. .... .... .... .. ", +" .. ..... .... ...... .. ", +" .. .. .. .. .. .. .. ", +" .. ..... ... .. .. ", +" .. .... ... .. .. ", +" .. .. .. .. .. .. .. ", +" .. .. .. .... ...... .. ", +" .. .. .. .... .... .. ", +" .. .. ", +" ....................... ", +" ....................... ", +" ", +" "}; diff --git a/symbols/small/recordingnew.xpm b/symbols/small/recordingnew.xpm new file mode 100644 index 0000000..11ce3b9 --- /dev/null +++ b/symbols/small/recordingnew.xpm @@ -0,0 +1,27 @@ +/* XPM */ +static const char *const recordingnew_xpm[] = { +"8 22 2 1", +". c #FFFFFF", +"+ c #000000", +"........", +"........", +"........", +"..++++..", +"..++++..", +".++++++.", +".++++++.", +".++++++.", +".++++++.", +"..++++..", +"..++++..", +"..++++..", +"..++++..", +"...++...", +"........", +"........", +"...++...", +"..++++..", +"..++++..", +"...++...", +"........", +"........"}; diff --git a/symbols/small/run.xpm b/symbols/small/run.xpm new file mode 100644 index 0000000..a26ce01 --- /dev/null +++ b/symbols/small/run.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * run_xpm[] = { +"27 18 2 1", +" c None", +". c #B9C000", +" ", +" ", +" ....................... ", +" ....................... ", +" .. .. ", +" .. .. .. ", +" .. .... .. ", +" .. ...... .. ", +" .. ........ .. ", +" .. ........ .. ", +" .. ...... .. ", +" .. .... .. ", +" .. .. .. ", +" .. .. ", +" ....................... ", +" ....................... ", +" ", +" "}; diff --git a/symbols/small/teletext.xpm b/symbols/small/teletext.xpm new file mode 100644 index 0000000..65a2fcd --- /dev/null +++ b/symbols/small/teletext.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * teletext_xpm[] = { +"27 18 2 1", +" c None", +". c #B9C000", +" ", +" ", +" ....................... ", +" ....................... ", +" .. .. ", +" .. .. ", +" .. ............... .. ", +" .. ............... .. ", +" .. .. ", +" .. .. ", +" .. ............... .. ", +" .. ............... .. ", +" .. .. ", +" .. .. ", +" ....................... ", +" ....................... ", +" ", +" "}; diff --git a/symbols/small/timer.xpm b/symbols/small/timer.xpm new file mode 100644 index 0000000..246dd10 --- /dev/null +++ b/symbols/small/timer.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * timer_xpm[] = { +"27 18 2 1", +" c None", +". c #B9C000", +" ", +" ", +" ....................... ", +" ....................... ", +" .. .. ", +" .. ...... .. .. .. .. ", +" .. ...... .. ... ... .. ", +" .. .. .. ....... .. ", +" .. .. .. ....... .. ", +" .. .. .. .. . .. .. ", +" .. .. .. .. .. .. ", +" .. .. .. .. .. .. ", +" .. .. .. .. .. .. ", +" .. .. ", +" ....................... ", +" ....................... ", +" ", +" "}; diff --git a/symbols/small/vps.xpm b/symbols/small/vps.xpm new file mode 100644 index 0000000..e97204b --- /dev/null +++ b/symbols/small/vps.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * vps_xpm[] = { +"27 18 2 1", +" c None", +". c #B9C000", +" ", +" ", +" ....................... ", +" ....................... ", +" .. .. ", +" .. .. .. .... .... .. ", +" .. .. .. ..... ..... .. ", +" .. .. .. .. .. .. .. ", +" .. .. .. ..... .... .. ", +" .. .. .. .... .... .. ", +" .. ..... .. .. .. ", +" .. ... .. ..... .. ", +" .. . .. .... .. ", +" .. .. ", +" ....................... ", +" ....................... ", +" ", +" "}; diff --git a/themes/EnigmaNG-AppleGreen.theme b/themes/EnigmaNG-AppleGreen.theme new file mode 100644 index 0000000..cce1922 --- /dev/null +++ b/themes/EnigmaNG-AppleGreen.theme @@ -0,0 +1,54 @@ +Description = AppleGreen +clrBackground = E5DFFADE +clrAltBackground = E5BFE6C2 +clrTitleBg = E547BC41 +clrLogoBg = E59AE38E +clrBottomBg = E52A7D1D +clrBotProgBarBg = FF808080 +clrBotProgBarFg = FFFFFFFF +clrMenuTxtFg = FF000000 +clrTitleFg = FFFFFFFF +clrTitleShadow = FF000000 +clrSymbolActive = FF41C400 +clrSymbolInactive = FF808080 +clrSymbolRecord = FFC40000 +clrSymbolTimerActive = FF98C400 +clrSymbolVpsActive = FFC4C400 +clrSymbolRecActive = FFC40000 +clrSymbolRunActive = FF00C400 +clrButtonRedFg = FFFFFFFF +clrButtonRedBg = E5C40000 +clrButtonGreenFg = FFFFFFFF +clrButtonGreenBg = E500C400 +clrButtonYellowFg = FFFFFFFF +clrButtonYellowBg = E5C4C400 +clrButtonBlueFg = FFFFFFFF +clrButtonBlueBg = E50000C4 +clrMessageBorder = E5FF0000 +clrMessageStatusFg = FF000000 +clrMessageStatusBg = E5D7FFD1 +clrMessageInfoFg = FF000000 +clrMessageInfoBg = E5D3FFD1 +clrMessageWarningFg = FF000000 +clrMessageWarningBg = E5D7FFD1 +clrMessageErrorFg = FF000000 +clrMessageErrorBg = E5FFD1D1 +clrVolumeBar = FF33CC33 +clrVolumeBarMute = FFFF0000 +clrMenuItemCurrentFg = FF000000 +clrMenuHighlight = E5BFE6C2 +clrMenuItemSelectableFg = FF000000 +clrMenuItemNotSelectableFg = E54158BC +clrMenuEventTitle = FF000000 +clrMenuEventShortText = FF1D2F7D +clrMenuScrollbarTotal = E5B2BBD6 +clrMenuScrollbarShown = FF000000 +clrReplayCurrent = FF2D7D1D +clrReplayTotal = FF2D7D1D +clrReplayModeJump = FF2D7D1D +clrReplayBarAreaBg = E5E6FADE +clrReplayProgressSeen = FF9AE38E +clrReplayProgressRest = E5E6FADE +clrReplayProgressSelected = FF61BC41 +clrReplayProgressMark = FF61BC41 +clrReplayProgressCurrent = FF61BC41 diff --git a/themes/EnigmaNG-DarkBlue.theme b/themes/EnigmaNG-DarkBlue.theme new file mode 100644 index 0000000..923e5ad --- /dev/null +++ b/themes/EnigmaNG-DarkBlue.theme @@ -0,0 +1,54 @@ +Description = DarkBlue +clrBackground = B82B2B3C +clrAltBackground = B8171720 +clrTitleBg = B84158BC +clrLogoBg = B8566489 +clrBottomBg = B81D2F7D +clrBotProgBarBg = FF808080 +clrBotProgBarFg = FFFFFFFF +clrMenuTxtFg = FFFFFFFF +clrTitleFg = FFFFFFFF +clrTitleShadow = FF000000 +clrSymbolActive = FFC4C400 +clrSymbolInactive = FF808080 +clrSymbolRecord = FFC40000 +clrSymbolTimerActive = FF0000C4 +clrSymbolVpsActive = FFC4C400 +clrSymbolRecActive = FFC40000 +clrSymbolRunActive = FF00C400 +clrButtonRedFg = FFFFFFFF +clrButtonRedBg = B8C40000 +clrButtonGreenFg = FFFFFFFF +clrButtonGreenBg = B800C400 +clrButtonYellowFg = FFFFFFFF +clrButtonYellowBg = B8C4C400 +clrButtonBlueFg = FFFFFFFF +clrButtonBlueBg = B80000C4 +clrMessageBorder = B8FF0000 +clrMessageStatusFg = FFFFFFFF +clrMessageStatusBg = B86B7F68 +clrMessageInfoFg = FFFFFFFF +clrMessageInfoBg = B8696F80 +clrMessageWarningFg = FFFFFFFF +clrMessageWarningBg = B87D8069 +clrMessageErrorFg = FFFFFFFF +clrMessageErrorBg = B87F6868 +clrVolumeBar = FF33CC33 +clrVolumeBarMute = FFFF0000 +clrMenuItemCurrentFg = FFFFFFFF +clrMenuHighlight = B8171720 +clrMenuItemSelectableFg = FFFFFFFF +clrMenuItemNotSelectableFg = B88EA4E3 +clrMenuEventTitle = FFFFFFFF +clrMenuEventShortText = FFC3CAFA +clrMenuScrollbarTotal = B8DEE5FA +clrMenuScrollbarShown = FF000000 +clrReplayCurrent = FFC3CAFA +clrReplayTotal = FFC3CAFA +clrReplayModeJump = FFC3CAFA +clrReplayBarAreaBg = B82B2B3C +clrReplayProgressSeen = FF24FC24 +clrReplayProgressRest = B8DEE5FA +clrReplayProgressSelected = FFFFFFFF +clrReplayProgressMark = FFFFFFFF +clrReplayProgressCurrent = FFFF0000 diff --git a/themes/EnigmaNG-WineRed.theme b/themes/EnigmaNG-WineRed.theme new file mode 100644 index 0000000..f5e4b6c --- /dev/null +++ b/themes/EnigmaNG-WineRed.theme @@ -0,0 +1,54 @@ +Description = WineRed +clrBackground = E5FADEDE +clrAltBackground = E5E6BFBF +clrTitleBg = E5BC5241 +clrLogoBg = E5E38E8E +clrBottomBg = E57D1D1D +clrBotProgBarBg = FF808080 +clrBotProgBarFg = FFFFFFFF +clrMenuTxtFg = FF000000 +clrTitleFg = FFFFFFFF +clrTitleShadow = FF000000 +clrSymbolActive = FF20C400 +clrSymbolInactive = FF808080 +clrSymbolRecord = FFC40000 +clrSymbolTimerActive = FF0000C4 +clrSymbolVpsActive = FFC4C400 +clrSymbolRecActive = FFC40000 +clrSymbolRunActive = FF00C400 +clrButtonRedFg = FFFFFFFF +clrButtonRedBg = E5C40000 +clrButtonGreenFg = FFFFFFFF +clrButtonGreenBg = E500C400 +clrButtonYellowFg = FFFFFFFF +clrButtonYellowBg = E5C4C400 +clrButtonBlueFg = FFFFFFFF +clrButtonBlueBg = E50000C4 +clrMessageBorder = E5FF0000 +clrMessageStatusFg = FF000000 +clrMessageStatusBg = E5D7FFD1 +clrMessageInfoFg = FF000000 +clrMessageInfoBg = E5FFD1D1 +clrMessageWarningFg = FF000000 +clrMessageWarningBg = E5F9FFD1 +clrMessageErrorFg = FF000000 +clrMessageErrorBg = E5FFD1D1 +clrVolumeBar = FF33CC33 +clrVolumeBarMute = FFFF0000 +clrMenuItemCurrentFg = FF000000 +clrMenuHighlight = E5E6BFBF +clrMenuItemSelectableFg = FF000000 +clrMenuItemNotSelectableFg = E54158BC +clrMenuEventTitle = FF000000 +clrMenuEventShortText = FF1D2F7D +clrMenuScrollbarTotal = E5B2BBD6 +clrMenuScrollbarShown = FF000000 +clrReplayCurrent = FF7D1D1D +clrReplayTotal = FF7D1D1D +clrReplayModeJump = FF7D1D1D +clrReplayBarAreaBg = E5FADEDE +clrReplayProgressSeen = FFE38E8E +clrReplayProgressRest = E5FADEDE +clrReplayProgressSelected = FFBC4141 +clrReplayProgressMark = FFBC4141 +clrReplayProgressCurrent = FFFF0000 diff --git a/themes/EnigmaNG-WomansLike.theme b/themes/EnigmaNG-WomansLike.theme new file mode 100644 index 0000000..4eabbbc --- /dev/null +++ b/themes/EnigmaNG-WomansLike.theme @@ -0,0 +1,54 @@ +Description = WomansLike +clrBackground = E5DADEFA +clrAltBackground = E5E6BFE3 +clrTitleBg = E5BC41B2 +clrLogoBg = E5E38EDC +clrBottomBg = E57D1D75 +clrBotProgBarBg = FF808080 +clrBotProgBarFg = FFFFFFFF +clrMenuTxtFg = FF000000 +clrTitleFg = FFFFFFFF +clrTitleShadow = FF000000 +clrSymbolActive = FF41C400 +clrSymbolInactive = FF808080 +clrSymbolRecord = FFC40000 +clrSymbolTimerActive = FF0000C4 +clrSymbolVpsActive = FFC4C400 +clrSymbolRecActive = FFC40000 +clrSymbolRunActive = FF00C400 +clrButtonRedFg = FFFFFFFF +clrButtonRedBg = E5C40000 +clrButtonGreenFg = FFFFFFFF +clrButtonGreenBg = E500C400 +clrButtonYellowFg = FFFFFFFF +clrButtonYellowBg = E5C4C400 +clrButtonBlueFg = FFFFFFFF +clrButtonBlueBg = E50000C4 +clrMessageBorder = E5FF0000 +clrMessageStatusFg = FF000000 +clrMessageStatusBg = E5D7FFD1 +clrMessageInfoFg = FF000000 +clrMessageInfoBg = E5FFD1FD +clrMessageWarningFg = FF000000 +clrMessageWarningBg = E5F9FFD1 +clrMessageErrorFg = FF000000 +clrMessageErrorBg = E5FFD1D1 +clrVolumeBar = FF33CC33 +clrVolumeBarMute = FFFF0000 +clrMenuItemCurrentFg = FF000000 +clrMenuHighlight = E5E6BFE3 +clrMenuItemSelectableFg = FF000000 +clrMenuItemNotSelectableFg = E54158BC +clrMenuEventTitle = FF000000 +clrMenuEventShortText = FF1D2F7D +clrMenuScrollbarTotal = E5B2BBD6 +clrMenuScrollbarShown = FF000000 +clrReplayCurrent = FF7D1D75 +clrReplayTotal = FF7D1D75 +clrReplayModeJump = FF7D1D75 +clrReplayBarAreaBg = E5FADEF9 +clrReplayProgressSeen = FFE38EDC +clrReplayProgressRest = E5FADEF9 +clrReplayProgressSelected = FFBC41B8 +clrReplayProgressMark = FFBC41BB +clrReplayProgressCurrent = FFBC41BB diff --git a/themes/EnigmaNG-default.theme b/themes/EnigmaNG-default.theme new file mode 100644 index 0000000..743f8be --- /dev/null +++ b/themes/EnigmaNG-default.theme @@ -0,0 +1,54 @@ +Description = Default +clrBackground = E5DEE5FA +clrAltBackground = E5B2BBD6 +clrTitleBg = E54158BC +clrLogoBg = E58EA4E3 +clrBottomBg = E51D2F7D +clrBotProgBarBg = FF808080 +clrBotProgBarFg = FFFFFFFF +clrMenuTxtFg = FF000000 +clrTitleFg = FFFFFFFF +clrTitleShadow = FF000000 +clrSymbolActive = FFC4C400 +clrSymbolInactive = FF808080 +clrSymbolRecord = FFC40000 +clrSymbolTimerActive = FF0000C4 +clrSymbolVpsActive = FFC4C400 +clrSymbolRecActive = FFC40000 +clrSymbolRunActive = FF00C400 +clrButtonRedFg = FFFFFFFF +clrButtonRedBg = E5C40000 +clrButtonGreenFg = FFFFFFFF +clrButtonGreenBg = E500C400 +clrButtonYellowFg = FFFFFFFF +clrButtonYellowBg = E5C4C400 +clrButtonBlueFg = FFFFFFFF +clrButtonBlueBg = E50000C4 +clrMessageBorder = E5FF0000 +clrMessageStatusFg = FF000000 +clrMessageStatusBg = E5D7FFD1 +clrMessageInfoFg = FF000000 +clrMessageInfoBg = E5D1DDFF +clrMessageWarningFg = FF000000 +clrMessageWarningBg = E5F9FFD1 +clrMessageErrorFg = FF000000 +clrMessageErrorBg = E5FFD1D1 +clrVolumeBar = FF33CC33 +clrVolumeBarMute = FFFF0000 +clrMenuItemCurrentFg = FF000000 +clrMenuHighlight = E5B2BBD6 +clrMenuItemSelectableFg = FF000000 +clrMenuItemNotSelectableFg = E54158BC +clrMenuEventTitle = FF000000 +clrMenuEventShortText = FF1D2F7D +clrMenuScrollbarTotal = E5B2BBD6 +clrMenuScrollbarShown = FF000000 +clrReplayCurrent = FF1D2F7D +clrReplayTotal = FF1D2F7D +clrReplayModeJump = FF1D2F7D +clrReplayBarAreaBg = E5DEE5FA +clrReplayProgressSeen = FF8EA4E3 +clrReplayProgressRest = E5DEE5FA +clrReplayProgressSelected = FF4158BC +clrReplayProgressMark = FF4158BC +clrReplayProgressCurrent = FFFF0000 @@ -0,0 +1,151 @@ +/* + * tools.c: The 'EnigmaNG' VDR skin + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include <sstream> + +#ifndef __STL_CONFIG_H +#define __STL_CONFIG_H +#endif + +#include <stdlib.h> +#include <string.h> +#include "common.h" +#include "tools.h" + +using namespace std; + +#define AUX_HEADER_EPGSEARCH "EPGSearch: " +#define AUX_TAGS_EPGSEARCH_START "<epgsearch>" +#define AUX_TAGS_EPGSEARCH_ITEM1_START "<Channel>" +#define AUX_TAGS_EPGSEARCH_ITEM1_END "</Channel>" +#define AUX_TAGS_EPGSEARCH_ITEM2_START "<Search timer>" +#define AUX_TAGS_EPGSEARCH_ITEM2_END "</Search timer>" +#define AUX_TAGS_EPGSEARCH_END "</epgsearch>" + +#define AUX_HEADER_VDRADMIN "VDRAdmin-AM: " +#define AUX_TAGS_VDRADMIN_START "<vdradmin-am>" +#define AUX_TAGS_VDRADMIN_ITEM1_START "<pattern>" +#define AUX_TAGS_VDRADMIN_ITEM1_END "</pattern>" +#define AUX_TAGS_VDRADMIN_END "</vdradmin-am>" + +#define AUX_HEADER_PIN "Protected: " +#define AUX_TAGS_PIN_START "<pin-plugin>" +#define AUX_TAGS_PIN_ITEM1_START "<protected>" +#define AUX_TAGS_PIN_ITEM1_END "</protected>" +#define AUX_TAGS_PIN_END "</pin-plugin>" + +const char *parseaux(const char *aux) +{ + bool founditem = false; + stringstream sstrReturn; + char *start, *end; + // check if egpsearch + start = strcasestr(aux, AUX_TAGS_EPGSEARCH_START); + end = strcasestr(aux, AUX_TAGS_EPGSEARCH_END); + if (start && end) { + // add header + sstrReturn << AUX_HEADER_EPGSEARCH; + // parse first item + char *tmp; + if ((tmp = strcasestr(start, AUX_TAGS_EPGSEARCH_ITEM1_START)) != NULL) { + if (tmp < end) { + tmp += strlen(AUX_TAGS_EPGSEARCH_ITEM1_START); + char *tmp2; + if ((tmp2 = strcasestr(tmp, AUX_TAGS_EPGSEARCH_ITEM1_END)) != NULL) { + // add channel + sstrReturn << string(tmp, tmp2 - tmp); + founditem = true; + } else { + founditem = false; + } + } + } + // parse second item + if ((tmp = strcasestr(start, "<Search timer>")) != NULL) { + if (tmp < end) { + tmp += strlen(AUX_TAGS_EPGSEARCH_ITEM2_START); + char *tmp2; + if ((tmp2 = strcasestr(tmp, AUX_TAGS_EPGSEARCH_ITEM2_END)) != NULL) { + // add separator + if (founditem) { + sstrReturn << ", "; + } + // add search item + sstrReturn << string(tmp, tmp2 - tmp); + founditem = true; + } else { + founditem = false; + } + } + } + // use old syntax + if (!founditem) { + start += strlen(AUX_HEADER_EPGSEARCH); + sstrReturn << string(start, end - start); + } + sstrReturn << endl; + } + // check if VDRAdmin-AM + start = strcasestr(aux, AUX_TAGS_VDRADMIN_START); + end = strcasestr(aux, AUX_TAGS_VDRADMIN_END); + if (start && end) { + // add header + sstrReturn << AUX_HEADER_VDRADMIN; + // parse first item + char *tmp; + if ((tmp = strcasestr(start, AUX_TAGS_VDRADMIN_ITEM1_START)) != NULL) { + if (tmp < end) { + tmp += strlen(AUX_TAGS_VDRADMIN_ITEM1_START); + char *tmp2; + if ((tmp2 = strcasestr(tmp, AUX_TAGS_VDRADMIN_ITEM1_END)) != NULL) { + // add search item + sstrReturn << string(tmp, tmp2 - tmp) << endl; + } + } + } + } + // check if pin + start = strcasestr(aux, AUX_TAGS_PIN_START); + end = strcasestr(aux, AUX_TAGS_PIN_END); + if (start && end) { + // add header + sstrReturn << AUX_HEADER_PIN; + // parse first item + char *tmp; + if ((tmp = strcasestr(start, AUX_TAGS_PIN_ITEM1_START)) != NULL) { + if (tmp < end) { + tmp += strlen(AUX_TAGS_PIN_ITEM1_START); + char *tmp2; + if ((tmp2 = strcasestr(tmp, AUX_TAGS_PIN_ITEM1_END)) != NULL) { + // add search item + sstrReturn << string(tmp, tmp2 - tmp) << endl; + } + } + } + } + + if (!sstrReturn.str().empty()) + return sstrReturn.str().c_str(); + + return aux; +} + +bool ischaracters(const char *str, const char *mask) +{ + bool match = true; + const char *p = str; + for (; *p; ++p) { + const char *m = mask; + bool tmp = false; + for (; *m; ++m) { + if (*p == *m) + tmp = true; + } + match = match && tmp; + } + return match; +} @@ -0,0 +1,16 @@ +/* + * tools.h: The 'EnigmaNG' VDR skin + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef __SKINENIGMA_TOOLS_H +#define __SKINENIGMA_TOOLS_H + +#include <vdr/tools.h> + +const char *parseaux(const char *str); +bool ischaracters(const char *aux, const char *mask); + +#endif // __SKINENIGMA_TOOLS_H |