summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Mair <amair.sob@googlemail.com>2007-02-14 12:24:25 +0100
committerAndreas Mair <amair.sob@googlemail.com>2007-02-14 12:24:25 +0100
commit2ced4d35c533969fdb0ee6fa963a2fef26c50b79 (patch)
treeb49facd42724fda2ad7c294479373d8f0f08a527
downloadvdr-plugin-skinenigmang-2ced4d35c533969fdb0ee6fa963a2fef26c50b79.tar.gz
vdr-plugin-skinenigmang-2ced4d35c533969fdb0ee6fa963a2fef26c50b79.tar.bz2
2007-02-14: Version 0.0.1v0.0.1
- Initial release.
-rw-r--r--COPYING340
-rw-r--r--HISTORY6
-rw-r--r--Makefile111
-rw-r--r--README173
-rw-r--r--common.h22
-rw-r--r--config.c32
-rw-r--r--config.h35
-rw-r--r--enigma.c2573
-rw-r--r--enigma.h24
-rw-r--r--i18n.c804
-rw-r--r--i18n.h15
-rw-r--r--logo.c139
-rw-r--r--logo.h35
-rw-r--r--patches/vdr-1.4.5-no_trans_found.diff11
-rw-r--r--patches/vdr-1.4.5-no_trans_found.txt26
-rw-r--r--skinenigmang.c275
-rw-r--r--status.c116
-rw-r--r--status.h87
-rw-r--r--symbols/small/audio.xpm23
-rw-r--r--symbols/small/audioleft.xpm23
-rw-r--r--symbols/small/audioright.xpm23
-rw-r--r--symbols/small/dolbydigital.xpm23
-rw-r--r--symbols/small/encrypted.xpm23
-rw-r--r--symbols/small/eventparttimer.xpm27
-rw-r--r--symbols/small/eventparttimer.xpm.old27
-rw-r--r--symbols/small/eventrecording.xpm27
-rw-r--r--symbols/small/eventrunning.xpm27
-rw-r--r--symbols/small/eventtimer.xpm27
-rw-r--r--symbols/small/eventvps.xpm27
-rw-r--r--symbols/small/radio.xpm23
-rw-r--r--symbols/small/recording.xpm23
-rw-r--r--symbols/small/recordingnew.xpm27
-rw-r--r--symbols/small/run.xpm23
-rw-r--r--symbols/small/teletext.xpm23
-rw-r--r--symbols/small/timer.xpm23
-rw-r--r--symbols/small/vps.xpm23
-rw-r--r--themes/EnigmaNG-AppleGreen.theme54
-rw-r--r--themes/EnigmaNG-DarkBlue.theme54
-rw-r--r--themes/EnigmaNG-WineRed.theme54
-rw-r--r--themes/EnigmaNG-WomansLike.theme54
-rw-r--r--themes/EnigmaNG-default.theme54
-rw-r--r--tools.c151
-rw-r--r--tools.h16
43 files changed, 5703 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/COPYING
@@ -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.
diff --git a/HISTORY b/HISTORY
new file mode 100644
index 0000000..4c5a8d2
--- /dev/null
+++ b/HISTORY
@@ -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* *~
diff --git a/README b/README
new file mode 100644
index 0000000..630844c
--- /dev/null
+++ b/README
@@ -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
diff --git a/i18n.c b/i18n.c
new file mode 100644
index 0000000..68fba2c
--- /dev/null
+++ b/i18n.c
@@ -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}
+};
diff --git a/i18n.h b/i18n.h
new file mode 100644
index 0000000..5068d5b
--- /dev/null
+++ b/i18n.h
@@ -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
diff --git a/logo.c b/logo.c
new file mode 100644
index 0000000..c8355c1
--- /dev/null
+++ b/logo.c
@@ -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;
+}
diff --git a/logo.h b/logo.h
new file mode 100644
index 0000000..d331c1d
--- /dev/null
+++ b/logo.h
@@ -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
diff --git a/tools.c b/tools.c
new file mode 100644
index 0000000..1219c91
--- /dev/null
+++ b/tools.c
@@ -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;
+}
diff --git a/tools.h b/tools.h
new file mode 100644
index 0000000..7c8ff13
--- /dev/null
+++ b/tools.h
@@ -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