summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlouis <louis.braun@gmx.de>2015-03-12 17:22:16 +0100
committerlouis <louis.braun@gmx.de>2015-03-12 17:22:16 +0100
commit3cc8e78e1bf00e16b49520ac416b74b5fd73c906 (patch)
tree4425c4eb4b9833d927aae2f65d0f3a99d3792f83
downloadvdr-plugin-tvguideng-3cc8e78e1bf00e16b49520ac416b74b5fd73c906.tar.gz
vdr-plugin-tvguideng-3cc8e78e1bf00e16b49520ac416b74b5fd73c906.tar.bz2
Version 0.0.10.0.1
-rw-r--r--.gitignore8
-rw-r--r--COPYING340
-rw-r--r--HISTORY6
-rw-r--r--Makefile146
-rw-r--r--README182
-rw-r--r--channelepg.c444
-rw-r--r--channelepg.h61
-rw-r--r--channelgroups.c196
-rw-r--r--channelgroups.h54
-rw-r--r--channeljump.c47
-rw-r--r--channeljump.h25
-rw-r--r--config.c87
-rw-r--r--config.h90
-rw-r--r--detailview.c621
-rw-r--r--detailview.h37
-rw-r--r--dummyelement.c26
-rw-r--r--dummyelement.h24
-rw-r--r--epgelement.c52
-rw-r--r--epgelement.h28
-rw-r--r--epggrid.c774
-rw-r--r--epggrid.h73
-rw-r--r--gridelement.c56
-rw-r--r--gridelement.h51
-rw-r--r--helpers.c514
-rw-r--r--helpers.h87
-rw-r--r--libskindesigner/osdelements.c203
-rw-r--r--libskindesigner/osdelements.h91
-rw-r--r--libskindesigner/services.h131
-rw-r--r--libskindesigner/skindesignerosdbase.c209
-rw-r--r--libskindesigner/skindesignerosdbase.h84
-rw-r--r--po/de_DE.po604
-rw-r--r--recmanager.c710
-rw-r--r--recmanager.h64
-rw-r--r--recmenu.c429
-rw-r--r--recmenu.h59
-rw-r--r--recmenuitem.c1573
-rw-r--r--recmenuitem.h495
-rw-r--r--recmenus.c1143
-rw-r--r--recmenus.h451
-rw-r--r--recmenuview.c846
-rw-r--r--recmenuview.h44
-rw-r--r--searchtimer.c570
-rw-r--r--searchtimer.h136
-rw-r--r--services/epgsearch.h202
-rw-r--r--services/remotetimers.h33
-rw-r--r--services/scraper2vdr.h194
-rw-r--r--setup.c158
-rw-r--r--setup.h30
-rw-r--r--switchtimer.c113
-rw-r--r--switchtimer.h31
-rw-r--r--templates/plug-tvguideng-detail.xml237
-rw-r--r--templates/plug-tvguideng-recmenu.xml186
-rw-r--r--templates/plug-tvguideng-root.xml156
-rw-r--r--timeline.c153
-rw-r--r--timeline.h50
-rw-r--r--timemanager.c172
-rw-r--r--timemanager.h61
-rw-r--r--timerconflict.c177
-rw-r--r--timerconflict.h38
-rw-r--r--tvguideng.c156
-rw-r--r--tvguidengosd.c475
-rw-r--r--tvguidengosd.h93
62 files changed, 14586 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..19526e0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+.dependencies
+*.o
+*.so
+*.orig
+*.rej
+*.mo
+*.pot
+*.tgz
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..f90922e
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/HISTORY b/HISTORY
new file mode 100644
index 0000000..a6958e3
--- /dev/null
+++ b/HISTORY
@@ -0,0 +1,6 @@
+VDR Plugin 'tvguideng' Revision History
+---------------------------------------
+
+2015-01-26: Version 0.0.1
+
+- Initial revision.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..128e041
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,146 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id$
+
+# The official name of this plugin.
+# This name will be used in the '-P...' option of VDR to load the plugin.
+# By default the main source file also carries this name.
+
+PLUGIN = tvguideng
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g')
+
+### The directory environment:
+
+# Use package data if installed...otherwise assume we're under the VDR source directory:
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+LIBDIR = $(call PKGCFG,libdir)
+LOCDIR = $(call PKGCFG,locdir)
+PLGCFG = $(call PKGCFG,plgcfg)
+#
+TMPDIR ?= /tmp
+
+### The compiler options:
+
+export CFLAGS = $(call PKGCFG,cflags)
+export CXXFLAGS = $(call PKGCFG,cxxflags)
+
+### The version number of VDR's plugin API:
+
+APIVERSION = $(call PKGCFG,apiversion)
+
+### Allow user defined options to overwrite defaults:
+
+-include $(PLGCFG)
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### The name of the shared object file:
+
+SOFILE = libvdr-$(PLUGIN).so
+
+### Includes and Defines (add further entries here):
+
+INCLUDES +=
+
+DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).o \
+ libskindesigner/skindesignerosdbase.o \
+ libskindesigner/osdelements.o \
+ helpers.o \
+ config.o \
+ setup.o \
+ timemanager.o \
+ recmanager.o \
+ searchtimer.o \
+ switchtimer.o \
+ timerconflict.o \
+ gridelement.o \
+ dummyelement.o \
+ epgelement.o \
+ timeline.o \
+ channeljump.o \
+ channelgroups.o \
+ channelepg.o \
+ epggrid.o \
+ detailview.o \
+ recmenuview.o \
+ recmenu.o \
+ recmenuitem.o \
+ recmenus.o \
+ tvguidengosd.o
+
+### The main target:
+
+all: $(SOFILE) i18n
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $<
+
+### Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Internationalization (I18N):
+
+PODIR = po
+I18Npo = $(wildcard $(PODIR)/*.po)
+I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file))))
+I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
+I18Npot = $(PODIR)/$(PLUGIN).pot
+
+%.mo: %.po
+ msgfmt -c -o $@ $<
+
+$(I18Npot): $(wildcard *.c)
+ xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='<see README>' -o $@ `ls $^`
+
+%.po: $(I18Npot)
+ msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $<
+ @touch $@
+
+$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
+ install -D -m644 $< $@
+
+.PHONY: i18n
+i18n: $(I18Nmo) $(I18Npot)
+
+install-i18n: $(I18Nmsgs)
+
+### Targets:
+
+$(SOFILE): $(OBJS)
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) -o $@
+
+install-lib: $(SOFILE)
+ install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)
+
+install: install-lib install-i18n
+
+dist: $(I18Npo) clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+ @-rm -f $(PODIR)/*.mo $(PODIR)/*.pot
+ @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~
diff --git a/README b/README
new file mode 100644
index 0000000..61fc557
--- /dev/null
+++ b/README
@@ -0,0 +1,182 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Louis Braun <louis DOT braun AT gmx DOT de>
+
+Project's homepage: http://projects.vdr-developer.org/projects/plg-tvguideng
+
+Latest version available at: git://projects.vdr-developer.org/vdr-plugin-tvguideng.git
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+See the file COPYING for more information.
+
+Requirements
+------------
+
+- Clone Git Repository: git clone git://projects.vdr-developer.org/vdr-plugin-tvguideng.git
+- VDR version >= 2.0.0
+- Skindesigner Plugin Version >= 0.3.0
+
+ IMPORTANT: this Plugin will NOT WORK WITHOUT installed skindesigner Plugin. The
+ used Skindesigner Skin has to provide Templates for TVGuideNG, otherwise the Plugin
+ will not work.
+
+- Installed epgsearch Plugin for extended search & recording features.
+- Installed remotetimers Plugin for creating timers on a vdr server remotely from a
+ vdr client.
+
+
+Description
+-----------
+
+"TvGuideNG" is a highly customizable 2D EPG viewer plugin. The "Search & Recordings"
+Menü provided by the red button allows to search in the EPG and manage timers,
+search timers, series timers and switch timers in an convenient way.
+
+Installation
+------------
+
+Just install the Plugin as usual. The Plugin has no start parameters
+
+Usage
+-----
+
+Remote Control Keys:
+
+Up/Down/Left/Right: Navigation in the EPG grid
+Ok: Detailed EPG View of the selected grid
+ 2nd Ok closes the detailed view
+Red: Search & Recording Menu
+Green / Yellow: Jump channels back / forward
+Blue: Depends on configured Mode:
+ Default: Favorites Menu, Switch to channel in
+ detailed EPG View
+ Optional: Switch to currently selected channel
+ Optional: Open detailed EPG view (OK switches
+ then to channel)
+
+Numeric Keys: If Setup Option "Functionality of numeric Keys" is configured
+to "Jump to specific channel", the numeric keys are used for channel number
+input. If "Timely Jump" is configured, the keys are used as follows:
+
+1 / 3: Big jump (default 3h) back / forward in time
+4 / 6: huge jump (default 24h) back / forward in time
+7 / 9: jump to previous / next prime time (8pm)
+
+Exit: closes plugin
+
+In Search & Recording menu the following options are available:
+
+- Instant Record: create a timer for the currently selected program. An
+ check for timer conflicts will be performed, if a timer conflict occurs,
+ the involved timers can be adapted to solve the conflict. Alternatively
+ reruns can be searched and used to solve timer conflicts.
+- Delete / Edit Timer: if a timer already exists for the currently selected
+ broadcast, this timer can be deleted or edited.
+- Timer Timeline: displays active timers per day
+- Create Search Timer: create an search timer with epgsearch. The most
+ important options can be defined directly in the menu, expert options
+ are available via the "advanced options" button.
+- Manage Search Timers: edit, delete and check results of search timers
+- Create Series Timer: create a periodical timer for a dedicated time on
+ dedicated days.
+- Create Switch Timer: create a switch timer for the currently selected
+ program.
+- Search: search with epgsearch
+- Check for Timer Conflicts: check for timer conflicts with epgsearch.
+- Search in Recordings: check if a recording already exists for the currently
+ selected program.
+
+The search & recordings menu is intended to present a convenient interface
+to different VDR core recording features and additional epgsearch features.
+The main goal is to reach better usability for "non expert users".
+
+The navigation through the search & recording menu can be done easily with
+up/down, left/right and the ok key. In scrolling lists, with left/right
+(after the first/last option of the displayed list element is selected) you
+jump to the next page of the list. Pressing up on the first / down on the
+last list item jumpps to the end / start of the list.
+
+In the Plugin Setup Menu in Section "Recording Menus and Favorites" you can
+choose three alternatives for the option "Folder for instant Recordings":
+- "Always use root video folder": the recording will always be placed in the
+ video root folder
+- "Select from folder list": before creating a timer, you'll get asked in which
+ directory the recording should be placed. If the epgsearch plugin is not
+ available, the entries from VDRs <VDRCONFIG>/folders.conf will be used. If
+ the epgsearch plugin is available, the more sophisticated epgsearch mechanism
+ will be used to identify the possible folders (folders from
+ <VDRCONFIG>/folders.conf and epgsearchfolders.conf and all already existing
+ folders in the video directory).
+- "Use fixed folder": the configured folder will always be used. If this string
+ is a epgsearch variable ("%variable%"), epgsearch will be used to replace
+ the variable with a concrete folder. All values defined in the epgsearch config
+ file "epgsearchuservars.conf" can be used. These variables use the definitions
+ from "epgsearchcats.conf". For a detailed description see the epgsearch Manual.
+ With that and a with an EPG with detailed information about movies and series
+ it is possible to create sophisticated paths for timers automatically (for
+ instance with series season and episode in the filename).
+
+For a search timer also all folders and the defined dedicated folder can be used.
+
+Favorites Menu: if configured, the blue key provides a favorite menu. In this
+menu the current scheduling ("What's on now") and the next upcomming Shows
+("What's on next") can be listed. Via the Plugin Setup Menu up to four user
+defined times can be configured with a dedicated description (for instance
+"Primetime" for 8:15 pm). These user defined times are also shown as options in
+the favorites menu. Finally all search timers with the option "use as favorite"
+enabled are displayed in the favorites menu. By selecting this entry, all hits
+for this search timer are shown.
+
+Setup Options
+-------------
+
+* General Settings:
+
+- Show Main Menu Entry
+ Display "tvguide" in main menu
+- Replace VDR Schedules Menu
+ If set to "yes", the original VDR schedules menu will be replaced by tvguide
+- Display Mode
+ Choose between horizontal or vertical display of the channels. All further
+ width / height settings are dependend of this setting
+- Number of Channels to display
+- Time to display in hours: period of time to display in main EPG view
+- Channel Jump Mode (Keys Green / Yellow)
+ select between jumping x channels back/forward or to the previous / next
+ channel group
+- Keys Blue and OK
+ Blue: Favorites in normal view, channel switch in det. EPG View, Ok: det. EPG View
+ or
+ Blue: Channel Switch, Ok: Detailed EPG
+ or
+ Blue: Detailed EPG, Ok: Channel Switch
+- Close TVGuide after channel switch: If set to "no", tvguide will not be closed
+ after a channel switch.
+- Functionality of numeric Keys: see chapter Usage / Remote Control Keys
+- Hide last Channel Group
+ If set to yes, the channels of the last channel group will not be displayed
+- Big Step (Keys 1 / 3) in hours
+ Hours to jump vertically with keys 1 / 3
+- Huge Step (Keys 4 / 6) in hours
+ Hours to jump vertically with keys 4 / 6
+
+
+- Folder for instant Recordings: (see chapter "Usage" for detailed description)
+ - Always use root video folder
+ - Select from folder list
+ - Use fixed folder
+- Use Remotetimers: Timers are handled not locally but on a VDR Server defined
+ with the remotetimers plugin.
+- Use "What's on now" in favorites
+- Use "What's on next" in favorites
+- Use user defined time 1 (to 4) in favorites
+ - Description (1 - 4)
+ - Time (1 - 4)
+- Limit channels in favorites: if set to yes, only the configured channel range
+ is used to display favorite results (except for search timer favorites)
+ - start channel
+ - stop channel
+ \ No newline at end of file
diff --git a/channelepg.c b/channelepg.c
new file mode 100644
index 0000000..e93eb6a
--- /dev/null
+++ b/channelepg.c
@@ -0,0 +1,444 @@
+#include "channelepg.h"
+
+cChannelEpg::cChannelEpg(int position, const cChannel *channel, cTimeManager *timeManager) {
+ init = true;
+ this->channel = channel;
+ this->position = position;
+ this->timeManager = timeManager;
+ channelsPerPage = (config.displayMode == eHorizontal) ? config.channelsPerPageHorizontal : config.channelsPerPageVertical;
+ SetTimer();
+ SetSwitchTimer();
+ schedulesLock = new cSchedulesLock(false, 100);
+}
+
+cChannelEpg::~cChannelEpg(void) {
+ grids.Clear();
+ delete schedulesLock;
+}
+
+void cChannelEpg::ClearGrids(void) {
+ grids.Clear();
+}
+
+bool cChannelEpg::ReadGrids(void) {
+ schedules = cSchedules::Schedules(*schedulesLock);
+ const cSchedule *Schedule = NULL;
+ Schedule = schedules->GetSchedule(channel);
+ if (!Schedule) {
+ AddDummyGrid(timeManager->GetStart(), timeManager->GetEnd());
+ return true;
+ }
+ bool eventFound = false;
+ bool dummyAtStart = false;
+ const cEvent *startEvent = Schedule->GetEventAround(timeManager->GetStart());
+ if (startEvent != NULL) {
+ eventFound = true;
+ } else {
+ for (int i=1; i<6; i++) {
+ startEvent = Schedule->GetEventAround(timeManager->GetStart()+i*5*60);
+ if (startEvent) {
+ eventFound = true;
+ dummyAtStart = true;
+ break;
+ }
+ }
+ }
+ if (eventFound) {
+ if (dummyAtStart) {
+ AddDummyGrid(timeManager->GetStart(), startEvent->StartTime());
+ }
+ bool dummyNeeded = true;
+ bool toFarInFuture = false;
+ time_t endLast = timeManager->GetStart();
+ const cEvent *event = startEvent;
+ const cEvent *eventLast = NULL;
+ for (; event; event = Schedule->Events()->Next(event)) {
+ if (endLast < event->StartTime()) {
+ if (dummyAtStart) {
+ dummyAtStart = false;
+ } else {
+ //gap, dummy needed
+ time_t endTime = event->StartTime();
+ if (endTime > timeManager->GetEnd()) {
+ endTime = timeManager->GetEnd();
+ toFarInFuture = true;
+ }
+ AddDummyGrid(endLast, endTime);
+ }
+ }
+ if (toFarInFuture) {
+ break;
+ }
+ if (event->StartTime() >= timeManager->GetEnd()) {
+ dummyNeeded = false;
+ break;
+ }
+ AddEpgGrid(event);
+ endLast = event->EndTime();
+ if (event->EndTime() > timeManager->GetEnd()) {
+ dummyNeeded = false;
+ break;
+ }
+ eventLast = event;
+ }
+ if (dummyNeeded) {
+ AddDummyGrid(eventLast->EndTime(), timeManager->GetEnd());
+ }
+ return true;
+ } else {
+ AddDummyGrid(timeManager->GetStart(), timeManager->GetEnd());
+ return true;
+ }
+ return false;
+}
+
+cGridElement *cChannelEpg::GetActive(void) {
+ cTimeManager t;
+ t.Now();
+ for (cGridElement *grid = grids.First(); grid; grid = grids.Next(grid)) {
+ if (grid->Match(t.Get()))
+ return grid;
+ }
+ return grids.First();
+}
+
+cGridElement *cChannelEpg::GetNext(cGridElement *activeGrid) {
+ if (activeGrid == NULL)
+ return NULL;
+ cGridElement *next = grids.Next(activeGrid);
+ if (next)
+ return next;
+ return NULL;
+}
+
+cGridElement *cChannelEpg::GetPrev(cGridElement *activeGrid) {
+ if (activeGrid == NULL)
+ return NULL;
+ cGridElement *prev = grids.Prev(activeGrid);
+ if (prev)
+ return prev;
+ return NULL;
+}
+
+cGridElement *cChannelEpg::GetNeighbor(cGridElement *activeGrid) {
+ if (!activeGrid)
+ return NULL;
+ cGridElement *neighbor = NULL;
+ int overlap = 0;
+ int overlapNew = 0;
+ cGridElement *grid = NULL;
+ grid = grids.First();
+ if (grid) {
+ for (; grid; grid = grids.Next(grid)) {
+ if ( (grid->StartTime() == activeGrid->StartTime()) ) {
+ neighbor = grid;
+ break;
+ }
+ overlapNew = activeGrid->CalcOverlap(grid);
+ if (overlapNew > overlap) {
+ neighbor = grid;
+ overlap = overlapNew;
+ }
+ }
+ }
+ if (!neighbor)
+ neighbor = grids.First();
+ return neighbor;
+}
+
+bool cChannelEpg::IsFirst(cGridElement *grid) {
+ if (grid == grids.First())
+ return true;
+ return false;
+}
+
+void cChannelEpg::AddNewGridsAtStart(void) {
+ cGridElement *firstGrid = NULL;
+ firstGrid = grids.First();
+ if (firstGrid == NULL)
+ return;
+ //if first event is long enough, nothing to do.
+ if (firstGrid->StartTime() <= timeManager->GetStart()) {
+ return;
+ }
+ //if not, i have to add new ones to the list
+ schedules = cSchedules::Schedules(*schedulesLock);
+ const cSchedule *Schedule = NULL;
+ Schedule = schedules->GetSchedule(channel);
+ if (!Schedule) {
+ if (firstGrid->IsDummy()) {
+ firstGrid->SetStartTime(timeManager->GetStart());
+ firstGrid->SetEndTime(timeManager->GetEnd());
+ }
+ return;
+ }
+ bool dummyNeeded = true;
+ for (const cEvent *event = Schedule->GetEventAround(firstGrid->StartTime()-60); event; event = Schedule->Events()->Prev(event)) {
+ if (!event)
+ break;
+ if (event->EndTime() < timeManager->GetStart()) {
+ break;
+ }
+ cGridElement *grid = AddEpgGrid(event, firstGrid);
+ firstGrid = grid;
+ if (event->StartTime() <= timeManager->GetStart()) {
+ dummyNeeded = false;
+ break;
+ }
+ }
+ if (dummyNeeded) {
+ firstGrid = grids.First();
+ if (firstGrid->IsDummy()) {
+ firstGrid->SetStartTime(timeManager->GetStart());
+ if (firstGrid->EndTime() >= timeManager->GetEnd())
+ firstGrid->SetEndTime(timeManager->GetEnd());
+ } else {
+ cGridElement *grid = AddDummyGrid(timeManager->GetStart(), firstGrid->StartTime(), firstGrid);
+ }
+ }
+}
+
+void cChannelEpg::AddNewGridsAtEnd(void) {
+ cGridElement *lastGrid = NULL;
+ lastGrid = grids.Last();
+ if (lastGrid == NULL)
+ return;
+ //if last event is long enough, nothing to do.
+ if (lastGrid->EndTime() >= timeManager->GetEnd()) {
+ return;
+ }
+ //if not, i have to add new ones to the list
+ schedules = cSchedules::Schedules(*schedulesLock);
+ const cSchedule *Schedule = NULL;
+ Schedule = schedules->GetSchedule(channel);
+ if (!Schedule) {
+ if (lastGrid->IsDummy()) {
+ lastGrid->SetStartTime(timeManager->GetStart());
+ lastGrid->SetEndTime(timeManager->GetEnd());
+ }
+ return;
+ }
+ bool dummyNeeded = true;
+ for (const cEvent *event = Schedule->GetEventAround(lastGrid->EndTime()+60); event; event = Schedule->Events()->Next(event)) {
+ if (!event)
+ break;
+ if (event->StartTime() > timeManager->GetEnd()) {
+ break;
+ }
+ cGridElement *grid = AddEpgGrid(event);
+ if (event->EndTime() > timeManager->GetEnd()) {
+ dummyNeeded = false;
+ break;
+ }
+ }
+ if (dummyNeeded) {
+ lastGrid = grids.Last();
+ if (lastGrid->IsDummy()) {
+ lastGrid->SetEndTime(timeManager->GetEnd());
+ if (lastGrid->StartTime() <= timeManager->GetStart())
+ lastGrid->SetStartTime(timeManager->GetStart());
+ } else {
+ cGridElement *grid = AddDummyGrid(lastGrid->EndTime(), timeManager->GetEnd());
+ }
+ }
+}
+
+void cChannelEpg::ClearOutdatedStart(void) {
+ deletedElements.clear();
+ cGridElement *firstGrid = NULL;
+ while (true) {
+ firstGrid = grids.First();
+ if (!firstGrid)
+ break;
+ if (firstGrid->EndTime() <= timeManager->GetStart()) {
+ deletedElements.insert(firstGrid->Id());
+ grids.Del(firstGrid);
+ firstGrid = NULL;
+ } else {
+ if (firstGrid->IsDummy()) {
+ firstGrid->SetStartTime(timeManager->GetStart());
+ cGridElement *next = GetNext(firstGrid);
+ if (next) {
+ firstGrid->SetEndTime(next->StartTime());
+ } else {
+ firstGrid->SetEndTime(timeManager->GetEnd());
+ }
+ }
+ break;
+ }
+ }
+}
+
+void cChannelEpg::ClearOutdatedEnd(void) {
+ deletedElements.clear();
+ cGridElement *lastGrid = NULL;
+ while (true) {
+ lastGrid = grids.Last();
+ if (!lastGrid)
+ break;
+ if (lastGrid->StartTime() >= timeManager->GetEnd()) {
+ deletedElements.insert(lastGrid->Id());
+ grids.Del(lastGrid);
+ lastGrid = NULL;
+ } else {
+ if (lastGrid->IsDummy()) {
+ lastGrid->SetEndTime(timeManager->GetEnd());
+ cGridElement *prev = GetPrev(lastGrid);
+ if (prev) {
+ lastGrid->SetStartTime(prev->EndTime());
+ } else {
+ lastGrid->SetStartTime(timeManager->GetStart());
+ }
+ }
+ break;
+ }
+ }
+}
+
+void cChannelEpg::SetTimers(void) {
+ SetTimer();
+ SetSwitchTimer();
+ for (cGridElement *grid = grids.First(); grid; grid = grids.Next(grid)) {
+ bool gridHadTimer = grid->HasTimer();
+ grid->SetTimer();
+ if (gridHadTimer != grid->HasTimer()) {
+ grid->Dirty();
+ }
+ bool gridHadSwitchTimer = grid->HasSwitchTimer();
+ grid->SetSwitchTimer();
+ if (gridHadSwitchTimer != grid->HasSwitchTimer())
+ grid->Dirty();
+ }
+}
+
+void cChannelEpg::DrawHeader(cViewGrid *channelsGrid) {
+
+ double x, y, width, height;
+
+ if (config.displayMode == eHorizontal) {
+ x = 0.0;
+ width = 1.0;
+ height = (double)1 / (double)channelsPerPage;
+ y = height * (double)position;
+ } else {
+ y = 0.0;
+ height = 1.0;
+ width = (double)1 / (double)channelsPerPage;
+ x = width * (double)position;
+ }
+
+ int id = channel->Number();
+ if (init) {
+ channelsGrid->ClearTokens();
+ channelsGrid->AddIntToken("number", id);
+ channelsGrid->AddStringToken("name", channel->Name());
+ string channelId = *(channel->GetChannelID().ToString());
+ bool channelLogoExisis = channelsGrid->ChannelLogoExists(channelId);
+ channelsGrid->AddStringToken("channelid", channelId);
+ channelsGrid->AddIntToken("channellogoexists", channelLogoExisis);
+ channelsGrid->SetGrid(id, x, y, width, height);
+ init = false;
+ } else {
+ channelsGrid->MoveGrid(id, x, y, width, height);
+ }
+}
+
+void cChannelEpg::DrawGrids(cViewGrid *epgGrid) {
+ int displaySeconds = timeManager->GetDisplaySeconds();
+
+ double x, y, width, height;
+ if (config.displayMode == eHorizontal) {
+ x = 0.0;
+ width = 0.0;
+ height = (double)1 / (double)channelsPerPage;
+ y = height * (double)position;
+ } else {
+ y = 0.0;
+ height = 0.0;
+ width = (double)1 / (double)channelsPerPage;
+ x = width * (double)position;
+ }
+ time_t startTime = timeManager->GetStart();
+ time_t stopTime = timeManager->GetEnd();
+
+ for (cGridElement *grid = grids.First(); grid; grid = grids.Next(grid)) {
+ time_t gridStart = grid->StartTime();
+ time_t gridStop = grid->EndTime();
+ if (grid->IsNew()) {
+ epgGrid->ClearTokens();
+ epgGrid->AddIntToken("color", grid->Id() % 2);
+ epgGrid->AddIntToken("dummy", grid->IsDummy());
+ epgGrid->AddIntToken("timer", grid->HasTimer());
+ epgGrid->AddIntToken("switchtimer", grid->HasSwitchTimer());
+ epgGrid->AddStringToken("title", grid->Title());
+ epgGrid->AddStringToken("shorttext", grid->ShortText());
+ epgGrid->AddStringToken("start", *TimeString(gridStart));
+ epgGrid->AddStringToken("stop", *TimeString(gridStop));
+ }
+
+ if (gridStart < startTime) {
+ gridStart = startTime;
+ }
+ if (gridStop > stopTime) {
+ gridStop = stopTime;
+ }
+ if (config.displayMode == eHorizontal) {
+ int xGrid = gridStart - startTime;
+ x = (double)xGrid / (double)displaySeconds;
+ int duration = gridStop - gridStart;
+ width = (double)duration / (double)displaySeconds;
+ } else {
+ int yGrid = gridStart - startTime;
+ y = (double)yGrid / (double)displaySeconds;
+ int duration = gridStop - gridStart;
+ height = (double)duration / (double)displaySeconds;
+ }
+
+ if (grid->IsNew()) {
+ epgGrid->SetGrid(grid->Id(), x, y, width, height);
+ grid->InitFinished();
+ } else {
+ epgGrid->MoveGrid(grid->Id(), x, y, width, height);
+ }
+ if (grid->Active())
+ epgGrid->SetCurrent(grid->Id(), true);
+
+ }
+}
+
+void cChannelEpg::DeleteOutdated(cViewGrid *epgGrid) {
+ for (set<long>::iterator it = deletedElements.begin(); it != deletedElements.end(); it++) {
+ epgGrid->Delete(*it);
+ }
+}
+
+void cChannelEpg::DeleteGridViews(cViewGrid *epgGrid) {
+ for (cGridElement *ge = grids.First(); ge; ge = grids.Next(ge)) {
+ epgGrid->Delete(ge->Id());
+ }
+}
+
+cGridElement *cChannelEpg::AddEpgGrid(const cEvent *event, cGridElement *after) {
+ cGridElement *grid = new cEpgElement(event, this);
+ if (!after)
+ grids.Add(grid);
+ else
+ grids.Ins(grid, after);
+ return grid;
+}
+
+cGridElement *cChannelEpg::AddDummyGrid(time_t start, time_t end, cGridElement *after) {
+ cGridElement *dummy = new cDummyElement(start, end, this);
+ if (!after)
+ grids.Add(dummy);
+ else
+ grids.Ins(dummy, after);
+ return dummy;
+}
+
+void cChannelEpg::Debug(void) {
+ esyslog("tvguideng: channel %s", channel->Name());
+ for (cGridElement *grid = grids.First(); grid; grid = grids.Next(grid)) {
+ grid->Debug();
+ }
+} \ No newline at end of file
diff --git a/channelepg.h b/channelepg.h
new file mode 100644
index 0000000..2a575ce
--- /dev/null
+++ b/channelepg.h
@@ -0,0 +1,61 @@
+#ifndef __TVGUIDE_CHANNELEPG_H
+#define __TVGUIDE_CHANNELEPG_H
+
+#include <set>
+#include <vdr/tools.h>
+#include "libskindesigner/osdelements.h"
+#include "config.h"
+#include "gridelement.h"
+#include "epgelement.h"
+#include "dummyelement.h"
+#include "timemanager.h"
+#include "switchtimer.h"
+
+// --- cChannelEpg -------------------------------------------------------------
+
+class cChannelEpg : public cListObject {
+private:
+ bool init;
+ int channelsPerPage;
+ cTimeManager *timeManager;
+ int position;
+ const cChannel *channel;
+ cList<cGridElement> grids;
+ set<long> deletedElements;
+ cSchedulesLock *schedulesLock;
+ const cSchedules *schedules;
+ bool hasTimer;
+ bool hasSwitchTimer;
+ cGridElement *AddEpgGrid(const cEvent *event, cGridElement *after = NULL);
+ cGridElement *AddDummyGrid(time_t start, time_t end, cGridElement *after = NULL);
+public:
+ cChannelEpg(int position, const cChannel *channel, cTimeManager *timeManager);
+ virtual ~cChannelEpg(void);
+ void SetPosition(int newPos) { position = newPos; };
+ bool ReadGrids(void);
+ const cChannel *Channel(void) { return channel; };
+ const char* Name(void) { return channel->Name(); };
+ int GetChannelNumber(void) {return channel->Number();}
+ cGridElement * GetActive(void);
+ cGridElement * GetNext(cGridElement *activeGrid);
+ cGridElement * GetPrev(cGridElement *activeGrid);
+ cGridElement * GetNeighbor(cGridElement *activeGrid);
+ bool IsFirst(cGridElement *grid);
+ void AddNewGridsAtStart(void);
+ void AddNewGridsAtEnd(void);
+ void ClearOutdatedStart(void);
+ void ClearOutdatedEnd(void);
+ void SetTimers(void);
+ void SetTimer(void) { hasTimer = channel->HasTimer(); };
+ bool HasTimer(void) { return hasTimer; };
+ void SetSwitchTimer() {hasSwitchTimer = SwitchTimers.ChannelInSwitchList(channel);};
+ bool HasSwitchTimer() { return hasSwitchTimer; };
+ void ClearGrids(void);
+ void DrawHeader(cViewGrid *channelsGrid);
+ void DrawGrids(cViewGrid *epgGrid);
+ void DeleteOutdated(cViewGrid *epgGrid);
+ void DeleteGridViews(cViewGrid *epgGrid);
+ void Debug(void);
+};
+
+#endif //__TVGUIDE_CHANNELEPG_H
diff --git a/channelgroups.c b/channelgroups.c
new file mode 100644
index 0000000..4f68037
--- /dev/null
+++ b/channelgroups.c
@@ -0,0 +1,196 @@
+#include "config.h"
+#include "channelgroups.h"
+
+// --- cChannelGroup -------------------------------------------------------------
+
+cChannelGroup::cChannelGroup(string name, int id) {
+ this->id = id;
+ channelStart = 0;
+ channelStop = 0;
+ this->name = name;
+}
+
+cChannelGroup::~cChannelGroup(void) {
+}
+
+void cChannelGroup::Debug(void) {
+ dsyslog("tvguideng: id %d channel group %s, start %d, stop %d", id, name.c_str(), channelStart, channelStop);
+}
+
+// --- cChannelgroups -------------------------------------------------------------
+
+cChannelgroups::cChannelgroups(cViewGrid *channelgroupGrid) {
+ this->channelgroupGrid = channelgroupGrid;
+}
+
+cChannelgroups::~cChannelgroups(void) {
+ Clear();
+}
+
+void cChannelgroups::Init(void) {
+ bool setStart = false;
+ int lastChannelNumber = 0;
+ int id = 0;
+ const cChannel *first = Channels.First();
+ if (!first->GroupSep()) {
+ channelGroups.push_back(cChannelGroup(tr("Main Program"), id++));
+ setStart = true;
+ }
+ for (const cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) {
+ if (setStart && (channelGroups.size() > 0)) {
+ channelGroups[channelGroups.size()-1].SetChannelStart(channel->Number());
+ setStart = false;
+ }
+ if (channel->GroupSep()) {
+ if (channelGroups.size() > 0) {
+ channelGroups[channelGroups.size()-1].SetChannelStop(lastChannelNumber);
+ }
+ channelGroups.push_back(cChannelGroup(channel->Name(), id++));
+ setStart = true;
+ } else {
+ lastChannelNumber = channel->Number();
+ }
+ }
+ if (channelGroups.size() > 0) {
+ channelGroups[channelGroups.size()-1].SetChannelStop(lastChannelNumber);
+ }
+
+ for (vector<cChannelGroup>::iterator it = channelGroups.begin(); it != channelGroups.end(); it++) {
+ (*it).Debug();
+ }
+}
+
+void cChannelgroups::Clear(void) {
+ channelgroupGrid->Clear();
+}
+
+void cChannelgroups::Draw(const cChannel *start, const cChannel *stop) {
+ if (!start || !stop)
+ return;
+
+ int group = GetGroup(start);
+ int groupLast = group;
+ int fields = 1;
+ double offset = 0.0;
+
+ for (const cChannel *channel = Channels.Next(start); channel; channel = Channels.Next(channel)) {
+ if (channel->GroupSep())
+ continue;
+ if (config.hideLastChannelGroup && IsInLastGroup(channel)) {
+ SetGroup(group, fields, offset);
+ break;
+ }
+ group = GetGroup(channel);
+ if (group != groupLast) {
+ offset = SetGroup(groupLast, fields, offset);
+ fields = 0;
+ }
+ fields++;
+ groupLast = group;
+ if (channel == stop) {
+ SetGroup(group, fields, offset);
+ break;
+ }
+ }
+ channelgroupGrid->Display();
+}
+
+double cChannelgroups::SetGroup(int groupId, int fields, double offset) {
+ int channelsPerPage;
+ double x, y, width, height;
+ if (config.displayMode == eHorizontal) {
+ channelsPerPage = config.channelsPerPageHorizontal;
+ x = 0.0;
+ y = offset;
+ width = 1.0;
+ height = (double)fields / (double)channelsPerPage;
+ offset += height;
+ } else {
+ channelsPerPage = config.channelsPerPageVertical;
+ x = offset;
+ y = 0.0;
+ width = (double)fields / (double)channelsPerPage;
+ height = 1.0;
+ offset += width;
+ }
+ string groupName = channelGroups[groupId].GetName();
+ channelgroupGrid->ClearTokens();
+ channelgroupGrid->AddIntToken("color", groupId % 2);
+ channelgroupGrid->AddStringToken("group", groupName);
+ channelgroupGrid->SetGrid(groupId, x, y, width, height);
+
+ return offset;
+}
+
+int cChannelgroups::GetGroup(const cChannel *channel) {
+ if (!channel)
+ return -1;
+ int channelNumber = channel->Number();
+ for (vector<cChannelGroup>::iterator group = channelGroups.begin(); group != channelGroups.end(); group++) {
+ if ( (*group).StartChannel() <= channelNumber && (*group).StopChannel() >= channelNumber ) {
+ return (*group).GetId();
+ }
+ }
+ return -1;
+}
+
+string cChannelgroups::GetPrevGroupName(int group) {
+ if (group <= 0 || group > channelGroups.size())
+ return "";
+ return channelGroups[group-1].GetName();
+}
+
+string cChannelgroups::GetNextGroupName(int group) {
+ if (group < 0 || group >= channelGroups.size() - 1)
+ return "";
+ return channelGroups[group+1].GetName();
+}
+
+int cChannelgroups::GetPrevGroupFirstChannel(int group) {
+ if (group <= 0 || group > channelGroups.size())
+ return -1;
+ return channelGroups[group-1].StartChannel();
+}
+
+int cChannelgroups::GetNextGroupFirstChannel(int group) {
+ if (group < 0 || group >= channelGroups.size())
+ return -1;
+ return channelGroups[group+1].StartChannel();
+}
+
+bool cChannelgroups::IsInFirstGroup(const cChannel *channel) {
+ if (channelGroups.size() == 0)
+ return false;
+ int channelNumber = channel->Number();
+ if (channelNumber >= channelGroups[0].StartChannel() && channelNumber <= channelGroups[0].StopChannel())
+ return true;
+ return false;
+}
+
+bool cChannelgroups::IsInLastGroup(const cChannel *channel) {
+ size_t numGroups = channelGroups.size();
+ if (numGroups == 0)
+ return false;
+ int channelNumber = channel->Number();
+ if (channelNumber >= channelGroups[numGroups-1].StartChannel() && channelNumber <= channelGroups[numGroups-1].StopChannel()) {
+ return true;
+ }
+ return false;
+}
+
+bool cChannelgroups::IsInSecondLastGroup(const cChannel *channel) {
+ size_t numGroups = channelGroups.size();
+ if (numGroups < 2)
+ return false;
+ int channelNumber = channel->Number();
+ if (channelNumber >= channelGroups[numGroups-2].StartChannel() && channelNumber <= channelGroups[numGroups-2].StopChannel())
+ return true;
+ return false;
+}
+
+int cChannelgroups::GetLastValidChannel(void) {
+ if (config.hideLastChannelGroup && channelGroups.size() > 1) {
+ return channelGroups[channelGroups.size()-2].StopChannel();
+ }
+ return Channels.MaxNumber();
+}
diff --git a/channelgroups.h b/channelgroups.h
new file mode 100644
index 0000000..8742ab4
--- /dev/null
+++ b/channelgroups.h
@@ -0,0 +1,54 @@
+#ifndef __TVGUIDE_CHANNELGROUPS_H
+#define __TVGUIDE_CHANNELGROUPS_H
+
+#include <set>
+#include <vdr/tools.h>
+#include "libskindesigner/osdelements.h"
+#include "config.h"
+
+// --- cChannelGroup -------------------------------------------------------------
+
+class cChannelGroup : public cListObject {
+private:
+ int id;
+ int channelStart;
+ int channelStop;
+ string name;
+public:
+ cChannelGroup(string name, int id);
+ virtual ~cChannelGroup(void);
+ int GetId(void) { return id; };
+ void SetChannelStart(int start) { channelStart = start; };
+ int StartChannel(void) { return channelStart; };
+ void SetChannelStop(int stop) { channelStop = stop; };
+ int StopChannel(void) { return channelStop; };
+ string GetName(void) { return name; };
+ void Debug(void);
+};
+
+
+// --- cChannelgroups -------------------------------------------------------------
+
+class cChannelgroups {
+private:
+ cViewGrid *channelgroupGrid;
+ vector<cChannelGroup> channelGroups;
+ double SetGroup(int groupId, int fields, double offset);
+public:
+ cChannelgroups(cViewGrid *channelgroupGrid);
+ virtual ~cChannelgroups(void);
+ void Init(void);
+ void Clear(void);
+ void Draw(const cChannel *start, const cChannel *stop);
+ int GetGroup(const cChannel *channel);
+ string GetPrevGroupName(int group);
+ string GetNextGroupName(int group);
+ int GetPrevGroupFirstChannel(int group);
+ int GetNextGroupFirstChannel(int group);
+ bool IsInFirstGroup(const cChannel *channel);
+ bool IsInLastGroup(const cChannel *channel);
+ bool IsInSecondLastGroup(const cChannel *channel);
+ int GetLastValidChannel(void);
+};
+
+#endif //__TVGUIDE_TIMELINE_H
diff --git a/channeljump.c b/channeljump.c
new file mode 100644
index 0000000..aeb5d40
--- /dev/null
+++ b/channeljump.c
@@ -0,0 +1,47 @@
+#include <vdr/channels.h>
+#include "channeljump.h"
+
+cChannelJump::cChannelJump(cViewElement *channelJump, int lastValidChannel) {
+ this->channelJump = channelJump;
+ channel = 0;
+ maxChannels = lastValidChannel;
+ startTime = 0;
+ timeout = Setup.ChannelEntryTimeout;
+}
+
+cChannelJump::~cChannelJump(void) {
+ channelJump->Clear();
+ delete channelJump;
+}
+
+void cChannelJump::Draw(void) {
+ channelJump->Clear();
+ channelJump->ClearTokens();
+ string channelString = *BuildChannelString();
+ channelJump->AddStringToken("channel", channelString);
+ channelJump->Display();
+}
+
+void cChannelJump::Set(int key) {
+ startTime = cTimeMs::Now();
+ if (channel == 0) {
+ channel = key;
+ return;
+ }
+ int newChannel = channel * 10 + key;
+ if (newChannel <= maxChannels)
+ channel = newChannel;
+}
+
+cString cChannelJump::BuildChannelString(void) {
+ if (channel*10 <= maxChannels)
+ return cString::sprintf("%d-", channel);
+ else
+ return cString::sprintf("%d", channel);
+}
+
+ bool cChannelJump::TimeOut(void) {
+ if ((cTimeMs::Now() - startTime) > timeout)
+ return true;
+ return false;
+ } \ No newline at end of file
diff --git a/channeljump.h b/channeljump.h
new file mode 100644
index 0000000..54e05c2
--- /dev/null
+++ b/channeljump.h
@@ -0,0 +1,25 @@
+#ifndef __TVGUIDE_CHANNELJUMP_H
+#define __TVGUIDE_CHANNELJUMP_H
+
+#include "libskindesigner/osdelements.h"
+
+// --- cChannelJump -------------------------------------------------------------
+
+class cChannelJump {
+private:
+ cViewElement *channelJump;
+ int channel;
+ int maxChannels;
+ int startTime;
+ int timeout;
+ cString BuildChannelString(void);
+public:
+ cChannelJump(cViewElement *channelJump, int lastValidChannel);
+ virtual ~cChannelJump(void);
+ void Set(int key);
+ bool TimeOut(void);
+ void Draw(void);
+ int GetChannel(void) { return channel; };
+};
+
+#endif //__TVGUIDE_CHANNELJUMP_H \ No newline at end of file
diff --git a/config.c b/config.c
new file mode 100644
index 0000000..10ab9e2
--- /dev/null
+++ b/config.c
@@ -0,0 +1,87 @@
+#include <vdr/plugin.h>
+#include "config.h"
+
+cTVGuideConfig::cTVGuideConfig(void) {
+ showMainMenuEntry = 1;
+ replaceOriginalSchedule = 0;
+ displayMode = eHorizontal;
+ channelsPerPageHorizontal = 8;
+ channelsPerPageVertical = 6;
+ displayHours = 4;
+ timeStep = 60;
+ bigStepHours = 3;
+ hugeStepHours = 24;
+ hideLastChannelGroup = 0;
+ channelJumpMode = eGroupJump;
+ closeOnSwitch = 0;
+ numKeyMode = eChannelJump;
+ blueKeyMode = eBlueKeyFavorites;
+ //settings for rerun display
+ rerunAmount = 10;
+ rerunDistance = 2;
+ rerunMaxChannel = 0;
+ useRemoteTimers = 1;
+ instRecFolderMode = eFolderRoot;
+ instRecFixedFolder = "";
+ favWhatsOnNow = 1;
+ favWhatsOnNext = 1;
+ favUseTime1 = 0;
+ favUseTime2 = 0;
+ favUseTime3 = 0;
+ favUseTime4 = 0;
+ favTime1 = 0;
+ favTime2 = 0;
+ favTime3 = 0;
+ favTime4 = 0;
+ descUser1 = "";
+ descUser2 = "";
+ descUser3 = "";
+ descUser4 = "";
+ favLimitChannels = 0;
+ favStartChannel = 0;
+ favStopChannel = 0;
+ useSubtitleRerun = 1;
+}
+
+bool cTVGuideConfig::SetupParse(const char *Name, const char *Value) {
+
+ if (!strcasecmp(Name, "showMainMenuEntry")) showMainMenuEntry = atoi(Value);
+ else if (!strcasecmp(Name, "replaceOriginalSchedule")) replaceOriginalSchedule = atoi(Value);
+ else if (!strcasecmp(Name, "displayMode")) displayMode = atoi(Value);
+ else if (!strcasecmp(Name, "channelsPerPageHorizontal")) channelsPerPageHorizontal = atoi(Value);
+ else if (!strcasecmp(Name, "channelsPerPageVertical")) channelsPerPageVertical = atoi(Value);
+ else if (!strcasecmp(Name, "displayHours")) displayHours = atoi(Value);
+ else if (!strcasecmp(Name, "bigStepHours")) bigStepHours = atoi(Value);
+ else if (!strcasecmp(Name, "hugeStepHours")) hugeStepHours = atoi(Value);
+ else if (!strcasecmp(Name, "hideLastChannelGroup")) hideLastChannelGroup = atoi(Value);
+ else if (!strcasecmp(Name, "channelJumpMode")) channelJumpMode = atoi(Value);
+ else if (!strcasecmp(Name, "closeOnSwitch")) closeOnSwitch = atoi(Value);
+ else if (!strcasecmp(Name, "numKeyMode")) numKeyMode = atoi(Value);
+ else if (!strcasecmp(Name, "blueKeyMode")) blueKeyMode = atoi(Value);
+ else if (!strcasecmp(Name, "rerunAmount")) rerunAmount = atoi(Value);
+ else if (!strcasecmp(Name, "rerunDistance")) rerunDistance = atoi(Value);
+ else if (!strcasecmp(Name, "rerunMaxChannel")) rerunMaxChannel = atoi(Value);
+ else if (!strcasecmp(Name, "useRemoteTimers")) useRemoteTimers = atoi(Value);
+ else if (!strcasecmp(Name, "instRecFolderMode")) instRecFolderMode = atoi(Value);
+ else if (!strcasecmp(Name, "instRecFixedFolder")) instRecFixedFolder = atoi(Value);
+ else if (!strcasecmp(Name, "favWhatsOnNow")) favWhatsOnNow = atoi(Value);
+ else if (!strcasecmp(Name, "favWhatsOnNext")) favWhatsOnNext = atoi(Value);
+ else if (!strcasecmp(Name, "favUseTime1")) favUseTime1 = atoi(Value);
+ else if (!strcasecmp(Name, "favUseTime2")) favUseTime2 = atoi(Value);
+ else if (!strcasecmp(Name, "favUseTime3")) favUseTime3 = atoi(Value);
+ else if (!strcasecmp(Name, "favUseTime4")) favUseTime4 = atoi(Value);
+ else if (!strcasecmp(Name, "favTime1")) favTime1 = atoi(Value);
+ else if (!strcasecmp(Name, "favTime2")) favTime2 = atoi(Value);
+ else if (!strcasecmp(Name, "favTime3")) favTime3 = atoi(Value);
+ else if (!strcasecmp(Name, "favTime4")) favTime4 = atoi(Value);
+ else if (!strcasecmp(Name, "descUser1")) descUser1 = Value;
+ else if (!strcasecmp(Name, "descUser2")) descUser2 = Value;
+ else if (!strcasecmp(Name, "descUser3")) descUser3 = Value;
+ else if (!strcasecmp(Name, "descUser4")) descUser4 = Value;
+ else if (!strcasecmp(Name, "favLimitChannels")) favLimitChannels = atoi(Value);
+ else if (!strcasecmp(Name, "favStartChannel")) favStartChannel = atoi(Value);
+ else if (!strcasecmp(Name, "favStopChannel")) favStopChannel = atoi(Value);
+ else return false;
+
+ return true;
+} \ No newline at end of file
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..019ca65
--- /dev/null
+++ b/config.h
@@ -0,0 +1,90 @@
+#ifndef __TVGUIDE_CONFIG_H
+#define __TVGUIDE_CONFIG_H
+
+#include "string"
+#include <vdr/plugin.h>
+
+using namespace std;
+
+enum eDisplayMode {
+ eVertical,
+ eHorizontal
+};
+
+enum eChannelJumpMode {
+ eNumJump,
+ eGroupJump
+};
+
+enum eNumKeyMode {
+ eTimeJump,
+ eChannelJump
+};
+
+enum eBlueKeyMode {
+ eBlueKeySwitch = 0,
+ eBlueKeyEPG,
+ eBlueKeyFavorites
+};
+
+enum eInstRecFolderMode {
+ eFolderRoot = 0,
+ eFolderSelect,
+ eFolderFixed
+};
+
+class cTVGuideConfig {
+private:
+public:
+ cTVGuideConfig(void);
+ ~cTVGuideConfig() {};
+ int showMainMenuEntry;
+ int replaceOriginalSchedule;
+ int displayMode;
+ int channelsPerPageHorizontal;
+ int channelsPerPageVertical;
+ int displayHours;
+ int timeStep;
+ int bigStepHours;
+ int hugeStepHours;
+ int hideLastChannelGroup;
+ int channelJumpMode;
+ int closeOnSwitch;
+ int numKeyMode;
+ int blueKeyMode;
+ int rerunAmount;
+ int rerunDistance;
+ int rerunMaxChannel;
+ int useRemoteTimers;
+ int instRecFolderMode;
+ string instRecFixedFolder;
+ int favWhatsOnNow;
+ int favWhatsOnNext;
+ int favUseTime1;
+ int favUseTime2;
+ int favUseTime3;
+ int favUseTime4;
+ int favTime1;
+ int favTime2;
+ int favTime3;
+ int favTime4;
+ string descUser1;
+ string descUser2;
+ string descUser3;
+ string descUser4;
+ int favLimitChannels;
+ int favStartChannel;
+ int favStopChannel;
+ int useSubtitleRerun;
+ bool SetupParse(const char *Name, const char *Value);
+};
+
+#ifdef DEFINE_CONFIG
+ cTVGuideConfig config;
+ cPlugin *pRemoteTimers = NULL;
+#else
+ extern cTVGuideConfig config;
+ extern cPlugin *pRemoteTimers;
+#endif
+
+#endif //__TVGUIDE_CONFIG_H \ No newline at end of file
diff --git a/detailview.c b/detailview.c
new file mode 100644
index 0000000..72c4eb6
--- /dev/null
+++ b/detailview.c
@@ -0,0 +1,621 @@
+#include "helpers.h"
+#include "tvguidengosd.h"
+#include "detailview.h"
+#include "services/scraper2vdr.h"
+#include "services/epgsearch.h"
+
+cDetailView::cDetailView(cOsdView *detailView, const cEvent *event) {
+ init = true;
+ lastSecond = -1;
+ this->detailView = detailView;
+ this->event = event;
+ back = detailView->GetViewElement(vedBackground);
+ header = detailView->GetViewElement(vedHeader);
+ footer = detailView->GetViewElement(vedFooter);
+ watch = detailView->GetViewElement(vedTime);
+ tabs = detailView->GetViewTabs();
+}
+
+cDetailView::~cDetailView() {
+ delete back;
+ delete header;
+ delete footer;
+ delete watch;
+ delete detailView;
+}
+
+void cDetailView::Draw(void) {
+ if (!event) {
+ return;
+ }
+ if (init) {
+ DrawBackground();
+ DrawHeader();
+ DrawFooter();
+ Flush();
+ SetTabTokens();
+ tabs->Init();
+ init = false;
+ }
+ tabs->Display();
+}
+
+void cDetailView::Left(void) {
+ tabs->Left();
+ tabs->Display();
+}
+
+void cDetailView::Right(void) {
+ tabs->Right();
+ tabs->Display();
+}
+
+void cDetailView::Up(void) {
+ tabs->Up();
+ tabs->Display();
+}
+
+void cDetailView::Down(void) {
+ tabs->Down();
+ tabs->Display();
+}
+
+void cDetailView::DrawBackground(void) {
+ back->Display();
+}
+
+void cDetailView::DrawHeader(void) {
+ if (!event)
+ return;
+ header->ClearTokens();
+ static cPlugin *pScraper = GetScraperPlugin();
+ if (!pScraper) {
+ header->AddIntToken("ismovie", false);
+ header->AddIntToken("isseries", false);
+ header->AddIntToken("posteravailable", false);
+ header->AddIntToken("banneravailable", false);
+ } else {
+ ScraperGetEventType getType;
+ getType.event = event;
+ if (!pScraper->Service("GetEventType", &getType)) {
+ header->AddIntToken("ismovie", false);
+ header->AddIntToken("isseries", false);
+ header->AddIntToken("posteravailable", false);
+ header->AddIntToken("banneravailable", false);
+ } else {
+ if (getType.type == tMovie) {
+ cMovie movie;
+ movie.movieId = getType.movieId;
+ pScraper->Service("GetMovie", &movie);
+ header->AddIntToken("ismovie", true);
+ header->AddIntToken("isseries", false);
+ header->AddIntToken("posteravailable", true);
+ header->AddIntToken("banneravailable", false);
+ header->AddStringToken("posterpath", movie.poster.path);
+ header->AddIntToken("posterwidth", movie.poster.width);
+ header->AddIntToken("posterheight", movie.poster.height);
+ } else if (getType.type == tSeries) {
+ cSeries series;
+ series.seriesId = getType.seriesId;
+ series.episodeId = getType.episodeId;
+ pScraper->Service("GetSeries", &series);
+ header->AddIntToken("ismovie", false);
+ header->AddIntToken("isseries", true);
+ vector<cTvMedia>::iterator poster = series.posters.begin();
+ if (poster != series.posters.end()) {
+ header->AddIntToken("posterwidth", (*poster).width);
+ header->AddIntToken("posterheight", (*poster).height);
+ header->AddStringToken("posterpath", (*poster).path);
+ header->AddIntToken("posteravailable", true);
+ } else {
+ header->AddIntToken("posterwidth", 0);
+ header->AddIntToken("posterheight", 0);
+ header->AddStringToken("posterpath", "");
+ header->AddIntToken("posteravailable", false);
+ }
+ vector<cTvMedia>::iterator banner = series.banners.begin();
+ if (banner != series.banners.end()) {
+ header->AddIntToken("bannerwidth", (*banner).width);
+ header->AddIntToken("bannerheight", (*banner).height);
+ header->AddStringToken("bannerpath", (*banner).path);
+ header->AddIntToken("banneravailable", true);
+ } else {
+ header->AddIntToken("bannerwidth", 0);
+ header->AddIntToken("bannerheight", 0);
+ header->AddStringToken("bannerpath", "");
+ header->AddIntToken("banneravailable", false);
+ }
+ } else {
+ header->AddIntToken("ismovie", false);
+ header->AddIntToken("isseries", false);
+ header->AddIntToken("posteravailable", false);
+ header->AddIntToken("banneravailable", false);
+ }
+ }
+ }
+
+ header->AddStringToken("title", event->Title() ? event->Title() : "");
+ header->AddStringToken("shorttext", event->ShortText() ? event->ShortText() : "");
+ header->AddStringToken("start", *(event->GetTimeString()));
+ header->AddStringToken("stop", *(event->GetEndTimeString()));
+
+ time_t startTime = event->StartTime();
+ header->AddStringToken("day", *WeekDayName(startTime));
+ header->AddStringToken("date", *ShortDateString(startTime));
+ struct tm * sStartTime = localtime(&startTime);
+ header->AddIntToken("year", sStartTime->tm_year + 1900);
+ header->AddIntToken("daynumeric", sStartTime->tm_mday);
+ header->AddIntToken("month", sStartTime->tm_mon+1);
+
+ const cChannel *channel = Channels.GetByChannelID(event->ChannelID());
+ if (channel) {
+ header->AddStringToken("channelname", channel->Name() ? channel->Name() : "");
+ header->AddIntToken("channelnumber", channel->Number());
+ } else {
+ header->AddStringToken("channelname", "");
+ header->AddIntToken("channelnumber", 0);
+ }
+ string channelID = *(channel->GetChannelID().ToString());
+ header->AddStringToken("channelid", channelID);
+ header->AddIntToken("channellogoexists", header->ChannelLogoExists(channelID));
+
+ bool isRunning = false;
+ time_t now = time(NULL);
+ if ((now >= event->StartTime()) && (now <= event->EndTime()))
+ isRunning = true;
+ header->AddIntToken("running", isRunning);
+ if (isRunning) {
+ header->AddIntToken("elapsed", (now - event->StartTime())/60);
+ } else {
+ header->AddIntToken("elapsed", 0);
+ }
+ header->AddIntToken("duration", event->Duration() / 60);
+ header->AddIntToken("durationhours", event->Duration() / 3600);
+ header->AddStringToken("durationminutes", *cString::sprintf("%.2d", (event->Duration() / 60)%60));
+ if (event->Vps())
+ header->AddStringToken("vps", *event->GetVpsString());
+ else
+ header->AddStringToken("vps", "");
+
+ stringstream epgImageName;
+ epgImageName << event->EventID();
+ string epgImagePath = header->GetEpgImagePath();
+
+ bool epgPicAvailable = FileExists(epgImagePath, epgImageName.str(), "jpg");
+ if (epgPicAvailable) {
+ header->AddIntToken("epgpicavailable", true);
+ header->AddStringToken("epgpicpath", *cString::sprintf("%s%s.jpg", epgImagePath.c_str(), epgImageName.str().c_str()));
+ } else {
+ epgImageName << "_0";
+ epgPicAvailable = FileExists(epgImagePath, epgImageName.str(), "jpg");
+ if (epgPicAvailable) {
+ header->AddIntToken("epgpicavailable", true);
+ header->AddStringToken("epgpicpath", *cString::sprintf("%s%s.jpg", epgImagePath.c_str(), epgImageName.str().c_str()));
+ } else {
+ header->AddIntToken("epgpicavailable", false);
+ header->AddStringToken("epgpicpath", "");
+ }
+ }
+
+ header->Display();
+}
+
+void cDetailView::DrawFooter(void) {
+ string textGreen = "";
+ string textYellow = "";
+ string textRed = tr("Search & Record");
+ string textBlue = tr("Switch");
+
+ int colorKeys[4] = { Setup.ColorKey0, Setup.ColorKey1, Setup.ColorKey2, Setup.ColorKey3 };
+
+ footer->Clear();
+ footer->ClearTokens();
+
+ footer->AddStringToken("red", textRed);
+ footer->AddStringToken("green", textGreen);
+ footer->AddStringToken("yellow", textYellow);
+ footer->AddStringToken("blue", textBlue);
+
+ for (int button = 1; button < 5; button++) {
+ string red = *cString::sprintf("red%d", button);
+ string green = *cString::sprintf("green%d", button);
+ string yellow = *cString::sprintf("yellow%d", button);
+ string blue = *cString::sprintf("blue%d", button);
+ bool isRed = false;
+ bool isGreen = false;
+ bool isYellow = false;
+ bool isBlue = false;
+ switch (colorKeys[button-1]) {
+ case 0:
+ isRed = true;
+ break;
+ case 1:
+ isGreen = true;
+ break;
+ case 2:
+ isYellow = true;
+ break;
+ case 3:
+ isBlue = true;
+ break;
+ default:
+ break;
+ }
+ footer->AddIntToken(red, isRed);
+ footer->AddIntToken(green, isGreen);
+ footer->AddIntToken(yellow, isYellow);
+ footer->AddIntToken(blue, isBlue);
+ }
+
+ footer->Display();
+}
+
+bool cDetailView::DrawTime(void) {
+ time_t t = time(0); // get time now
+ struct tm * now = localtime(&t);
+ int sec = now->tm_sec;
+ if (sec == lastSecond)
+ return false;
+
+ int min = now->tm_min;
+ int hour = now->tm_hour;
+ int hourMinutes = hour%12 * 5 + min / 12;
+
+ watch->ClearTokens();
+ watch->AddIntToken("sec", sec);
+ watch->AddIntToken("min", min);
+ watch->AddIntToken("hour", hour);
+ watch->AddIntToken("hmins", hourMinutes);
+ watch->AddStringToken("time", *TimeString(t));
+ watch->Display();
+
+ lastSecond = sec;
+ return true;
+}
+
+void cDetailView::SetTabTokens(void) {
+ tabs->ClearTokens();
+
+ tabs->AddStringToken("title", event->Title() ? event->Title() : "");
+ tabs->AddStringToken("shorttext", event->ShortText() ? event->ShortText() : "");
+ tabs->AddStringToken("description", event->Description() ? event->Description() : "");
+ tabs->AddStringToken("start", *(event->GetTimeString()));
+ tabs->AddStringToken("stop", *(event->GetEndTimeString()));
+ time_t startTime = event->StartTime();
+ tabs->AddStringToken("day", *WeekDayName(startTime));
+ tabs->AddStringToken("date", *ShortDateString(startTime));
+ struct tm * sStartTime = localtime(&startTime);
+ tabs->AddIntToken("year", sStartTime->tm_year + 1900);
+ tabs->AddIntToken("daynumeric", sStartTime->tm_mday);
+ tabs->AddIntToken("month", sStartTime->tm_mon+1);
+
+ string channelID = *(event->ChannelID().ToString());
+ tabs->AddStringToken("channelid", channelID);
+ tabs->AddIntToken("channellogoexists", tabs->ChannelLogoExists(channelID));
+
+ bool isRunning = false;
+ time_t now = time(NULL);
+ if ((now >= event->StartTime()) && (now <= event->EndTime()))
+ isRunning = true;
+ tabs->AddIntToken("running", isRunning);
+ if (isRunning) {
+ tabs->AddIntToken("elapsed", (now - event->StartTime())/60);
+ } else {
+ tabs->AddIntToken("elapsed", 0);
+ }
+ tabs->AddIntToken("duration", event->Duration() / 60);
+ tabs->AddIntToken("durationhours", event->Duration() / 3600);
+ tabs->AddStringToken("durationminutes", *cString::sprintf("%.2d", (event->Duration() / 60)%60));
+ if (event->Vps())
+ tabs->AddStringToken("vps", *event->GetVpsString());
+ else
+ tabs->AddStringToken("vps", "");
+
+
+ bool hasReruns = LoadReruns();
+ tabs->AddIntToken("hasreruns", hasReruns);
+
+ SetScraperTokens();
+ SetEpgPictures(event->EventID());
+}
+
+bool cDetailView::LoadReruns(void) {
+ if (!event)
+ return false;
+
+ cPlugin *epgSearchPlugin = cPluginManager::GetPlugin("epgsearch");
+ if (!epgSearchPlugin)
+ return false;
+
+ if (isempty(event->Title()))
+ return false;
+
+ int maxNumReruns = config.rerunAmount;
+ int rerunDistance = config.rerunDistance * 3600;
+ int rerunNaxChannel = config.rerunMaxChannel;
+
+ Epgsearch_searchresults_v1_0 data;
+ string strQuery = (event->Title()) ? event->Title() : "";
+ data.query = (char *)strQuery.c_str();
+ data.mode = 0;
+ data.channelNr = 0;
+ data.useTitle = true;
+ data.useSubTitle = true;
+ data.useDescription = false;
+
+ bool foundRerun = false;
+ if (epgSearchPlugin->Service("Epgsearch-searchresults-v1.0", &data)) {
+ cList<Epgsearch_searchresults_v1_0::cServiceSearchResult>* list = data.pResultList;
+ if (list && (list->Count() > 1)) {
+ foundRerun = true;
+ int i = 0;
+ for (Epgsearch_searchresults_v1_0::cServiceSearchResult *r = list->First(); r && i < maxNumReruns; r = list->Next(r)) {
+ time_t eventStart = event->StartTime();
+ time_t rerunStart = r->event->StartTime();
+ cChannel *channel = Channels.GetByChannelID(r->event->ChannelID(), true, true);
+ //check for identical event
+ if ((event->ChannelID() == r->event->ChannelID()) && (eventStart == rerunStart))
+ continue;
+ //check for timely distance
+ if (rerunDistance > 0) {
+ if (rerunStart - eventStart < rerunDistance) {
+ continue;
+ }
+ }
+ //check for maxchannel
+ if (rerunNaxChannel > 0) {
+ if (channel && channel->Number() > rerunNaxChannel) {
+ continue;
+ }
+ }
+ i++;
+ map< string, string > rerun;
+ rerun.insert(pair<string, string>("reruns[title]", r->event->Title() ? r->event->Title() : ""));
+ rerun.insert(pair<string, string>("reruns[shorttext]", r->event->ShortText() ? r->event->ShortText() : ""));
+ rerun.insert(pair<string, string>("reruns[start]", *(r->event->GetTimeString())));
+ rerun.insert(pair<string, string>("reruns[start]", *(r->event->GetTimeString())));
+ rerun.insert(pair<string, string>("reruns[stop]", *(r->event->GetEndTimeString())));
+ rerun.insert(pair<string, string>("reruns[date]", *ShortDateString(r->event->StartTime())));
+ rerun.insert(pair<string, string>("reruns[day]", *WeekDayName(r->event->StartTime())));
+ string channelID = *(r->event->ChannelID().ToString());
+ rerun.insert(pair<string, string>("reruns[channelid]", channelID));
+ bool logoExists = tabs->ChannelLogoExists(channelID);
+ rerun.insert(pair<string, string>("reruns[channellogoexists]", logoExists ? "1" : "0"));
+
+ if (channel) {
+ stringstream channelNumber;
+ channelNumber << channel->Number();
+ rerun.insert(pair<string, string>("reruns[channelname]", channel->ShortName(true)));
+ rerun.insert(pair<string, string>("reruns[channelnumber]", channelNumber.str()));
+ } else {
+ rerun.insert(pair<string, string>("reruns[channelname]", ""));
+ rerun.insert(pair<string, string>("reruns[channelnumber]", ""));
+ }
+ tabs->AddLoopToken("reruns", rerun);
+ }
+ delete list;
+ }
+ }
+ return foundRerun;
+}
+
+void cDetailView::SetScraperTokens(void) {
+ static cPlugin *pScraper = GetScraperPlugin();
+ if (!pScraper || !event) {
+ tabs->AddIntToken("ismovie", false);
+ tabs->AddIntToken("isseries", false);
+ return;
+ }
+
+ ScraperGetEventType getType;
+ getType.event = event;
+ getType.recording = NULL;
+ if (!pScraper->Service("GetEventType", &getType)) {
+ tabs->AddIntToken("ismovie", false);
+ tabs->AddIntToken("isseries", false);
+ return;
+ }
+
+ if (getType.type == tMovie) {
+ cMovie movie;
+ movie.movieId = getType.movieId;
+ pScraper->Service("GetMovie", &movie);
+ tabs->AddIntToken("ismovie", true);
+ tabs->AddIntToken("isseries", false);
+
+ tabs->AddStringToken("movietitle", movie.title);
+ tabs->AddStringToken("movieoriginalTitle", movie.originalTitle);
+ tabs->AddStringToken("movietagline", movie.tagline);
+ tabs->AddStringToken("movieoverview", movie.overview);
+ tabs->AddStringToken("moviegenres", movie.genres);
+ tabs->AddStringToken("moviehomepage", movie.homepage);
+ tabs->AddStringToken("moviereleasedate", movie.releaseDate);
+ stringstream pop;
+ pop << movie.popularity;
+ tabs->AddStringToken("moviepopularity", pop.str());
+ stringstream vote;
+ vote << movie.voteAverage;
+ tabs->AddStringToken("movievoteaverage", pop.str());
+ tabs->AddStringToken("posterpath", movie.poster.path);
+ tabs->AddStringToken("fanartpath", movie.fanart.path);
+ tabs->AddStringToken("collectionposterpath", movie.collectionPoster.path);
+ tabs->AddStringToken("collectionfanartpath", movie.collectionFanart.path);
+
+ tabs->AddIntToken("movieadult", movie.adult);
+ tabs->AddIntToken("moviebudget", movie.budget);
+ tabs->AddIntToken("movierevenue", movie.revenue);
+ tabs->AddIntToken("movieruntime", movie.runtime);
+ tabs->AddIntToken("posterwidth", movie.poster.width);
+ tabs->AddIntToken("posterheight", movie.poster.height);
+ tabs->AddIntToken("fanartwidth", movie.fanart.width);
+ tabs->AddIntToken("fanartheight", movie.fanart.height);
+ tabs->AddIntToken("collectionposterwidth", movie.collectionPoster.width);
+ tabs->AddIntToken("collectionposterheight", movie.collectionPoster.height);
+ tabs->AddIntToken("collectionfanartwidth", movie.collectionFanart.width);
+ tabs->AddIntToken("collectionfanartheight", movie.collectionFanart.height);
+
+ for (vector<cActor>::iterator act = movie.actors.begin(); act != movie.actors.end(); act++) {
+ map< string, string > actor;
+ actor.insert(pair<string, string>("actors[name]", (*act).name));
+ actor.insert(pair<string, string>("actors[role]", (*act).role));
+ actor.insert(pair<string, string>("actors[thumb]", (*act).actorThumb.path));
+ stringstream actWidth, actHeight;
+ actWidth << (*act).actorThumb.width;
+ actHeight << (*act).actorThumb.height;
+ actor.insert(pair<string, string>("actors[thumbwidth]", actWidth.str()));
+ actor.insert(pair<string, string>("actors[thumbheight]", actHeight.str()));
+ tabs->AddLoopToken("actors", actor);
+ }
+
+ } else if (getType.type == tSeries) {
+ cSeries series;
+ series.seriesId = getType.seriesId;
+ series.episodeId = getType.episodeId;
+ pScraper->Service("GetSeries", &series);
+ tabs->AddIntToken("ismovie", false);
+ tabs->AddIntToken("isseries", true);
+ //Series Basics
+ tabs->AddStringToken("seriesname", series.name);
+ tabs->AddStringToken("seriesoverview", series.overview);
+ tabs->AddStringToken("seriesfirstaired", series.firstAired);
+ tabs->AddStringToken("seriesnetwork", series.network);
+ tabs->AddStringToken("seriesgenre", series.genre);
+ stringstream rating;
+ rating << series.rating;
+ tabs->AddStringToken("seriesrating", rating.str());
+ tabs->AddStringToken("seriesstatus", series.status);
+ //Episode Information
+ tabs->AddIntToken("episodenumber", series.episode.number);
+ tabs->AddIntToken("episodeseason", series.episode.season);
+ tabs->AddStringToken("episodetitle", series.episode.name);
+ tabs->AddStringToken("episodefirstaired", series.episode.firstAired);
+ tabs->AddStringToken("episodegueststars", series.episode.guestStars);
+ tabs->AddStringToken("episodeoverview", series.episode.overview);
+ stringstream eprating;
+ eprating << series.episode.rating;
+ tabs->AddStringToken("episoderating", eprating.str());
+ tabs->AddIntToken("episodeimagewidth", series.episode.episodeImage.width);
+ tabs->AddIntToken("episodeimageheight", series.episode.episodeImage.height);
+ tabs->AddStringToken("episodeimagepath", series.episode.episodeImage.path);
+ //Seasonposter
+ tabs->AddIntToken("seasonposterwidth", series.seasonPoster.width);
+ tabs->AddIntToken("seasonposterheight", series.seasonPoster.height);
+ tabs->AddStringToken("seasonposterpath", series.seasonPoster.path);
+
+ //Posters
+ int current = 1;
+ for(vector<cTvMedia>::iterator poster = series.posters.begin(); poster != series.posters.end(); poster++) {
+ stringstream labelWidth, labelHeight, labelPath;
+ labelWidth << "seriesposter" << current << "width";
+ labelHeight << "seriesposter" << current << "height";
+ labelPath << "seriesposter" << current << "path";
+
+ tabs->AddIntToken(labelWidth.str(), (*poster).width);
+ tabs->AddIntToken(labelHeight.str(), (*poster).height);
+ tabs->AddStringToken(labelPath.str(), (*poster).path);
+ current++;
+ }
+ if (current < 3) {
+ for (; current < 4; current++) {
+ stringstream labelWidth, labelHeight, labelPath;
+ labelWidth << "seriesposter" << current << "width";
+ labelHeight << "seriesposter" << current << "height";
+ labelPath << "seriesposter" << current << "path";
+
+ tabs->AddIntToken(labelWidth.str(), 0);
+ tabs->AddIntToken(labelHeight.str(), 0);
+ tabs->AddStringToken(labelPath.str(), "");
+ }
+ }
+
+ //Banners
+ current = 1;
+ for(vector<cTvMedia>::iterator banner = series.banners.begin(); banner != series.banners.end(); banner++) {
+ stringstream labelWidth, labelHeight, labelPath;
+ labelWidth << "seriesbanner" << current << "width";
+ labelHeight << "seriesbanner" << current << "height";
+ labelPath << "seriesbanner" << current << "path";
+
+ tabs->AddIntToken(labelWidth.str(), (*banner).width);
+ tabs->AddIntToken(labelHeight.str(), (*banner).height);
+ tabs->AddStringToken(labelPath.str(), (*banner).path);
+ current++;
+ }
+ if (current < 3) {
+ for (; current < 4; current++) {
+ stringstream labelWidth, labelHeight, labelPath;
+ labelWidth << "seriesbanner" << current << "width";
+ labelHeight << "seriesbanner" << current << "height";
+ labelPath << "seriesbanner" << current << "path";
+
+ tabs->AddIntToken(labelWidth.str(), 0);
+ tabs->AddIntToken(labelHeight.str(), 0);
+ tabs->AddStringToken(labelPath.str(), "");
+ }
+ }
+
+ //Fanarts
+ current = 1;
+ for(vector<cTvMedia>::iterator fanart = series.fanarts.begin(); fanart != series.fanarts.end(); fanart++) {
+ stringstream labelWidth, labelHeight, labelPath;
+ labelWidth << "seriesfanart" << current << "width";
+ labelHeight << "seriesfanart" << current << "height";
+ labelPath << "seriesfanart" << current << "path";
+
+ tabs->AddIntToken(labelWidth.str(), (*fanart).width);
+ tabs->AddIntToken(labelHeight.str(), (*fanart).height);
+ tabs->AddStringToken(labelPath.str(), (*fanart).path);
+ current++;
+ }
+ if (current < 3) {
+ for (; current < 4; current++) {
+ stringstream labelWidth, labelHeight, labelPath;
+ labelWidth << "seriesfanart" << current << "width";
+ labelHeight << "seriesfanart" << current << "height";
+ labelPath << "seriesfanart" << current << "path";
+
+ tabs->AddIntToken(labelWidth.str(), 0);
+ tabs->AddIntToken(labelHeight.str(), 0);
+ tabs->AddStringToken(labelPath.str(), "");
+ }
+ }
+
+ //Actors
+ for (vector<cActor>::iterator act = series.actors.begin(); act != series.actors.end(); act++) {
+ map< string, string > actor;
+ actor.insert(pair<string, string>("actors[name]", (*act).name));
+ actor.insert(pair<string, string>("actors[role]", (*act).role));
+ actor.insert(pair<string, string>("actors[thumb]", (*act).actorThumb.path));
+ stringstream actWidth, actHeight;
+ actWidth << (*act).actorThumb.width;
+ actHeight << (*act).actorThumb.height;
+ actor.insert(pair<string, string>("actors[thumbwidth]", actWidth.str()));
+ actor.insert(pair<string, string>("actors[thumbheight]", actHeight.str()));
+ tabs->AddLoopToken("actors", actor);
+ }
+
+ } else {
+ tabs->AddIntToken("ismovie", false);
+ tabs->AddIntToken("isseries", false);
+ }
+}
+
+void cDetailView::SetEpgPictures(int eventId) {
+ string epgImagePath = tabs->GetEpgImagePath();
+ for (int i=0; i<3; i++) {
+ stringstream picName;
+ picName << eventId << "_" << i;
+ bool epgPicAvailable = FileExists(epgImagePath, picName.str(), "jpg");
+ stringstream available;
+ stringstream path;
+ available << "epgpic" << i+1 << "avaialble";
+ path << "epgpic" << i+1 << "path";
+ if (epgPicAvailable) {
+ tabs->AddIntToken(available.str(), true);
+ tabs->AddStringToken(path.str(), *cString::sprintf("%s%s.jpg", epgImagePath.c_str(), picName.str().c_str()));
+ } else {
+ tabs->AddIntToken(available.str(), false);
+ tabs->AddStringToken(path.str(), "");
+ }
+ }
+} \ No newline at end of file
diff --git a/detailview.h b/detailview.h
new file mode 100644
index 0000000..cb3cd3f
--- /dev/null
+++ b/detailview.h
@@ -0,0 +1,37 @@
+#ifndef __TVGUIDE_DETAILVIEW_H
+#define __TVGUIDE_DETAILVIEW_H
+
+#include "config.h"
+#include "libskindesigner/osdelements.h"
+
+class cDetailView {
+private:
+ bool init;
+ int lastSecond;
+ cOsdView *detailView;
+ const cEvent *event;
+ cViewElement *back;
+ cViewElement *header;
+ cViewElement *footer;
+ cViewElement *watch;
+ cViewTab *tabs;
+ void DrawBackground(void);
+ void DrawHeader(void);
+ void DrawFooter(void);
+ void SetTabTokens(void);
+ bool LoadReruns(void);
+ void SetScraperTokens(void);
+ void SetEpgPictures(int eventId);
+public:
+ cDetailView(cOsdView *detailView, const cEvent *event);
+ virtual ~cDetailView(void);
+ void Draw(void);
+ void Left(void);
+ void Right(void);
+ void Up(void);
+ void Down(void);
+ bool DrawTime(void);
+ void Flush(void) { detailView->Display(); };
+};
+
+#endif //__TVGUIDE_DETAILVIEW_H
diff --git a/dummyelement.c b/dummyelement.c
new file mode 100644
index 0000000..b33c744
--- /dev/null
+++ b/dummyelement.c
@@ -0,0 +1,26 @@
+#include "dummyelement.h"
+
+cDummyElement::cDummyElement(time_t start, time_t end, cChannelEpg *owner) : cGridElement(owner) {
+ this->start = start;
+ this->end = end;
+ dummy = true;
+}
+
+cDummyElement::~cDummyElement(void) {
+}
+
+time_t cDummyElement::Duration(void) {
+ //max Duration 5h
+ int dur = end - start;
+ if (dur > 18000)
+ return 18000;
+ return dur;
+};
+
+const char *cDummyElement::Title(void) {
+ return tr("No EPG available");
+}
+
+void cDummyElement::Debug(void) {
+ esyslog("tvguideng: dummyelement %ld, %s - %s, Channel %s", id, *TimeString(start), *TimeString(end), Channel()->Name());
+}
diff --git a/dummyelement.h b/dummyelement.h
new file mode 100644
index 0000000..b2de4fb
--- /dev/null
+++ b/dummyelement.h
@@ -0,0 +1,24 @@
+#ifndef __TVGUIDE_DUMMYGRID_H
+#define __TVGUIDE_DUMMYGRID_H
+
+#include "gridelement.h"
+
+// --- cDummyGrid -------------------------------------------------------------
+
+class cDummyElement : public cGridElement {
+private:
+ time_t start;
+ time_t end;
+ time_t Duration(void);
+public:
+ cDummyElement(time_t start, time_t end, cChannelEpg *owner);
+ virtual ~cDummyElement(void);
+ time_t StartTime(void) { return start; };
+ time_t EndTime(void) { return end; };
+ void SetStartTime(time_t start) { this->start = start; };
+ void SetEndTime(time_t end) { this->end = end; };
+ const char *Title(void);
+ void Debug(void);
+};
+
+#endif //__TVGUIDE_DUMMYGRID_H \ No newline at end of file
diff --git a/epgelement.c b/epgelement.c
new file mode 100644
index 0000000..1ef5542
--- /dev/null
+++ b/epgelement.c
@@ -0,0 +1,52 @@
+#include "switchtimer.h"
+#include "services/remotetimers.h"
+#include "epgelement.h"
+
+cEpgElement::cEpgElement(const cEvent *event, cChannelEpg *owner) : cGridElement(owner) {
+ this->event = event;
+ hasTimer = false;
+ SetTimer();
+ hasSwitchTimer = false;
+ SetSwitchTimer();
+ dummy = false;
+}
+
+cEpgElement::~cEpgElement(void) {
+}
+
+void cEpgElement::SetTimer() {
+ if (config.useRemoteTimers && pRemoteTimers) {
+ RemoteTimers_Event_v1_0 rt;
+ rt.event = event;
+ if (pRemoteTimers->Service("RemoteTimers::GetTimerByEvent-v1.0", &rt))
+ hasTimer = true;
+ else
+ hasTimer = false;
+ } else if (owner->HasTimer()) {
+ hasTimer = event->HasTimer();
+ } else {
+ hasTimer = false;
+ }
+}
+
+void cEpgElement::SetSwitchTimer() {
+ if (owner->HasSwitchTimer()) {
+ hasSwitchTimer = SwitchTimers.EventInSwitchList(event);
+ } else {
+ hasSwitchTimer = false;
+ }
+}
+
+const char *cEpgElement::Title(void) {
+ return event->Title();
+}
+
+const char *cEpgElement::ShortText(void) {
+ if (event->ShortText())
+ return event->ShortText();
+ return "";
+}
+
+void cEpgElement::Debug() {
+ esyslog("tvguideng: epgelement %ld: \"%s\" %s - %s, channel %s, timer: %d", id, event->Title(), *(event->GetTimeString()), *(event->GetEndTimeString()), Channel()->Name(), hasTimer);
+}
diff --git a/epgelement.h b/epgelement.h
new file mode 100644
index 0000000..3a7b48d
--- /dev/null
+++ b/epgelement.h
@@ -0,0 +1,28 @@
+#ifndef __TVGUIDE_EPGGRID_H
+#define __TVGUIDE_EPGGRID_H
+
+#include <vdr/epg.h>
+#include "gridelement.h"
+#include "channelepg.h"
+
+// --- cEpgGrid -------------------------------------------------------------
+
+class cEpgElement : public cGridElement {
+private:
+ const cEvent *event;
+ time_t Duration(void) { return event->Duration(); };
+public:
+ cEpgElement(const cEvent *event, cChannelEpg *owner);
+ virtual ~cEpgElement(void);
+ const cEvent *GetEvent(void) {return event;};
+ time_t StartTime(void) { return event->StartTime(); };
+ time_t EndTime(void) { return event->EndTime(); };
+ void SetTimer(void);
+ void SetSwitchTimer(void);
+ const char *Title(void);
+ const char *ShortText(void);
+ const cEvent *Event(void) { return event; };
+ void Debug(void);
+};
+
+#endif //__TVGUIDE_EPGGRID_H \ No newline at end of file
diff --git a/epggrid.c b/epggrid.c
new file mode 100644
index 0000000..79cdae7
--- /dev/null
+++ b/epggrid.c
@@ -0,0 +1,774 @@
+#include "tvguidengosd.h"
+#include "epggrid.h"
+#include "helpers.h"
+#include "services/scraper2vdr.h"
+
+cEpgGrid::cEpgGrid(cOsdView *rootView, cTimeManager *timeManager) {
+ lastSecond = -1;
+ channelGroupLast = -1;
+ active = NULL;
+ oldActiveGridId = -1;
+
+ this->rootView = rootView;
+ this->timeManager = timeManager;
+
+ channelsPerPage = (config.displayMode == eHorizontal) ? config.channelsPerPageHorizontal : config.channelsPerPageVertical;
+
+ back = rootView->GetViewElement(config.displayMode == eHorizontal ? verBackgroundHor : verBackgroundVer);
+ back->Display();
+
+ header = rootView->GetViewElement(verHeader);
+ footer = rootView->GetViewElement(verFooter);
+ watch = rootView->GetViewElement(verTime);
+
+ channelsGrid = rootView->GetViewGrid(config.displayMode == eHorizontal ? vgChannelsHor : vgChannelsVer);
+ epgGrid = rootView->GetViewGrid(config.displayMode == eHorizontal ? vgSchedulesHor : vgSchedulesVer);
+
+ cViewElement *timelineDate = rootView->GetViewElement(config.displayMode == eHorizontal ? verDateTimelineHor : verDateTimelineVer);
+ cViewElement *timeIndicator = rootView->GetViewElement(config.displayMode == eHorizontal ? verTimeIndicatorHor : verTimeIndicatorVer);
+ cViewGrid *timelineGrid = rootView->GetViewGrid(config.displayMode == eHorizontal ? vgTimelineHor : vgTimelineVer);
+ timeline = new cTimeline(timelineGrid, timelineDate, timeIndicator, timeManager);
+
+ cViewGrid *channelgroupsGrid = rootView->GetViewGrid(config.displayMode == eHorizontal ? vgChannelGroupsHor : vgChannelGroupsVer);
+ channelGroups = new cChannelgroups(channelgroupsGrid);
+ channelGroups->Init();
+}
+
+cEpgGrid::~cEpgGrid(void) {
+ delete back;
+ delete header;
+ delete footer;
+ channels.Clear();
+ delete channelsGrid;
+ delete epgGrid;
+ delete timeline;
+ delete watch;
+ delete rootView;
+}
+
+void cEpgGrid::Init(const cChannel *startChannel) {
+ if (!startChannel)
+ return;
+ timeline->Init();
+ int numBack = channelsPerPage / 2;
+ int offset = 0;
+ const cChannel *newStartChannel = startChannel;
+ for (; newStartChannel ; newStartChannel = Channels.Prev(newStartChannel)) {
+ if (newStartChannel && !newStartChannel->GroupSep()) {
+ offset++;
+ }
+ if (offset == numBack)
+ break;
+ }
+ if (!newStartChannel)
+ newStartChannel = Channels.First();
+ offset--;
+ if (offset < 0)
+ offset = 0;
+
+ CreateChannels(newStartChannel, offset);
+}
+
+void cEpgGrid::CreateChannels(const cChannel *startChannel, int activeChannel) {
+ int pos = 0;
+ bool foundEnough = false;
+ channels.Clear();
+ if (!startChannel)
+ return;
+ for (const cChannel *channel = startChannel; channel; channel = Channels.Next(channel)) {
+ if (config.hideLastChannelGroup && channelGroups->IsInLastGroup(channel)) {
+ break;
+ }
+ if (!channel->GroupSep()) {
+ cChannelEpg *channelEpg = new cChannelEpg(pos, channel, timeManager);
+ if (channelEpg->ReadGrids()) {
+ channels.Add(channelEpg);
+ if (pos == activeChannel) {
+ active = channelEpg->GetActive();
+ if (active) {
+ active->SetActive();
+ }
+ }
+ pos++;
+ } else {
+ delete channelEpg;
+ }
+ }
+ if (pos == channelsPerPage) {
+ foundEnough = true;
+ break;
+ }
+ }
+ if (!foundEnough) {
+ int numCurrent = channels.Count();
+ int numBack = channelsPerPage - numCurrent;
+ int newChannelNumber = channels.First()->GetChannelNumber() - numBack;
+ const cChannel *newStart = Channels.GetByNumber(newChannelNumber);
+ CreateChannels(newStart, pos+1);
+ }
+}
+
+void cEpgGrid::Clear(void) {
+ timeline->Clear();
+ channelsGrid->Clear();
+ channels.Clear();
+ header->Clear();
+ footer->Clear();
+ active = NULL;
+ oldActiveGridId = -1;
+ epgGrid->Clear();
+}
+
+void cEpgGrid::SetTimers(void) {
+ for (cChannelEpg *channelEpg = channels.First(); channelEpg; channelEpg = channels.Next(channelEpg)) {
+ channelEpg->SetTimers();
+ }
+}
+
+
+void cEpgGrid::RebuildEpgGrid(void) {
+ if (!active)
+ return;
+ int activeChannelNumber = active->Channel()->Number();
+ oldActiveGridId = -1;
+ active = NULL;
+ epgGrid->Clear();
+ timeline->Clear();
+ timeline->Init();
+ for (cChannelEpg *channelEpg = channels.First(); channelEpg; channelEpg = channels.Next(channelEpg)) {
+ channelEpg->ClearGrids();
+ channelEpg->ReadGrids();
+ if (channelEpg->Channel()->Number() == activeChannelNumber) {
+ active = channelEpg->GetActive();
+ active->SetActive();
+ }
+ }
+}
+
+
+void cEpgGrid::SetActiveGrid(cGridElement *newActive) {
+ if (!newActive || !active) {
+ return;
+ }
+ oldActiveGridId = active->Id();
+ if (newActive->Id() == active->Id()) {
+ return;
+ }
+ active->SetInActive();
+ active = newActive;
+ active->SetActive();
+}
+
+bool cEpgGrid::TimeForward(void) {
+ bool scrolled = false;
+ if (!active) {
+ return false;
+ }
+ bool actionDone = false;
+ int minutesLeft = (timeManager->GetEnd() - active->EndTime())/60;
+ if ( minutesLeft < 30 ) {
+ actionDone = true;
+ ScrollForward();
+ scrolled = true;
+ }
+ cGridElement *next = (cGridElement*)active->Next();
+ if (next) {
+ if ( (next->EndTime() < timeManager->GetEnd()) || ( (timeManager->GetEnd() - next->StartTime())/60 > 30 ) ) {
+ SetActiveGrid(next);
+ actionDone = true;
+ }
+ }
+ if (!actionDone) {
+ ScrollForward();
+ scrolled = true;
+ }
+ return scrolled;
+}
+
+bool cEpgGrid::TimeBack(void) {
+ bool scrolled = false;
+ if (!active) {
+ return false;
+ }
+
+ if (timeManager->IsStart(active->StartTime())) {
+ oldActiveGridId = active->Id();
+ return false;
+ }
+
+ bool actionDone = false;
+ int minutesLeft = (active->StartTime() - timeManager->GetStart())/60;
+ if ( minutesLeft < 30 ) {
+ actionDone = true;
+ ScrollBack();
+ scrolled = true;
+ }
+ cGridElement *prev = (cGridElement*)active->Prev();
+ if (prev) {
+ if ( (prev->StartTime() > timeManager->GetStart())
+ || ( (prev->EndTime() - timeManager->GetStart())/60 > 30 )
+ || ( prev->IsFirst()) ) {
+ SetActiveGrid(prev);
+ actionDone = true;
+ }
+ }
+ if (!actionDone) {
+ ScrollBack();
+ scrolled = true;
+ }
+ return scrolled;
+}
+
+bool cEpgGrid::ChannelForward(void) {
+ if (!active) {
+ return false;
+ }
+ //Scrolling in Grid
+ cChannelEpg *channelNext = channels.Next(active->Owner());
+ if (!channelNext && config.hideLastChannelGroup) {
+ const cChannel *next = SeekChannelForward(1);
+ if (next == active->Channel())
+ return false;
+ }
+
+ if (channelNext) {
+ cGridElement *neighbor = channelNext->GetNeighbor(active);
+ if (neighbor) {
+ SetActiveGrid(neighbor);
+ }
+ return false;
+ }
+ //end of grid reached, scrolling half page forward
+ int numJumpForward = channelsPerPage/2;
+ const cChannel *currentChannel = active->Channel();
+ const cChannel *newLastChannel = SeekChannelForward(numJumpForward);
+ if (!newLastChannel)
+ return false;
+ //insert new channels at end
+ int numInserted = 0;
+ for (const cChannel *channel = (const cChannel*)currentChannel->Next(); channel ; channel = Channels.Next(channel)) {
+ if (channel->GroupSep()) {
+ continue;
+ }
+ cChannelEpg *channelEpg = new cChannelEpg(0, channel, timeManager);
+ if (channelEpg->ReadGrids()) {
+ channels.Add(channelEpg);
+ numInserted++;
+ }
+ if (numInserted == 1) {
+ cGridElement *neighbor = channelEpg->GetNeighbor(active);
+ if (neighbor) {
+ SetActiveGrid(neighbor);
+ }
+ }
+ if (channel->Number() == newLastChannel->Number())
+ break;
+ }
+ if (!numInserted)
+ return false;
+ //delete first channels
+ deletedChannels.clear();
+ for (int i=0; i < numInserted; i++) {
+ cChannelEpg *first = channels.First();
+ first->DeleteGridViews(epgGrid);
+ deletedChannels.insert(first->Channel()->Number());
+ channels.Del(first);
+ }
+ //renumber channels
+ int newPos = 0;
+ for (cChannelEpg *channelEpg = channels.First(); channelEpg; channelEpg = channels.Next(channelEpg)) {
+ channelEpg->SetPosition(newPos);
+ newPos++;
+ }
+
+ return true;
+}
+
+bool cEpgGrid::ChannelBack(void) {
+ if (!active) {
+ return false;
+ }
+ //Scrolling in Grid
+ cChannelEpg *channelPrev = channels.Prev(active->Owner());
+ if (channelPrev) {
+ cGridElement *neighbor = channelPrev->GetNeighbor(active);
+ if (neighbor) {
+ SetActiveGrid(neighbor);
+ }
+ return false;
+ }
+ //start of grid reached, scrolling half page back
+ int numJumpBack = channelsPerPage/2;
+ const cChannel *currentChannel = active->Channel();
+ const cChannel *newFirstChannel = SeekChannelBack(numJumpBack);
+ if (!newFirstChannel)
+ return false;
+ //insert new channels at start
+ int numInserted = 0;
+ for (const cChannel *channel = (const cChannel*)currentChannel->Prev(); channel ; channel = Channels.Prev(channel)) {
+ if (channel->GroupSep()) {
+ continue;
+ }
+ cChannelEpg *channelEpg = new cChannelEpg(0, channel, timeManager);
+ if (channelEpg->ReadGrids()) {
+ channels.Ins(channelEpg, channels.First());
+ numInserted++;
+ }
+ if (numInserted == 1) {
+ cGridElement *neighbor = channelEpg->GetNeighbor(active);
+ if (neighbor) {
+ SetActiveGrid(neighbor);
+ }
+ }
+ if (channel->Number() == newFirstChannel->Number())
+ break;
+ }
+ if (!numInserted)
+ return false;
+ //delete last channels
+ deletedChannels.clear();
+ for (int i=0; i < numInserted; i++) {
+ cChannelEpg *last = channels.Last();
+ last->DeleteGridViews(epgGrid);
+ deletedChannels.insert(last->Channel()->Number());
+ channels.Del(last);
+ }
+ //renumber channels
+ int newPos = 0;
+ for (cChannelEpg *channelEpg = channels.First(); channelEpg; channelEpg = channels.Next(channelEpg)) {
+ channelEpg->SetPosition(newPos);
+ newPos++;
+ }
+ return true;
+}
+
+const cChannel *cEpgGrid::GetNextChannelNumJump(void) {
+ const cChannel *newFirst = SeekChannelForward(channelsPerPage);
+ if (!newFirst)
+ newFirst = Channels.Last();
+ return newFirst;
+}
+
+const cChannel *cEpgGrid::GetPrevChannelNumJump(void) {
+ const cChannel *newFirst = SeekChannelBack(channelsPerPage);
+ if (!newFirst)
+ newFirst = Channels.First();
+ return newFirst;
+}
+
+const cChannel *cEpgGrid::GetNextChannelGroupJump(void) {
+ if (!active) {
+ return Channels.Last();
+ }
+ int currentGroup = channelGroups->GetGroup(active->Channel());
+ int nextChannelNumber = channelGroups->GetNextGroupFirstChannel(currentGroup);
+ const cChannel *next = Channels.GetByNumber(nextChannelNumber);
+ if (next)
+ return next;
+ return Channels.Last();
+}
+
+const cChannel *cEpgGrid::GetPrevChannelGroupJump(void) {
+ if (!active) {
+ return Channels.First();
+ }
+ int currentGroup = channelGroups->GetGroup(active->Channel());
+ int prevChannelNumber = channelGroups->GetPrevGroupFirstChannel(currentGroup);
+ const cChannel *prev = Channels.GetByNumber(prevChannelNumber);
+ if (prev)
+ return prev;
+ return Channels.First();
+}
+
+bool cEpgGrid::IsFirstGroup(void) {
+ if (!active) {
+ return true;
+ }
+ return channelGroups->IsInFirstGroup(active->Channel());
+}
+
+bool cEpgGrid::IsLastGroup(void) {
+ if (!active) {
+ return true;
+ }
+ return channelGroups->IsInLastGroup(active->Channel());
+}
+
+bool cEpgGrid::IsSecondLastGroup(void) {
+ if (!active) {
+ return true;
+ }
+ return channelGroups->IsInSecondLastGroup(active->Channel());
+}
+
+const cChannel *cEpgGrid::GetCurrentChannel(void) {
+ if (!active) {
+ return NULL;
+ }
+ return active->Channel();
+}
+
+int cEpgGrid::GetLastValidChannel(void) {
+ return channelGroups->GetLastValidChannel();
+}
+
+const cEvent *cEpgGrid::GetCurrentEvent(void) {
+ if (!active)
+ return NULL;
+ return active->Event();
+}
+
+void cEpgGrid::DrawChannelHeaders(void) {
+ for (set<int>::iterator it = deletedChannels.begin(); it != deletedChannels.end(); it++) {
+ channelsGrid->Delete(*it);
+ }
+ for (cChannelEpg *channel = channels.First(); channel; channel = channels.Next(channel)) {
+ channel->DrawHeader(channelsGrid);
+ }
+ channelsGrid->Display();
+}
+
+void cEpgGrid::DrawChannelgroups(void) {
+ channelGroups->Clear();
+ channelGroups->Draw(channels.First()->Channel(), channels.Last()->Channel());
+}
+
+void cEpgGrid::DrawTimeline(void) {
+ timeline->Draw();
+}
+
+void cEpgGrid::DrawGrid(void) {
+ if (!active) {
+ return;
+ }
+ epgGrid->SetCurrent(oldActiveGridId, false);
+ for (cChannelEpg *channel = channels.First(); channel; channel = channels.Next(channel)) {
+ channel->DeleteOutdated(epgGrid);
+ channel->DrawGrids(epgGrid);
+ }
+ epgGrid->Display();
+}
+
+void cEpgGrid::UpdateActive(void) {
+ if (!active) {
+ return;
+ }
+ if (oldActiveGridId == active->Id())
+ return;
+ epgGrid->SetCurrent(oldActiveGridId, false);
+ epgGrid->SetCurrent(active->Id(), true);
+ epgGrid->Display();
+}
+
+void cEpgGrid::DrawHeader(void) {
+ if (!active)
+ return;
+ if (oldActiveGridId == active->Id())
+ return;
+
+ int isDummy = 0;
+ string title = active->Title();
+ string shorttext = "";
+ string description = "";
+ string start = "";
+ string stop = "";
+ string day = "";
+ string date = "";
+ int daynumeric = 0;
+ int month = 0;
+ int year = 0;
+ int running = 0;
+ int elapsed = 0;
+ int duration = 0;
+ int durationhours = 0;
+ string durationminutes = "";
+ string channelname = "";
+ int channelnumber = 0;
+ string channelid = "";
+ int channellogoexists = 0;
+ int hasposter = 0;
+ int posterwidth = -1;
+ int posterheight = -1;
+ string posterpath = "";
+
+ header->ClearTokens();
+ header->Clear();
+ if (active->IsDummy()) {
+ isDummy = 1;
+ start = *(TimeString(active->StartTime()));
+ stop = *(TimeString(active->EndTime()));
+ } else {
+ const cEvent *event = active->Event();
+ if (event) {
+ shorttext = event->ShortText() ? event->ShortText() : "";
+ description = event->Description() ? event->Description() : "";
+ start = *(event->GetTimeString());
+ stop = *(event->GetEndTimeString());
+ time_t startTime = event->StartTime();
+ day = *WeekDayName(startTime);
+ date = *ShortDateString(startTime);
+ struct tm * sStartTime = localtime(&startTime);
+ year = sStartTime->tm_year + 1900;
+ daynumeric = sStartTime->tm_mday;
+ month = sStartTime->tm_mon+1;
+ time_t now = time(NULL);
+ if ((now >= event->StartTime()) && (now <= event->EndTime()))
+ running = 1;
+ if (running) {
+ elapsed = (now - event->StartTime())/60;
+ }
+ duration = event->Duration() / 60;
+ durationhours = event->Duration() / 3600;
+ durationminutes = *cString::sprintf("%.2d", (event->Duration() / 60)%60);
+
+ static cPlugin *pScraper = GetScraperPlugin();
+ if (pScraper) {
+ ScraperGetPoster call;
+ call.event = event;
+ if (pScraper->Service("GetPoster", &call)) {
+ hasposter = FileExists(call.poster.path);
+ posterwidth = call.poster.width;
+ posterheight = call.poster.height;
+ posterpath = call.poster.path;
+ }
+ }
+ }
+ const cChannel *channel = active->Channel();
+ if (channel) {
+ channelname = channel->Name() ? channel->Name() : "";
+ channelid = *(channel->GetChannelID().ToString());
+ channelnumber = channel->Number();
+ channellogoexists = header->ChannelLogoExists(channelid);
+ }
+ }
+ header->AddIntToken("isdummy", isDummy);
+ header->AddStringToken("title", title);
+ header->AddStringToken("shorttext", shorttext);
+ header->AddStringToken("description", description);
+ header->AddStringToken("start", start);
+ header->AddStringToken("stop", stop);
+ header->AddStringToken("day", day);
+ header->AddStringToken("date", date);
+ header->AddIntToken("daynumeric", daynumeric);
+ header->AddIntToken("month", month);
+ header->AddIntToken("year", year);
+ header->AddIntToken("running", running);
+ header->AddIntToken("elapsed", elapsed);
+ header->AddIntToken("duration", duration);
+ header->AddIntToken("durationhours", durationhours);
+ header->AddStringToken("durationminutes", durationminutes);
+ header->AddStringToken("channelname", channelname);
+ header->AddStringToken("channelid", channelid);
+ header->AddIntToken("channelnumber", channelnumber);
+ header->AddIntToken("channellogoexists", channellogoexists);
+ header->AddIntToken("hasposter", hasposter);
+ header->AddIntToken("posterwidth", posterwidth);
+ header->AddIntToken("posterheight", posterheight);
+ header->AddStringToken("posterpath", posterpath);
+ header->Display();
+}
+
+void cEpgGrid::DrawFooter(void) {
+ if (!active)
+ return;
+
+ string textGreen = "";
+ string textYellow = "";
+ string textRed = tr("Search & Record");
+ string textBlue = "";
+ if (config.channelJumpMode == eNumJump) {
+ textGreen = *cString::sprintf("%d %s", channelsPerPage, tr("Channels back"));
+ textYellow = *cString::sprintf("%d %s", channelsPerPage, tr("Channels forward"));
+ } else if (config.channelJumpMode == eGroupJump) {
+ int currentGroup = channelGroups->GetGroup(active->Channel());
+ if (currentGroup == channelGroupLast) {
+ return;
+ }
+ channelGroupLast = currentGroup;
+ textGreen = channelGroups->GetPrevGroupName(currentGroup);
+ textYellow = channelGroups->GetNextGroupName(currentGroup);
+ }
+ if (config.blueKeyMode == eBlueKeySwitch) {
+ textBlue = tr("Switch");
+ } else if (config.blueKeyMode == eBlueKeyEPG) {
+ textBlue = tr("Detailed EPG");
+ } else if (config.blueKeyMode == eBlueKeyFavorites) {
+ textBlue = tr("Favorites");
+ }
+
+ int colorKeys[4] = { Setup.ColorKey0, Setup.ColorKey1, Setup.ColorKey2, Setup.ColorKey3 };
+
+ footer->Clear();
+ footer->ClearTokens();
+
+ footer->AddStringToken("red", textRed);
+ footer->AddStringToken("green", textGreen);
+ footer->AddStringToken("yellow", textYellow);
+ footer->AddStringToken("blue", textBlue);
+
+ for (int button = 1; button < 5; button++) {
+ string red = *cString::sprintf("red%d", button);
+ string green = *cString::sprintf("green%d", button);
+ string yellow = *cString::sprintf("yellow%d", button);
+ string blue = *cString::sprintf("blue%d", button);
+ bool isRed = false;
+ bool isGreen = false;
+ bool isYellow = false;
+ bool isBlue = false;
+ switch (colorKeys[button-1]) {
+ case 0:
+ isRed = true;
+ break;
+ case 1:
+ isGreen = true;
+ break;
+ case 2:
+ isYellow = true;
+ break;
+ case 3:
+ isBlue = true;
+ break;
+ default:
+ break;
+ }
+ footer->AddIntToken(red, isRed);
+ footer->AddIntToken(green, isGreen);
+ footer->AddIntToken(yellow, isYellow);
+ footer->AddIntToken(blue, isBlue);
+ }
+
+ footer->Display();
+}
+
+bool cEpgGrid::DrawTime(void) {
+ time_t t = time(0); // get time now
+ struct tm * now = localtime(&t);
+ int sec = now->tm_sec;
+ if (sec == lastSecond)
+ return false;
+
+ int min = now->tm_min;
+ int hour = now->tm_hour;
+ int hourMinutes = hour%12 * 5 + min / 12;
+
+ char monthname[20];
+ char monthshort[10];
+ strftime(monthshort, sizeof(monthshort), "%b", now);
+ strftime(monthname, sizeof(monthname), "%B", now);
+
+ watch->Clear();
+ watch->ClearTokens();
+ watch->AddIntToken("sec", sec);
+ watch->AddIntToken("min", min);
+ watch->AddIntToken("hour", hour);
+ watch->AddIntToken("hmins", hourMinutes);
+ watch->AddIntToken("year", now->tm_year + 1900);
+ watch->AddIntToken("day", now->tm_mday);
+ watch->AddStringToken("time", *TimeString(t));
+ watch->AddStringToken("monthname", monthname);
+ watch->AddStringToken("monthnameshort", monthshort);
+ watch->AddStringToken("month", *cString::sprintf("%02d", now->tm_mon + 1));
+ watch->AddStringToken("dayleadingzero", *cString::sprintf("%02d", now->tm_mday));
+ watch->AddStringToken("dayname", *WeekDayNameFull(now->tm_wday));
+ watch->AddStringToken("daynameshort", *WeekDayName(now->tm_wday));
+ watch->Display();
+
+ lastSecond = sec;
+ return true;
+}
+
+cChannelJump *cEpgGrid::GetChannelJumper(void) {
+ cViewElement *channelJump = rootView->GetViewElement(verChannelJump);
+ int lastValidChannel = GetLastValidChannel();
+ return new cChannelJump(channelJump, lastValidChannel);
+}
+
+/*******************************************************************************************************
+* Private Functions
+*******************************************************************************************************/
+
+void cEpgGrid::ScrollForward(void) {
+ timeManager->AddMinutes(config.timeStep);
+ timeline->ScrollForward(config.timeStep);
+ timeline->Draw();
+ for (cChannelEpg *channel = channels.First(); channel; channel = channels.Next(channel)) {
+ channel->AddNewGridsAtEnd();
+ channel->ClearOutdatedStart();
+ }
+}
+
+void cEpgGrid::ScrollBack(void) {
+ timeManager->DelMinutes(config.timeStep);
+ timeline->ScrollBack(config.timeStep);
+ timeline->Draw();
+ for (cChannelEpg *channel = channels.First(); channel; channel = channels.Next(channel)) {
+ channel->AddNewGridsAtStart();
+ channel->ClearOutdatedEnd();
+ }
+}
+
+const cChannel *cEpgGrid::SeekChannelForward(int num) {
+ if (!active)
+ return NULL;
+ const cChannel *currentChannel = active->Channel();
+ const cChannel *destChannel;
+ int found = 0;
+ for (destChannel = currentChannel; destChannel ; destChannel = Channels.Next(destChannel)) {
+ if (destChannel->GroupSep()) {
+ continue;
+ }
+ if (config.hideLastChannelGroup && channelGroups->IsInLastGroup(destChannel)) {
+ destChannel = Channels.Prev(destChannel);
+ while (destChannel && destChannel->GroupSep()) {
+ destChannel = Channels.Prev(destChannel);
+ }
+ break;
+ }
+ if (found == num)
+ break;
+ found++;
+ }
+ if (!destChannel)
+ destChannel = Channels.Last();
+ while (destChannel && destChannel->GroupSep()) {
+ destChannel = Channels.Prev(destChannel);
+ }
+ return destChannel;
+}
+
+const cChannel *cEpgGrid::SeekChannelBack(int num) {
+ if (!active)
+ return NULL;
+ const cChannel *currentChannel = active->Channel();
+ const cChannel *destChannel;
+ int found = 0;
+ for (destChannel = currentChannel; destChannel ; destChannel = Channels.Prev(destChannel)) {
+ if (destChannel->GroupSep()) {
+ continue;
+ }
+ if (found == num)
+ break;
+ found++;
+ }
+ if (!destChannel)
+ destChannel = Channels.First();
+ while (destChannel && destChannel->GroupSep()) {
+ destChannel = Channels.Next(destChannel);
+ }
+ return destChannel;
+}
+
+void cEpgGrid::Debug(void) {
+ esyslog("tvguideng: ------------ debugging EpgGrid -------------");
+ timeManager->Debug();
+ for (cChannelEpg *channel = channels.First(); channel; channel = channels.Next(channel)) {
+ channel->Debug();
+ }
+ if (active) {
+ esyslog("tvguideng: Active Grid Element:");
+ active->Debug();
+ } else {
+ esyslog("tvguideng: No Active Grid Element");
+ }
+ esyslog("tvguideng: -----------------------------------------------");
+} \ No newline at end of file
diff --git a/epggrid.h b/epggrid.h
new file mode 100644
index 0000000..4514f5b
--- /dev/null
+++ b/epggrid.h
@@ -0,0 +1,73 @@
+#ifndef __TVGUIDE_EPGGRID_H
+#define __TVGUIDE_EPGGRID_H
+
+#include "config.h"
+#include "channelepg.h"
+#include "timeline.h"
+#include "channelgroups.h"
+#include "channeljump.h"
+#include "libskindesigner/osdelements.h"
+
+class cEpgGrid {
+private:
+ int lastSecond;
+ int channelsPerPage;
+ int channelGroupLast;
+ long oldActiveGridId;
+ cOsdView *rootView;
+ cTimeManager *timeManager;
+ cViewElement *back;
+ cViewElement *header;
+ cViewElement *watch;
+ cViewElement *footer;
+ cViewGrid *channelsGrid;
+ cViewGrid *epgGrid;
+ cTimeline *timeline;
+ cChannelgroups *channelGroups;
+ cList<cChannelEpg> channels;
+ cGridElement *active;
+ set<int> deletedChannels;
+ void SetActiveGrid(cGridElement *newActive);
+ void ScrollForward(void);
+ void ScrollBack(void);
+ const cChannel *SeekChannelForward(int num);
+ const cChannel *SeekChannelBack(int num);
+ void ClearOutdatedChannelHeaders(void);
+public:
+ cEpgGrid(cOsdView *rootView, cTimeManager *timeManager);
+ virtual ~cEpgGrid(void);
+ void Deactivate(bool hide) { lastSecond = -1; rootView->Deactivate(hide); };
+ void Activate(void) { DrawTime(); rootView->Activate(); };
+ void Init(const cChannel *startChannel);
+ void CreateChannels(const cChannel *startChannel, int activeChannel);
+ void Clear(void);
+ void SetTimers(void);
+ void RebuildEpgGrid(void);
+ bool TimeForward(void);
+ bool TimeBack(void);
+ bool ChannelForward(void);
+ bool ChannelBack(void);
+ const cChannel *GetNextChannelNumJump(void);
+ const cChannel *GetPrevChannelNumJump(void);
+ const cChannel *GetNextChannelGroupJump(void);
+ const cChannel *GetPrevChannelGroupJump(void);
+ bool IsFirstGroup(void);
+ bool IsLastGroup(void);
+ bool IsSecondLastGroup(void);
+ const cChannel *GetCurrentChannel(void);
+ int GetLastValidChannel(void);
+ const cEvent *GetCurrentEvent(void);
+ void DrawChannelHeaders(void);
+ void DrawTimeline(void);
+ void DrawGrid(void);
+ void UpdateActive(void);
+ void DrawHeader(void);
+ void DrawFooter(void);
+ void DrawChannelgroups(void);
+ bool DrawTime(void);
+ void Flush(void) { rootView->Display(); };
+ cChannelJump *GetChannelJumper(void);
+ void Debug(void);
+};
+
+#endif //__TVGUIDE_EPGGRID_H
diff --git a/gridelement.c b/gridelement.c
new file mode 100644
index 0000000..fe36128
--- /dev/null
+++ b/gridelement.c
@@ -0,0 +1,56 @@
+#include "gridelement.h"
+#include "channelepg.h"
+
+long cGridElement::idCounter;
+
+cGridElement::cGridElement(cChannelEpg *owner) {
+ id = idCounter;
+ idCounter++;
+ init = true;
+ active = false;
+ hasTimer = false;
+ hasSwitchTimer = false;
+ this->owner = owner;
+}
+
+cGridElement::~cGridElement(void) {
+}
+
+bool cGridElement::Match(time_t t) {
+ if ((StartTime() < t) && (EndTime() > t))
+ return true;
+ else
+ return false;
+}
+
+bool cGridElement::IsFirst(void) {
+ return owner->IsFirst(this);
+}
+
+int cGridElement::CalcOverlap(cGridElement *neighbor) {
+ int overlap = 0;
+ if (Intersects(neighbor)) {
+ if ((StartTime() <= neighbor->StartTime()) && (EndTime() <= neighbor->EndTime())) {
+ overlap = EndTime() - neighbor->StartTime();
+ } else if ((StartTime() >= neighbor->StartTime()) && (EndTime() >= neighbor->EndTime())) {
+ overlap = neighbor->EndTime() - StartTime();
+ } else if ((StartTime() >= neighbor->StartTime()) && (EndTime() <= neighbor->EndTime())) {
+ overlap = Duration();
+ } else if ((StartTime() <= neighbor->StartTime()) && (EndTime() >= neighbor->EndTime())) {
+ overlap = neighbor->EndTime() - neighbor->StartTime();
+ }
+ }
+ return overlap;
+}
+
+bool cGridElement::Intersects(cGridElement *neighbor) {
+ return ! ( (neighbor->EndTime() <= StartTime()) || (neighbor->StartTime() >= EndTime()) );
+}
+
+const cChannel *cGridElement::Channel(void) {
+ return owner->Channel();
+}
+
+cChannelEpg *cGridElement::Owner(void) {
+ return owner;
+}
diff --git a/gridelement.h b/gridelement.h
new file mode 100644
index 0000000..1183047
--- /dev/null
+++ b/gridelement.h
@@ -0,0 +1,51 @@
+#ifndef __TVGUIDE_GRID_H
+#define __TVGUIDE_GRID_H
+
+#include <vdr/tools.h>
+#include <vdr/epg.h>
+
+class cChannelEpg;
+
+class cGridElement : public cListObject {
+protected:
+ static long idCounter;
+ long id;
+ bool init;
+ bool active;
+ bool hasTimer;
+ bool hasSwitchTimer;
+ cChannelEpg *owner;
+ bool Intersects(cGridElement *neighbor);
+ virtual time_t Duration(void) {};
+ bool dummy;
+public:
+ cGridElement(cChannelEpg *owner);
+ virtual ~cGridElement(void);
+ bool IsNew(void) { return init; };
+ void Dirty(void) { init = true; };
+ void InitFinished(void) { init = false; };
+ void SetActive(void) {active = true;};
+ void SetInActive(void) {active = false;};
+ bool Match(time_t t);
+ virtual time_t StartTime(void) {};
+ virtual time_t EndTime(void) {};
+ virtual void SetStartTime(time_t start) {};
+ virtual void SetEndTime(time_t end) {};
+ int CalcOverlap(cGridElement *neighbor);
+ virtual void SetTimer(void) {};
+ virtual void SetSwitchTimer(void) {};
+ long Id(void) { return id; };
+ bool Active(void) { return active; };
+ bool HasTimer(void) {return hasTimer;};
+ bool HasSwitchTimer(void) {return hasSwitchTimer;};
+ bool IsFirst(void);
+ bool IsDummy(void) { return dummy; };
+ const cChannel *Channel(void);
+ virtual const char *Title(void) {};
+ virtual const char *ShortText(void) { return ""; };
+ virtual const cEvent *Event(void) { return NULL; };
+ cChannelEpg *Owner(void);
+ virtual void Debug(void) {};
+};
+
+#endif //__TVGUIDE_GRID_H \ No newline at end of file
diff --git a/helpers.c b/helpers.c
new file mode 100644
index 0000000..1a2ef4a
--- /dev/null
+++ b/helpers.c
@@ -0,0 +1,514 @@
+#include <string>
+#include <vector>
+#include <sstream>
+#include <algorithm>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <vdr/osd.h>
+#include <vdr/plugin.h>
+#include <vdr/skins.h>
+#include "services/epgsearch.h"
+#include "helpers.h"
+
+cPlugin *GetScraperPlugin(void) {
+ static cPlugin *pScraper = cPluginManager::GetPlugin("scraper2vdr");
+ if( !pScraper ) // if it doesn't exit, try tvscraper
+ pScraper = cPluginManager::GetPlugin("tvscraper");
+ return pScraper;
+}
+
+bool FileExists(const string &fullpath) {
+ struct stat buffer;
+ return (stat (fullpath.c_str(), &buffer) == 0);
+}
+
+bool FileExists(const string &path, const string &name, const string &ext) {
+ stringstream fileName;
+ fileName << path << name << "." << ext;
+ struct stat buffer;
+ return (stat (fileName.str().c_str(), &buffer) == 0);
+}
+
+/****************************************************************************************
+* STOPWATCH
+****************************************************************************************/
+
+cStopWatch::cStopWatch(const char* message) {
+ start = cTimeMs::Now();
+ last = start;
+ if (message) {
+ dsyslog("tvguideng: Starting StopWatch %s", message);
+ }
+}
+
+void cStopWatch::Report(const char* message) {
+ dsyslog("tvguideng: %s - needed %d ms", message, (int)(cTimeMs::Now() - last));
+ last = cTimeMs::Now();
+}
+
+void cStopWatch::Stop(const char* message) {
+ dsyslog("tvguideng: %s - needed %d ms", message, (int)(cTimeMs::Now() - start));
+}
+
+/****************************************************************************************
+* CUTTEXT
+****************************************************************************************/
+std::string CutText(std::string text, int width, const cFont *font) {
+ if (width <= font->Size())
+ return text.c_str();
+ if (font->Width(text.c_str()) < width)
+ return text.c_str();
+ cTextWrapper twText;
+ twText.Set(text.c_str(), font, width);
+ std::string cuttedTextNative = twText.GetLine(0);
+ std::stringstream sstrText;
+ sstrText << cuttedTextNative << "...";
+ std::string cuttedText = sstrText.str();
+ int actWidth = font->Width(cuttedText.c_str());
+ if (actWidth > width) {
+ int overlap = actWidth - width;
+ int charWidth = font->Width(".");
+ if (charWidth == 0)
+ charWidth = 1;
+ int cutChars = overlap / charWidth;
+ if (cutChars > 0) {
+ cuttedTextNative = cuttedTextNative.substr(0, cuttedTextNative.length() - cutChars);
+ std::stringstream sstrText2;
+ sstrText2 << cuttedTextNative << "...";
+ cuttedText = sstrText2.str();
+ }
+ }
+ return cuttedText;
+}
+
+/****************************************************************************************
+* StrToLowerCase
+****************************************************************************************/
+std::string StrToLowerCase(std::string str) {
+ std::string lowerCase = str;
+ const int length = lowerCase.length();
+ for(int i=0; i < length; ++i) {
+ lowerCase[i] = std::tolower(lowerCase[i]);
+ }
+ return lowerCase;
+}
+
+/****************************************************************************************
+* GetDirectoryFromTimer
+****************************************************************************************/
+std::string GetDirectoryFromTimer(std::string file) {
+ std::string dir = "";
+ size_t found = file.find_last_of('~');
+ if (found != std::string::npos) {
+ dir = file.substr(0, found);
+ }
+ return dir;
+}
+
+/****************************************************************************************
+* GetDirectoryFromTimer
+****************************************************************************************/
+void ReadRecordingDirectories(std::vector<std::string> *folders, cList<cNestedItem> *rootFolders, cString path) {
+ cPlugin *epgSearchPlugin = NULL;
+ epgSearchPlugin = cPluginManager::GetPlugin("epgsearch");
+ if (epgSearchPlugin) {
+ Epgsearch_services_v1_0 *epgSearch = new Epgsearch_services_v1_0;
+ if (epgSearchPlugin->Service("Epgsearch-services-v1.0", epgSearch)) {
+ std::set<std::string> epgSearchDirs = epgSearch->handler->DirectoryList();
+ std::set<std::string>::iterator it;
+ for (it = epgSearchDirs.begin(); it != epgSearchDirs.end(); it++) {
+ std::string newFolder = *it;
+ std::replace(newFolder.begin(), newFolder.end(), '/', '~');
+ folders->push_back(newFolder);
+ }
+ }
+ } else {
+ cList<cNestedItem> *foldersLevel = NULL;
+ if (rootFolders) {
+ foldersLevel = rootFolders;
+ } else {
+ foldersLevel = &Folders;
+ }
+ for (cNestedItem *folder = foldersLevel->First(); folder; folder = foldersLevel->Next(folder)) {
+ std::string strFolder = *cString::sprintf("%s%s", *path, folder->Text());
+ std::replace(strFolder.begin(), strFolder.end(), '/', '~');
+ folders->push_back(strFolder);
+ cList<cNestedItem> *subItems = folder->SubItems();
+ if (subItems) {
+ std::string strFolder2 = *cString::sprintf("%s%s", *path, folder->Text());
+ std::replace(strFolder2.begin(), strFolder2.end(), '/', '~');
+ ReadRecordingDirectories(folders, subItems, strFolder2.c_str());
+ }
+ }
+ }
+}
+
+
+/****************************************************************************************
+* DrawRoundedCorners
+****************************************************************************************/
+void DrawRoundedCorners(cPixmap *p, int posX, int posY, int width, int height, int radius, int borderWidth, tColor borderColor) {
+ if( height > 2*radius) {
+ p->DrawEllipse(cRect(posX, posY, radius, radius), borderColor, -2);
+ p->DrawEllipse(cRect(posX - borderWidth, posY - borderWidth, radius, radius), clrTransparent, -2);
+
+ p->DrawEllipse(cRect(posX+width - radius, posY, radius, radius), borderColor, -1);
+ p->DrawEllipse(cRect(posX+width - radius + borderWidth, posY - borderWidth, radius, radius), clrTransparent, -1);
+
+ p->DrawEllipse(cRect(posX, posY + height - radius, radius, radius), borderColor, -3);
+ p->DrawEllipse(cRect(posX - borderWidth, posY + height - radius + borderWidth, radius, radius), clrTransparent, -3);
+
+ p->DrawEllipse(cRect(posX + width - radius, posY + height - radius, radius, radius), borderColor, -4);
+ p->DrawEllipse(cRect(posX + width - radius + borderWidth, posY + height - radius + borderWidth, radius, radius), clrTransparent, -4);
+ }
+}
+
+
+/****************************************************************************************
+* SPLTSTRING
+****************************************************************************************/
+// split: receives a char delimiter; returns a vector of strings
+// By default ignores repeated delimiters, unless argument rep == 1.
+std::vector<std::string>& splitstring::split(char delim, int rep) {
+ if (!flds.empty()) flds.clear(); // empty vector if necessary
+ std::string work = data();
+ std::string buf = "";
+ int i = 0;
+ while (i < work.length()) {
+ if (work[i] != delim)
+ buf += work[i];
+ else if (rep == 1) {
+ flds.push_back(buf);
+ buf = "";
+ } else if (buf.length() > 0) {
+ flds.push_back(buf);
+ buf = "";
+ }
+ i++;
+ }
+ if (!buf.empty())
+ flds.push_back(buf);
+ return flds;
+}
+
+/****************************************************************************************
+* FINDIGNORECASE
+****************************************************************************************/
+int FindIgnoreCase(const std::string& expr, const std::string& query)
+{
+ const char *p = expr.c_str();
+ const char *r = strcasestr(p, query.c_str());
+
+ if (!r)
+ return -1;
+ return r - p;
+}
+
+
+/****************************************************************************************
+* GetAuxValue
+****************************************************************************************/
+char* GetAuxValue(const char* aux, const char* name) {
+ if (isempty(aux))
+ return NULL;
+
+ char* descr = strdup(aux);
+ char* beginaux = strstr(descr, "<epgsearch>");
+ char* endaux = strstr(descr, "</epgsearch>");
+ if (!beginaux || !endaux) {
+ free(descr);
+ return NULL;
+ }
+
+ beginaux += 11; // strlen("<epgsearch>");
+ endaux[0] = 0;
+ memmove(descr, beginaux, endaux - beginaux + 1);
+
+ if (strcmp(name, "epgsearch") == 0)
+ return descr; // full aux
+
+ int namelen = strlen(name);
+ char catname[100] = "";
+ catname[0] = '<';
+ memcpy(catname + 1, name, namelen);
+ catname[1 + namelen] = '>';
+ catname[2 + namelen] = 0;
+
+ char* cat = strcasestr(descr, catname);
+ if (!cat) {
+ free(descr);
+ return NULL;
+ }
+
+ cat += namelen + 2;
+ char* end = strstr(cat, "</");
+ if (!end) {
+ free(descr);
+ return NULL;
+ }
+ end[0] = 0;
+
+ int catlen = end - cat + 1;
+ char* value = (char *) malloc(catlen);
+ memcpy(value, cat, catlen);
+
+ free(descr);
+ return value;
+}
+
+char* GetAuxValue(const cRecording *recording, const char* name) {
+ if (!recording || !recording->Info())
+ return NULL;
+ return GetAuxValue(recording->Info()->Aux(), name);
+}
+
+char* GetAuxValue(const cTimer *timer, const char* name) {
+ if (!timer || !timer->Aux())
+ return NULL;
+ return GetAuxValue(timer->Aux(), name);
+}
+
+/****************************************************************************************
+* FUZZYSEARCH
+****************************************************************************************/
+
+/******************************************************************************
+FUNCTION afuzzy_init()
+ Initialization of the fuzzy search routine. This applies to the consequent
+ calls of the afuzzy_CheckRTR (whole string matching) and afuzzy_CheckSUB
+ (substring match) routines. afuzzy_init() should be called for each
+ new pattern or error length. The search is case sensitive
+
+ARGUMENTS:
+ p Pattern
+ kerr Number of possible errors. Shouldn't exceed pattern length
+ UseFilter Use agrep filter algorithm that speeds up search.
+ fuzzy pointer to the structure that will be later passes to Check*
+ (the first 6 elements should be NULLs for the first call)
+
+RETURN VALUE:
+ none
+
+ALGORITHM
+ see. the article on agrep algorithms.
+ The only change is accounting transpositions as one edit operation .
+******************************************************************************/
+void afuzzy_init(const char *p, int kerr, int UseFilter, AFUZZY *fuzzy)
+{
+ int cnt, p_len, i, j, l, d, m, dd;
+ char PatFilter[sizeof(Uint)*8 + 1];
+
+ fuzzy->k = kerr;
+ m = strlen(p);
+ fuzzy->FilterSet = 0;
+ memset(fuzzy->Map, 0 , sizeof(fuzzy->Map) );
+
+ if (fuzzy->S)
+ free(fuzzy->S);
+ if (fuzzy->R)
+ free(fuzzy->R);
+ if (fuzzy->R1)
+ free(fuzzy->R1);
+ if (fuzzy->RP)
+ free(fuzzy->RP);
+ if (fuzzy->RI)
+ free(fuzzy->RI);
+ if (fuzzy->FilterS)
+ free(fuzzy->FilterS);
+
+ fuzzy->FilterS = NULL;
+ fuzzy->S = (Uint *)calloc(m + 1, sizeof(Uint));
+ fuzzy->R = (Uint *)calloc(fuzzy->k + 1, sizeof(Uint));
+ fuzzy->R1 = (Uint *)calloc(fuzzy->k + 1, sizeof(Uint));
+ fuzzy->RI = (Uint *)calloc(fuzzy->k + 1, sizeof(Uint));
+ fuzzy->RP = (Uint *)calloc(fuzzy->k + 1, sizeof(Uint));
+
+ for (i = 0, cnt = 0; i < m; i++)
+ {
+ l = fuzzy->Map[(unsigned char)p[i]];
+ if (!l)
+ {
+ l = fuzzy->Map[(unsigned char)p[i]] = ++cnt;
+ fuzzy->S[l] = 0;
+ }
+ fuzzy->S[l] |= 1 << i;
+ }
+
+
+ for (d = 0; d <= fuzzy->k; d++)
+ fuzzy->RI[d] = (1 << d) - 1;
+
+ fuzzy->mask_ok = (1 << (m - 1));
+ fuzzy->r_size = sizeof(Uint) * (fuzzy->k + 1);
+ p_len = m;
+
+ if (p_len > (int) sizeof(Uint)*8)
+ p_len = (int) sizeof(Uint)*8;
+
+ /* If k is zero then no filter is needed! */
+ if (fuzzy->k && (p_len >= 2*(fuzzy->k + 1)) )
+ {
+ if (UseFilter)
+ {
+ fuzzy->FilterSet = 1;
+ memset(fuzzy->FilterMap, 0 , sizeof(fuzzy->FilterMap) );
+ fuzzy->FilterS = (Uint *)calloc(m + 1, sizeof(Uint));
+
+ /* Not let's fill the interleaved pattern */
+ dd = p_len / (fuzzy->k + 1);
+ p_len = dd * (fuzzy->k + 1);
+
+ for (i = 0, cnt = 0; i < dd; i++)
+ for (j = 0; j < fuzzy->k + 1; j++, cnt++)
+ PatFilter[cnt] = (unsigned char)p[j*dd + i];
+ PatFilter[p_len] = 0;
+
+ for (i = 0, cnt = 0; i < p_len; i++)
+ {
+ l = fuzzy->FilterMap[(unsigned char)PatFilter[i]];
+ if (!l)
+ {
+ l = fuzzy->FilterMap[(unsigned char)PatFilter[i]] = ++cnt;
+ fuzzy->FilterS[l] = 0;
+ }
+ fuzzy->FilterS[l] |= 1 << i;
+ }
+ fuzzy->filter_ok = 0;
+ for (i = p_len - fuzzy->k - 1; i <= p_len - 1; i++) /* k+1 times */
+ fuzzy->filter_ok |= 1 << i;
+
+ /* k+1 first bits set to 1 */
+ fuzzy->filter_shift = (1 << (fuzzy->k + 2)) - 1;
+ }
+ }
+}
+
+/******************************************************************************
+FUNCTION afuzzy_free()
+ Cleaning up after previous afuzzy_init() call.
+
+ARGUMENTS:
+ fuzzy pointer to the afuzzy parameters structure
+
+RETURN VALUE:
+ none
+******************************************************************************/
+void afuzzy_free(AFUZZY *fuzzy)
+{
+ if (fuzzy->S)
+ {
+ free(fuzzy->S);
+ fuzzy->S = NULL;
+ }
+ if (fuzzy->R)
+ {
+ free(fuzzy->R);
+ fuzzy->R = NULL;
+ }
+ if (fuzzy->R1)
+ {
+ free(fuzzy->R1);
+ fuzzy->R1 = NULL;
+ }
+ if (fuzzy->RP)
+ {
+ free(fuzzy->RP);
+ fuzzy->RP = NULL;
+ }
+ if (fuzzy->RI)
+ {
+ free(fuzzy->RI);
+ fuzzy->RI = NULL;
+ }
+ if (fuzzy->FilterS)
+ {
+ free(fuzzy->FilterS);
+ fuzzy->FilterS = NULL;
+ }
+}
+
+
+/******************************************************************************
+FUNCTION afuzzy_CheckSUB()
+ Perform a fuzzy pattern substring matching. afuzzy_init() should be
+ called previously to initialize the pattern and error length.
+ Positive result means that some part of the string given matches the
+ pattern with no more than afuzzy->k errors (1 error = 1 letter
+ replacement or transposition)
+
+ARGUMENTS:
+ t the string to test
+ fuzzy pointer to the afuzzy parameters structure
+
+RETURN VALUE:
+ 0 - no match
+ > 0 - strings match
+
+ALGORITHM
+ ????????????????
+******************************************************************************/
+int afuzzy_checkSUB(const char *t, AFUZZY *fuzzy)
+{
+ register char c;
+ register int j, d;
+
+ /* For eficciency this case should be little bit optimized */
+ if (!fuzzy->k)
+ {
+ Uint R = 0, R1;
+
+ for (j = 0; (c = t[j]) != '\0'; j++)
+ {
+ R1 = ( ((R<<1) | 1) & fuzzy->S[fuzzy->Map[(unsigned char)c]]);
+ R = R1;
+
+ if (R1 & fuzzy->mask_ok)
+ return 1;
+ } /* end for (register int j = 0 ... */
+ return 0;
+ }
+
+ if (fuzzy->FilterSet && !afuzzy_checkFLT(t, fuzzy))
+ return 0;
+
+ memcpy(fuzzy->R, fuzzy->RI, fuzzy->r_size); /* R = RI */
+
+ for (j = 0; (c = t[j]); j++)
+ {
+ for (d = 0; d <= fuzzy->k; d++)
+ {
+ fuzzy->R1[d] = (((fuzzy->R[d]<<1) | 1) &
+ fuzzy->S[fuzzy->Map[(unsigned char)c]]);
+ if (d > 0)
+ fuzzy->R1[d] |= ((fuzzy->R[d-1] | fuzzy->R1[d-1])<<1) | 1 |
+ fuzzy->R[d-1];
+ }
+ if (fuzzy->R1[fuzzy->k] & fuzzy->mask_ok)
+ return j;
+
+ memcpy(fuzzy->R, fuzzy->R1, fuzzy->r_size);
+
+ } /* end for (register int j = 0 ... */
+
+ return 0;
+}
+
+static int afuzzy_checkFLT(const char *t, AFUZZY *fuzzy)
+{
+ register Uint FilterR = 0;
+ register Uint FilterR1;
+ register int j;
+
+ for (j = 0; t[j] != '\0'; j++)
+ {
+ FilterR1 = ( ((FilterR<<(fuzzy->k+1)) | fuzzy->filter_shift) &
+ fuzzy->FilterS[fuzzy->FilterMap[(unsigned char)t[j]]]);
+ if (FilterR1 & fuzzy->filter_ok)
+ return 1;
+ FilterR = FilterR1;
+ } /* end for (register int j = 0 ... */
+
+ return 0;
+}
diff --git a/helpers.h b/helpers.h
new file mode 100644
index 0000000..5f16be6
--- /dev/null
+++ b/helpers.h
@@ -0,0 +1,87 @@
+#ifndef __TVGUIDE_HELPERS_H
+#define __TVGUIDE_HELPERS_H
+
+#include <string>
+#include <vector>
+#include <vdr/font.h>
+#include <vdr/recording.h>
+#include <vdr/plugin.h>
+
+using namespace std;
+
+cPlugin *GetScraperPlugin(void);
+
+bool FileExists(const string &fullpath);
+bool FileExists(const string &path, const string &name, const string &ext);
+
+class cStopWatch {
+private:
+ uint64_t start;
+ uint64_t last;
+public:
+ cStopWatch(const char* message = NULL);
+ ~cStopWatch(void) {};
+ void Report(const char* message);
+ void Stop(const char* message);
+};
+
+std::string CutText(std::string text, int width, const cFont *font);
+std::string StrToLowerCase(std::string str);
+std::string GetDirectoryFromTimer(std::string file);
+void ReadRecordingDirectories(std::vector<std::string> *folders, cList<cNestedItem> *rootFolders, cString path);
+void DrawRoundedCorners(cPixmap *p, int posX, int posY, int width, int height, int radius, int borderWidth, tColor borderColor);
+
+class splitstring : public std::string {
+ std::vector<std::string> flds;
+public:
+ splitstring(const char *s) : std::string(s) { };
+ std::vector<std::string>& split(char delim, int rep=0);
+};
+
+int FindIgnoreCase(const std::string& expr, const std::string& query);
+
+char* GetAuxValue(const char* aux, const char* name);
+char* GetAuxValue(const cRecording *recording, const char* name);
+char* GetAuxValue(const cTimer* timer, const char* name);
+
+#ifndef _AFUZZY_H
+#define _AFUZZY_H
+
+// source from:
+/*
+ Leonid Boitsov 2002. (itman@narod.ru)
+ C version of Stas Namin.
+ This code is a GPL software and is distributed under GNU
+ public licence without any warranty.
+*/
+
+typedef unsigned int Uint;
+
+#define MaxPatSize (sizeof(Uint) * 8)
+
+typedef struct
+{
+ Uint *R,
+ *R1,
+ *RP,
+ *S,
+ *RI;
+ Uint *FilterS;
+
+ int Map[256];
+ int FilterMap[256];
+ int k;
+ Uint mask_ok;
+ Uint filter_ok;
+ Uint filter_shift;
+ int r_size;
+ int FilterSet;
+} AFUZZY;
+
+void afuzzy_init(const char *p, int kerr, int UseFilter, AFUZZY *fuzzy);
+void afuzzy_free(AFUZZY *fuzzy);
+int afuzzy_checkSUB(const char *t, AFUZZY *fuzzy);
+static int afuzzy_checkFLT(const char *t, AFUZZY *fuzzy);
+#endif
+
+#endif // __TVGUIDE_HELPERS_H
diff --git a/libskindesigner/osdelements.c b/libskindesigner/osdelements.c
new file mode 100644
index 0000000..11915a8
--- /dev/null
+++ b/libskindesigner/osdelements.c
@@ -0,0 +1,203 @@
+#include "osdelements.h"
+
+/**********************************************************************
+* cOsdElement
+**********************************************************************/
+cOsdElement::cOsdElement(cSkinDisplayPlugin *view) {
+ this->view = view;
+}
+
+cOsdElement::~cOsdElement() {
+}
+
+void cOsdElement::ClearTokens(void) {
+ stringTokens.clear();
+ intTokens.clear();
+ loopTokens.clear();
+}
+
+void cOsdElement::AddStringToken(string key, string value) {
+ stringTokens.insert(pair<string,string>(key, value));
+}
+
+void cOsdElement::AddIntToken(string key, int value) {
+ intTokens.insert(pair<string,int>(key, value));
+}
+
+void cOsdElement::AddLoopToken(string loopName, map<string, string> &tokens) {
+ map<string, vector<map<string, string> > >::iterator hitLoop = loopTokens.find(loopName);
+ if (hitLoop == loopTokens.end()) {
+ vector<map<string, string> > tokenVector;
+ tokenVector.push_back(tokens);
+ loopTokens.insert(pair<string, vector<map<string, string> > >(loopName, tokenVector));
+ } else {
+ vector<map<string, string> > *tokenVector = &hitLoop->second;
+ tokenVector->push_back(tokens);
+ }
+}
+
+bool cOsdElement::ChannelLogoExists(string channelId) {
+ return view->ChannelLogoExists(channelId);
+}
+
+string cOsdElement::GetEpgImagePath(void) {
+ return view->GetEpgImagePath();
+}
+
+
+/**********************************************************************
+* cViewElement
+**********************************************************************/
+cViewElement::cViewElement(cSkinDisplayPlugin *view, int viewElementID) : cOsdElement(view) {
+ this->viewElementID = viewElementID;
+}
+
+cViewElement::~cViewElement() {
+}
+
+void cViewElement::Clear(void) {
+ if (!view)
+ return;
+ view->ClearViewElement(viewElementID);
+}
+
+void cViewElement::Display(void) {
+ if (!view)
+ return;
+ view->SetViewElementIntTokens(&intTokens);
+ view->SetViewElementStringTokens(&stringTokens);
+ view->SetViewElementLoopTokens(&loopTokens);
+ view->DisplayViewElement(viewElementID);
+}
+
+/**********************************************************************
+* cViewGrid
+**********************************************************************/
+cViewGrid::cViewGrid(cSkinDisplayPlugin *view, int viewGridID) : cOsdElement(view) {
+ this->viewGridID = viewGridID;
+}
+
+cViewGrid::~cViewGrid() {
+}
+
+void cViewGrid::SetGrid(long gridID, double x, double y, double width, double height) {
+ if (!view)
+ return;
+ view->SetGrid(viewGridID, gridID, x, y, width, height, &intTokens, &stringTokens);
+}
+
+void cViewGrid::SetCurrent(long gridID, bool current) {
+ if (!view)
+ return;
+ view->SetGridCurrent(viewGridID, gridID, current);
+}
+
+void cViewGrid::MoveGrid(long gridID, double x, double y, double width, double height) {
+ if (!view)
+ return;
+ view->SetGrid(viewGridID, gridID, x, y, width, height, NULL, NULL);
+}
+
+void cViewGrid::Delete(long gridID) {
+ if (!view)
+ return;
+ view->DeleteGrid(viewGridID, gridID);
+}
+
+void cViewGrid::Clear(void) {
+ if (!view)
+ return;
+ view->ClearGrids(viewGridID);
+}
+
+void cViewGrid::Display(void) {
+ if (!view)
+ return;
+ view->DisplayGrids(viewGridID);
+}
+
+/**********************************************************************
+* cViewTab
+**********************************************************************/
+cViewTab::cViewTab(cSkinDisplayPlugin *view) : cOsdElement(view) {
+}
+
+cViewTab::~cViewTab() {
+}
+
+void cViewTab::Init(void) {
+ view->SetTabIntTokens(&intTokens);
+ view->SetTabStringTokens(&stringTokens);
+ view->SetTabLoopTokens(&loopTokens);
+ view->SetTabs();
+}
+
+void cViewTab::Left(void) {
+ view->TabLeft();
+}
+
+void cViewTab::Right(void) {
+ view->TabRight();
+}
+
+void cViewTab::Up(void) {
+ view->TabUp();
+}
+
+void cViewTab::Down(void) {
+ view->TabDown();
+}
+
+void cViewTab::Display(void) {
+ if (!view)
+ return;
+ view->DisplayTabs();
+}
+
+/**********************************************************************
+* cOsdView
+**********************************************************************/
+cOsdView::cOsdView(cSkinDisplayPlugin *displayPlugin) {
+ this->displayPlugin = displayPlugin;
+}
+
+cOsdView::~cOsdView() {
+ delete displayPlugin;
+}
+
+void cOsdView::Deactivate(bool hide) {
+ if (!displayPlugin)
+ return;
+ displayPlugin->Deactivate(hide);
+}
+
+void cOsdView::Activate(void) {
+ if (!displayPlugin)
+ return;
+ displayPlugin->Activate();
+}
+
+cViewElement *cOsdView::GetViewElement(int viewElementID) {
+ if (!displayPlugin)
+ return NULL;
+ return new cViewElement(displayPlugin, viewElementID);
+}
+
+cViewGrid *cOsdView::GetViewGrid(int viewGridID) {
+ if (!displayPlugin)
+ return NULL;
+ displayPlugin->InitGrids(viewGridID);
+ return new cViewGrid(displayPlugin, viewGridID);
+}
+
+cViewTab *cOsdView::GetViewTabs(void) {
+ if (!displayPlugin)
+ return NULL;
+ return new cViewTab(displayPlugin);
+}
+
+void cOsdView::Display(void) {
+ if (!displayPlugin)
+ return;
+ displayPlugin->Flush();
+}
diff --git a/libskindesigner/osdelements.h b/libskindesigner/osdelements.h
new file mode 100644
index 0000000..057a1fd
--- /dev/null
+++ b/libskindesigner/osdelements.h
@@ -0,0 +1,91 @@
+#ifndef __OSDELEMENTS_H
+#define __OSDELEMENTS_H
+
+#include <vdr/plugin.h>
+#include "services.h"
+
+/**********************************************************************
+* cOsdElement
+**********************************************************************/
+
+class cOsdElement {
+protected:
+ cSkinDisplayPlugin *view;
+ map < string, string > stringTokens;
+ map < string, int > intTokens;
+ map < string, vector< map< string, string > > > loopTokens;
+public:
+ cOsdElement(cSkinDisplayPlugin *view);
+ virtual ~cOsdElement();
+ void AddLoopToken(string loopName, map<string, string> &tokens);
+ void AddStringToken(string key, string value);
+ void AddIntToken(string key, int value);
+ void ClearTokens(void);
+ bool ChannelLogoExists(string channelId);
+ string GetEpgImagePath(void);
+};
+
+/**********************************************************************
+* cViewElement
+**********************************************************************/
+class cViewElement : public cOsdElement {
+private:
+ int viewElementID;
+public:
+ cViewElement(cSkinDisplayPlugin *view, int viewElementID);
+ virtual ~cViewElement();
+ void Clear(void);
+ void Display(void);
+};
+
+/**********************************************************************
+* cViewGrid
+**********************************************************************/
+class cViewGrid : public cOsdElement {
+private:
+ int viewGridID;
+public:
+ cViewGrid(cSkinDisplayPlugin *view, int viewGridID);
+ virtual ~cViewGrid();
+ void SetGrid(long gridID, double x, double y, double width, double height);
+ void SetCurrent(long gridID, bool current);
+ void MoveGrid(long gridID, double x, double y, double width, double height);
+ void Delete(long gridID);
+ void Clear(void);
+ void Display(void);
+};
+
+/**********************************************************************
+* cViewTab
+**********************************************************************/
+class cViewTab : public cOsdElement {
+private:
+public:
+ cViewTab(cSkinDisplayPlugin *view);
+ virtual ~cViewTab();
+ void Init(void);
+ void Left(void);
+ void Right(void);
+ void Up(void);
+ void Down(void);
+ void Display(void);
+};
+
+/**********************************************************************
+* cOsdView
+**********************************************************************/
+class cOsdView {
+private:
+ cSkinDisplayPlugin *displayPlugin;
+public:
+ cOsdView(cSkinDisplayPlugin *displayPlugin);
+ virtual ~cOsdView();
+ void Deactivate(bool hide);
+ void Activate(void);
+ cViewElement *GetViewElement(int viewElementID);
+ cViewGrid *GetViewGrid(int viewGridID);
+ cViewTab *GetViewTabs(void);
+ void Display(void);
+};
+
+#endif // __OSDELEMENTS_H \ No newline at end of file
diff --git a/libskindesigner/services.h b/libskindesigner/services.h
new file mode 100644
index 0000000..8fa9c69
--- /dev/null
+++ b/libskindesigner/services.h
@@ -0,0 +1,131 @@
+#ifndef __SKINDESIGNERSERVICES_H
+#define __SKINDESIGNERSERVICES_H
+
+using namespace std;
+
+#include <string>
+#include <vector>
+#include <map>
+
+enum eMenuType {
+ mtList,
+ mtText
+};
+
+class cSDDisplayMenu : public cSkinDisplayMenu {
+public:
+ virtual void SetTitle(const char *Title);
+ virtual void SetPluginMenu(string name, int menu, int type, bool init);
+ virtual bool SetItemPlugin(map<string,string> *stringTokens, map<string,int> *intTokens, map<string,vector<map<string,string> > > *loopTokens, int Index, bool Current, bool Selectable);
+ virtual bool SetPluginText(map<string,string> *stringTokens, map<string,int> *intTokens, map<string,vector<map<string,string> > > *loopTokens);
+};
+
+class cSkinDisplayPlugin {
+public:
+ cSkinDisplayPlugin(void);
+ virtual ~cSkinDisplayPlugin(void);
+ virtual void Deactivate(bool hide);
+ virtual void Activate(void);
+ virtual void ClearViewElement(int id);
+ virtual void DisplayViewElement(int id);
+ virtual void SetViewElementIntTokens(map<string,int> *intTokens);
+ virtual void SetViewElementStringTokens(map<string,string> *stringTokens);
+ virtual void SetViewElementLoopTokens(map<string,vector<map<string,string> > > *loopTokens);
+ virtual void InitGrids(int viewGridID);
+ virtual void SetGrid(int viewGridID, long gridID, double x, double y, double width, double height, map<string,int> *intTokens, map<string,string> *stringTokens);
+ virtual void SetGridCurrent(int viewGridID, long gridID, bool current);
+ virtual void DeleteGrid(int viewGridID, long gridID);
+ virtual void DisplayGrids(int viewGridID);
+ virtual void ClearGrids(int viewGridID);
+ virtual void SetTabIntTokens(map<string,int> *intTokens);
+ virtual void SetTabStringTokens(map<string,string> *stringTokens);
+ virtual void SetTabLoopTokens(map<string,vector<map<string,string> > > *loopTokens);
+ virtual void SetTabs(void);
+ virtual void TabLeft(void);
+ virtual void TabRight(void);
+ virtual void TabUp(void);
+ virtual void TabDown(void);
+ virtual void DisplayTabs(void);
+ virtual void Flush(void);
+ virtual bool ChannelLogoExists(string channelId);
+ virtual string GetEpgImagePath(void);
+};
+
+/*********************************************************************
+* Data Structures for Service Calls
+*********************************************************************/
+
+// Data structure for service "RegisterPlugin"
+class RegisterPlugin {
+public:
+ RegisterPlugin(void) {
+ name = "";
+ };
+ void SetMenu(int key, string templateName) {
+ menus.insert(pair<int, string>(key, templateName));
+ }
+ void SetView(int key, string templateName) {
+ views.insert(pair<int, string>(key, templateName));
+ }
+ void SetSubView(int view, int subView, string templateName) {
+ pair<int, string> sub = make_pair(subView, templateName);
+ subViews.insert(pair<int, pair<int, string> >(view, sub));
+ }
+ void SetViewElement(int view, int viewElement, string name) {
+ map< int, map<int, string> >::iterator hit = viewElements.find(view);
+ if (hit == viewElements.end()) {
+ map<int, string> vE;
+ vE.insert(pair<int, string >(viewElement, name));
+ viewElements.insert(pair<int, map < int, string > >(view, vE));
+ } else {
+ (hit->second).insert(pair<int, string >(viewElement, name));
+ }
+ }
+ void SetViewGrid(int view, int viewGrid, string name) {
+ map< int, map<int, string> >::iterator hit = viewGrids.find(view);
+ if (hit == viewGrids.end()) {
+ map<int, string> vG;
+ vG.insert(pair<int, string >(viewGrid, name));
+ viewGrids.insert(pair<int, map < int, string > >(view, vG));
+ } else {
+ (hit->second).insert(pair<int, string >(viewGrid, name));
+ }
+ }
+// in
+ string name; //name of plugin
+ map< int, string > menus; //menus as key -> templatename hashmap
+ map< int, string> views; //standalone views as key -> templatename hashmap
+ multimap< int, pair <int, string> > subViews; //subviews of standalone views as view -> (subview, templatename) multimap
+ map< int, map <int, string> > viewElements; //viewelements as key -> (viewelement, viewelementname) hashmap
+ map< int, map <int, string> > viewGrids; //viewgrids as key -> (viewgrid, viewgridname) hashmap
+//out
+};
+
+// Data structure for service "GetDisplayMenu"
+class GetDisplayMenu {
+public:
+ GetDisplayMenu(void) {
+ displayMenu = NULL;
+ };
+// in
+//out
+ cSDDisplayMenu *displayMenu;
+};
+
+// Data structure for service "GetDisplayPlugin"
+class GetDisplayPlugin {
+public:
+ GetDisplayPlugin(void) {
+ pluginName = "";
+ viewID = -1;
+ subViewID = -1;
+ displayPlugin = NULL;
+ };
+// in
+ string pluginName;
+ int viewID;
+ int subViewID;
+//out
+ cSkinDisplayPlugin *displayPlugin;
+};
+#endif //__SKINDESIGNERSERVICES_H
diff --git a/libskindesigner/skindesignerosdbase.c b/libskindesigner/skindesignerosdbase.c
new file mode 100644
index 0000000..4d40615
--- /dev/null
+++ b/libskindesigner/skindesignerosdbase.c
@@ -0,0 +1,209 @@
+#include "skindesignerosdbase.h"
+#include "osdelements.h"
+
+/**********************************************************************
+* cSkindesignerOsdObject
+**********************************************************************/
+
+cSkindesignerOsdObject::cSkindesignerOsdObject(void) {
+ pSkinDesigner = NULL;
+ pluginName = "";
+}
+
+cSkindesignerOsdObject::~cSkindesignerOsdObject() {
+}
+
+bool cSkindesignerOsdObject::InitSkindesignerInterface(string pluginName) {
+ this->pluginName = pluginName;
+ pSkinDesigner = cPluginManager::GetPlugin("skindesigner");
+ if (!pSkinDesigner) {
+ return false;
+ }
+ return true;
+}
+
+cOsdView *cSkindesignerOsdObject::GetOsdView(int viewID, int subViewID) {
+ cSkinDisplayPlugin *displayPlugin = NULL;
+ cOsdView *view = NULL;
+ GetDisplayPlugin call;
+ call.pluginName = pluginName;
+ call.viewID = viewID;
+ call.subViewID = subViewID;
+ bool ok = pSkinDesigner->Service("GetDisplayPlugin", &call);
+ if (ok) {
+ displayPlugin = call.displayPlugin;
+ view = new cOsdView(displayPlugin);
+ }
+ return view;
+}
+
+/**********************************************************************
+* cSkindesignerOsdItem
+**********************************************************************/
+cSkindesignerOsdItem::cSkindesignerOsdItem(eOSState State) : cOsdItem(State) {
+ sdDisplayMenu = NULL;
+}
+
+cSkindesignerOsdItem::cSkindesignerOsdItem(const char *Text, eOSState State, bool Selectable) : cOsdItem(Text, State, Selectable) {
+ sdDisplayMenu = NULL;
+}
+
+cSkindesignerOsdItem::~cSkindesignerOsdItem() {
+
+}
+
+void cSkindesignerOsdItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) {
+ if (sdDisplayMenu) {
+ if (!sdDisplayMenu->SetItemPlugin(&stringTokens, &intTokens, &loopTokens, Index, Current, Selectable)) {
+ DisplayMenu->SetItem(Text(), Index, Current, Selectable);
+ }
+ } else {
+ DisplayMenu->SetItem(Text(), Index, Current, Selectable);
+ }
+}
+
+void cSkindesignerOsdItem::AddStringToken(string key, string value) {
+ stringTokens.insert(pair<string,string>(key, value));
+}
+
+void cSkindesignerOsdItem::AddIntToken(string key, int value) {
+ intTokens.insert(pair<string,int>(key, value));
+}
+
+void cSkindesignerOsdItem::AddLoopToken(string loopName, map<string, string> &tokens) {
+ map<string, vector<map<string, string> > >::iterator hitLoop = loopTokens.find(loopName);
+ if (hitLoop == loopTokens.end()) {
+ vector<map<string, string> > tokenVector;
+ tokenVector.push_back(tokens);
+ loopTokens.insert(pair<string, vector<map<string, string> > >(loopName, tokenVector));
+ } else {
+ vector<map<string, string> > *tokenVector = &hitLoop->second;
+ tokenVector->push_back(tokens);
+ }
+}
+
+
+/**********************************************************************
+* cSkindesignerOsdMenu
+**********************************************************************/
+cSkindesignerOsdMenu::cSkindesignerOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4) : cOsdMenu(Title, c0, c1, c2, c3, c4) {
+ init = true;
+ displayText = false;
+ sdDisplayMenu = NULL;
+ pluginName = "";
+ SetMenuCategory(mcPlugin);
+ SetSkinDesignerDisplayMenu();
+}
+
+cSkindesignerOsdMenu::~cSkindesignerOsdMenu() {
+
+}
+
+void cSkindesignerOsdMenu::SetPluginMenu(int menu, eMenuType type) {
+ if (type == mtList)
+ displayText = false;
+ else if (type == mtText)
+ displayText = true;
+
+ if (sdDisplayMenu) {
+ sdDisplayMenu->SetPluginMenu(pluginName, menu, type, init);
+ }
+ init = false;
+}
+
+bool cSkindesignerOsdMenu::SetSkinDesignerDisplayMenu(void) {
+ static cPlugin *pSkinDesigner = cPluginManager::GetPlugin("skindesigner");
+ if (!pSkinDesigner) {
+ return false;
+ }
+ GetDisplayMenu call;
+ bool ok = pSkinDesigner->Service("GetDisplayMenu", &call);
+ if (ok && call.displayMenu) {
+ sdDisplayMenu = call.displayMenu;
+ return true;
+ }
+ return false;
+}
+
+void cSkindesignerOsdMenu::ClearTokens(void) {
+ text = "";
+ stringTokens.clear();
+ intTokens.clear();
+ loopTokens.clear();
+}
+
+void cSkindesignerOsdMenu::AddStringToken(string key, string value) {
+ stringTokens.insert(pair<string,string>(key, value));
+}
+
+void cSkindesignerOsdMenu::AddIntToken(string key, int value) {
+ intTokens.insert(pair<string,int>(key, value));
+}
+
+void cSkindesignerOsdMenu::AddLoopToken(string loopName, map<string, string> &tokens) {
+ map<string, vector<map<string, string> > >::iterator hitLoop = loopTokens.find(loopName);
+ if (hitLoop == loopTokens.end()) {
+ vector<map<string, string> > tokenVector;
+ tokenVector.push_back(tokens);
+ loopTokens.insert(pair<string, vector<map<string, string> > >(loopName, tokenVector));
+ } else {
+ vector<map<string, string> > *tokenVector = &hitLoop->second;
+ tokenVector->push_back(tokens);
+ }
+}
+
+void cSkindesignerOsdMenu::TextKeyLeft(void) {
+ if (!displayText)
+ return;
+ DisplayMenu()->Scroll(true, true);
+}
+
+void cSkindesignerOsdMenu::TextKeyRight(void) {
+ if (!displayText)
+ return;
+ DisplayMenu()->Scroll(false, true);
+}
+
+void cSkindesignerOsdMenu::TextKeyUp(void) {
+ if (!displayText)
+ return;
+ DisplayMenu()->Scroll(true, false);
+}
+
+void cSkindesignerOsdMenu::TextKeyDown(void) {
+ if (!displayText)
+ return;
+ DisplayMenu()->Scroll(false, false);
+}
+
+void cSkindesignerOsdMenu::Display(void) {
+ if (displayText) {
+ if (sdDisplayMenu) {
+ sdDisplayMenu->SetTitle(Title());
+ if (sdDisplayMenu->SetPluginText(&stringTokens, &intTokens, &loopTokens)) {
+ sdDisplayMenu->Flush();
+ } else {
+ DisplayMenu()->Clear();
+ DisplayMenu()->SetTitle(Title());
+ DisplayMenu()->SetText(text.c_str(), false);
+ DisplayMenu()->Flush();
+ }
+ } else {
+ DisplayMenu()->Clear();
+ DisplayMenu()->SetTitle(Title());
+ DisplayMenu()->SetText(text.c_str(), false);
+ DisplayMenu()->Flush();
+ }
+ return;
+ }
+ if (sdDisplayMenu) {
+ sdDisplayMenu->SetTitle(Title());
+ for (cOsdItem *item = First(); item; item = Next(item)) {
+ cSkindesignerOsdItem *sdItem = dynamic_cast<cSkindesignerOsdItem*>(item);
+ if (sdItem) {
+ sdItem->SetDisplayMenu(sdDisplayMenu);
+ }
+ }
+ }
+ cOsdMenu::Display();
+}
diff --git a/libskindesigner/skindesignerosdbase.h b/libskindesigner/skindesignerosdbase.h
new file mode 100644
index 0000000..060ec11
--- /dev/null
+++ b/libskindesigner/skindesignerosdbase.h
@@ -0,0 +1,84 @@
+#ifndef __SKINDESIGNEROSDBASE_H
+#define __SKINDESIGNEROSDBASE_H
+
+#include <string>
+#include <vector>
+#include <map>
+#include <set>
+#include <sstream>
+#include <vdr/osdbase.h>
+#include <vdr/plugin.h>
+#include "services.h"
+
+class cOsdView;
+
+/**********************************************************************
+* cSkindesignerOsdObject
+**********************************************************************/
+class cSkindesignerOsdObject : public cOsdObject {
+protected:
+ string pluginName;
+ cPlugin *pSkinDesigner;
+ bool InitSkindesignerInterface(string pluginName);
+ cOsdView *GetOsdView(int viewID, int subViewID = -1);
+public:
+ cSkindesignerOsdObject(void);
+ virtual ~cSkindesignerOsdObject();
+ virtual void Show(void) {};
+};
+
+/**********************************************************************
+* cSkindesignerOsdItem
+**********************************************************************/
+class cSkindesignerOsdItem : public cOsdItem {
+private:
+ cSDDisplayMenu *sdDisplayMenu;
+ map < string, string > stringTokens;
+ map < string, int > intTokens;
+ map < string, vector< map< string, string > > > loopTokens;
+protected:
+public:
+ cSkindesignerOsdItem(eOSState State = osUnknown);
+ cSkindesignerOsdItem(const char *Text, eOSState State = osUnknown, bool Selectable = true);
+ virtual ~cSkindesignerOsdItem();
+ virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable);
+ void SetDisplayMenu(cSDDisplayMenu *sdDisplayMenu) { this->sdDisplayMenu = sdDisplayMenu; };
+ void AddStringToken(string key, string value);
+ void AddIntToken(string key, int value);
+ void AddLoopToken(string loopName, map<string, string> &tokens);
+};
+
+/**********************************************************************
+* cSkindesignerOsdMenu
+**********************************************************************/
+class cSkindesignerOsdMenu : public cOsdMenu {
+private:
+ bool init;
+ bool displayText;
+ string pluginName;
+ cSDDisplayMenu *sdDisplayMenu;
+ string text;
+ map < string, string > stringTokens;
+ map < string, int > intTokens;
+ map < string, vector< map< string, string > > > loopTokens;
+ bool SetSkinDesignerDisplayMenu(void);
+protected:
+ void ClearTokens(void);
+ void SetPluginName(string name) {pluginName = name; };
+ void SetPluginMenu(int menu, eMenuType type);
+ void SetText(string text) { this->text = text; };
+ void AddStringToken(string key, string value);
+ void AddIntToken(string key, int value);
+ void AddLoopToken(string loopName, map<string, string> &tokens);
+ void TextKeyLeft(void);
+ void TextKeyRight(void);
+ void TextKeyUp(void);
+ void TextKeyDown(void);
+public:
+ cSkindesignerOsdMenu(const char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0);
+ virtual ~cSkindesignerOsdMenu();
+ virtual void Display(void);
+};
+
+#endif // __SKINDESIGNEROSDBASE_H
+
diff --git a/po/de_DE.po b/po/de_DE.po
new file mode 100644
index 0000000..4ab8c7e
--- /dev/null
+++ b/po/de_DE.po
@@ -0,0 +1,604 @@
+# VDR plugin language source file.
+msgid ""
+msgstr ""
+"Project-Id-Version: vdr-tvguideng 0.0.1\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2015-03-08 17:24+0200\n"
+"PO-Revision-Date: 2015-03-08 17:49+0200\n"
+"Last-Translator: Louis\n"
+"Language-Team: \n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Main Program"
+msgstr "Hauptprogramm"
+
+msgid "Search & Record"
+msgstr "Suchen & Aufnehmen"
+
+msgid "Switch"
+msgstr "Umschalten"
+
+msgid "No EPG available"
+msgstr "Keine EPG Daten verfügbar"
+
+msgid "Channels back"
+msgstr "Kanäle zurück"
+
+msgid "Channels forward"
+msgstr "Kanäle vor"
+
+msgid "Detailed EPG"
+msgstr "Detailiertes EPG"
+
+msgid "Favorites"
+msgstr "Favoriten"
+
+msgid "all Channels"
+msgstr "alle Kanäle"
+
+msgid "root video folder"
+msgstr "Video Hauptverzeichnis"
+
+msgid "Timer Conflict"
+msgstr "Timer Konflikt"
+
+msgid "unknown channel"
+msgstr "unbekannter Kanal"
+
+msgid "Instant Record"
+msgstr "Aufnahme"
+
+msgid "Delete Timer"
+msgstr "Timer löschen"
+
+msgid "Edit Timer"
+msgstr "Timer bearbeiten"
+
+msgid "Timer Timeline"
+msgstr "Timer Tagesübersicht"
+
+msgid "Create Search Timer"
+msgstr "Suchtimer anlegen"
+
+msgid "Search Timers"
+msgstr "Suchtimer"
+
+msgid "Create Series Timer"
+msgstr "Serientimer anlegen"
+
+msgid "Create Switch Timer"
+msgstr "Umschalttimer anlegen"
+
+msgid "Delete Switch Timer"
+msgstr "Umschalttimer löschen"
+
+msgid "Search"
+msgstr "Suchen"
+
+msgid "Check for Timer Conflicts"
+msgstr "Auf Timerkonflikte prüfen"
+
+msgid "Search in Recordings"
+msgstr "In Aufnahmen suchen"
+
+msgid "Timer created"
+msgstr "Timer angelegt"
+
+msgid "Timer NOT created"
+msgstr "Timer NICHT angelegt"
+
+msgid "OK"
+msgstr "OK"
+
+msgid "Set Folder for"
+msgstr "Verzeichnis festlegen für"
+
+msgid "Timer deleted"
+msgstr "Timer gelöscht"
+
+msgid "Timer"
+msgstr "Timer"
+
+msgid "still recording - really delete?"
+msgstr "Aufzeichnung läuft - wirklich löschen?"
+
+msgid "Yes"
+msgstr "Ja"
+
+msgid "No"
+msgstr "Nein"
+
+msgid "One"
+msgstr "Ein"
+
+msgid "detected"
+msgstr "gefunden"
+
+msgid "Timer Conflicts"
+msgstr "Timerkonflikte"
+
+msgid "Show conflict"
+msgstr "Konflikt zeigen"
+
+msgid "timers involved"
+msgstr "Timer beteiligt"
+
+msgid "Ignore Conflicts"
+msgstr ""
+
+msgid "Ignore Conflict"
+msgstr "Konflikt ignorieren"
+
+msgid "No Timer Conflicts found"
+msgstr "Keine Timerkonflikte gefunden"
+
+msgid "Close"
+msgstr "Schließen"
+
+msgid "reruns for"
+msgstr "Wiederholungen für"
+
+msgid "rerun for"
+msgstr "Wiederholung für"
+
+msgid "found"
+msgstr "gefunden"
+
+msgid "Ignore reruns"
+msgstr "Wiederholungen ignorieren"
+
+msgid "No reruns found for Event"
+msgstr "Keine Wiederholungen gefunden für"
+
+msgid "Timer for"
+msgstr "Timer für"
+
+msgid "replaced by rerun"
+msgstr "ersetzt durch Wiederholung"
+
+msgid "Timer Active"
+msgstr "Timer aktiv"
+
+msgid "Priority"
+msgstr "Priorität"
+
+msgid "Lifetime"
+msgstr "Lebensdauer"
+
+msgid "Day"
+msgstr "Tag"
+
+msgid "Timer start time"
+msgstr "Timer Start Zeit"
+
+msgid "Timer stop time"
+msgstr "Timer Stop Zeit"
+
+msgid "Timer File"
+msgstr "Timer Datei"
+
+msgid "New Folder"
+msgstr "Neues Verzeichnis"
+
+msgid "Save"
+msgstr "Speichern"
+
+msgid "Cancel"
+msgstr "Abbrechen"
+
+msgid "Create Series Timer based on"
+msgstr "Serientimer anlegen basierend auf"
+
+msgid "Channel"
+msgstr "Kanal"
+
+msgid "Series Timer start time"
+msgstr "Serientimer Start Zeit"
+
+msgid "Series Timer stop time"
+msgstr "Serientimer Stop Zeit"
+
+msgid "Days to record"
+msgstr "Tage"
+
+msgid "Day to start"
+msgstr "Beginnen am"
+
+msgid "Create Timer"
+msgstr "Timer anlegen"
+
+msgid "Series Timer created"
+msgstr "Serientimer angelegt"
+
+msgid "Start"
+msgstr "Start"
+
+msgid "Stop"
+msgstr "Stop"
+
+msgid "Configure Search Timer based on"
+msgstr "Suchtimer konfigurieren basierend auf"
+
+msgid "Search Expression:"
+msgstr "Suchausdruck:"
+
+msgid "Continue"
+msgstr "Weiter"
+
+msgid "Configure Search Timer for Search String"
+msgstr "Suchtimer konfigurieren für Suchbegriff"
+
+msgid "Manually configure Options"
+msgstr "Optionen manuell konfigurieren"
+
+msgid "Use Template"
+msgstr "Template benutzen"
+
+msgid "EPGSearch Search Timers"
+msgstr "EPGSearch Suchtimer"
+
+msgid "No Search Timers Configured"
+msgstr "Keine Suchtimer angelegt"
+
+msgid "Configure Search Timer Options"
+msgstr "Suchtimer konfigurieren"
+
+msgid "Save Search Timer"
+msgstr "Suchtimer speichern"
+
+msgid "Search String"
+msgstr "Suchbegriff"
+
+msgid "Active"
+msgstr "Aktiv"
+
+msgid "Search Mode"
+msgstr "Suchmodus"
+
+msgid "Use Title"
+msgstr "Titel benutzen"
+
+msgid "Use Subtitle"
+msgstr "Untertitel benutzen"
+
+msgid "Use Description"
+msgstr "Beschreibung benutzen"
+
+msgid "Limit Channels"
+msgstr "Kanäle einschränken"
+
+msgid "Start Channel"
+msgstr "Startkanal"
+
+msgid "Stop Channel"
+msgstr "Endekanal"
+
+msgid "Use Time"
+msgstr "Zeit benutzen"
+
+msgid "Start after"
+msgstr "Start nach"
+
+msgid "Start before"
+msgstr "Start vor"
+
+msgid "Display advanced Options"
+msgstr "Erweiterte Optionen anzeigen"
+
+msgid "Limit Days of the Week"
+msgstr "Wochentage beschränken"
+
+msgid "Select Days"
+msgstr "Tage auswählen"
+
+msgid "Time margin for start in minutes"
+msgstr "Zeit vor Start in Minuten"
+
+msgid "Time margin for stop in minutes"
+msgstr "Zeit nach Ende in Minuten"
+
+msgid "Series Recording"
+msgstr "Serienaufnahme"
+
+msgid "Folder"
+msgstr "Verzeichnis"
+
+msgid "Use VPS"
+msgstr "VPS benutzen"
+
+msgid "Avoid Repeats"
+msgstr "Wiederholungen vermeiden"
+
+msgid "Number of allowed repeats"
+msgstr "Anzahl erlaubter Wiederholungen"
+
+msgid "Compare Title"
+msgstr "Titel verglechen"
+
+msgid "Compare Subtitle"
+msgstr "Untertitel vergleichen"
+
+msgid "Compare Description"
+msgstr "Beschreibung vergleichen"
+
+msgid "Use in Favorites"
+msgstr "In Favoriten benutzen"
+
+msgid "Hide advanced Options"
+msgstr "Erweiterte Optionen ausblenden"
+
+msgid "Display Results for Search Timer"
+msgstr "Ergebnisse für Suchtimer anzeigen"
+
+msgid "Really delete Search Timer"
+msgstr "Suchtimer wirklich löschen"
+
+msgid "Delete only Search Timer"
+msgstr "Nur Suchtimer löschen"
+
+msgid "Delete Search Timer and created Timers"
+msgstr "Suchtimer und erzeugte Timer löschen"
+
+msgid "Search Timer sucessfully created"
+msgstr "Suchtimer erfolgreich angelegt"
+
+msgid "Search Timer update initialised"
+msgstr "Suchtimer update initialisiert"
+
+msgid "Search Timer NOT sucessfully created"
+msgstr "Suchtimer NICHT erfolgreich angelegt"
+
+msgid "Creating Search Timer"
+msgstr "Suchtimer anlegen"
+
+msgid "Search Term"
+msgstr "Suchbegriff"
+
+msgid "Using Template"
+msgstr "Template"
+
+msgid "Use other Template"
+msgstr "Anderes Template benutzen"
+
+msgid "search results for Favorite"
+msgstr "Suchergebnisse für Favorit"
+
+msgid "search result for Favorite"
+msgstr "Suchergebnis für Favorit"
+
+msgid "search results for Search Timer"
+msgstr "Treffer für Suchtimer"
+
+msgid "search result for Search Timer"
+msgstr "Treffer für Suchtimer"
+
+msgid "Nothing found for Search String"
+msgstr "Keine Treffer für Suchbegriff"
+
+msgid "Configure Options for Switchtimer"
+msgstr "Optionen für Umschalttimer konfigurieren"
+
+msgid "Minutes before switching"
+msgstr "Minuten vor umschalten"
+
+msgid "switch"
+msgstr "umschalten"
+
+msgid "announce only"
+msgstr "nur ankündigen"
+
+msgid "ask for switch"
+msgstr "vor umschalten fragen"
+
+msgid "Switch Mode"
+msgstr "Umschaltmodus"
+
+msgid "Create"
+msgstr "Anlegen"
+
+msgid "Switch Timer sucessfully created"
+msgstr "Umschalttimer erfolgreich angelegt"
+
+msgid "Switch Timer NOT sucessfully created"
+msgstr "Umschalttimer NICHT erfolgreich angelegt"
+
+msgid "Switch Timer deleted"
+msgstr "Umschalttimer gelöscht"
+
+msgid "Channel to Search"
+msgstr "Suche auf Kanal"
+
+msgid "Search in title"
+msgstr "In Titel suchen"
+
+msgid "Search in Subtitle"
+msgstr "In Untertitel suchen"
+
+msgid "Search in Description"
+msgstr "In Beschreibung suchen"
+
+msgid "Show Search Options"
+msgstr "Suchoptionen anzeigen"
+
+msgid "Perform Search"
+msgstr "Suche ausführen"
+
+msgid "search results for"
+msgstr "Suchergebnisse für"
+
+msgid "search result for"
+msgstr "Suchergebnis für"
+
+msgid "Adapt Search"
+msgstr "Suche anpassen"
+
+msgid "Search String has to have at least three letters"
+msgstr "Suchausdruck muss mindestens drei Zeichen haben"
+
+msgid "No active Timers"
+msgstr "Keine aktiven Timer"
+
+msgid "Found"
+msgstr " "
+
+msgid "recordings"
+msgstr "Aufnahmen gefunden"
+
+msgid "recording"
+msgstr "Aufnahme gefunden"
+
+msgid "for"
+msgstr "für"
+
+msgid "No recordings found for"
+msgstr "Keine Aufnahmen gefunden für"
+
+msgid "No Favorites available"
+msgstr "Keine Favoriten verfügbar"
+
+msgid "What's on now"
+msgstr "Was läuft jetzt"
+
+msgid "What's on next"
+msgstr "Was läuft als nächstes"
+
+msgid "whole term must appear"
+msgstr "vollständiger Ausdruck"
+
+msgid "all terms must exist"
+msgstr "alle Worte"
+
+msgid "one term must exist"
+msgstr "ein Wort"
+
+msgid "exact match"
+msgstr "exakt"
+
+msgid "regular expression"
+msgstr "Regulärer Ausdruck"
+
+msgid "x channels back / forward"
+msgstr "x Kanäle zurück / vor"
+
+msgid "previous / next channel group"
+msgstr "vorherige / nächste Kanalgruppe"
+
+msgid "Timely Jump"
+msgstr "Zeitsprung"
+
+msgid "Jump to specific channel"
+msgstr "Sprung zu deinem bestimmten Kanal"
+
+msgid "Blue: Channel Switch, Ok: Detailed EPG"
+msgstr "Blau: Umschalten, OK: Detailiertes EPG"
+
+msgid "Blue: Detailed EPG, Ok: Channel Switch"
+msgstr "Blau: Detailiertes EPG, OK: Umschalten"
+
+msgid "Blue: Favorites / Switch, Ok: Detailed EPG"
+msgstr "Blau: Favoriten / Umschalten, OK: Det. EPG"
+
+msgid "Always use root video folder"
+msgstr "Immer root Video Verzeichnis benutzen"
+
+msgid "Select from folder list"
+msgstr "Verzeichnis aus Liste auswählen"
+
+msgid "Use fixed folder"
+msgstr "Festes Verzeichnis benutzen"
+
+msgid "never"
+msgstr "nie"
+
+msgid "if exists"
+msgstr "falls vorhanden"
+
+msgid "always"
+msgstr "immer"
+
+msgid "Show Main Menu Entry"
+msgstr "Hauptmenüeintrag anzeigen"
+
+msgid "Replace VDR Schedules Menu"
+msgstr "VDR Programm Menü ersetzen"
+
+msgid "Display Mode"
+msgstr "Anzeigemodus"
+
+msgid "Channels to display in horizontal View"
+msgstr "Anzahl der angezeigten Kanäle horizontal"
+
+msgid "Channels to display in vertical View"
+msgstr "Anzahl der angezeigten Kanäle vertikal"
+
+msgid "Number of Hours to display"
+msgstr "Anzuzeigende Stunden"
+
+msgid "Big Step (Keys 1 / 3) in hours"
+msgstr "Großer Sprung (Tasten 1 / 3) in Stunden"
+
+msgid "Huge Step (Keys 4 / 6) in hours"
+msgstr "Sehr großer Sprung (Tasten 4 / 6) in Stunden"
+
+msgid "Hide last Channel Group"
+msgstr "Letzte Kanalgruppe verstecken"
+
+msgid "Channel Jump Mode (Keys Green / Yellow)"
+msgstr "Kanalsprung Modus (Tasten grün / gelb)"
+
+msgid "Close TVGuide after channel switch"
+msgstr "TVGuide nach Umschalten schließen"
+
+msgid "Functionality of numeric Keys"
+msgstr "Funktion der Nummerntasten"
+
+msgid "Keys Blue and OK"
+msgstr "Tasten Blau und OK"
+
+msgid "Maximum number of reruns to display"
+msgstr "Maximale Anzahl dargestellter Wiederholungen"
+
+msgid "Minimum timely distance of rerun (in hours)"
+msgstr "Zeitlicher Mindestabstand von Wiederholungen (in Stunden)"
+
+msgid "Limit Channel Numbers for reruns"
+msgstr "Kanäle für Wiederholungen beschränken"
+
+msgid "no limit"
+msgstr "keine Beschränkung"
+
+msgid "Folder for instant Recordings"
+msgstr "Verzeichnis für Direktaufnahmen"
+
+msgid "Use Remotetimers"
+msgstr "Remotetimers verwenden"
+
+msgid "Use \"What's on now\" in favorites"
+msgstr "\"Was läuft jetzt\" in Favoriten benutzen"
+
+msgid "Use \"What's on next\" in favorites"
+msgstr "\"Was läuft als nächstes\" in Favoriten benutzen"
+
+msgid "Use user defined time 1 in favorites"
+msgstr "Benutzerdef. Zeit 1 in Favoriten benutzen"
+
+msgid "Description"
+msgstr "Beschreibung"
+
+msgid "Time"
+msgstr "Zeit"
+
+msgid "Use user defined time 2 in favorites"
+msgstr "Benutzerdef. Zeit 2 in Favoriten benutzen"
+
+msgid "Use user defined time 3 in favorites"
+msgstr "Benutzerdef. Zeit 3 in Favoriten benutzen"
+
+msgid "Use user defined time 4 in favorites"
+msgstr "Benutzerdef. Zeit 4 in Favoriten benutzen"
+
+msgid "Limit channels in favorites"
+msgstr "Kanäle in Favoriten beschränken"
diff --git a/recmanager.c b/recmanager.c
new file mode 100644
index 0000000..fbbf890
--- /dev/null
+++ b/recmanager.c
@@ -0,0 +1,710 @@
+#define __STL_CONFIG_H
+
+#include <string>
+#include <sstream>
+#include <vector>
+#include <algorithm>
+
+#include <vdr/menu.h>
+#include "recmanager.h"
+
+static int CompareRecording(const void *p1, const void *p2) {
+ return (int)((*(cRecording **)p1)->Start() - (*(cRecording **)p2)->Start());
+}
+
+cRecManager::cRecManager(void) {
+ epgSearchPlugin = NULL;
+ epgSearchAvailable = false;
+}
+
+cRecManager::~cRecManager(void) {
+}
+
+void cRecManager::SetEPGSearchPlugin(void) {
+ epgSearchPlugin = cPluginManager::GetPlugin("epgsearch");
+ if (epgSearchPlugin) {
+ epgSearchAvailable = true;
+ }
+}
+
+bool cRecManager::RefreshRemoteTimers(void) {
+ cString errorMsg;
+ if (!pRemoteTimers->Service("RemoteTimers::RefreshTimers-v1.0", &errorMsg)) {
+ esyslog("tvguide: %s", *errorMsg);
+ return false;
+ }
+ return true;
+}
+
+bool cRecManager::CheckEventForTimer(const cEvent *event) {
+ bool hasTimer = false;
+
+ if (config.useRemoteTimers && pRemoteTimers) {
+ RemoteTimers_GetMatch_v1_0 rtMatch;
+ rtMatch.event = event;
+ pRemoteTimers->Service("RemoteTimers::GetMatch-v1.0", &rtMatch);
+ if (rtMatch.timerMatch == tmFull)
+ hasTimer = true;
+ } else
+ hasTimer = event->HasTimer();
+
+ return hasTimer;
+}
+
+cTimer *cRecManager::GetTimerForEvent(const cEvent *event) {
+ cTimer *timer = NULL;
+ if (config.useRemoteTimers && pRemoteTimers) {
+ RemoteTimers_GetMatch_v1_0 rtMatch;
+ rtMatch.event = event;
+ pRemoteTimers->Service("RemoteTimers::GetMatch-v1.0", &rtMatch);
+ timer = rtMatch.timer;
+ } else
+ timer = Timers.GetMatch(event);
+ return timer;
+}
+
+cTimer *cRecManager::createTimer(const cEvent *event, std::string path) {
+ cTimer *timer = NULL;
+ if (config.useRemoteTimers && pRemoteTimers) {
+ timer = createRemoteTimer(event, path);
+ } else {
+ timer = createLocalTimer(event, path);
+ }
+ return timer;
+}
+
+cTimer *cRecManager::createLocalTimer(const cEvent *event, std::string path) {
+ cTimer *timer = new cTimer(event);
+ cTimer *t = Timers.GetTimer(timer);
+ if (t) {
+ t->OnOff();
+ t->SetEventFromSchedule();
+ delete timer;
+ timer = t;
+ isyslog("timer %s reactivated", *t->ToDescr());
+ } else {
+ Timers.Add(timer);
+ isyslog("timer %s added (active)", *timer->ToDescr());
+ }
+ SetTimerPath(timer, event, path);
+ Timers.SetModified();
+ return timer;
+}
+
+cTimer *cRecManager::createRemoteTimer(const cEvent *event, std::string path) {
+ cTimer *t = new cTimer(event);
+ SetTimerPath(t, event, path);
+ RemoteTimers_Timer_v1_0 rt;
+ rt.timer = t;
+ pRemoteTimers->Service("RemoteTimers::GetTimer-v1.0", &rt.timer);
+ if (rt.timer) {
+ rt.timer->OnOff();
+ if (!pRemoteTimers->Service("RemoteTimers::ModTimer-v1.0", &rt))
+ rt.timer = NULL;
+ } else {
+ rt.timer = t;
+ if (!pRemoteTimers->Service("RemoteTimers::NewTimer-v1.0", &rt))
+ isyslog("%s", *rt.errorMsg);
+ }
+ RefreshRemoteTimers();
+ return rt.timer;
+}
+
+void cRecManager::SetTimerPath(cTimer *timer, const cEvent *event, std::string path) {
+ if (config.instRecFolderMode == eFolderFixed) {
+ Epgsearch_services_v1_2 *epgSearch = new Epgsearch_services_v1_2;
+ std::string recDir = config.instRecFixedFolder;
+ std::replace(recDir.begin(), recDir.end(), '/', '~');
+ if (strchr(recDir.c_str(), '%') != NULL) {
+ if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) {
+ std::string newFileName = epgSearch->handler->Evaluate(recDir, event);
+ if (strchr(newFileName.c_str(), '%') == NULL) // only set directory to new value if all categories could have been replaced
+ timer->SetFile(newFileName.c_str());
+ else
+ esyslog("tvguide: timer path not set because replacing variable was not successfull: %s", newFileName.c_str());
+ }
+ } else {
+ cString newFileName;
+ if (recDir.size() > 0) {
+ newFileName = cString::sprintf("%s~%s", recDir.c_str(), timer->File());
+ timer->SetFile(*newFileName);
+ }
+ }
+ return;
+ }
+ //Set choosen path
+ cString newFileName;
+ if (path.size() > 0) {
+ std::replace(path.begin(), path.end(), '/', '~');
+ newFileName = cString::sprintf("%s~%s", path.c_str(), timer->File());
+ } else {
+ newFileName = event->Title();
+ }
+ timer->SetFile(*newFileName);
+}
+
+void cRecManager::DeleteTimer(int timerID) {
+ cTimer *t = Timers.Get(timerID);
+ if (!t)
+ return;
+ DeleteTimer(t);
+}
+
+void cRecManager::DeleteTimer(const cEvent *event) {
+ if (!event)
+ return;
+ if (config.useRemoteTimers && pRemoteTimers) {
+ DeleteRemoteTimer(event);
+ } else {
+ DeleteLocalTimer(event);
+ }
+}
+
+void cRecManager::DeleteLocalTimer(const cEvent *event) {
+ cTimer *t = Timers.GetMatch(event);
+ if (!t)
+ return;
+ DeleteTimer(t);
+}
+
+
+void cRecManager::DeleteTimer(cTimer *timer) {
+ if (timer->Recording()) {
+ timer->Skip();
+ cRecordControls::Process(time(NULL));
+ }
+ isyslog("timer %s deleted", *timer->ToDescr());
+ Timers.Del(timer, true);
+ Timers.SetModified();
+}
+
+void cRecManager::DeleteRemoteTimer(const cEvent *event) {
+ RemoteTimers_GetMatch_v1_0 rtMatch;
+ rtMatch.event = event;
+ pRemoteTimers->Service("RemoteTimers::GetMatch-v1.0", &rtMatch);
+ if (rtMatch.timer) {
+ RemoteTimers_Timer_v1_0 rt;
+ rt.timer = rtMatch.timer;
+ isyslog("remotetimer %s deleted", *rt.timer->ToDescr());
+ if (!pRemoteTimers->Service("RemoteTimers::DelTimer-v1.0", &rt))
+ isyslog("remotetimer error");
+ RefreshRemoteTimers();
+ }
+}
+
+void cRecManager::SaveTimer(cTimer *timer, cTimer newTimerSettings) {
+ if (!timer)
+ return;
+
+ bool active = newTimerSettings.HasFlags(tfActive);
+ int prio = newTimerSettings.Priority();
+ int lifetime = newTimerSettings.Lifetime();
+ time_t day = newTimerSettings.Day();
+ int start = newTimerSettings.Start();
+ int stop = newTimerSettings.Stop();
+ std::string fileName = newTimerSettings.File();
+
+ timer->SetDay(day);
+ timer->SetStart(start);
+ timer->SetStop(stop);
+ timer->SetPriority(prio);
+ timer->SetLifetime(lifetime);
+ timer->SetFile(fileName.c_str());
+
+ if (timer->HasFlags(tfActive) && !active)
+ timer->ClrFlags(tfActive);
+ else if (!timer->HasFlags(tfActive) && active)
+ timer->SetFlags(tfActive);
+
+ timer->SetEventFromSchedule();
+ if (config.useRemoteTimers && pRemoteTimers) {
+ RemoteTimers_Timer_v1_0 rt;
+ rt.timer = timer;
+ if (!pRemoteTimers->Service("RemoteTimers::ModTimer-v1.0", &rt))
+ rt.timer = NULL;
+ RefreshRemoteTimers();
+ } else {
+ Timers.SetModified();
+ }
+}
+
+
+bool cRecManager::IsRecorded(const cEvent *event) {
+ cTimer *timer = Timers.GetMatch(event);
+ if (!timer)
+ return false;
+ return timer->Recording();
+}
+
+cTVGuideTimerConflicts *cRecManager::CheckTimerConflict(void) {
+ cTVGuideTimerConflicts *conflictList = new cTVGuideTimerConflicts();
+ if (!epgSearchAvailable)
+ return conflictList;
+ Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1;
+ if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) {
+ std::list<std::string> conflicts = epgSearch->handler->TimerConflictList();
+ int numConflicts = conflicts.size();
+ if (numConflicts == 0)
+ return conflictList;
+ for (std::list<std::string>::iterator it=conflicts.begin(); it != conflicts.end(); ++it) {
+ conflictList->AddConflict(*it);
+ }
+ }
+ delete epgSearch;
+ conflictList->CalculateConflicts();
+ return conflictList;
+}
+
+void cRecManager::CreateSeriesTimer(cTimer *seriesTimer) {
+ seriesTimer->SetEventFromSchedule();
+ if (config.useRemoteTimers && pRemoteTimers) {
+ RemoteTimers_Timer_v1_0 rt;
+ rt.timer = seriesTimer;
+ if (!pRemoteTimers->Service("RemoteTimers::NewTimer-v1.0", &rt))
+ isyslog("%s", *rt.errorMsg);
+ RefreshRemoteTimers();
+ } else {
+ Timers.Add(seriesTimer);
+ Timers.SetModified();
+ }
+}
+
+
+void cRecManager::ReadEPGSearchTemplates(std::vector<TVGuideEPGSearchTemplate> *epgTemplates) {
+ cString ConfigDir = cPlugin::ConfigDirectory("epgsearch");
+ cString epgsearchConf = "epgsearchtemplates.conf";
+ cString fileName = AddDirectory(*ConfigDir, *epgsearchConf);
+ if (access(fileName, F_OK) == 0) {
+ FILE *f = fopen(fileName, "r");
+ if (f) {
+ char *s;
+ cReadLine ReadLine;
+ while ((s = ReadLine.Read(f)) != NULL) {
+ char *p = strchr(s, '#');
+ if (p)
+ *p = 0;
+ stripspace(s);
+ try {
+ if (!isempty(s)) {
+ std::string templ = s;
+ int posID = templ.find_first_of(":");
+ int posName = templ.find_first_of(":", posID+1);
+ std::string name = templ.substr(posID+1, posName - posID - 1);
+ std::string templValue = templ.substr(posName);
+ TVGuideEPGSearchTemplate tmp;
+ tmp.name = name;
+ tmp.templValue = templValue;
+ epgTemplates->push_back(tmp);
+ }
+ } catch (...){}
+ }
+ }
+ }
+}
+
+const cEvent **cRecManager::PerformSearchTimerSearch(std::string epgSearchString, int &numResults) {
+ if (!epgSearchAvailable)
+ return NULL;
+ const cEvent **searchResults = NULL;
+ Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1;
+ if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) {
+ std::list<std::string> results = epgSearch->handler->QuerySearch(epgSearchString);
+ numResults = results.size();
+ if (numResults > 0) {
+ searchResults = new const cEvent *[numResults];
+ cSchedulesLock schedulesLock;
+ const cSchedules *schedules;
+ schedules = cSchedules::Schedules(schedulesLock);
+ const cEvent *event = NULL;
+ int index=0;
+ for (std::list<std::string>::iterator it=results.begin(); it != results.end(); ++it) {
+ try {
+ splitstring s(it->c_str());
+ std::vector<std::string> flds = s.split(':', 1);
+ int eventID = atoi(flds[1].c_str());
+ std::string channelID = flds[7];
+ tChannelID chanID = tChannelID::FromString(channelID.c_str());
+ cChannel *channel = Channels.GetByChannelID(chanID);
+ if (channel) {
+ const cSchedule *Schedule = NULL;
+ Schedule = schedules->GetSchedule(channel);
+ event = Schedule->GetEvent(eventID);
+ if (event) {
+ searchResults[index] = event;
+ } else
+ return NULL;
+ } else
+ return NULL;
+ index++;
+ } catch (...){}
+ }
+ }
+ }
+ return searchResults;
+}
+
+const cEvent **cRecManager::PerformSearch(Epgsearch_searchresults_v1_0 data, int &numResults) {
+ if (epgSearchAvailable) {
+ if (epgSearchPlugin->Service("Epgsearch-searchresults-v1.0", &data)) {
+ cList<Epgsearch_searchresults_v1_0::cServiceSearchResult> *list = data.pResultList;
+ if (!list)
+ return NULL;
+ int numElements = list->Count();
+ const cEvent **searchResults = NULL;
+ if (numElements > 0) {
+ searchResults = new const cEvent *[numElements];
+ numResults = numElements;
+ int index = 0;
+ for (Epgsearch_searchresults_v1_0::cServiceSearchResult *r = list->First(); r ; r = list->Next(r)) {
+ searchResults[index] = r->event;
+ index++;
+ }
+ }
+ delete list;
+ return searchResults;
+ }
+ }
+ return NULL;
+}
+
+void cRecManager::GetSearchTimers(std::vector<cTVGuideSearchTimer> *searchTimer) {
+ if (!epgSearchAvailable) {
+ return;
+ }
+ Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1;
+ if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) {
+ std::list<std::string> searchTimerList;
+ searchTimerList = epgSearch->handler->SearchTimerList();
+ for(std::list<std::string>::iterator it = searchTimerList.begin(); it != searchTimerList.end(); it++) {
+ cTVGuideSearchTimer timer;
+ timer.SetEPGSearchString(it->c_str());
+ if (timer.Parse())
+ searchTimer->push_back(timer);
+ }
+ }
+ std::sort(searchTimer->begin(), searchTimer->end());
+}
+
+int cRecManager::CreateSearchTimer(std::string epgSearchString) {
+ int timerID = -1;
+ if (!epgSearchAvailable)
+ return timerID;
+ Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1;
+ if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) {
+ timerID = epgSearch->handler->AddSearchTimer(epgSearchString);
+ }
+ return timerID;
+}
+
+bool cRecManager::SaveSearchTimer(cTVGuideSearchTimer *searchTimer) {
+ if (!epgSearchAvailable)
+ return false;
+ Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1;
+ if (searchTimer->GetID() > -1) {
+ if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) {
+ bool success = epgSearch->handler->ModSearchTimer(searchTimer->BuildSearchString());
+ if (success) {
+ esyslog("tvguide: search timer with id %d sucessfully modified", searchTimer->GetID());
+ return true;
+ } else {
+ esyslog("tvguide: error modifying search timer with id %d, build string %s", searchTimer->GetID(), searchTimer->BuildSearchString().c_str());
+ return false;
+ }
+ }
+ } else {
+ if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) {
+ int timerID = epgSearch->handler->AddSearchTimer(searchTimer->BuildSearchString());
+ if (timerID >=0) {
+ esyslog("tvguide: search timer with id %d sucessfully created", timerID);
+ return true;
+ } else {
+ esyslog("tvguide: error creating search timer, build string %s", searchTimer->BuildSearchString().c_str());
+ return false;
+ }
+ }
+ }
+ return false;
+}
+
+void cRecManager::DeleteSearchTimer(cTVGuideSearchTimer *searchTimer, bool delTimers) {
+ if (!epgSearchAvailable)
+ return;
+ int searchTimerID = searchTimer->GetID();
+ if (delTimers) {
+ cTimer *timer = Timers.First();
+ while(timer) {
+ if (!timer->Recording()) {
+ char* searchID = GetAuxValue(timer, "s-id");
+ if (searchID) {
+ if (searchTimerID == atoi(searchID)) {
+ cTimer* timerNext = Timers.Next(timer);
+ DeleteTimer(timer);
+ timer = timerNext;
+ } else {
+ timer = Timers.Next(timer);
+ }
+ free(searchID);
+ } else {
+ timer = Timers.Next(timer);
+ }
+ } else {
+ timer = Timers.Next(timer);
+ }
+ }
+ }
+ Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1;
+ if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) {
+ bool success = epgSearch->handler->DelSearchTimer(searchTimerID);
+ if (success) {
+ esyslog("tvguide: search timer \"%s\" sucessfully deleted", searchTimer->SearchString().c_str());
+ } else {
+ esyslog("tvguide: error deleting search timer \"%s\"", searchTimer->SearchString().c_str());
+ }
+ }
+}
+
+void cRecManager::UpdateSearchTimers(void) {
+ if (epgSearchAvailable) {
+ Epgsearch_updatesearchtimers_v1_0 data;
+ data.showMessage = false;
+ epgSearchPlugin->Service("Epgsearch-updatesearchtimers-v1.0", &data);
+ }
+}
+
+// announceOnly: 0 = switch, 1 = announce only, 2 = ask for switch
+bool cRecManager::CreateSwitchTimer(const cEvent *event, cSwitchTimer switchTimer) {
+ if (epgSearchAvailable && event) {
+ Epgsearch_switchtimer_v1_0 data;
+ data.event = event;
+ data.mode = 1;
+ data.switchMinsBefore = switchTimer.switchMinsBefore;
+ data.announceOnly = switchTimer.announceOnly;
+ data.success = false;
+ epgSearchPlugin->Service("Epgsearch-switchtimer-v1.0", &data);
+ cSwitchTimer *t = new cSwitchTimer(event);
+ SwitchTimers.Add(t);
+ return data.success;
+ }
+ return false;
+}
+
+void cRecManager::DeleteSwitchTimer(const cEvent *event) {
+ SwitchTimers.DeleteSwitchTimer(event);
+ if (epgSearchAvailable) {
+ Epgsearch_switchtimer_v1_0 data;
+ data.event = event;
+ data.mode = 2;
+ data.switchMinsBefore = 0;
+ data.announceOnly = 0;
+ data.success = false;
+ epgSearchPlugin->Service("Epgsearch-switchtimer-v1.0", &data);
+ }
+}
+
+cRecording **cRecManager::SearchForRecordings(std::string searchString, int &numResults) {
+
+ cRecording **matchingRecordings = NULL;
+ int num = 0;
+ numResults = 0;
+
+ for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
+ std::string s1 = recording->Name();
+ std::string s2 = searchString;
+ if (s1.empty() || s2.empty()) continue;
+
+ // tolerance for fuzzy searching: 90% of the shorter text length, but at least 1
+ int tolerance = std::max(1, (int)std::min(s1.size(), s2.size()) / 10);
+
+ bool match = FindIgnoreCase(s1, s2) >= 0 || FindIgnoreCase(s2, s1) >= 0;
+
+ if (!match) {
+ AFUZZY af = { NULL, NULL, NULL, NULL, NULL, NULL, { 0 }, { 0 }, 0, 0, 0, 0, 0, 0 };
+ if (s1.size() > 32) s1 = s1.substr(0, 32);
+ afuzzy_init(s1.c_str(), tolerance, 0, &af);
+ /* Checking substring */
+ int res = afuzzy_checkSUB(s2.c_str(), &af);
+ afuzzy_free(&af);
+ match = (res > 0);
+ }
+
+ if (!match) {
+ AFUZZY af = { NULL, NULL, NULL, NULL, NULL, NULL, { 0 }, { 0 }, 0, 0, 0, 0, 0, 0 };
+ if (s2.size() > 32) s2 = s2.substr(0, 32);
+ afuzzy_init(s2.c_str(), tolerance, 0, &af);
+ /* Checking substring */
+ int res = afuzzy_checkSUB(s1.c_str(), &af);
+ afuzzy_free(&af);
+ match = (res > 0);
+ }
+
+ if (match) {
+ matchingRecordings = (cRecording **)realloc(matchingRecordings, (num + 1) * sizeof(cRecording *));
+ matchingRecordings[num++] = recording;
+ }
+ }
+ if (num > 0) {
+ qsort(matchingRecordings, num, sizeof(cRecording *), CompareRecording);
+ numResults = num;
+ return matchingRecordings;
+ }
+ return NULL;
+}
+
+const cEvent **cRecManager::LoadReruns(const cEvent *event, int &numResults) {
+ if (epgSearchAvailable && !isempty(event->Title())) {
+ Epgsearch_searchresults_v1_0 data;
+ std::string strQuery = event->Title();
+ if (config.useSubtitleRerun > 0) {
+ if (config.useSubtitleRerun == 2 || !isempty(event->ShortText()))
+ strQuery += "~";
+ if (!isempty(event->ShortText()))
+ strQuery += event->ShortText();
+ data.useSubTitle = true;
+ } else {
+ data.useSubTitle = false;
+ }
+ data.query = (char *)strQuery.c_str();
+ data.mode = 0;
+ data.channelNr = 0;
+ data.useTitle = true;
+ data.useDescription = false;
+
+ if (epgSearchPlugin->Service("Epgsearch-searchresults-v1.0", &data)) {
+ cList<Epgsearch_searchresults_v1_0::cServiceSearchResult>* list = data.pResultList;
+ if (!list)
+ return NULL;
+ const cEvent **searchResults = NULL;
+ int numElements = list->Count();
+ if (numElements > 0) {
+ searchResults = new const cEvent *[numElements];
+ int index = 0;
+ for (Epgsearch_searchresults_v1_0::cServiceSearchResult *r = list->First(); r; r = list->Next(r)) {
+ if ((event->ChannelID() == r->event->ChannelID()) && (event->StartTime() == r->event->StartTime()))
+ continue;
+ searchResults[index] = r->event;
+ index++;
+ }
+ delete list;
+ numResults = index;
+ return searchResults;
+ }
+ }
+ }
+ return NULL;
+}
+
+void cRecManager::GetFavorites(std::vector<cTVGuideSearchTimer> *favorites) {
+ if (!epgSearchAvailable) {
+ return;
+ }
+ Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1;
+ if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) {
+ std::list<std::string> searchTimerList;
+ searchTimerList = epgSearch->handler->SearchTimerList();
+ for(std::list<std::string>::iterator it = searchTimerList.begin(); it != searchTimerList.end(); it++) {
+ cTVGuideSearchTimer timer;
+ timer.SetEPGSearchString(it->c_str());
+ if (timer.Parse()) {
+ if (timer.UseInFavorites())
+ favorites->push_back(timer);
+ }
+ }
+ }
+
+}
+
+const cEvent **cRecManager::WhatsOnNow(bool nowOrNext, int &numResults) {
+ std::vector<const cEvent*> tmpResults;
+ cSchedulesLock schedulesLock;
+ const cSchedules *schedules = cSchedules::Schedules(schedulesLock);
+ const cChannel *startChannel = NULL, *stopChannel = NULL;
+ if (config.favLimitChannels) {
+ startChannel = Channels.GetByNumber(config.favStartChannel);
+ stopChannel = Channels.GetByNumber(config.favStopChannel);
+ }
+ if (!startChannel)
+ startChannel = Channels.First();
+
+ for (const cChannel *channel = startChannel; channel; channel = Channels.Next(channel)) {
+ if (channel->GroupSep()) continue;
+ const cSchedule *Schedule = schedules->GetSchedule(channel);
+ if (!Schedule) continue;
+
+ const cEvent *event = NULL;
+ if (nowOrNext)
+ event = Schedule->GetPresentEvent();
+ else
+ event = Schedule->GetFollowingEvent();
+ if (event) {
+ tmpResults.push_back(event);
+ }
+ if (stopChannel && (stopChannel->Number() <= channel->Number()))
+ break;
+ }
+ numResults = tmpResults.size();
+ const cEvent **results = new const cEvent *[numResults];
+ for (int i=0; i<numResults; i++) {
+ results[i] = tmpResults[i];
+ }
+
+ return results;
+}
+
+const cEvent **cRecManager::UserDefinedTime(int userTime, int &numResults) {
+ std::vector<const cEvent*> tmpResults;
+ int favTime = 0;
+ if (userTime == 1) {
+ favTime = config.favTime1;
+ } else if (userTime == 2) {
+ favTime = config.favTime2;
+ } else if (userTime == 3) {
+ favTime = config.favTime3;
+ } else if (userTime == 4) {
+ favTime = config.favTime4;
+ }
+
+ time_t now = time(0);
+ tm *midn = localtime(&now);
+ midn->tm_sec = 0;
+ midn->tm_min = 0;
+ midn->tm_hour = 0;
+ time_t midnight = mktime(midn);
+ int hours = favTime/100;
+ int mins = favTime - hours * 100;
+ time_t searchTime = midnight + hours*60*60 + mins*60;
+ if (searchTime < now)
+ searchTime += 24*60*60;
+
+ cSchedulesLock schedulesLock;
+ const cSchedules *schedules = cSchedules::Schedules(schedulesLock);
+ const cChannel *startChannel = NULL, *stopChannel = NULL;
+ if (config.favLimitChannels) {
+ startChannel = Channels.GetByNumber(config.favStartChannel);
+ stopChannel = Channels.GetByNumber(config.favStopChannel);
+ }
+ if (!startChannel)
+ startChannel = Channels.First();
+
+ for (const cChannel *channel = startChannel; channel; channel = Channels.Next(channel)) {
+ if (channel->GroupSep()) continue;
+ const cSchedule *Schedule = schedules->GetSchedule(channel);
+ if (!Schedule) continue;
+ const cEvent *event = Schedule->GetEventAround(searchTime);
+ if (!event) continue;
+ //if event is more or less over (only 15mns left), take next
+ if ((event->EndTime() - searchTime) < 15*60) {
+ event = Schedule->Events()->Next(event);
+ }
+ if (event)
+ tmpResults.push_back(event);
+ if (stopChannel && (stopChannel->Number() <= channel->Number()))
+ break;
+ }
+
+ numResults = tmpResults.size();
+ const cEvent **results = new const cEvent *[numResults];
+ for (int i=0; i<numResults; i++) {
+ results[i] = tmpResults[i];
+ }
+ return results;
+}
diff --git a/recmanager.h b/recmanager.h
new file mode 100644
index 0000000..cff14fd
--- /dev/null
+++ b/recmanager.h
@@ -0,0 +1,64 @@
+#ifndef __TVGUIDE_RECMMANAGER_H
+#define __TVGUIDE_RECMMANAGER_H
+
+#include <string>
+#include <vector>
+#include <vdr/plugin.h>
+#include "config.h"
+#include "services/epgsearch.h"
+#include "services/remotetimers.h"
+#include "searchtimer.h"
+#include "switchtimer.h"
+#include "timerconflict.h"
+#include "helpers.h"
+
+struct TVGuideEPGSearchTemplate {
+public:
+ std::string name;
+ std::string templValue;
+};
+
+// --- cRecManager -------------------------------------------------------------
+class cRecManager {
+private:
+ cPlugin *epgSearchPlugin;
+ bool epgSearchAvailable;
+public:
+ cRecManager(void);
+ void SetEPGSearchPlugin(void);
+ bool EpgSearchAvailable(void) {return epgSearchAvailable;};
+ bool RefreshRemoteTimers(void);
+ bool CheckEventForTimer(const cEvent *event);
+ cTimer *GetTimerForEvent(const cEvent *event);
+ cTimer *createTimer(const cEvent *event, std::string path = "");
+ cTimer *createLocalTimer(const cEvent *event, std::string path);
+ cTimer *createRemoteTimer(const cEvent *event, std::string path);
+ void SetTimerPath(cTimer *timer, const cEvent *event, std::string path);
+ void DeleteTimer(cTimer *timer);
+ void DeleteTimer(int timerID);
+ void DeleteTimer(const cEvent *event);
+ void DeleteLocalTimer(const cEvent *event);
+ void DeleteRemoteTimer(const cEvent *event);
+ void SaveTimer(cTimer *timer, cTimer newTimerSettings);
+ bool IsRecorded(const cEvent *event);
+ cTVGuideTimerConflicts *CheckTimerConflict(void);
+ void CreateSeriesTimer(cTimer *seriesTimer);
+ const cEvent **PerformSearchTimerSearch(std::string epgSearchString, int &numResults);
+ const cEvent **PerformSearch(Epgsearch_searchresults_v1_0 data, int &numResults);
+ void ReadEPGSearchTemplates(std::vector<TVGuideEPGSearchTemplate> *epgTemplates);
+ void GetSearchTimers(std::vector<cTVGuideSearchTimer> *timers);
+ int CreateSearchTimer(std::string epgSearchString);
+ bool SaveSearchTimer(cTVGuideSearchTimer *searchTimer);
+ void DeleteSearchTimer(cTVGuideSearchTimer *searchTimer, bool delTimers);
+ void UpdateSearchTimers(void);
+ bool CreateSwitchTimer(const cEvent *event, cSwitchTimer switchTimer);
+ void DeleteSwitchTimer(const cEvent *event);
+ cRecording **SearchForRecordings(std::string searchString, int &numResults);
+ const cEvent **LoadReruns(const cEvent *event, int &numResults);
+ void GetFavorites(std::vector<cTVGuideSearchTimer> *favorites);
+ const cEvent **WhatsOnNow(bool nowOrNext, int &numResults);
+ const cEvent **UserDefinedTime(int userTime, int &numResults);
+ virtual ~cRecManager (void);
+};
+
+#endif //__TVGUIDE_RECMMANAGER_H \ No newline at end of file
diff --git a/recmenu.c b/recmenu.c
new file mode 100644
index 0000000..76a97ee
--- /dev/null
+++ b/recmenu.c
@@ -0,0 +1,429 @@
+#include "recmenu.h"
+#include "tvguidengosd.h"
+
+// --- cRecMenu -------------------------------------------------------------
+
+cRecMenu::cRecMenu() {
+ menuWidth = 50;
+ menuHeight = 0;
+ maxMenuHeight = 98;
+ recMenuGrid = NULL;
+ osdView = NULL;
+ scrollBar = NULL;
+ back = NULL;
+ start = NULL;
+ stop = NULL;
+ itemCount = 0;
+ active = NULL;
+ header = NULL;
+ footer = NULL;
+ scrolling = false;
+}
+
+cRecMenu::~cRecMenu(void) {
+ menuItems.Clear();
+ if (scrollBar) {
+ scrollBar->Clear();
+ delete scrollBar;
+ scrollBar = NULL;
+ }
+ if (back) {
+ back->Clear();
+ delete back;
+ back = NULL;
+ }
+ if (recMenuGrid) {
+ recMenuGrid->Clear();
+ delete recMenuGrid;
+ }
+}
+
+/********************************************************************
+* Public Functions
+********************************************************************/
+
+void cRecMenu::Init(cOsdView *osdView) {
+ this->osdView = osdView;
+ recMenuGrid = osdView->GetViewGrid(vgRecordingMenu);
+ scrollBar = osdView->GetViewElement(vemScrollbar);
+ back = osdView->GetViewElement(vemBackground);
+ InitMenuItems();
+}
+
+void cRecMenu::Draw(void) {
+ DrawHeader();
+ double width = (double)menuWidth / (double)100;
+ double x = (double)(100 - menuWidth)/(double)200;
+ int totalHeight = GetHeight();
+ int yPerc = (100 - totalHeight) / 2;
+ if (header)
+ yPerc += header->GetHeight();
+ double y = (double)yPerc/(double)100;
+
+ for (cRecMenuItem *current = start; current; current = menuItems.Next(current)) {
+
+ double itemHeight = (double)(current->GetHeight())/(double)100;
+ if (current->IsNew()) {
+ current->SetTokens(recMenuGrid);
+ recMenuGrid->SetGrid(current->Id(), x, y, width, itemHeight);
+ } else {
+ recMenuGrid->MoveGrid(current->Id(), x, y, width, itemHeight);
+ }
+ if (current->Active()) {
+ recMenuGrid->SetCurrent(current->Id(), true);
+ }
+ y += itemHeight;
+ if (current == stop)
+ break;
+ }
+ DrawFooter();
+ recMenuGrid->Display();
+}
+
+eRecMenuState cRecMenu::ProcessKey(eKeys Key) {
+ eRecMenuState state = rmsContinue;
+ if (!active)
+ return state;
+
+ state = active->ProcessKey(Key);
+ if (state == rmsRefresh) {
+ //Refresh current
+ active->SetTokens(recMenuGrid);
+ active->SetNew();
+ Draw();
+ } else if (state == rmsNotConsumed) {
+ switch (Key & ~k_Repeat) {
+ case kUp:
+ if (ScrollUp(false))
+ Draw();
+ state = rmsConsumed;
+ break;
+ case kDown:
+ if (ScrollDown(false))
+ Draw();
+ state = rmsConsumed;
+ break;
+ case kLeft:
+ if (PageUp())
+ Draw();
+ state = rmsConsumed;
+ break;
+ case kRight:
+ if (PageDown())
+ Draw();
+ state = rmsConsumed;
+ break;
+ case kBack:
+ state = rmsClose;
+ break;
+ default:
+ break;
+ }
+ }
+ return state;
+}
+
+/********************************************************************
+* Protected Functions
+********************************************************************/
+void cRecMenu::AddMenuItem(cRecMenuItem *item, bool inFront) {
+ if (!inFront)
+ menuItems.Add(item);
+ else
+ menuItems.Ins(item);
+}
+
+void cRecMenu::AddHeader(cRecMenuItem *header) {
+ this->header = header;
+ maxMenuHeight -= header->GetHeight();
+}
+
+void cRecMenu::AddFooter(cRecMenuItem *footer) {
+ this->footer = footer;
+ maxMenuHeight -= footer->GetHeight();
+}
+
+int cRecMenu::GetNumActive(void) {
+ int num = 0;
+ for (cRecMenuItem *current = start; current; current = menuItems.Next(current)) {
+ if (current == active)
+ return num;
+ num++;
+ }
+}
+
+bool cRecMenu::ScrollUp(bool retry) {
+ if (active == start) {
+ bool scrolled = SeekBack(false);
+ if (scrolled && scrolling) DrawScrollbar();
+ }
+ if (footer && active == footer) {
+ recMenuGrid->SetCurrent(footer->Id(), false);
+ footer->SetInactive();
+ active = stop;
+ active->SetActive();
+ return true;
+ }
+ cRecMenuItem *prev = (cRecMenuItem*)active->Prev();
+ while (prev && !prev->Selectable()) {
+ prev = (cRecMenuItem*)prev->Prev();
+ }
+ if (prev) {
+ recMenuGrid->SetCurrent(active->Id(), false);
+ active->SetInactive();
+ active = prev;
+ active->SetActive();
+ return true;
+ } else {
+ SeekBack(false);
+ if (!retry)
+ ScrollUp(true);
+ }
+ return false;
+}
+
+bool cRecMenu::ScrollDown(bool retry) {
+ if (active == stop || retry) {
+ bool scrolled = SeekForward(false);
+ if (scrolled && scrolling) DrawScrollbar();
+ }
+ cRecMenuItem *next = (cRecMenuItem*)active->Next();
+ while (next && !next->Selectable()) {
+ if (next == stop) {
+ return ScrollDown(true);
+ }
+ next = (cRecMenuItem*)next->Next();
+ }
+ if (next) {
+ recMenuGrid->SetCurrent(active->Id(), false);
+ active->SetInactive();
+ active = next;
+ active->SetActive();
+ return true;
+ } else {
+ SeekForward(false);
+ if (!retry)
+ ScrollDown(true);
+ }
+ if (footer) {
+ recMenuGrid->SetCurrent(active->Id(), false);
+ active->SetInactive();
+ active = footer;
+ active->SetActive();
+ return true;
+ }
+ return false;
+}
+
+bool cRecMenu::PageUp(void) {
+ bool scrolled = SeekBack(true);
+ if (scrolled && scrolling) DrawScrollbar();
+ if (!scrolled) {
+ recMenuGrid->SetCurrent(active->Id(), false);
+ active->SetInactive();
+ active = start;
+ active->SetActive();
+ return true;
+ }
+ if (!active) {
+ active = stop;
+ active->SetActive();
+ }
+ return scrolled;
+}
+
+bool cRecMenu::PageDown(void) {
+ bool scrolled = SeekForward(true);
+ if (scrolled && scrolling) DrawScrollbar();
+ if (!scrolled) {
+ recMenuGrid->SetCurrent(active->Id(), false);
+ active->SetInactive();
+ active = stop;
+ active->SetActive();
+ return true;
+ }
+ if (!active) {
+ active = start;
+ active->SetActive();
+ }
+ return scrolled;
+}
+
+void cRecMenu::ClearMenuItems(void) {
+ menuItems.Clear();
+ back->Clear();
+ scrollBar->Clear();
+ recMenuGrid->Clear();
+ if (footer)
+ footer->SetNew();
+ active = NULL;
+}
+
+void cRecMenu::InitMenuItems(void) {
+ if (menuItems.Count() == 0)
+ return;
+ scrolling = false;
+ menuHeight = 0;
+ start = menuItems.First();
+ cRecMenuItem *current = start;
+ while (current) {
+ int itemHeight = current->GetHeight();
+ if (menuHeight + itemHeight > maxMenuHeight) {
+ scrolling = true;
+ break;
+ }
+ if (current->Active())
+ active = current;
+ itemCount++;
+ stop = current;
+ menuHeight += itemHeight;
+ current = menuItems.Next(current);
+ }
+ DrawBackground();
+ if (scrolling)
+ DrawScrollbar();
+}
+
+int cRecMenu::GetHeight(void) {
+ int totalHeight = menuHeight;
+ if (header)
+ totalHeight += header->GetHeight();
+ if (footer)
+ totalHeight += footer->GetHeight();
+ return totalHeight;
+}
+
+/********************************************************************
+* Private Functions
+********************************************************************/
+
+bool cRecMenu::SeekForward(bool page) {
+ int jumpStep = 0;
+ if (page)
+ jumpStep = itemCount;
+ else
+ jumpStep = itemCount/2;
+ int jump = 0;
+ cRecMenuItem *next = (cRecMenuItem*)stop->Next();
+ while (next && jump < jumpStep) {
+ stop = next;
+ menuHeight += next->GetHeight();
+ next = (cRecMenuItem*)next->Next();
+ jump++;
+ }
+ while (start && menuHeight > maxMenuHeight) {
+ if (active == start) {
+ active = NULL;
+ start->SetInactive();
+ }
+ menuHeight -= start->GetHeight();
+ recMenuGrid->Delete(start->Id());
+ start->SetNew();
+ start = (cRecMenuItem*)start->Next();
+ }
+ if (jump > 0)
+ return true;
+ return false;
+}
+
+bool cRecMenu::SeekBack(bool page) {
+ int jumpStep = 0;
+ if (page)
+ jumpStep = itemCount;
+ else
+ jumpStep = itemCount/2;
+ int jump = 0;
+ cRecMenuItem *prev = (cRecMenuItem*)start->Prev();
+ while (prev && jump < jumpStep) {
+ start = prev;
+ menuHeight += prev->GetHeight();
+ prev = (cRecMenuItem*)prev->Prev();
+ jump++;
+ }
+ while (stop && menuHeight > maxMenuHeight) {
+ if (active == stop) {
+ active = NULL;
+ stop->SetInactive();
+ }
+ menuHeight -= stop->GetHeight();
+ recMenuGrid->Delete(stop->Id());
+ stop->SetNew();
+ stop = (cRecMenuItem*)stop->Prev();
+ }
+ if (jump > 0)
+ return true;
+ return false;
+}
+
+void cRecMenu::DrawBackground(void) {
+ back->Clear();
+ back->ClearTokens();
+ back->AddIntToken("menuwidth", menuWidth + 2);
+ back->AddIntToken("menuheight", GetHeight() + 2);
+ back->AddIntToken("hasscrollbar", scrolling);
+ back->Display();
+}
+
+void cRecMenu::DrawScrollbar(void) {
+ if (menuItems.Count() == 0)
+ return;
+ int scrollBarHeight = (double)itemCount / (double)menuItems.Count() * 1000;
+ int startPos = 0;
+ for (cRecMenuItem *current = menuItems.First(); current; current = menuItems.Next(current)) {
+ if (start == current)
+ break;
+ startPos++;
+ }
+ int offset = (double)startPos / (double)menuItems.Count() * 1000;
+ scrollBar->Clear();
+ scrollBar->ClearTokens();
+ scrollBar->AddIntToken("menuwidth", menuWidth + 2);
+ int y = (100 - GetHeight())/2;
+ if (header)
+ y += header->GetHeight();
+
+ scrollBar->AddIntToken("posy", y);
+ scrollBar->AddIntToken("totalheight", menuHeight);
+ scrollBar->AddIntToken("height", scrollBarHeight);
+ scrollBar->AddIntToken("offset", offset);
+ scrollBar->Display();
+}
+
+void cRecMenu::DrawHeader(void) {
+ if (!header)
+ return;
+ double width = (double)menuWidth / (double)100;
+ double x = (double)(100 - menuWidth)/(double)200;
+ int totalHeight = GetHeight();
+ double y = (double)((100 - totalHeight) / 2) / (double)100;
+
+ if (header->IsNew()) {
+ recMenuGrid->ClearTokens();
+ header->SetTokens(recMenuGrid);
+ recMenuGrid->SetGrid(header->Id(), x, y, width, (double)header->GetHeight()/(double)100);
+ }
+}
+
+void cRecMenu::DrawFooter(void) {
+ if (!footer)
+ return;
+ double width = (double)menuWidth / (double)100;
+ double x = (double)(100 - menuWidth)/(double)200;
+ int totalHeight = GetHeight();
+ int totalMenuHeight = menuHeight;
+ if (header)
+ totalMenuHeight += header->GetHeight();
+ double y = (double)((100 - totalHeight) / 2 + totalMenuHeight) / (double)100;
+
+ if (footer->IsNew()) {
+ recMenuGrid->ClearTokens();
+ footer->SetTokens(recMenuGrid);
+ recMenuGrid->SetGrid(footer->Id(), x, y, width, (double)footer->GetHeight()/(double)100);
+ } else {
+ recMenuGrid->MoveGrid(footer->Id(), x, y, width, (double)footer->GetHeight()/(double)100);
+ }
+ if (footer->Active()) {
+ active = footer;
+ recMenuGrid->SetCurrent(footer->Id(), true);
+ }
+} \ No newline at end of file
diff --git a/recmenu.h b/recmenu.h
new file mode 100644
index 0000000..99111c2
--- /dev/null
+++ b/recmenu.h
@@ -0,0 +1,59 @@
+#ifndef __TVGUIDE_RECMENU_H
+#define __TVGUIDE_RECMENU_H
+
+#include <list>
+#include "libskindesigner/osdelements.h"
+#include "recmenuitem.h"
+
+// --- cRecMenu -------------------------------------------------------------
+
+class cRecMenu {
+private:
+ int menuWidth;
+ int menuHeight;
+ int maxMenuHeight;
+ cOsdView *osdView;
+ cViewElement *back;
+ cViewElement *scrollBar;
+ cViewGrid *recMenuGrid;
+ cList<cRecMenuItem> menuItems;
+ cRecMenuItem *start;
+ cRecMenuItem *stop;
+ int itemCount;
+ cRecMenuItem *active;
+ cRecMenuItem *header;
+ cRecMenuItem *footer;
+ bool scrolling;
+ bool SeekForward(bool page);
+ bool SeekBack(bool page);
+ void AdjustActive(bool down);
+ void DrawScrollbar(void);
+ void DrawHeader(void);
+ void DrawFooter(void);
+protected:
+ void SetMenuWidth(int width) { menuWidth = width; };
+ void AddMenuItem(cRecMenuItem *item, bool inFront = false);
+ void AddHeader(cRecMenuItem *header);
+ void AddFooter(cRecMenuItem *footer);
+ int GetNumActive(void);
+ cRecMenuItem *GetActive(void) { return active; };
+ bool ScrollUp(bool retry);
+ bool ScrollDown(bool retry);
+ bool PageUp(void);
+ bool PageDown(void);
+ void ClearMenuItems(void);
+ void InitMenuItems(void);
+ int GetWidth(void) { return menuWidth; };
+ int GetHeight(void);
+public:
+ cRecMenu(void);
+ virtual ~cRecMenu(void);
+ void Init(cOsdView *osdView);
+ void DrawBackground(void);
+ void Draw(void);
+ void Hide(void) { osdView->Deactivate(true); };
+ void Show(void) { osdView->Activate(); };
+ void Flush(void) { osdView->Display(); };
+ virtual eRecMenuState ProcessKey(eKeys Key);
+};
+#endif //__TVGUIDE_RECMENU_H \ No newline at end of file
diff --git a/recmenuitem.c b/recmenuitem.c
new file mode 100644
index 0000000..566de82
--- /dev/null
+++ b/recmenuitem.c
@@ -0,0 +1,1573 @@
+#include "recmenuitem.h"
+#include "recmenus.h"
+#include "config.h"
+#include "helpers.h"
+#include <math.h>
+#include <wctype.h>
+#include <vdr/remote.h>
+
+long cRecMenuItem::idCounter;
+
+// --- cRecMenuItem -------------------------------------------------------------
+
+cRecMenuItem::cRecMenuItem(void) {
+ id = idCounter;
+ idCounter++;
+ init = true;
+ active = false;
+ selectable = true;
+ action = rmsNotConsumed;
+ height = 0;
+ text = "";
+}
+
+cRecMenuItem::~cRecMenuItem(void) {
+}
+
+bool cRecMenuItem::IsNew(void) {
+ if (init) {
+ init = false;
+ return true;
+ }
+ return false;
+}
+
+// --- cRecMenuItemInfo -------------------------------------------------------
+
+cRecMenuItemInfo::cRecMenuItemInfo(string line1, int numLines, string line2, string line3, string line4) {
+ selectable = false;
+ this->numLines = numLines;
+ this->line1 = line1;
+ this->line2 = line2;
+ this->line3 = line3;
+ this->line4 = line4;
+ if (numLines == 1)
+ height = 12;
+ else if (numLines == 2)
+ height = 16;
+ else if (numLines == 3)
+ height = 20;
+ else if (numLines == 4)
+ height = 24;
+ this->active = false;
+}
+
+cRecMenuItemInfo::~cRecMenuItemInfo(void) {
+}
+
+void cRecMenuItemInfo::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("info", 1);
+ menu->AddIntToken("lines", numLines);
+ menu->AddStringToken("line1", line1);
+ menu->AddStringToken("line2", line2);
+ menu->AddStringToken("line3", line3);
+ menu->AddStringToken("line4", line4);
+}
+
+// --- cRecMenuItemButton -------------------------------------------------------
+
+cRecMenuItemButton::cRecMenuItemButton(string text, eRecMenuState action, bool active) {
+ selectable = true;
+ height = 8;
+ this->text = text;
+ this->action = action;
+ this->active = active;
+}
+
+cRecMenuItemButton::~cRecMenuItemButton(void) {
+}
+
+void cRecMenuItemButton::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("button", 1);
+ menu->AddStringToken("buttontext", text);
+}
+
+eRecMenuState cRecMenuItemButton::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kOk:
+ return action;
+ break;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemButtonYesNo -------------------------------------------------------
+cRecMenuItemButtonYesNo::cRecMenuItemButtonYesNo(string textYes,
+ string textNo,
+ eRecMenuState actionYes,
+ eRecMenuState actionNo,
+ bool active) {
+ height = 8;
+ selectable = true;
+ this->textYes = textYes;
+ this->textNo = textNo;
+ this->action = actionYes;
+ this->actionNo = actionNo;
+ this->active = active;
+ yesActive = true;
+}
+
+cRecMenuItemButtonYesNo::~cRecMenuItemButtonYesNo(void) {
+}
+
+void cRecMenuItemButtonYesNo::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("buttonyesno", 1);
+ menu->AddIntToken("yes", yesActive);
+ menu->AddStringToken("textyes", textYes);
+ menu->AddStringToken("textno", textNo);
+}
+
+eRecMenuState cRecMenuItemButtonYesNo::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ if (!yesActive) {
+ yesActive = true;
+ return rmsRefresh;
+ } else
+ return rmsNotConsumed;
+ break;
+ case kRight:
+ if (yesActive) {
+ yesActive = false;
+ return rmsRefresh;
+ } else
+ return rmsNotConsumed;
+ break;
+ case kOk:
+ if (yesActive)
+ return action;
+ return actionNo;
+ break;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+
+// --- cRecMenuItemInt -------------------------------------------------------
+cRecMenuItemInt::cRecMenuItemInt(string text,
+ int initialVal,
+ int minVal,
+ int maxVal,
+ bool active,
+ int *callback,
+ eRecMenuState action) {
+
+ height = 8;
+ selectable = true;
+ this->text = text;
+ this->currentVal = initialVal;
+ this->minVal = minVal;
+ this->maxVal = maxVal;
+ this->active = active;
+ this->callback = callback;
+ this->action = action;
+ fresh = true;
+}
+
+cRecMenuItemInt::~cRecMenuItemInt(void) {
+}
+
+void cRecMenuItemInt::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("intselector", 1);
+ menu->AddIntToken("value", currentVal);
+ menu->AddStringToken("text", text);
+}
+
+eRecMenuState cRecMenuItemInt::ProcessKey(eKeys Key) {
+ int oldValue = currentVal;
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ fresh = true;
+ if (currentVal > minVal) {
+ currentVal--;
+ if (callback)
+ *callback = currentVal;
+ }
+ return rmsRefresh;
+ break;
+ case kRight:
+ fresh = true;
+ if (currentVal < maxVal) {
+ currentVal++;
+ if (callback)
+ *callback = currentVal;
+ }
+ return rmsRefresh;
+ break;
+ case k0 ... k9:
+ if (fresh) {
+ currentVal = 0;
+ fresh = false;
+ }
+ currentVal = currentVal * 10 + (Key - k0);
+ if (!((currentVal >= minVal) && (currentVal <= maxVal)))
+ currentVal = oldValue;
+ if (callback)
+ *callback = currentVal;
+ return rmsRefresh;
+ break;
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemBool -------------------------------------------------------
+cRecMenuItemBool::cRecMenuItemBool(string text,
+ bool initialVal,
+ bool active,
+ bool *callback,
+ eRecMenuState action) {
+ height = 8;
+ selectable = true;
+ this->text = text;
+ this->yes = initialVal;
+ this->active = active;
+ this->callback = callback;
+ this->action = action;
+}
+
+cRecMenuItemBool::~cRecMenuItemBool(void) {
+}
+
+void cRecMenuItemBool::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("boolselector", 1);
+ menu->AddIntToken("value", yes);
+ menu->AddStringToken("text", text);
+}
+
+eRecMenuState cRecMenuItemBool::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ case kRight:
+ yes = !yes;
+ if (callback)
+ *callback = yes;
+ return rmsRefresh;
+ break;
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemSelect -------------------------------------------------------
+cRecMenuItemSelect::cRecMenuItemSelect(string text,
+ vector<string> strings,
+ int initialVal,
+ bool active,
+ int *callback,
+ eRecMenuState action) {
+ height = 8;
+ selectable = true;
+ this->text = text;
+ this->strings = strings;
+ numValues = strings.size();
+ if ((initialVal < 0) || (initialVal > numValues-1))
+ this->currentVal = 0;
+ else
+ this->currentVal = initialVal;
+ this->active = active;
+ this->callback = callback;
+ this->action = action;
+}
+
+cRecMenuItemSelect::~cRecMenuItemSelect(void) {
+}
+
+void cRecMenuItemSelect::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("stringselector", 1);
+ menu->AddStringToken("text", text);
+ menu->AddStringToken("value", strings[currentVal]);
+}
+
+eRecMenuState cRecMenuItemSelect::ProcessKey(eKeys Key) {
+ int oldValue = currentVal;
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ currentVal--;
+ if (currentVal<0)
+ currentVal = numValues - 1;
+ if (callback)
+ *callback = currentVal;
+ return rmsRefresh;
+ break;
+ case kRight:
+ currentVal = (currentVal+1)%numValues;
+ if (callback)
+ *callback = currentVal;
+ return rmsRefresh;
+ break;
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemText -------------------------------------------------------
+cRecMenuItemText::cRecMenuItemText(string text,
+ char *initialVal,
+ int length,
+ bool active,
+ char *callback) {
+
+ height = 16;
+ selectable = true;
+ this->text = text;
+ value = initialVal;
+ this->active = active;
+ this->callback = callback;
+ this->length = length;
+ allowed = trVDR(FileNameChars);
+ pos = -1;
+ offset = 0;
+ insert = uppercase = false;
+ newchar = true;
+ lengthUtf8 = 0;
+ valueUtf8 = NULL;
+ allowedUtf8 = NULL;
+ charMapUtf8 = NULL;
+ currentCharUtf8 = NULL;
+ lastKey = kNone;
+ buffer = "";
+}
+
+cRecMenuItemText::~cRecMenuItemText(void) {
+ delete[] valueUtf8;
+ delete[] allowedUtf8;
+ delete[] charMapUtf8;
+}
+
+void cRecMenuItemText::EnterEditMode(void) {
+ if (!valueUtf8) {
+ valueUtf8 = new uint[length];
+ lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
+ int l = strlen(allowed) + 1;
+ allowedUtf8 = new uint[l];
+ Utf8ToArray(allowed, allowedUtf8, l);
+ const char *charMap = trVDR("CharMap$ 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9");
+ l = strlen(charMap) + 1;
+ charMapUtf8 = new uint[l];
+ Utf8ToArray(charMap, charMapUtf8, l);
+ currentCharUtf8 = charMapUtf8;
+ AdvancePos();
+ }
+}
+
+void cRecMenuItemText::LeaveEditMode(bool SaveValue) {
+ if (valueUtf8) {
+ if (SaveValue) {
+ Utf8FromArray(valueUtf8, value, length);
+ stripspace(value);
+ if (callback) {
+ strncpy(callback, value, TEXTINPUTLENGTH);
+ }
+ }
+ lengthUtf8 = 0;
+ delete[] valueUtf8;
+ valueUtf8 = NULL;
+ delete[] allowedUtf8;
+ allowedUtf8 = NULL;
+ delete[] charMapUtf8;
+ charMapUtf8 = NULL;
+ pos = -1;
+ offset = 0;
+ newchar = true;
+ }
+}
+
+void cRecMenuItemText::AdvancePos(void) {
+ if (pos < length - 2 && pos < lengthUtf8) {
+ if (++pos >= lengthUtf8) {
+ if (pos >= 2 && valueUtf8[pos - 1] == ' ' && valueUtf8[pos - 2] == ' ')
+ pos--; // allow only two blanks at the end
+ else {
+ valueUtf8[pos] = ' ';
+ valueUtf8[pos + 1] = 0;
+ lengthUtf8++;
+ }
+ }
+ }
+ newchar = true;
+ if (!insert && Utf8is(alpha, valueUtf8[pos]))
+ uppercase = Utf8is(upper, valueUtf8[pos]);
+}
+
+uint cRecMenuItemText::Inc(uint c, bool Up) {
+ uint *p = IsAllowed(c);
+ if (!p)
+ p = allowedUtf8;
+ if (Up) {
+ if (!*++p)
+ p = allowedUtf8;
+ } else if (--p < allowedUtf8) {
+ p = allowedUtf8;
+ while (*p && *(p + 1))
+ p++;
+ }
+ return *p;
+}
+
+void cRecMenuItemText::Type(uint c) {
+ if (insert && lengthUtf8 < length - 1)
+ Insert();
+ valueUtf8[pos] = c;
+ if (pos < length - 2)
+ pos++;
+ if (pos >= lengthUtf8) {
+ valueUtf8[pos] = ' ';
+ valueUtf8[pos + 1] = 0;
+ lengthUtf8 = pos + 1;
+ }
+}
+
+void cRecMenuItemText::Insert(void) {
+ memmove(valueUtf8 + pos + 1, valueUtf8 + pos, (lengthUtf8 - pos + 1) * sizeof(*valueUtf8));
+ lengthUtf8++;
+ valueUtf8[pos] = ' ';
+}
+
+void cRecMenuItemText::Delete(void) {
+ memmove(valueUtf8 + pos, valueUtf8 + pos + 1, (lengthUtf8 - pos) * sizeof(*valueUtf8));
+ lengthUtf8--;
+}
+
+uint *cRecMenuItemText::IsAllowed(uint c) {
+ if (allowedUtf8) {
+ for (uint *a = allowedUtf8; *a; a++) {
+ if (c == *a)
+ return a;
+ }
+ }
+ return NULL;
+}
+
+void cRecMenuItemText::SetText(void) {
+ if (InEditMode()) {
+ int textAreaWidth = 800;
+ if (pos < offset)
+ offset = pos;
+ int WidthFromOffset = 0;
+ int EndPos = lengthUtf8;
+ for (int i = offset; i < lengthUtf8; i++) {
+ WidthFromOffset += 20;
+ if (WidthFromOffset > textAreaWidth) {
+ if (pos >= i) {
+ do {
+ WidthFromOffset -= 20;
+ offset++;
+ } while (WidthFromOffset > textAreaWidth && offset < pos);
+ EndPos = pos + 1;
+ } else {
+ EndPos = i;
+ break;
+ }
+ }
+ }
+ char buf[1000];
+ char *p = buf;
+ if (offset)
+ *p++ = '<';
+ p += Utf8FromArray(valueUtf8 + offset, p, sizeof(buf) - (p - buf), pos - offset);
+ *p++ = '[';
+ if (insert && newchar)
+ *p++ = ']';
+ p += Utf8FromArray(&valueUtf8[pos], p, sizeof(buf) - (p - buf), 1);
+ if (!(insert && newchar))
+ *p++ = ']';
+ p += Utf8FromArray(&valueUtf8[pos + 1], p, sizeof(buf) - (p - buf), EndPos - pos - 1);
+ if (EndPos != lengthUtf8)
+ *p++ = '>';
+ *p = 0;
+ buffer = buf;
+ } else {
+ buffer = "";
+ }
+}
+
+void cRecMenuItemText::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("textinput", 1);
+ menu->AddIntToken("editmode", InEditMode());
+ menu->AddStringToken("text", text);
+ if (buffer.size() > 0) {
+ menu->AddStringToken("value", buffer);
+ } else {
+ menu->AddStringToken("value", value);
+ }
+}
+
+eRecMenuState cRecMenuItemText::ProcessKey(eKeys Key) {
+ bool consumed = false;
+ bool SameKey = NORMALKEY(Key) == lastKey;
+
+ if (Key != kNone) {
+ lastKey = NORMALKEY(Key);
+ } else if (!newchar && k0 <= lastKey && lastKey <= k9 && autoAdvanceTimeout.TimedOut()) {
+ AdvancePos();
+ newchar = true;
+ currentCharUtf8 = NULL;
+ SetText();
+ return rmsRefresh;
+ }
+
+ switch ((int)Key) {
+ case kRed: // Switch between upper- and lowercase characters
+ if (InEditMode()) {
+ if (!insert || !newchar) {
+ uppercase = !uppercase;
+ valueUtf8[pos] = uppercase ? Utf8to(upper, valueUtf8[pos]) : Utf8to(lower, valueUtf8[pos]);
+ }
+ consumed = true;
+ }
+ break;
+ case kGreen: // Toggle insert/overwrite modes
+ if (InEditMode()) {
+ insert = !insert;
+ newchar = true;
+ consumed = true;
+ }
+ break;
+ case kYellow|k_Repeat:
+ case kYellow: // Remove the character at the current position; in insert mode it is the character to the right of the cursor
+ if (InEditMode()) {
+ if (lengthUtf8 > 1) {
+ if (!insert || pos < lengthUtf8 - 1)
+ Delete();
+ else if (insert && pos == lengthUtf8 - 1)
+ valueUtf8[pos] = ' '; // in insert mode, deleting the last character replaces it with a blank to keep the cursor position
+ // reduce position, if we removed the last character
+ if (pos == lengthUtf8)
+ pos--;
+ } else if (lengthUtf8 == 1)
+ valueUtf8[0] = ' '; // This is the last character in the string, replace it with a blank
+ if (Utf8is(alpha, valueUtf8[pos]))
+ uppercase = Utf8is(upper, valueUtf8[pos]);
+ newchar = true;
+ consumed = true;
+ }
+ break;
+ case kLeft|k_Repeat:
+ case kLeft:
+
+ if (pos > 0) {
+ if (!insert || newchar)
+ pos--;
+ newchar = true;
+ if (!insert && Utf8is(alpha, valueUtf8[pos]))
+ uppercase = Utf8is(upper, valueUtf8[pos]);
+ }
+ consumed = true;
+ break;
+ case kRight|k_Repeat:
+ case kRight:
+ if (InEditMode()) {
+ AdvancePos();
+ } else {
+ EnterEditMode();
+ }
+ consumed = true;
+ break;
+ case kUp|k_Repeat:
+ case kUp:
+ case kDown|k_Repeat:
+ case kDown:
+ if (InEditMode()) {
+ if (insert && newchar) {
+ // create a new character in insert mode
+ if (lengthUtf8 < length - 1)
+ Insert();
+ }
+ if (uppercase)
+ valueUtf8[pos] = Utf8to(upper, Inc(Utf8to(lower, valueUtf8[pos]), NORMALKEY(Key) == kUp));
+ else
+ valueUtf8[pos] = Inc( valueUtf8[pos], NORMALKEY(Key) == kUp);
+ newchar = false;
+ consumed = true;
+ }
+ break;
+ case k0|k_Repeat ... k9|k_Repeat:
+ case k0 ... k9: {
+ if (InEditMode()) {
+ if (Setup.NumberKeysForChars) {
+ if (!SameKey) {
+ if (!newchar)
+ AdvancePos();
+ currentCharUtf8 = NULL;
+ }
+ if (!currentCharUtf8 || !*currentCharUtf8 || *currentCharUtf8 == '\t') {
+ // find the beginning of the character map entry for Key
+ int n = NORMALKEY(Key) - k0;
+ currentCharUtf8 = charMapUtf8;
+ while (n > 0 && *currentCharUtf8) {
+ if (*currentCharUtf8++ == '\t')
+ n--;
+ }
+ // find first allowed character
+ while (*currentCharUtf8 && *currentCharUtf8 != '\t' && !IsAllowed(*currentCharUtf8))
+ currentCharUtf8++;
+ }
+ if (*currentCharUtf8 && *currentCharUtf8 != '\t') {
+ if (insert && newchar) {
+ // create a new character in insert mode
+ if (lengthUtf8 < length - 1)
+ Insert();
+ }
+ valueUtf8[pos] = *currentCharUtf8;
+ if (uppercase)
+ valueUtf8[pos] = Utf8to(upper, valueUtf8[pos]);
+ // find next allowed character
+ do {
+ currentCharUtf8++;
+ } while (*currentCharUtf8 && *currentCharUtf8 != '\t' && !IsAllowed(*currentCharUtf8));
+ newchar = false;
+ autoAdvanceTimeout.Set(AUTO_ADVANCE_TIMEOUT);
+ }
+ } else {
+ Type('0' + NORMALKEY(Key) - k0);
+ }
+ consumed = true;
+ }
+ break; }
+ case kBack:
+ case kOk:
+ if (InEditMode()) {
+ LeaveEditMode(Key == kOk);
+ } else {
+ EnterEditMode();
+ }
+ consumed = true;
+ break;
+ default:
+ if (InEditMode() && BASICKEY(Key) == kKbd) {
+ int c = KEYKBD(Key);
+ if (c <= 0xFF) {
+ if (IsAllowed(Utf8to(lower, c)))
+ Type(c);
+ else {
+ switch (c) {
+ case 0x7F: // backspace
+ if (pos > 0) {
+ pos--;
+ ProcessKey(kYellow);
+ }
+ break;
+ default: ;
+ }
+ }
+ } else {
+ switch (c) {
+ case kfHome: pos = 0; break;
+ case kfEnd: pos = lengthUtf8 - 1; break;
+ case kfIns: ProcessKey(kGreen);
+ case kfDel: ProcessKey(kYellow);
+ default: ;
+ }
+ }
+ consumed = true;
+ }
+ break;
+ }
+ SetText();
+ if (consumed)
+ return rmsRefresh;
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemTime -------------------------------------------------------
+cRecMenuItemTime::cRecMenuItemTime(string text,
+ int initialVal,
+ bool active,
+ int *callback,
+ eRecMenuState action) {
+ height = 8;
+ selectable = true;
+ this->text = text;
+ this->value = initialVal;
+ hh = value / 100;
+ mm = value % 100;
+ pos = 0;
+ fresh = true;
+ this->active = active;
+ this->callback = callback;
+ this->action = action;
+}
+
+cRecMenuItemTime::~cRecMenuItemTime(void) {
+}
+
+void cRecMenuItemTime::SetTokens(cViewGrid *menu) {
+ char buf[10];
+ switch (pos) {
+ case 1: snprintf(buf, sizeof(buf), "%01d-:--", hh / 10); break;
+ case 2: snprintf(buf, sizeof(buf), "%02d:--", hh); break;
+ case 3: snprintf(buf, sizeof(buf), "%02d:%01d-", hh, mm / 10); break;
+ default: snprintf(buf, sizeof(buf), "%02d:%02d", hh, mm);
+ }
+ menu->ClearTokens();
+ menu->AddIntToken("timeselector", 1);
+ menu->AddStringToken("text", text);
+ menu->AddStringToken("value", buf);
+}
+
+eRecMenuState cRecMenuItemTime::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kLeft|k_Repeat:
+ case kLeft: {
+ if (--mm < 0) {
+ mm = 59;
+ if (--hh < 0)
+ hh = 23;
+ }
+ fresh = true;
+ value = hh * 100 + mm;
+ if (callback)
+ *callback = value;
+ return rmsRefresh;
+ break; }
+ case kRight|k_Repeat:
+ case kRight: {
+ if (++mm > 59) {
+ mm = 0;
+ if (++hh > 23)
+ hh = 0;
+ }
+ fresh = true;
+ value = hh * 100 + mm;
+ if (callback)
+ *callback = value;
+ return rmsRefresh;
+ break; }
+ case k0|k_Repeat ... k9|k_Repeat:
+ case k0 ... k9: {
+ if (fresh || pos > 3) {
+ pos = 0;
+ fresh = false;
+ }
+ int n = Key - k0;
+ switch (pos) {
+ case 0:
+ if (n <= 2) {
+ hh = n * 10;
+ mm = 0;
+ pos++;
+ }
+ break;
+ case 1:
+ if (hh + n <= 23) {
+ hh += n;
+ pos++;
+ }
+ break;
+ case 2:
+ if (n <= 5) {
+ mm += n * 10;
+ pos++;
+ }
+ break;
+ case 3:
+ if (mm + n <= 59) {
+ mm += n;
+ pos++;
+ }
+ break;
+ default: ;
+ }
+ value = hh * 100 + mm;
+ if (callback)
+ *callback = value;
+ return rmsRefresh;
+ break; }
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemDay -------------------------------------------------------
+cRecMenuItemDay::cRecMenuItemDay(string text,
+ time_t initialVal,
+ bool active,
+ time_t *callback,
+ eRecMenuState action) {
+
+ height = 8;
+ selectable = true;
+ this->text = text;
+ this->currentVal = cTimer::SetTime(initialVal, 0);
+ this->active = active;
+ this->callback = callback;
+ this->action = action;
+}
+
+cRecMenuItemDay::~cRecMenuItemDay(void) {
+}
+
+void cRecMenuItemDay::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("dayselector", 1);
+ menu->AddStringToken("text", text);
+ menu->AddStringToken("value", *DateString(currentVal));
+}
+
+eRecMenuState cRecMenuItemDay::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ currentVal -= 60*60*24;
+ if (callback)
+ *callback = currentVal;
+ return rmsRefresh;
+ break;
+ case kRight:
+ currentVal += 60*60*24;
+ if (callback)
+ *callback = currentVal;
+ return rmsRefresh;
+ break;
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemChannelChooser -------------------------------------------------------
+cRecMenuItemChannelChooser::cRecMenuItemChannelChooser(string text,
+ cChannel *initialChannel,
+ bool active,
+ int *callback,
+ eRecMenuState action) {
+
+ height = 8;
+ selectable = true;
+ this->text = text;
+ this->channel = initialChannel;
+ if (initialChannel)
+ initialChannelSet = true;
+ else
+ initialChannelSet = false;
+ channelNumber = 0;
+ fresh = true;
+ this->active = active;
+ this->callback = callback;
+ this->action = action;
+}
+
+cRecMenuItemChannelChooser::~cRecMenuItemChannelChooser(void) {
+}
+
+void cRecMenuItemChannelChooser::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("channelselector", 1);
+ menu->AddStringToken("text", text);
+ if (channel) {
+ menu->AddIntToken("channelnumber", channel->Number());
+ menu->AddStringToken("channelname", channel->Name());
+ string channelId = *(channel->GetChannelID().ToString());
+ menu->AddStringToken("channelid", channelId);
+ menu->AddIntToken("channellogoexisis", menu->ChannelLogoExists(channelId));
+ } else {
+ menu->AddIntToken("channelnumber", 0);
+ menu->AddStringToken("channelname", tr("all Channels"));
+ menu->AddStringToken("channelid", "");
+ menu->AddIntToken("channellogoexisis", false);
+ }
+}
+
+eRecMenuState cRecMenuItemChannelChooser::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kLeft: {
+ fresh = true;
+ if (!channel)
+ return rmsConsumed;
+ cChannel *prev = channel;
+ cChannel *firstChannel = Channels.First();
+ if(firstChannel->GroupSep())
+ firstChannel = Channels.Next(firstChannel);
+ if (prev == firstChannel) {
+ if (!initialChannelSet)
+ channel = NULL;
+ } else {
+ while (prev = Channels.Prev(prev)) {
+ if(!prev->GroupSep()) {
+ channel = prev;
+ break;
+ }
+ }
+ }
+ if (callback) {
+ if (channel)
+ *callback = channel->Number();
+ else
+ *callback = 0;
+ }
+ return rmsRefresh;
+ break; }
+ case kRight: {
+ fresh = true;
+ if (!channel) {
+ channel = Channels.First();
+ if(channel->GroupSep())
+ channel = Channels.Next(channel);
+ } else {
+ cChannel *next = channel;
+ while (next = Channels.Next(next)) {
+ if(!next->GroupSep()) {
+ channel = next;
+ break;
+ }
+ }
+ }
+ if (callback) {
+ if (channel)
+ *callback = channel->Number();
+ else
+ *callback = 0;
+ }
+ return rmsRefresh;
+ break; }
+ case k0 ... k9: {
+ if (fresh) {
+ channelNumber = 0;
+ fresh = false;
+ }
+ channelNumber = channelNumber * 10 + (Key - k0);
+ cChannel *chanNew = Channels.GetByNumber(channelNumber);
+ if (chanNew) {
+ channel = chanNew;
+ if (callback)
+ *callback = channel->Number();
+ }
+ return rmsRefresh;
+ break; }
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemDayChooser -------------------------------------------------------
+cRecMenuItemDayChooser::cRecMenuItemDayChooser(string text,
+ int weekdays,
+ bool active,
+ int *callback) {
+ height = 8;
+ selectable = true;
+ this->text = text;
+ if (weekdays < 1)
+ weekdays *= -1;
+ this->weekdays = weekdays;
+ this->active = active;
+ this->callback = callback;
+ selectedDay = 0;
+}
+
+cRecMenuItemDayChooser::~cRecMenuItemDayChooser(void) {
+}
+
+void cRecMenuItemDayChooser::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("weekdayselector", 1);
+ menu->AddStringToken("text", text);
+ menu->AddIntToken("dayselected", selectedDay);
+
+ string days = trVDR("MTWTFSS");
+ for (unsigned day=0; day<days.length(); ++day) {
+ string strDay = *cString::sprintf("%c", days.at(day));
+ stringstream nameWeekday;
+ nameWeekday << "day" << day << "abbr";
+ stringstream nameWeekdaySet;
+ nameWeekdaySet << "day" << day << "set";
+ menu->AddStringToken(nameWeekday.str(), strDay);
+ menu->AddIntToken(nameWeekdaySet.str(), WeekDaySet(day));
+ }
+}
+
+bool cRecMenuItemDayChooser::WeekDaySet(unsigned day) {
+ return weekdays & (1 << day);
+}
+
+void cRecMenuItemDayChooser::ToggleDay(void) {
+ bool dayActive = WeekDaySet(selectedDay);
+ int dayBit = pow(2, selectedDay);
+ if (dayActive) {
+ weekdays -= dayBit;
+ } else {
+ weekdays += dayBit;
+ }
+ if (callback) {
+ *callback = weekdays;
+ }
+}
+
+eRecMenuState cRecMenuItemDayChooser::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kLeft: {
+ selectedDay--;
+ if (selectedDay<0)
+ selectedDay += 7;
+ return rmsRefresh;
+ break; }
+ case kRight: {
+ selectedDay = (selectedDay+1)%7;
+ return rmsRefresh;
+ break; }
+ case kOk:
+ ToggleDay();
+ return rmsRefresh;
+ break;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemSelectDirectory -------------------------------------------------------
+cRecMenuItemSelectDirectory::cRecMenuItemSelectDirectory(string text,
+ string originalFolder,
+ bool active,
+ char *callback,
+ eRecMenuState action,
+ bool isSearchTimer) {
+
+ height = 8;
+ selectable = true;
+ this->text = text;
+ this->originalFolder = originalFolder;
+ this->active = active;
+ this->callback = callback;
+ this->action = action;
+ folders.push_back(tr("root video folder"));
+ if (isSearchTimer && config.instRecFixedFolder.size() > 0)
+ folders.push_back(config.instRecFixedFolder);
+ ReadRecordingDirectories(&folders, NULL, "");
+ numValues = folders.size();
+ this->currentVal = GetInitial();
+}
+
+void cRecMenuItemSelectDirectory::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("directoryselector", 1);
+ menu->AddStringToken("text", text);
+ menu->AddStringToken("folder", folders[currentVal]);
+}
+
+eRecMenuState cRecMenuItemSelectDirectory::ProcessKey(eKeys Key) {
+ int oldValue = currentVal;
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ currentVal--;
+ if (currentVal<0)
+ currentVal = numValues - 1;
+ if (callback) {
+ SetCallback();
+ }
+ return rmsRefresh;
+ break;
+ case kRight: {
+ currentVal = (currentVal+1)%numValues;
+ if (callback) {
+ SetCallback();
+ }
+ return rmsRefresh;
+ break; }
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+void cRecMenuItemSelectDirectory::SetCallback(void) {
+ string newFolder = folders[currentVal];
+ if (!newFolder.compare(tr("root video folder")))
+ newFolder = "";
+ strncpy(callback, newFolder.c_str(), TEXTINPUTLENGTH);
+}
+
+int cRecMenuItemSelectDirectory::GetInitial(void) {
+ if (originalFolder.size() == 0)
+ return 0;
+ for (int i=0; i < numValues; i++) {
+ if (!folders[i].compare(originalFolder)) {
+ return i;
+ }
+ }
+ return 0;
+}
+
+// --- cRecMenuItemTimerConflictHeader -------------------------------------------------------
+cRecMenuItemTimerConflictHeader::cRecMenuItemTimerConflictHeader(time_t conflictStart,
+ time_t conflictStop,
+ time_t overlapStart,
+ time_t overlapStop) {
+ height = 10;
+ selectable = false;
+ active = false;
+ this->conflictStart = conflictStart;
+ this->conflictStop = conflictStop;
+ this->overlapStart = overlapStart;
+ this->overlapStop = overlapStop;
+}
+
+cRecMenuItemTimerConflictHeader::~cRecMenuItemTimerConflictHeader(void) {
+}
+
+void cRecMenuItemTimerConflictHeader::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("timerconflictheader", 1);
+ menu->AddStringToken("text", tr("Timer Conflict"));
+ menu->AddStringToken("conflictstart", *TimeString(conflictStart));
+ menu->AddStringToken("conflictstop", *TimeString(conflictStop));
+ menu->AddStringToken("overlapstart", *TimeString(overlapStart));
+ menu->AddStringToken("overlapstop", *TimeString(overlapStop));
+
+ int olStart = (double)(overlapStart - conflictStart) / (double)(conflictStop - conflictStart) * 100;
+ int olWidth = (double)(overlapStop - overlapStart) / (double)(conflictStop - conflictStart) * 100;
+ menu->AddIntToken("overlapstartpercent", olStart);
+ menu->AddIntToken("overlapwidthpercent", olWidth);
+}
+
+// --- cRecMenuItemTimer -------------------------------------------------------
+cRecMenuItemTimer::cRecMenuItemTimer(const cTimer *timer,
+ eRecMenuState action1,
+ eRecMenuState action2,
+ eRecMenuState action3,
+ eRecMenuState action4,
+ time_t conflictStart,
+ time_t conflictStop,
+ time_t overlapStart,
+ time_t overlapStop,
+ bool active) {
+ height = 12;
+ selectable = true;
+ this->timer = timer;
+ this->action = action1;
+ this->action2 = action2;
+ this->action3 = action3;
+ this->action4 = action4;
+ iconActive = 0;
+ this->conflictStart = conflictStart;
+ this->conflictStop = conflictStop;
+ this->overlapStart = overlapStart;
+ this->overlapStop = overlapStop;
+ this->active = active;
+}
+
+cRecMenuItemTimer::~cRecMenuItemTimer(void) {
+}
+
+void cRecMenuItemTimer::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("timerconflict", 1);
+
+ const cChannel *channel = timer->Channel();
+ int channelTransponder = 0;
+ string channelName = "";
+ string channelId = "";
+ if (channel) {
+ channelTransponder = channel->Transponder();
+ channelName = channel->Name() ? channel->Name() : "";
+ channelId = *(channel->GetChannelID().ToString());
+ }
+ menu->AddIntToken("transponder", channelTransponder);
+ menu->AddStringToken("channelname", channelName);
+ menu->AddStringToken("channelid", channelId);
+
+ const cEvent *event = timer->Event();
+ string timerTitle = "";
+ if (event && event->Title()) {
+ timerTitle = event->Title();
+ }
+ menu->AddStringToken("timertitle", timerTitle);
+ menu->AddStringToken("starttime", *TimeString(timer->StartTime()));
+ menu->AddStringToken("stoptime", *TimeString(timer->StopTime()));
+ menu->AddStringToken("date", *ShortDateString(timer->StartTime()));
+ menu->AddStringToken("weekday", *WeekDayName(timer->StartTime()));
+
+ menu->AddIntToken("infoactive", (iconActive==0)?true:false);
+ menu->AddIntToken("deleteactive", (iconActive==1)?true:false);
+ menu->AddIntToken("editactive", (iconActive==2)?true:false);
+ menu->AddIntToken("searchactive", (iconActive==3)?true:false);
+
+
+ int conflictTime = conflictStop - conflictStart;
+ int timerStart = (double)(timer->StartTime() - conflictStart) / (double)conflictTime * 100;
+ int timerWidth = (double)(timer->StopTime() - timer->StartTime()) / (double)conflictTime * 100;
+ int olStart = (double)(overlapStart - conflictStart) / (double)(conflictStop - conflictStart) * 100;
+ int olWidth = (double)(overlapStop - overlapStart) / (double)(conflictStop - conflictStart) * 100;
+ menu->AddIntToken("timerstartpercent", timerStart);
+ menu->AddIntToken("timerwidthpercent", timerWidth);
+ menu->AddIntToken("overlapstartpercent", olStart);
+ menu->AddIntToken("overlapwidthpercent", olWidth);
+}
+
+eRecMenuState cRecMenuItemTimer::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ if (iconActive > 0) {
+ iconActive--;
+ return rmsRefresh;
+ } else
+ return rmsNotConsumed;
+ break;
+ case kRight:
+ if (iconActive < 3) {
+ iconActive++;
+ return rmsRefresh;
+ } else
+ return rmsNotConsumed;
+ break;
+ case kOk:
+ if (iconActive == 0)
+ return action;
+ else if (iconActive == 1)
+ return action2;
+ else if (iconActive == 2)
+ return action3;
+ else if (iconActive == 3)
+ return action4;
+ break;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemEvent -------------------------------------------------------
+cRecMenuItemEvent::cRecMenuItemEvent(const cEvent *event,
+ eRecMenuState action1,
+ eRecMenuState action2,
+ bool active) {
+ height = 8;
+ selectable = true;
+ this->event = event;
+ this->action = action1;
+ this->action2 = action2;
+ iconActive = 0;
+ this->active = active;
+}
+
+cRecMenuItemEvent::~cRecMenuItemEvent(void) {
+}
+
+void cRecMenuItemEvent::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("event", 1);
+
+ const cChannel *channel = Channels.GetByChannelID(event->ChannelID());
+ string channelName = "";
+ string channelId = "";
+ if (channel && channel->Name()) {
+ channelName = channel->Name();
+ channelId = *(channel->GetChannelID().ToString());
+ menu->AddIntToken("channelnumber", channel->Number());
+ }
+
+ menu->AddStringToken("channelname", channelName);
+ menu->AddStringToken("channelid", channelId);
+ menu->AddIntToken("channellogoexisis", menu->ChannelLogoExists(channelId));
+ menu->AddStringToken("title", event->Title() ? event->Title() : "");
+ menu->AddStringToken("shorttext", event->ShortText() ? event->ShortText() : "");
+ menu->AddStringToken("date", *ShortDateString(event->StartTime()));
+ menu->AddStringToken("weekday", *WeekDayName(event->StartTime()));
+ menu->AddStringToken("starttime", *TimeString(event->StartTime()));
+ menu->AddStringToken("stoptime", *TimeString(event->EndTime()));
+ menu->AddIntToken("hastimer", event->HasTimer());
+}
+
+eRecMenuState cRecMenuItemEvent::ProcessKey(eKeys Key) {
+ bool consumed = false;
+ switch (Key & ~k_Repeat) {
+ case kOk:
+ return action;
+ break;
+ case kRed:
+ if (!event->HasTimer())
+ return action2;
+ break;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemRecording -------------------------------------------------------
+cRecMenuItemRecording::cRecMenuItemRecording(cRecording *recording, bool active) {
+ height = 8;
+ selectable = true;
+ this->recording = recording;
+ this->active = active;
+}
+
+void cRecMenuItemRecording::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("recording", 1);
+ if (!recording)
+ return;
+ const cRecordingInfo *recInfo = recording->Info();
+ cChannel *channel = NULL;
+ if (recInfo)
+ channel = Channels.GetByChannelID(recInfo->ChannelID());
+ string channelName = tr("unknown channel");
+ string channelId = "";
+ if (channel && channel->Name()) {
+ channelName = channel->Name();
+ channelId = *(channel->GetChannelID().ToString());
+ menu->AddIntToken("channelnumber", channel->Number());
+ }
+ menu->AddStringToken("channelname", channelName);
+ menu->AddStringToken("channelid", channelId);
+ menu->AddIntToken("channellogoexisis", menu->ChannelLogoExists(channelId));
+
+ string recName = recording->Name() ? recording->Name() : "";
+ string recDate = *ShortDateString(recording->Start());
+ string recStartTime = *TimeString(recording->Start());
+ int recDuration = recording->LengthInSeconds() / 60;
+
+ menu->AddStringToken("recname", recName);
+ menu->AddStringToken("recdate", recDate);
+ menu->AddStringToken("recstarttime", recStartTime);
+ menu->AddIntToken("recduration", recDuration);
+}
+
+// --- cRecMenuItemSearchTimer -------------------------------------------------------
+cRecMenuItemSearchTimer::cRecMenuItemSearchTimer(cTVGuideSearchTimer timer,
+ eRecMenuState action1,
+ eRecMenuState action2,
+ eRecMenuState action3,
+ bool active) {
+ height = 8;
+ this->timer = timer;
+ this->action = action1;
+ this->action2 = action2;
+ this->action3 = action3;
+ selectable = true;
+ this->active = active;
+ iconActive = 0;
+}
+
+cRecMenuItemSearchTimer::~cRecMenuItemSearchTimer(void) {
+}
+
+void cRecMenuItemSearchTimer::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("searchtimer", 1);
+ menu->AddIntToken("timeractive", timer.Active());
+ menu->AddStringToken("searchstring", timer.SearchString());
+ menu->AddIntToken("activetimers", timer.GetNumTimers());
+ menu->AddIntToken("recordingsdone", timer.GetNumRecordings());
+ menu->AddIntToken("searchactive", (iconActive==0)?true:false);
+ menu->AddIntToken("editactive", (iconActive==1)?true:false);
+ menu->AddIntToken("deleteactive", (iconActive==2)?true:false);
+}
+
+eRecMenuState cRecMenuItemSearchTimer::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ if (iconActive > 0) {
+ iconActive--;
+ return rmsRefresh;
+ }
+ return rmsNotConsumed;
+ break;
+ case kRight: {
+ if (iconActive < 2) {
+ iconActive++;
+ return rmsRefresh;
+ }
+ return rmsNotConsumed;
+ break; }
+ case kOk:
+ if (iconActive == 0)
+ return action;
+ else if (iconActive == 1)
+ return action2;
+ else if (iconActive == 2)
+ return action3;
+ break;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemTimelineHeader -------------------------------------------------------
+cRecMenuItemTimelineHeader::cRecMenuItemTimelineHeader(time_t day) {
+ height = 15;
+ timer = NULL;
+ this->day = day;
+ selectable = false;
+ active = false;
+}
+
+cRecMenuItemTimelineHeader::~cRecMenuItemTimelineHeader(void) {
+}
+
+void cRecMenuItemTimelineHeader::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("timelineheader", 1);
+ string daydate = *DateString(day);
+ menu->AddStringToken("date", daydate);
+ if (!timer) {
+ menu->AddIntToken("timerset", false);
+ return;
+ }
+ menu->AddIntToken("timerset", true);
+
+ const cChannel *channel = timer->Channel();
+ string channelName = "";
+ string channelId = "";
+ int channelNumber = 0;
+ int transponder = 0;
+ if (channel) {
+ channelName = channel->Name() ? channel->Name() : "";
+ channelId = *(channel->GetChannelID().ToString());
+ channelNumber = channel->Number();
+ transponder = channel->Transponder();
+ }
+ menu->AddStringToken("channelname", channelName);
+ menu->AddStringToken("channelid", channelId);
+ menu->AddIntToken("channelnumber", channelNumber);
+ menu->AddIntToken("channeltransponder", transponder);
+ menu->AddIntToken("channellogoexisis", menu->ChannelLogoExists(channelId));
+
+ menu->AddStringToken("timerstart", *TimeString(timer->StartTime()));
+ menu->AddStringToken("timerstop", *TimeString(timer->StopTime()));
+
+ const cEvent *event = timer->Event();
+ string eventTitle = "";
+ string eventShortText = "";
+ string eventStart = "";
+ string eventStop = "";
+ if (event) {
+ eventTitle = event->Title() ? event->Title() : "";
+ eventShortText = event->ShortText() ? event->ShortText() : "";
+ eventStart = *event->GetTimeString();
+ eventStop = *event->GetEndTimeString();
+ }
+ menu->AddStringToken("eventtitle", eventTitle);
+ menu->AddStringToken("eventshorttext", eventShortText);
+ menu->AddStringToken("eventstart", eventStart);
+ menu->AddStringToken("eventstop", eventStop);
+}
+
+// --- cRecMenuItemTimelineTimer -------------------------------------------------------
+cRecMenuItemTimelineTimer::cRecMenuItemTimelineTimer(cTimer *timer, time_t start, time_t stop, bool active) {
+ height = 8;
+ this->timer = timer;
+ this->start = start;
+ this->stop = stop;
+ selectable = true;
+ this->active = active;
+}
+
+cRecMenuItemTimelineTimer::~cRecMenuItemTimelineTimer(void) {
+}
+
+void cRecMenuItemTimelineTimer::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("timelinetimer", 1);
+
+ int secsPerDay = 24*60*60;
+ time_t timerStart = timer->StartTime() - start;
+ time_t timerStop = timer->StopTime() - start;
+ if (timerStart < 0)
+ timerStart = 0;
+ if (timerStop > secsPerDay)
+ timerStop = secsPerDay;
+
+ int percentStart = ((double)timerStart / (double)secsPerDay) * 1000;
+ int percentWidth = ((double)(timerStop - timerStart) / (double)secsPerDay) * 1000;
+ menu->AddIntToken("timerstart", percentStart);
+ menu->AddIntToken("timerwidth", percentWidth);
+}
+
+cTimer *cRecMenuItemTimelineTimer::GetTimer(void) {
+ return timer;
+}
+
+eRecMenuState cRecMenuItemTimelineTimer::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kOk:
+ return rmsTimelineTimerEdit;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemFavorite -------------------------------------------------------
+cRecMenuItemFavorite::cRecMenuItemFavorite(cTVGuideSearchTimer favorite,
+ eRecMenuState action1,
+ bool active) {
+ height = 8;
+ this->favorite = favorite;
+ this->action = action1;
+ selectable = true;
+ this->active = active;
+}
+
+void cRecMenuItemFavorite::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("favorite", 1);
+ menu->AddStringToken("favdesc", favorite.SearchString());
+}
+
+eRecMenuState cRecMenuItemFavorite::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemFavoriteStatic -------------------------------------------------------
+cRecMenuItemFavoriteStatic::cRecMenuItemFavoriteStatic(string text, eRecMenuState action, bool active) {
+ height = 8;
+ this->text = text;
+ this->action = action;
+ selectable = true;
+ this->active = active;
+}
+
+void cRecMenuItemFavoriteStatic::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("favorite", 1);
+ menu->AddStringToken("favdesc", text);
+}
+
+eRecMenuState cRecMenuItemFavoriteStatic::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
diff --git a/recmenuitem.h b/recmenuitem.h
new file mode 100644
index 0000000..9ea6d19
--- /dev/null
+++ b/recmenuitem.h
@@ -0,0 +1,495 @@
+#ifndef __TVGUIDE_RECMENUITEM_H
+#define __TVGUIDE_RECMENUITEM_H
+
+#define AUTO_ADVANCE_TIMEOUT 1500
+
+using namespace std;
+
+#include <string>
+#include <sstream>
+#include <vdr/plugin.h>
+#include <vdr/tools.h>
+#include "libskindesigner/osdelements.h"
+#include "searchtimer.h"
+
+enum eRecMenuState {
+ rmsConsumed,
+ rmsNotConsumed,
+ rmsRefresh,
+ rmsContinue,
+ rmsClose,
+ rmsDisabled,
+ //INSTANT TIMER
+ rmsInstantRecord,
+ rmsInstantRecordFolder,
+ rmsIgnoreTimerConflict,
+ rmsDeleteTimerConflictMenu,
+ rmsEditTimerConflictMenu,
+ rmsSearchRerunsTimerConflictMenu,
+ rmsSaveTimerConflictMenu,
+ rmsTimerConflictShowInfo,
+ rmsDeleteTimer,
+ rmsDeleteTimerConfirmation,
+ rmsEditTimer,
+ rmsSaveTimer,
+ //SEARCH
+ rmsSearch,
+ rmsSearchWithOptions,
+ rmsSearchPerform,
+ rmsSearchShowInfo,
+ rmsSearchRecord,
+ rmsSearchRecordConfirm,
+ rmsSearchNothingFoundConfirm,
+ //SERIES TIMER
+ rmsSeriesTimer,
+ rmsSeriesTimerFolder,
+ rmsSeriesTimerCreate,
+ //SEARCHTIMER
+ rmsSearchTimer,
+ rmsSearchTimerOptions,
+ rmsSearchTimers,
+ rmsSearchTimerEdit,
+ rmsSearchTimerEditAdvanced,
+ rmsSearchTimerTest,
+ rmsSearchTimerSave,
+ rmsSearchTimerCreateWithTemplate,
+ rmsSearchTimerDeleteConfirm,
+ rmsSearchTimerDelete,
+ rmsSearchTimerDeleteWithTimers,
+ rmsSearchTimerRecord,
+ //SWITCHTIMER
+ rmsSwitchTimer,
+ rmsSwitchTimerCreate,
+ rmsSwitchTimerDelete,
+ //RECORDINGS SEARCH
+ rmsRecordingSearch,
+ rmsRecordingSearchResult,
+ //TIMER CONFLICTS
+ rmsTimerConflict,
+ rmsTimerConflicts,
+ rmsTimerConflictIgnoreReruns,
+ rmsTimerConflictRecordRerun,
+ //TIMELINE
+ rmsTimeline,
+ rmsTimelineTimerEdit,
+ rmsTimelineTimerSave,
+ rmsTimelineTimerDelete,
+ //FAVORITES
+ rmsFavoritesRecord,
+ rmsFavoritesNow,
+ rmsFavoritesNext,
+ rmsFavoritesUser1,
+ rmsFavoritesUser2,
+ rmsFavoritesUser3,
+ rmsFavoritesUser4,
+};
+
+// --- cRecMenuItem -------------------------------------------------------------
+class cRecMenuItem : public cListObject {
+protected:
+ static long idCounter;
+ long id;
+ bool init;
+ bool active;
+ bool selectable;
+ eRecMenuState action;
+ //height of item in percent of screen height
+ int height;
+ string text;
+public:
+ cRecMenuItem(void);
+ virtual ~cRecMenuItem(void);
+ virtual void SetActive(void) { active = true; }
+ virtual void SetInactive(void) { active = false; }
+ long Id(void) { return id; };
+ bool Selectable(void) { return selectable; }
+ bool IsNew(void);
+ bool SetNew(void) { init = true; };
+ bool Active(void) { return active; }
+ int GetHeight(void) { return height; };
+ string GetText(void) { return text; };
+ virtual void SetTokens(cViewGrid *menu) {};
+ virtual eRecMenuState ProcessKey(eKeys Key) { return rmsNotConsumed; };
+};
+
+// --- cRecMenuItemInfo -------------------------------------------------------
+class cRecMenuItemInfo : public cRecMenuItem {
+private:
+ int numLines;
+ string line1;
+ string line2;
+ string line3;
+ string line4;
+public:
+ cRecMenuItemInfo(string line1, int numLines = 1, string line2 = "", string line3 = "", string line4 = "");
+ virtual ~cRecMenuItemInfo(void);
+ string GetText(void) { return text; };
+ void SetTokens(cViewGrid *menu);
+};
+
+// --- cRecMenuItemButton -------------------------------------------------------
+class cRecMenuItemButton : public cRecMenuItem {
+private:
+ string text;
+public:
+ cRecMenuItemButton(string text, eRecMenuState action, bool active);
+ virtual ~cRecMenuItemButton(void);
+ string GetText(void) { return text; };
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemButtonYesNo -------------------------------------------------------
+class cRecMenuItemButtonYesNo : public cRecMenuItem {
+private:
+ string textYes;
+ string textNo;
+ eRecMenuState actionNo;
+ bool yesActive;
+public:
+ cRecMenuItemButtonYesNo(string textYes,
+ string textNo,
+ eRecMenuState actionYes,
+ eRecMenuState actionNo,
+ bool active);
+ virtual ~cRecMenuItemButtonYesNo(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemInt -------------------------------------------------------
+class cRecMenuItemInt : public cRecMenuItem {
+private:
+ int currentVal;
+ int *callback;
+ int minVal;
+ int maxVal;
+ bool fresh;
+public:
+ cRecMenuItemInt(string text,
+ int initialVal,
+ int minVal,
+ int maxVal,
+ bool active = false,
+ int *callback = NULL,
+ eRecMenuState action = rmsNotConsumed);
+ virtual ~cRecMenuItemInt(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemBool -------------------------------------------------------
+class cRecMenuItemBool : public cRecMenuItem {
+private:
+ bool yes;
+ bool *callback;
+public:
+ cRecMenuItemBool(string text,
+ bool initialVal,
+ bool active = false,
+ bool *callback = NULL,
+ eRecMenuState action = rmsNotConsumed);
+ virtual ~cRecMenuItemBool(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemSelect -------------------------------------------------------
+class cRecMenuItemSelect : public cRecMenuItem {
+private:
+ int currentVal;
+ int *callback;
+ vector<string> strings;
+ int numValues;
+public:
+ cRecMenuItemSelect(string text,
+ vector<string> strings,
+ int initialVal,
+ bool active = false,
+ int *callback = NULL,
+ eRecMenuState action = rmsNotConsumed);
+ virtual ~cRecMenuItemSelect(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemText -------------------------------------------------------
+class cRecMenuItemText : public cRecMenuItem {
+private:
+ char *value;
+ string buffer;
+ char *callback;
+ int length;
+ const char *allowed;
+ int pos, offset;
+ bool insert, newchar, uppercase;
+ int lengthUtf8;
+ uint *valueUtf8;
+ uint *allowedUtf8;
+ uint *charMapUtf8;
+ uint *currentCharUtf8;
+ eKeys lastKey;
+ cTimeMs autoAdvanceTimeout;
+ uint *IsAllowed(uint c);
+ void AdvancePos(void);
+ uint Inc(uint c, bool Up);
+ void Type(uint c);
+ void Insert(void);
+ void Delete(void);
+ void EnterEditMode(void);
+ void LeaveEditMode(bool SaveValue = false);
+ bool InEditMode(void) { return valueUtf8 != NULL; };
+ void SetText(void);
+public:
+ cRecMenuItemText(string title,
+ char *initialVal,
+ int length,
+ bool active = false,
+ char *callback = NULL);
+ virtual ~cRecMenuItemText(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemTime -------------------------------------------------------
+class cRecMenuItemTime : public cRecMenuItem {
+private:
+ int value;
+ int *callback;
+ int mm;
+ int hh;
+ int pos;
+ bool fresh;
+public:
+ cRecMenuItemTime(string text,
+ int initialVal,
+ bool active = false,
+ int *callback = NULL,
+ eRecMenuState action = rmsNotConsumed);
+ virtual ~cRecMenuItemTime(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemDay -------------------------------------------------------
+class cRecMenuItemDay : public cRecMenuItem {
+private:
+ time_t currentVal;
+ time_t *callback;
+public:
+ cRecMenuItemDay(string text,
+ time_t initialVal,
+ bool active = false,
+ time_t *callback = NULL,
+ eRecMenuState action = rmsNotConsumed);
+ virtual ~cRecMenuItemDay(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemChannelChooser -------------------------------------------------------
+class cRecMenuItemChannelChooser : public cRecMenuItem {
+private:
+ cChannel *channel;
+ int channelNumber;
+ int *callback;
+ bool initialChannelSet;
+ bool fresh;
+public:
+ cRecMenuItemChannelChooser (string text,
+ cChannel *initialChannel,
+ bool active = false,
+ int *callback = NULL,
+ eRecMenuState action = rmsNotConsumed);
+ virtual ~cRecMenuItemChannelChooser(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemDayChooser -------------------------------------------------------
+class cRecMenuItemDayChooser : public cRecMenuItem {
+private:
+ int weekdays;
+ int *callback;
+ int selectedDay;
+ void ToggleDay(void);
+ bool WeekDaySet(unsigned day);
+public:
+ cRecMenuItemDayChooser (string text,
+ int weekdays,
+ bool active = false,
+ int *callback = NULL);
+ virtual ~cRecMenuItemDayChooser(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemSelectDirectory -------------------------------------------------------
+class cRecMenuItemSelectDirectory : public cRecMenuItem {
+private:
+ string text;
+ string originalFolder;
+ vector<string> folders;
+ int currentVal;
+ char *callback;
+ int numValues;
+ void SetCallback(void);
+ int GetInitial(void);
+public:
+ cRecMenuItemSelectDirectory(string text,
+ string originalFolder,
+ bool active = false,
+ char *callback = NULL,
+ eRecMenuState action = rmsNotConsumed,
+ bool isSearchTimer = false);
+ virtual ~cRecMenuItemSelectDirectory(void) {};
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemTimerConflictHeader -------------------------------------------------------
+class cRecMenuItemTimerConflictHeader: public cRecMenuItem {
+private:
+ time_t conflictStart;
+ time_t conflictStop;
+ time_t overlapStart;
+ time_t overlapStop;
+public:
+ cRecMenuItemTimerConflictHeader(time_t conflictStart,
+ time_t conflictStop,
+ time_t overlapStart,
+ time_t overlapStop);
+ void SetTokens(cViewGrid *menu);
+ virtual ~cRecMenuItemTimerConflictHeader(void);
+};
+
+// --- cRecMenuItemTimer -------------------------------------------------------
+class cRecMenuItemTimer : public cRecMenuItem {
+private:
+ const cTimer *timer;
+ eRecMenuState action2;
+ eRecMenuState action3;
+ eRecMenuState action4;
+ int iconActive;
+ time_t conflictStart;
+ time_t conflictStop;
+ time_t overlapStart;
+ time_t overlapStop;
+public:
+ cRecMenuItemTimer(const cTimer *timer,
+ eRecMenuState action1,
+ eRecMenuState action2,
+ eRecMenuState action3,
+ eRecMenuState action4,
+ time_t conflictStart,
+ time_t conflictStop,
+ time_t overlapStart,
+ time_t overlapStop,
+ bool active);
+ virtual ~cRecMenuItemTimer(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemEvent -------------------------------------------------------
+class cRecMenuItemEvent : public cRecMenuItem {
+private:
+ const cEvent *event;
+ eRecMenuState action2;
+ int iconActive;
+public:
+ cRecMenuItemEvent(const cEvent *event,
+ eRecMenuState action1,
+ eRecMenuState action2,
+ bool active);
+ virtual ~cRecMenuItemEvent(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+ const cEvent *GetEvent(void) { return event; };
+};
+
+// --- cRecMenuItemRecording -------------------------------------------------------
+class cRecMenuItemRecording : public cRecMenuItem {
+private:
+ cRecording *recording;
+public:
+ cRecMenuItemRecording(cRecording *recording, bool active);
+ virtual ~cRecMenuItemRecording(void) {};
+ void SetTokens(cViewGrid *menu);
+};
+
+// --- cRecMenuItemSearchTimer -------------------------------------------------------
+class cRecMenuItemSearchTimer : public cRecMenuItem {
+private:
+ cTVGuideSearchTimer timer;
+ eRecMenuState action1;
+ eRecMenuState action2;
+ eRecMenuState action3;
+ int iconActive;
+public:
+ cRecMenuItemSearchTimer(cTVGuideSearchTimer timer,
+ eRecMenuState action1,
+ eRecMenuState action2,
+ eRecMenuState action3,
+ bool active);
+ virtual ~cRecMenuItemSearchTimer(void);
+ cTVGuideSearchTimer GetTimer(void) { return timer; };
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemTimelineHeader -------------------------------------------------------
+class cRecMenuItemTimelineHeader : public cRecMenuItem {
+private:
+ time_t day;
+ cTimer *timer;
+public:
+ cRecMenuItemTimelineHeader(time_t day);
+ virtual ~cRecMenuItemTimelineHeader(void);
+ void SetTokens(cViewGrid *menu);
+ void SetDay(time_t day) { this->day = day; };
+ void SetCurrentTimer(cTimer *timer) { this->timer = timer; };
+ void UnsetCurrentTimer(void) { timer = NULL; };
+};
+
+// --- cRecMenuItemTimelineTimer -------------------------------------------------------
+class cRecMenuItemTimelineTimer : public cRecMenuItem {
+private:
+ cTimer *timer;
+ time_t start;
+ time_t stop;
+public:
+ cRecMenuItemTimelineTimer(cTimer *timer, time_t start, time_t stop, bool active);
+ virtual ~cRecMenuItemTimelineTimer(void);
+ void SetTokens(cViewGrid *menu);
+ cTimer *GetTimer(void);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemFavorite -------------------------------------------------------
+class cRecMenuItemFavorite : public cRecMenuItem {
+private:
+ cTVGuideSearchTimer favorite;
+ eRecMenuState action1;
+public:
+ cRecMenuItemFavorite(cTVGuideSearchTimer favorite,
+ eRecMenuState action1,
+ bool active);
+ virtual ~cRecMenuItemFavorite(void) {};
+ void SetTokens(cViewGrid *menu);
+ cTVGuideSearchTimer GetFavorite(void) { return favorite; };
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemFavoriteStatic -------------------------------------------------------
+class cRecMenuItemFavoriteStatic : public cRecMenuItem {
+private:
+ string text;
+public:
+ cRecMenuItemFavoriteStatic(string text, eRecMenuState action, bool active);
+ virtual ~cRecMenuItemFavoriteStatic(void) {};
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+#endif //__TVGUIDE_RECMENUITEM_H \ No newline at end of file
diff --git a/recmenus.c b/recmenus.c
new file mode 100644
index 0000000..d20250a
--- /dev/null
+++ b/recmenus.c
@@ -0,0 +1,1143 @@
+#include <string>
+#include <sstream>
+#include <algorithm>
+#include "config.h"
+#include "services/remotetimers.h"
+#include "helpers.h"
+#include "recmenus.h"
+
+// --- cRecMenuMain ---------------------------------------------------------
+cRecMenuMain::cRecMenuMain(bool epgSearchAvailable, bool timerActive, bool switchTimerActive) {
+ SetMenuWidth(50);
+ eRecMenuState action = rmsInstantRecord;
+ if (!timerActive) {
+ if (config.instRecFolderMode == eFolderSelect)
+ action = rmsInstantRecordFolder;
+ AddMenuItem(new cRecMenuItemButton(tr("Instant Record"), action, true));
+ } else {
+ AddMenuItem(new cRecMenuItemButton(tr("Delete Timer"), rmsDeleteTimer, true));
+ AddMenuItem(new cRecMenuItemButton(tr("Edit Timer"), rmsEditTimer, false));
+ }
+
+ AddMenuItem(new cRecMenuItemButton(tr("Timer Timeline"), rmsTimeline, false));
+
+ if (epgSearchAvailable) {
+ AddMenuItem(new cRecMenuItemButton(tr("Create Search Timer"), rmsSearchTimer, false));
+ AddMenuItem(new cRecMenuItemButton(tr("Search Timers"), rmsSearchTimers, false));
+ }
+
+ if (config.instRecFolderMode == eFolderSelect)
+ action = rmsSeriesTimerFolder;
+ else
+ action = rmsSeriesTimer;
+ AddMenuItem(new cRecMenuItemButton(tr("Create Series Timer"), action, false));
+
+ if (epgSearchAvailable) {
+ if (!switchTimerActive) {
+ AddMenuItem(new cRecMenuItemButton(tr("Create Switch Timer"), rmsSwitchTimer, false));
+ } else {
+ AddMenuItem(new cRecMenuItemButton(tr("Delete Switch Timer"), rmsSwitchTimerDelete, false));
+ }
+ AddMenuItem(new cRecMenuItemButton(tr("Search"), rmsSearch, false));
+ }
+
+ if (epgSearchAvailable) {
+ AddMenuItem(new cRecMenuItemButton(tr("Check for Timer Conflicts"), rmsTimerConflicts, false));
+ }
+
+ AddMenuItem(new cRecMenuItemButton(tr("Search in Recordings"), rmsRecordingSearch, false));
+};
+
+/******************************************************************************************
+* Instant Timer Menus
+******************************************************************************************/
+
+// --- cRecMenuConfirmTimer ---------------------------------------------------------
+cRecMenuConfirmTimer::cRecMenuConfirmTimer(const cEvent *event) {
+ SetMenuWidth(50);
+
+ bool eventHasTimer = false;
+ if (config.useRemoteTimers && pRemoteTimers) {
+ RemoteTimers_GetMatch_v1_0 rtMatch;
+ rtMatch.event = event;
+ pRemoteTimers->Service("RemoteTimers::GetMatch-v1.0", &rtMatch);
+ if (rtMatch.timerMatch == tmFull) {
+ eventHasTimer = true;
+ }
+ } else {
+ eventHasTimer = event->HasTimer();
+ }
+
+ string message;
+ if (eventHasTimer) {
+ message = tr("Timer created");
+ } else {
+ message = tr("Timer NOT created");
+ }
+ cString channelName = Channels.GetByChannelID(event->ChannelID())->Name();
+ string datetime = *cString::sprintf("%s %s - %s", *event->GetDateString(), *event->GetTimeString(), *event->GetEndTimeString());
+ string eventTitle = event->Title() ? event->Title() : "";
+ AddHeader(new cRecMenuItemInfo(message, 4, *channelName, datetime, eventTitle));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+// --- cRecMenuAskFolder ---------------------------------------------------------
+cRecMenuAskFolder::cRecMenuAskFolder(const cEvent *event, eRecMenuState nextAction) {
+ SetMenuWidth(80);
+ this->nextAction = nextAction;
+ string message = tr("Set Folder for");
+ string eventTitle = event->Title() ? event->Title() : "";
+ AddHeader(new cRecMenuItemInfo(message, 2, eventTitle));
+ AddMenuItem(new cRecMenuItemButton(tr("root video folder"), nextAction, true));
+
+ ReadRecordingDirectories(&folders, NULL, "");
+ int numFolders = folders.size();
+ for (int i=0; i < numFolders; i++) {
+ AddMenuItem(new cRecMenuItemButton(folders[i].c_str(), nextAction, false));
+ }
+}
+
+string cRecMenuAskFolder::GetFolder(void) {
+ std::string folder = "";
+ int folderActive = GetNumActive();
+ if (folderActive > 0 && folderActive < folders.size() + 1)
+ folder = folders[folderActive - 1];
+ return folder;
+}
+
+// --- cRecMenuConfirmDeleteTimer ---------------------------------------------------------
+cRecMenuConfirmDeleteTimer::cRecMenuConfirmDeleteTimer(const cEvent *event) {
+ SetMenuWidth(50);
+ string message = tr("Timer deleted");
+ cString channelName = Channels.GetByChannelID(event->ChannelID())->Name();
+ string datetime = *cString::sprintf("%s %s - %s", *event->GetDateString(), *event->GetTimeString(), *event->GetEndTimeString());
+ string eventTitle = event->Title() ? event->Title() : "";
+
+ AddHeader(new cRecMenuItemInfo(message, 4, *channelName, datetime, eventTitle));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+// --- cRecMenuAskDeleteTimer ---------------------------------------------------------
+cRecMenuAskDeleteTimer::cRecMenuAskDeleteTimer(const cEvent *event) {
+ SetMenuWidth(50);
+ string message = tr("Timer");
+ cString channelName = Channels.GetByChannelID(event->ChannelID())->Name();
+ string eventTitle = event->Title() ? event->Title() : "";
+ string message2 = tr("still recording - really delete?");
+
+ AddHeader(new cRecMenuItemInfo(message, 4, *channelName, eventTitle, message2));
+ AddMenuItem(new cRecMenuItemButtonYesNo(tr("Yes"), tr("No"), rmsDeleteTimerConfirmation, rmsClose, true));
+}
+
+// --- cRecMenuTimerConflicts ---------------------------------------------------------
+cRecMenuTimerConflicts::cRecMenuTimerConflicts(cTVGuideTimerConflicts *conflicts) {
+ SetMenuWidth(80);
+ int numConflicts = conflicts->NumConflicts();
+
+ string text;
+ if (numConflicts == 1) {
+ text = *cString::sprintf("%s %s %s", tr("One"), tr("Timer Conflict"), tr("detected"));
+ } else {
+ text = *cString::sprintf("%d %s %s", conflicts->NumConflicts(), tr("Timer Conflicts"), tr("detected"));
+ }
+ AddHeader(new cRecMenuItemInfo(text));
+
+ for (int i=0; i<numConflicts; i++) {
+ cTVGuideTimerConflict *conflict = conflicts->GetConflict(i);
+ if (!conflict)
+ continue;
+ cString dateTime = DayDateTime(conflict->time);
+ int numTimers = conflict->timerIDs.size();
+ cString textConflict = cString::sprintf("%s: %s (%d %s)", tr("Show conflict"), *dateTime, numTimers, tr("timers involved"));
+ bool isActive = (i==0)?true:false;
+ AddMenuItem(new cRecMenuItemButton(*textConflict, rmsTimerConflict, isActive));
+ }
+
+ AddFooter(new cRecMenuItemButton(tr("Ignore Conflicts"), rmsClose, false));
+}
+
+int cRecMenuTimerConflicts::GetTimerConflict(void) {
+ return GetNumActive();
+}
+
+// --- cRecMenuTimerConflict ---------------------------------------------------------
+cRecMenuTimerConflict::cRecMenuTimerConflict(cTVGuideTimerConflict *conflict) {
+ SetMenuWidth(95);
+ this->conflict = conflict;
+ AddHeader(new cRecMenuItemTimerConflictHeader(conflict->timeStart,
+ conflict->timeStop,
+ conflict->overlapStart,
+ conflict->overlapStop));
+
+ int i=0;
+ for(vector<int>::iterator it = conflict->timerIDs.begin(); it != conflict->timerIDs.end(); it++) {
+ const cTimer *timer = Timers.Get(*it);
+ if (timer) {
+ AddMenuItem(new cRecMenuItemTimer(timer,
+ rmsTimerConflictShowInfo,
+ rmsDeleteTimerConflictMenu,
+ rmsEditTimerConflictMenu,
+ rmsSearchRerunsTimerConflictMenu,
+ conflict->timeStart,
+ conflict->timeStop,
+ conflict->overlapStart,
+ conflict->overlapStop,
+ (!i)?true:false));
+ i++;
+ }
+ }
+ AddMenuItem(new cRecMenuItemButton(tr("Ignore Conflict"), rmsIgnoreTimerConflict, false));
+}
+
+int cRecMenuTimerConflict::GetTimerConflictIndex(void) {
+ return GetNumActive();
+}
+
+// --- cRecMenuNoTimerConflict ---------------------------------------------------------
+cRecMenuNoTimerConflict::cRecMenuNoTimerConflict(void) {
+ SetMenuWidth(50);
+ string text = tr("No Timer Conflicts found");
+ AddHeader(new cRecMenuItemInfo(text));
+ AddMenuItem(new cRecMenuItemButton(tr("Close"), rmsClose, true));
+}
+
+// --- cRecMenuRerunResults ---------------------------------------------------------
+cRecMenuRerunResults::cRecMenuRerunResults(const cEvent *original, const cEvent **reruns, int numReruns) {
+ this->reruns = reruns;
+ this->numReruns = numReruns;
+ SetMenuWidth(70);
+
+ string message1 = tr("reruns for");
+ string message2 = tr("rerun for");
+ string message3 = tr("found");
+ string line1 = *cString::sprintf("%d %s:", numReruns, (numReruns>1)?(message1.c_str()):(message2.c_str()));
+ string line2 = *cString::sprintf("\"%s\" %s", original->Title(), message3.c_str());
+
+ AddHeader(new cRecMenuItemInfo(line1, 2, line2));
+
+ cRecMenuItem *button = new cRecMenuItemButton(tr("Ignore reruns"), rmsTimerConflictIgnoreReruns, false);
+ AddFooter(button);
+
+ if (reruns && (numReruns > 0)) {
+ for (int i=0; i<numReruns; i++) {
+ AddMenuItem(new cRecMenuItemEvent(reruns[i], rmsSearchShowInfo, rmsTimerConflictRecordRerun, (i==0)?true:false));
+ }
+ }
+}
+
+int cRecMenuRerunResults::GetTotalNumMenuItems(void) {
+ return numReruns;
+}
+
+const cEvent *cRecMenuRerunResults::GetEvent(void) {
+ cRecMenuItemEvent *activeItem = dynamic_cast<cRecMenuItemEvent*>(GetActive());
+ if (activeItem)
+ return activeItem->GetEvent();
+ return NULL;
+}
+
+// --- cRecMenuNoRerunsFound ---------------------------------------------------------
+cRecMenuNoRerunsFound::cRecMenuNoRerunsFound(string searchString) {
+ SetMenuWidth(50);
+ string message = tr("No reruns found for Event");
+ string eventQuoted = *cString::sprintf("\"%s\"", searchString.c_str());
+
+ AddHeader(new cRecMenuItemInfo(message, 2, eventQuoted));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+// --- cRecMenuConfirmRerunUsed ---------------------------------------------------------
+cRecMenuConfirmRerunUsed::cRecMenuConfirmRerunUsed(const cEvent *original, const cEvent *replace) {
+ SetMenuWidth(70);
+ cString channelOrig = Channels.GetByChannelID(original->ChannelID())->Name();
+ cString channelReplace = Channels.GetByChannelID(replace->ChannelID())->Name();
+ string line1 = *cString::sprintf("%s \"%s\"", tr("Timer for"), original->Title());
+ string line2 = *cString::sprintf("%s %s %s", *original->GetDateString(), *original->GetTimeString(), *channelOrig);
+ string line3 = *cString::sprintf("%s \"%s\"", tr("replaced by rerun"), replace->Title());
+ string line4 = *cString::sprintf("%s %s %s", *replace->GetDateString(), *replace->GetTimeString(), *channelReplace);
+
+ AddHeader(new cRecMenuItemInfo(line1, 4, line2, line3, line4));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsTimerConflicts, true));
+}
+
+// --- cRecMenuEditTimer ---------------------------------------------------------
+cRecMenuEditTimer::cRecMenuEditTimer(cTimer *timer, eRecMenuState nextState) {
+ SetMenuWidth(70);
+ if (!timer)
+ return;
+ originalTimer = timer;
+ string title = "";
+ string channelName = "";
+ if (timer->Event() && timer->Event()->Title())
+ title = timer->Event()->Title();
+ if (timer->Channel() && timer->Channel()->Name())
+ channelName = timer->Channel()->Name();
+ cRecMenuItemInfo *infoItem = new cRecMenuItemInfo(tr("Edit Timer"), 3, title, channelName);
+ AddHeader(infoItem);
+
+ timerActive = false;
+ if (config.useRemoteTimers && pRemoteTimers) {
+ RemoteTimers_GetMatch_v1_0 rtMatch;
+ rtMatch.event = timer->Event();
+ pRemoteTimers->Service("RemoteTimers::GetMatch-v1.0", &rtMatch);
+ if (rtMatch.timer) {
+ if (rtMatch.timerMatch == tmFull)
+ timerActive = true;
+ }
+ } else
+ timerActive = timer->HasFlags(tfActive);
+
+ day = timer->Day();
+ start = timer->Start();
+ stop = timer->Stop();
+ prio = timer->Priority();
+ lifetime = timer->Lifetime();
+ strncpy(folder, GetDirectoryFromTimer(timer->File()).c_str(), TEXTINPUTLENGTH);
+
+ AddMenuItem(new cRecMenuItemBool(tr("Timer Active"), timerActive, true, &timerActive));
+ AddMenuItem(new cRecMenuItemInt(tr("Priority"), prio, 0, MAXPRIORITY, false, &prio));
+ AddMenuItem(new cRecMenuItemInt(tr("Lifetime"), lifetime, 0, MAXLIFETIME, false, &lifetime));
+ AddMenuItem(new cRecMenuItemDay(tr("Day"), day, false, &day));
+ AddMenuItem(new cRecMenuItemTime(tr("Timer start time"), start, false, &start));
+ AddMenuItem(new cRecMenuItemTime(tr("Timer stop time"), stop, false, &stop));
+ AddMenuItem(new cRecMenuItemInfo(tr("Timer File"), 2, timer->File() ? timer->File() : ""));
+ AddMenuItem(new cRecMenuItemSelectDirectory(tr("New Folder"), string(folder), false, folder));
+ if (nextState == rmsTimelineTimerSave) {
+ AddMenuItem(new cRecMenuItemButton(tr("Delete Timer"), rmsTimelineTimerDelete, false));
+ AddFooter(new cRecMenuItemButtonYesNo(tr("Save"), tr("Cancel"), nextState, rmsTimeline, false));
+ } else {
+ AddFooter(new cRecMenuItemButtonYesNo(tr("Save"), tr("Cancel"), nextState, rmsClose, false));
+ }
+}
+
+cTimer *cRecMenuEditTimer::GetOriginalTimer(void) {
+ return originalTimer;
+}
+
+cTimer cRecMenuEditTimer::GetTimer(void) {
+ cTimer t;
+ if (timerActive)
+ t.SetFlags(tfActive);
+ else
+ t.SetFlags(tfNone);
+ t.SetDay(day);
+ t.SetStart(start);
+ t.SetStop(stop);
+ t.SetPriority(prio);
+ t.SetLifetime(lifetime);
+ string newFolder(folder);
+ string newFile = originalTimer->File();
+ size_t found = newFile.find_last_of('~');
+ if (found != string::npos) {
+ string fileName = newFile.substr(found+1);
+ if (newFolder.size() > 0)
+ newFile = *cString::sprintf("%s~%s", newFolder.c_str(), fileName.c_str());
+ else
+ newFile = fileName;
+ } else {
+ if (newFolder.size() > 0)
+ newFile = *cString::sprintf("%s~%s", newFolder.c_str(), newFile.c_str());
+ }
+ std::replace(newFile.begin(), newFile.end(), '/', '~');
+ t.SetFile(newFile.c_str());
+ return t;
+}
+
+/******************************************************************************************
+* Series Timer Menus
+******************************************************************************************/
+
+// --- cRecMenuSeriesTimer ---------------------------------------------------------
+cRecMenuSeriesTimer::cRecMenuSeriesTimer(cChannel *initialChannel, const cEvent *event, string folder) {
+ if (!initialChannel)
+ return;
+ timerActive = true;
+ channel = initialChannel->Number();
+ dayOfWeek = 127;
+ priority = MAXPRIORITY;
+ lifetime = MAXLIFETIME;
+ CalculateTimes(event);
+ this->folder = folder;
+
+ SetMenuWidth(70);
+ string line1 = tr("Create Series Timer based on");
+ string line2 = *cString::sprintf("\"%s\"", event->Title());
+ AddHeader(new cRecMenuItemInfo(line1, 2, line2));
+
+ AddMenuItem(new cRecMenuItemBool(tr("Timer Active"), timerActive, false, &timerActive));
+ AddMenuItem(new cRecMenuItemChannelChooser(tr("Channel"), initialChannel, false, &channel));
+ AddMenuItem(new cRecMenuItemTime(tr("Series Timer start time"), start, false, &start));
+ AddMenuItem(new cRecMenuItemTime(tr("Series Timer stop time"), stop, false, &stop));
+ AddMenuItem(new cRecMenuItemDayChooser(tr("Days to record"), dayOfWeek, false, &dayOfWeek));
+ AddMenuItem(new cRecMenuItemDay(tr("Day to start"), tstart, false, &tstart));
+ AddMenuItem(new cRecMenuItemInt(tr("Priority"), priority, 0, MAXPRIORITY, false, &priority));
+ AddMenuItem(new cRecMenuItemInt(tr("Lifetime"), lifetime, 0, MAXLIFETIME, false, &lifetime));
+
+ AddFooter(new cRecMenuItemButtonYesNo(tr("Create Timer"), tr("Cancel"), rmsSeriesTimerCreate, rmsClose, true));
+}
+
+cTimer *cRecMenuSeriesTimer::GetTimer(void) {
+ cChannel *chan = Channels.GetByNumber(channel);
+ cTimer *seriesTimer = new cTimer(NULL, NULL, chan);
+ cString fileName = "TITLE EPISODE";
+ if (folder.size() > 0) {
+ std::replace(folder.begin(), folder.end(), '/', '~');
+ fileName = cString::sprintf("%s~%s", folder.c_str(), *fileName);
+ }
+ seriesTimer->SetDay(tstart);
+ seriesTimer->SetStart(start);
+ seriesTimer->SetStop(stop);
+ seriesTimer->SetPriority(priority);
+ seriesTimer->SetLifetime(lifetime);
+ seriesTimer->SetWeekDays(dayOfWeek);
+ seriesTimer->SetFile(*fileName);
+ if (timerActive)
+ seriesTimer->SetFlags(tfActive);
+ else
+ seriesTimer->SetFlags(tfNone);
+ return seriesTimer;
+}
+
+void cRecMenuSeriesTimer::CalculateTimes(const cEvent *event) {
+ tstart = event->StartTime();
+ tstart -= Setup.MarginStart * 60;
+ time_t tstop = tstart + event->Duration();
+ tstop += Setup.MarginStop * 60;
+
+ struct tm tm_r;
+ struct tm *time = localtime_r(&tstart, &tm_r);
+ start = time->tm_hour * 100 + time->tm_min;
+ time = localtime_r(&tstop, &tm_r);
+ stop = time->tm_hour * 100 + time->tm_min;
+ if (stop >= 2400)
+ stop -= 2400;
+}
+
+// --- cRecMenuConfirmSeriesTimer ---------------------------------------------------------
+cRecMenuConfirmSeriesTimer::cRecMenuConfirmSeriesTimer(cTimer *seriesTimer) {
+ SetMenuWidth(50);
+ string line1 = tr("Series Timer created");
+ string line2 = "";
+ if (seriesTimer) {
+ cString days = cTimer::PrintDay(seriesTimer->Day(), seriesTimer->WeekDays(), true);
+ line2 = cString::sprintf("%s, %s: %s, %s: %s", *days, tr("Start"), *TimeString(seriesTimer->StartTime()), tr("Stop"), *TimeString(seriesTimer->StopTime()));
+ }
+ AddHeader(new cRecMenuItemInfo(line1, 2, line2));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+/******************************************************************************************
+* SearchTimer Menus
+******************************************************************************************/
+
+// --- cRecMenuSearchTimer ---------------------------------------------------------
+cRecMenuSearchTimer::cRecMenuSearchTimer(const cEvent *event) {
+ SetMenuWidth(70);
+ string message = tr("Configure Search Timer based on");
+ string infoText = *cString::sprintf("\"%s\"", event->Title());
+ AddHeader(new cRecMenuItemInfo(message, 2, infoText));
+ strncpy(searchString, event->Title(), TEXTINPUTLENGTH);
+ AddMenuItem(new cRecMenuItemText(tr("Search Expression:"), searchString, TEXTINPUTLENGTH, false));
+ AddFooter(new cRecMenuItemButtonYesNo(tr("Continue"), tr("Cancel"), rmsSearchTimerOptions, rmsClose, true));
+}
+
+// --- cRecMenuSearchTimerTemplates ---------------------------------------------------------
+cRecMenuSearchTimerTemplates::cRecMenuSearchTimerTemplates(cTVGuideSearchTimer searchTimer, vector<TVGuideEPGSearchTemplate> templates) {
+ SetMenuWidth(70);
+ this->searchTimer = searchTimer;
+ this->templates = templates;
+ string message = tr("Configure Search Timer for Search String");
+ AddHeader(new cRecMenuItemInfo(message, 2, searchTimer.SearchString()));
+ AddMenuItem(new cRecMenuItemButton(tr("Manually configure Options"), rmsSearchTimerEdit, true));
+
+ numTemplates = templates.size();
+ for (int i=0; i<numTemplates; i++) {
+ string buttonText = *cString::sprintf("%s \"%s\"", tr("Use Template"), templates[i].name.c_str());
+ AddMenuItem(new cRecMenuItemButton(buttonText, rmsSearchTimerCreateWithTemplate, false));
+ }
+}
+
+TVGuideEPGSearchTemplate cRecMenuSearchTimerTemplates::GetTemplate(void) {
+ TVGuideEPGSearchTemplate templ;
+ int tmplActive = GetNumActive() - 1;
+ if (tmplActive >= 0 && tmplActive < templates.size())
+ templ = templates[tmplActive];
+ return templ;
+}
+
+// --- cRecMenuSearchTimers ---------------------------------------------------------
+cRecMenuSearchTimers::cRecMenuSearchTimers(std::vector<cTVGuideSearchTimer> searchTimers) {
+ SetMenuWidth(70);
+ this->searchTimers = searchTimers;
+ numSearchTimers = searchTimers.size();
+ string headline;
+ if (numSearchTimers > 0) {
+ headline = tr("EPGSearch Search Timers");
+ } else {
+ headline = tr("No Search Timers Configured");
+ }
+ AddHeader(new cRecMenuItemInfo(headline));
+ cRecMenuItem *button = new cRecMenuItemButton(tr("Close"), rmsClose, (!numSearchTimers)?true:false);
+ if (numSearchTimers > 0) {
+ SetMenuItems();
+ AddFooter(button);
+ } else {
+ AddMenuItem(button);
+ }
+}
+
+void cRecMenuSearchTimers::SetMenuItems(void) {
+ for (int i = 0; i < numSearchTimers; i++) {
+ AddMenuItem(new cRecMenuItemSearchTimer(searchTimers[i], rmsSearchTimerTest, rmsSearchTimerEdit, rmsSearchTimerDeleteConfirm, (i==0)?true:false));
+ }
+}
+
+cTVGuideSearchTimer cRecMenuSearchTimers::GetSearchTimer(void) {
+ cRecMenuItemSearchTimer *activeItem = dynamic_cast<cRecMenuItemSearchTimer*>(GetActive());
+ return activeItem->GetTimer();
+}
+
+// --- cRecMenuSearchTimerEdit ---------------------------------------------------------
+cRecMenuSearchTimerEdit::cRecMenuSearchTimerEdit(cTVGuideSearchTimer searchTimer, bool advancedOptions) {
+ this->advancedOptions = advancedOptions;
+ this->searchTimer = searchTimer;
+ strncpy(searchString, searchTimer.SearchString().c_str(), TEXTINPUTLENGTH);
+ timerActive = searchTimer.Active();
+ mode = searchTimer.SearchMode();
+ useTitle = searchTimer.UseTitle();
+ useSubtitle = searchTimer.UseSubtitle();
+ useDescription = searchTimer.UseDescription();
+ useChannel = searchTimer.UseChannel();
+ startChannel = searchTimer.StartChannel();
+ stopChannel = searchTimer.StopChannel();
+ useTime = searchTimer.UseTime();
+ startTime = searchTimer.StartTime();
+ stopTime = searchTimer.StopTime();
+ useDayOfWeek = searchTimer.UseDayOfWeek();
+ dayOfWeek = searchTimer.DayOfWeek();
+ priority = searchTimer.Priority();
+ lifetime = searchTimer.Lifetime();
+ useEpisode = searchTimer.UseEpisode();
+ std::string dir = searchTimer.Directory();
+ strncpy(directory, dir.c_str(), TEXTINPUTLENGTH);
+ marginStart = searchTimer.MarginStart();
+ marginStop = searchTimer.MarginStop();
+ useVPS = searchTimer.UseVPS();
+ avoidRepeats = searchTimer.AvoidRepeats();
+ allowedRepeats = searchTimer.AllowedRepeats();
+ compareTitle = searchTimer.CompareTitle();
+ compareSubtitle = searchTimer.CompareSubtitle();
+ compareSummary = searchTimer.CompareSummary();
+ useInFavorites = searchTimer.UseInFavorites();
+
+ SetMenuWidth(70);
+ string infoText;
+ if (searchTimer.GetID() > -1) {
+ infoText = tr("Configure Search Timer Options");
+ } else {
+ infoText = tr("Create Search Timer");
+ }
+ AddHeader(new cRecMenuItemInfo(infoText));
+ cRecMenuItemButtonYesNo *footerButton = new cRecMenuItemButtonYesNo(tr("Save Search Timer"), tr("Cancel"), rmsSearchTimerSave, rmsSearchTimers, false);
+ AddFooter(footerButton);
+ CreateMenuItems();
+}
+
+void cRecMenuSearchTimerEdit::CreateMenuItems(void) {
+ AddMenuItem(new cRecMenuItemText(tr("Search String"), searchString, TEXTINPUTLENGTH, true, searchString));
+ AddMenuItem(new cRecMenuItemBool(tr("Active"), timerActive, false, &timerActive, rmsSearchTimerSave));
+ vector<string> searchModes;
+ searchTimer.GetSearchModes(&searchModes);
+ AddMenuItem(new cRecMenuItemSelect(tr("Search Mode"), searchModes, mode, false, &mode, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Use Title"), useTitle, false, &useTitle, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Use Subtitle"), useSubtitle, false, &useSubtitle, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Use Description"), useDescription, false, &useDescription, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Limit Channels"), useChannel, false, &useChannel, rmsSearchTimerSave));
+ if (startChannel == 0)
+ startChannel = 1;
+ if (stopChannel == 0)
+ stopChannel = 1;
+ AddMenuItem(new cRecMenuItemChannelChooser(tr("Start Channel"), Channels.GetByNumber(startChannel), false, &startChannel, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemChannelChooser(tr("Stop Channel"), Channels.GetByNumber(stopChannel), false, &stopChannel, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Use Time"), useTime, false, &useTime, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemTime(tr("Start after"), startTime, false, &startTime, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemTime(tr("Start before"), stopTime, false, &stopTime, rmsSearchTimerSave));
+
+ if (!advancedOptions) {
+ AddMenuItem(new cRecMenuItemButton(tr("Display advanced Options"), rmsSearchTimerEditAdvanced, false));
+ } else {
+ AddMenuItem(new cRecMenuItemBool(tr("Limit Days of the Week"), useDayOfWeek, false, &useDayOfWeek, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemDayChooser(tr("Select Days"), dayOfWeek, false, &dayOfWeek));
+ AddMenuItem(new cRecMenuItemInt(tr("Priority"), priority, 0, 99, false, &priority, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemInt(tr("Lifetime"), lifetime, 0, 99, false, &lifetime, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemInt(tr("Time margin for start in minutes"), marginStart, 0, 30, false, &marginStart, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemInt(tr("Time margin for stop in minutes"), marginStop, 0, 30, false, &marginStop, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Series Recording"), useEpisode, false, &useEpisode, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemSelectDirectory(tr("Folder"), string(directory), false, directory, rmsSearchTimerSave, true));
+ AddMenuItem(new cRecMenuItemBool(tr("Use VPS"), useVPS, false, &useVPS, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Avoid Repeats"), avoidRepeats, false, &avoidRepeats, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemInt(tr("Number of allowed repeats"), allowedRepeats, 0, 30, false, &allowedRepeats, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Compare Title"), compareTitle, false, &compareTitle, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Compare Subtitle"), compareSubtitle, false, &compareSubtitle, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Compare Description"), compareSummary, false, &compareSummary, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Use in Favorites"), useInFavorites, false, &useInFavorites, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemButton(tr("Hide advanced Options"), rmsSearchTimerEdit, false));
+ }
+
+ AddMenuItem(new cRecMenuItemButton(tr("Display Results for Search Timer"), rmsSearchTimerTest, false));
+}
+
+cTVGuideSearchTimer cRecMenuSearchTimerEdit::GetSearchTimer(void) {
+ searchTimer.SetSearchString(searchString);
+ searchTimer.SetActive(timerActive);
+ searchTimer.SetSearchMode(mode);
+ searchTimer.SetUseTitle(useTitle);
+ searchTimer.SetUseSubtitle(useSubtitle);
+ searchTimer.SetUseDesription(useDescription);
+ searchTimer.SetUseChannel(useChannel);
+ if (useChannel) {
+ searchTimer.SetStartChannel(startChannel);
+ searchTimer.SetStopChannel(stopChannel);
+ }
+ searchTimer.SetUseTime(useTime);
+ if (useTime) {
+ searchTimer.SetStartTime(startTime);
+ searchTimer.SetStopTime(stopTime);
+ }
+ searchTimer.SetUseDayOfWeek(useDayOfWeek);
+ if (useDayOfWeek) {
+ searchTimer.SetDayOfWeek(dayOfWeek);
+ }
+ searchTimer.SetPriority(priority);
+ searchTimer.SetLifetime(lifetime);
+ searchTimer.SetUseEpisode(useEpisode);
+ string dir(directory);
+ std::replace(dir.begin(), dir.end(), '/', '~');
+ searchTimer.SetDirectory(dir);
+ searchTimer.SetMarginStart(marginStart);
+ searchTimer.SetMarginStop(marginStop);
+ searchTimer.SetUseVPS(useVPS);
+ searchTimer.SetAvoidRepeats(avoidRepeats);
+ if (avoidRepeats) {
+ searchTimer.SetAllowedRepeats(allowedRepeats);
+ searchTimer.SetCompareTitle(compareTitle);
+ searchTimer.SetCompareSubtitle(compareSubtitle);
+ searchTimer.SetCompareSummary(compareSummary);
+ }
+ searchTimer.SetUseInFavorites(useInFavorites);
+ return searchTimer;
+}
+
+// --- cRecMenuSearchTimerDeleteConfirm ---------------------------------------------
+cRecMenuSearchTimerDeleteConfirm::cRecMenuSearchTimerDeleteConfirm(cTVGuideSearchTimer searchTimer) {
+ SetMenuWidth(70);
+ this->searchTimer = searchTimer;
+ string line1 = tr("Really delete Search Timer");
+ string line2 = *cString::sprintf("\"%s\"?", searchTimer.SearchString().c_str());
+ AddHeader(new cRecMenuItemInfo(line1, 2, line2));
+ AddMenuItem(new cRecMenuItemButton(tr("Delete only Search Timer"), rmsSearchTimerDelete, true));
+ AddMenuItem(new cRecMenuItemButton(tr("Delete Search Timer and created Timers"), rmsSearchTimerDeleteWithTimers, false));
+ AddFooter(new cRecMenuItemButton(tr("Cancel"), rmsClose, false));
+}
+
+cTVGuideSearchTimer cRecMenuSearchTimerDeleteConfirm::GetSearchTimer(void) {
+ return searchTimer;
+}
+
+// --- cRecMenuSearchTimerCreateConfirm ---------------------------------------------------------
+cRecMenuSearchTimerCreateConfirm::cRecMenuSearchTimerCreateConfirm(bool success) {
+ SetMenuWidth(50);
+ cRecMenuItemInfo *infoItem = NULL;
+ if (success) {
+ string line1 = tr("Search Timer sucessfully created");
+ string line2 = tr("Search Timer update initialised");
+ infoItem = new cRecMenuItemInfo(line1, 2, line2);
+ } else {
+ string line1 = tr("Search Timer NOT sucessfully created");
+ infoItem = new cRecMenuItemInfo(line1);
+ }
+ AddHeader(infoItem);
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+// --- cRecMenuSearchTimerTemplatesCreate ---------------------------------------------------------
+cRecMenuSearchTimerTemplatesCreate::cRecMenuSearchTimerTemplatesCreate(TVGuideEPGSearchTemplate templ, cTVGuideSearchTimer searchTimer) {
+ SetMenuWidth(70);
+ this->templ = templ;
+ this->searchTimer = searchTimer;
+
+ string line1 = tr("Creating Search Timer");
+ string line2 = *cString::sprintf("%s: \"%s\"", tr("Search Term"), searchTimer.SearchString().c_str());
+ string line3 = *cString::sprintf("%s \"%s\"", tr("Using Template"), templ.name.c_str());
+
+ AddHeader(new cRecMenuItemInfo(line1, 3, line2, line3));
+ AddMenuItem(new cRecMenuItemButton(tr("Display Results for Search Timer"), rmsSearchTimerTest, true));
+ AddMenuItem(new cRecMenuItemButtonYesNo(tr("Create Search Timer"), tr("Use other Template"), rmsSearchTimerSave, rmsSearchTimerOptions, false));
+}
+
+
+// --- cRecMenuSearchTimerResults ---------------------------------------------------------
+cRecMenuSearchTimerResults::cRecMenuSearchTimerResults(string searchString, const cEvent **searchResults, int numResults, string templateName, eRecMenuState action2) {
+ SetMenuWidth(70);
+ this->searchResults = searchResults;
+ this->action2 = action2;
+ this->numResults = numResults;
+ string message1 = "", message2 = "", message3 = "";
+ if (action2 == rmsFavoritesRecord) {
+ message1 = tr("search results for Favorite");
+ message2 = tr("search result for Favorite");
+ } else {
+ message1 = tr("search results for Search Timer");
+ message2 = tr("search result for Search Timer");
+ message3 = tr("Using Template");
+ }
+ cRecMenuItem *infoItem = NULL;
+ if (templateName.size() > 0) {
+ string line1 = *cString::sprintf("%d %s:", numResults, (numResults>1)?(message1.c_str()):(message2.c_str()));
+ string line2 = *cString::sprintf("\"%s\"", searchString.c_str());
+ string line3 = *cString::sprintf("%s \"%s\"", message3.c_str(), templateName.c_str());
+ infoItem = new cRecMenuItemInfo(line1, 3, line2, line3);
+ } else {
+ string line1 = *cString::sprintf("%d %s:", numResults, (numResults>1)?(message1.c_str()):(message2.c_str()), searchString.c_str());
+ string line2 = *cString::sprintf("\"%s\"", searchString.c_str());
+ infoItem = new cRecMenuItemInfo(line1, 2, line2);
+ }
+ AddHeader(infoItem);
+
+ cRecMenuItem *button = new cRecMenuItemButton(tr("Close"), rmsClose, false);
+ if (searchResults && (numResults > 0)) {
+ for (int i=0; i<numResults; i++) {
+ AddMenuItem(new cRecMenuItemEvent(searchResults[i], rmsSearchShowInfo, action2, (i==0)?true:false));
+ }
+ AddFooter(button);
+ } else {
+ AddMenuItem(button);
+ }
+}
+
+const cEvent *cRecMenuSearchTimerResults::GetEvent(void) {
+ const cEvent *ev = NULL;
+ if (cRecMenuItemEvent *activeItem = dynamic_cast<cRecMenuItemEvent*>(GetActive()))
+ ev = activeItem->GetEvent();
+ return ev;
+}
+
+// --- cRecMenuSearchTimerNothingFound ---------------------------------------------------------
+cRecMenuSearchTimerNothingFound::cRecMenuSearchTimerNothingFound(string searchString) {
+ SetMenuWidth(50);
+ string line1 = tr("Nothing found for Search String");
+ string line2 = *cString::sprintf("\"%s\"", searchString.c_str());
+ AddHeader(new cRecMenuItemInfo(line1, 2, line2));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+/******************************************************************************************
+* SwitchTimer Menus
+******************************************************************************************/
+
+// --- cRecMenuSwitchTimer ---------------------------------------------------------
+cRecMenuSwitchTimer::cRecMenuSwitchTimer(void) {
+ SetMenuWidth(60);
+ switchMinsBefore = 2;
+ announceOnly = 0;
+
+ AddHeader(new cRecMenuItemInfo(tr("Configure Options for Switchtimer")));
+ AddMenuItem(new cRecMenuItemInt(tr("Minutes before switching"), switchMinsBefore, 0, 10, false, &switchMinsBefore));
+ vector<string> switchModes;
+ switchModes.push_back(tr("switch"));
+ switchModes.push_back(tr("announce only"));
+ switchModes.push_back(tr("ask for switch"));
+ AddMenuItem(new cRecMenuItemSelect(tr("Switch Mode"), switchModes, announceOnly, false, &announceOnly));
+
+ AddFooter(new cRecMenuItemButtonYesNo(tr("Create"), tr("Cancel"), rmsSwitchTimerCreate, rmsClose, true));
+}
+
+cSwitchTimer cRecMenuSwitchTimer::GetSwitchTimer(void) {
+ cSwitchTimer st;
+ st.switchMinsBefore = switchMinsBefore;
+ st.announceOnly = announceOnly;
+ return st;
+}
+
+// --- cRecMenuSwitchTimerConfirm ---------------------------------------------------------
+cRecMenuSwitchTimerConfirm::cRecMenuSwitchTimerConfirm(bool success) {
+ SetMenuWidth(50);
+ string message1 = tr("Switch Timer sucessfully created");
+ string message2 = tr("Switch Timer NOT sucessfully created");
+ string infoText = success?message1:message2;
+ AddHeader(new cRecMenuItemInfo(infoText));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+// --- cRecMenuSwitchTimerDelete ---------------------------------------------------------
+cRecMenuSwitchTimerDelete::cRecMenuSwitchTimerDelete(void) {
+ SetMenuWidth(50);
+ AddHeader(new cRecMenuItemInfo(tr("Switch Timer deleted")));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+/******************************************************************************************
+* Search Menus
+******************************************************************************************/
+
+// --- cRecMenuSearch ---------------------------------------------------------
+cRecMenuSearch::cRecMenuSearch(string searchString, bool withOptions) {
+ SetMenuWidth(60);
+ strncpy(this->searchString, searchString.c_str(), TEXTINPUTLENGTH);
+ mode = 0;
+ channelNr = 0;
+ useTitle = true;
+ useSubTitle = true;
+ useDescription = false;
+
+ AddHeader(new cRecMenuItemInfo(tr("Search")));
+ AddMenuItem(new cRecMenuItemText(tr("Search Expression:"), this->searchString, TEXTINPUTLENGTH, false, this->searchString));
+
+ if (withOptions) {
+ vector<string> searchModes;
+ cTVGuideSearchTimer searchTimer;
+ searchTimer.GetSearchModes(&searchModes);
+ AddMenuItem(new cRecMenuItemSelect(tr("Search Mode"), searchModes, 0, false, &mode));
+ AddMenuItem(new cRecMenuItemChannelChooser(tr("Channel to Search"), NULL, false, &channelNr));
+ AddMenuItem(new cRecMenuItemBool(tr("Search in title"), true, false, &useTitle));
+ AddMenuItem(new cRecMenuItemBool(tr("Search in Subtitle"), true, false, &useSubTitle));
+ AddMenuItem(new cRecMenuItemBool(tr("Search in Description"), false, false, &useDescription));
+ } else {
+ AddMenuItem(new cRecMenuItemButton(tr("Show Search Options"), rmsSearchWithOptions, false));
+ }
+
+ cRecMenuItemButtonYesNo *button = new cRecMenuItemButtonYesNo(tr("Perform Search"), tr("Cancel"), rmsSearchPerform, rmsClose, true);
+ AddFooter(button);
+}
+
+Epgsearch_searchresults_v1_0 cRecMenuSearch::GetEPGSearchStruct(void) {
+ Epgsearch_searchresults_v1_0 data;
+ data.query = searchString;
+ data.mode = mode;
+ data.channelNr = channelNr;
+ data.useTitle = useTitle;
+ data.useSubTitle = useSubTitle;
+ data.useDescription = useDescription;
+ return data;
+}
+
+// --- cRecMenuSearchResults ---------------------------------------------------------
+cRecMenuSearchResults::cRecMenuSearchResults(string searchString, const cEvent **searchResults, int numResults) {
+ this->searchResults = searchResults;
+ SetMenuWidth(70);
+ this->searchString = searchString;
+ this->numResults = numResults;
+
+ string message = "";
+ if (numResults > 1) {
+ message = *cString::sprintf("%d %s", numResults, tr("search results for"));
+ } else {
+ message = *cString::sprintf("%d %s", numResults, tr("search result for"));
+ }
+ string searchStringQuoted = *cString::sprintf("\"%s\"", searchString.c_str());
+ AddHeader(new cRecMenuItemInfo(message, 2, searchStringQuoted));
+
+ cRecMenuItem *buttons = new cRecMenuItemButtonYesNo(tr("Adapt Search"), tr("Close"), rmsSearch, rmsClose, false);
+ AddFooter(buttons);
+ if (searchResults && (numResults > 0)) {
+ for (int i=0; i<numResults; i++) {
+ AddMenuItem(new cRecMenuItemEvent(searchResults[i], rmsSearchShowInfo, rmsSearchRecord, (i==0)?true:false));
+ }
+ }
+}
+
+const cEvent *cRecMenuSearchResults::GetEvent(void) {
+ cRecMenuItemEvent *activeItem = dynamic_cast<cRecMenuItemEvent*>(GetActive());
+ if (activeItem)
+ return activeItem->GetEvent();
+ return NULL;
+}
+
+// --- cRecMenuSearchNothingFound ---------------------------------------------------------
+cRecMenuSearchNothingFound::cRecMenuSearchNothingFound(string searchString, bool tooShort) {
+ SetMenuWidth(50);
+ string text;
+ if (!tooShort) {
+ cString message = tr("Nothing found for Search String");
+ text = *cString::sprintf("%s\n\"%s\"",
+ *message,
+ searchString.c_str());
+ } else {
+ cString message = tr("Search String has to have at least three letters");
+ text = *cString::sprintf("%s\n\"%s\"",
+ *message,
+ searchString.c_str());
+
+ }
+ AddHeader(new cRecMenuItemInfo(text));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+// --- cRecMenuSearchConfirmTimer ---------------------------------------------------------
+cRecMenuSearchConfirmTimer::cRecMenuSearchConfirmTimer(const cEvent *event) {
+ SetMenuWidth(50);
+
+ string message = tr("Timer created");
+ cString channelName = Channels.GetByChannelID(event->ChannelID())->Name();
+ string line3 = *cString::sprintf("%s %s - %s", *event->GetDateString(), *event->GetTimeString(), *event->GetEndTimeString());
+ string line4 = event->Title() ? event->Title() : "";
+
+ AddHeader(new cRecMenuItemInfo(message, 4, *channelName, line3, line4));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+/******************************************************************************************
+* Timeline
+******************************************************************************************/
+
+// --- cRecMenuTimeline ---------------------------------------------------------
+cRecMenuTimeline::cRecMenuTimeline(void) {
+ SetMenuWidth(95);
+ SetStartStop();
+ GetTimersForDay();
+
+ timelineHeader = new cRecMenuItemTimelineHeader(timeStart);
+ AddHeader(timelineHeader);
+
+ timelineFooter = new cRecMenuItemButton(tr("Close"), rmsClose, false);
+ AddFooter(timelineFooter);
+ SetTimers();
+}
+
+void cRecMenuTimeline::SetHeaderTimer(void) {
+ cTimer *currentTimer = NULL;
+ cRecMenuItem *currentItem = GetActive();
+ if (!currentItem) {
+ timelineHeader->UnsetCurrentTimer();
+ return;
+ }
+ if (cRecMenuItemTimelineTimer *myActiveItem = dynamic_cast<cRecMenuItemTimelineTimer*>(currentItem)) {
+ currentTimer = myActiveItem->GetTimer();
+ }
+ timelineHeader->SetCurrentTimer(currentTimer);
+ timelineHeader->SetNew();
+}
+
+void cRecMenuTimeline::SetStartStop(void) {
+ time_t now = time(0);
+ tm *timeStruct = localtime(&now);
+ timeStart = now - timeStruct->tm_hour * 3600 - timeStruct->tm_min * 60 - timeStruct->tm_sec;
+ today = timeStart;
+ timeStop = timeStart + 24*3600 - 1;
+}
+
+void cRecMenuTimeline::GetTimersForDay(void) {
+ timersToday.clear();
+ for (cTimer *t = Timers.First(); t; t = Timers.Next(t)) {
+ if (((t->StartTime() > timeStart) && (t->StartTime() <= timeStop)) || ((t->StopTime() > timeStart) && (t->StopTime() <= timeStop))) {
+ timersToday.push_back(t);
+ }
+ }
+ numTimersToday = timersToday.size();
+}
+
+void cRecMenuTimeline::SetTimers(void) {
+ if (numTimersToday == 0) {
+ AddMenuItem(new cRecMenuItemInfo(tr("No active Timers")));
+ timelineFooter->SetActive();
+ } else {
+ for (int i=0; i<numTimersToday; i++) {
+ AddMenuItem(new cRecMenuItemTimelineTimer(timersToday[i], timeStart, timeStop, (i==0)?true:false));
+ }
+ timelineFooter->SetInactive();
+ }
+}
+
+void cRecMenuTimeline::PrevDay(void) {
+ if ((timeStart - 3600*24) < today)
+ return;
+ timeStart -= 3600*24;
+ timeStop -= 3600*24;
+ ClearMenuItems();
+ GetTimersForDay();
+ SetTimers();
+ InitMenuItems();
+ SetHeaderTimer();
+ timelineHeader->SetDay(timeStart);
+ timelineHeader->SetNew();
+}
+
+void cRecMenuTimeline::NextDay(void) {
+ timeStart += 3600*24;
+ timeStop += 3600*24;
+ ClearMenuItems();
+ GetTimersForDay();
+ SetTimers();
+ InitMenuItems();
+ SetHeaderTimer();
+ timelineHeader->SetDay(timeStart);
+ timelineHeader->SetNew();
+}
+
+cTimer *cRecMenuTimeline::GetTimer(void) {
+ if (cRecMenuItemTimelineTimer *activeItem = dynamic_cast<cRecMenuItemTimelineTimer*>(GetActive()))
+ return activeItem->GetTimer();
+ return NULL;
+}
+
+
+eRecMenuState cRecMenuTimeline::ProcessKey(eKeys Key) {
+ eRecMenuState state = rmsContinue;
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ PrevDay();
+ Draw();
+ break;
+ state = rmsConsumed;
+ case kRight:
+ NextDay();
+ Draw();
+ state = rmsConsumed;
+ break;
+ case kUp: {
+ if (ScrollUp(false)) {
+ SetHeaderTimer();
+ Draw();
+ }
+ state = rmsConsumed;
+ break; }
+ case kDown: {
+ if (ScrollDown(false)) {
+ SetHeaderTimer();
+ Draw();
+ }
+ state = rmsConsumed;
+ break; }
+ default:
+ break;
+ }
+ if (state != rmsConsumed) {
+ state = cRecMenu::ProcessKey(Key);
+ }
+ return state;
+}
+
+/******************************************************************************************
+* Recording Search Menus
+******************************************************************************************/
+
+// --- cRecMenuRecordingSearch ---------------------------------------------------------
+cRecMenuRecordingSearch::cRecMenuRecordingSearch(string search) {
+ SetMenuWidth(60);
+ strncpy(searchString, search.c_str(), TEXTINPUTLENGTH);
+ AddHeader(new cRecMenuItemInfo(tr("Search in Recordings")));
+ AddMenuItem(new cRecMenuItemText(tr("Search Expression:"), searchString, TEXTINPUTLENGTH, false, searchString));
+ AddMenuItem(new cRecMenuItemButtonYesNo(tr("Perform Search"), tr("Cancel"), rmsRecordingSearchResult, rmsClose, true));
+}
+
+// --- cRecMenuRecordingSearchResults ---------------------------------------------------------
+cRecMenuRecordingSearchResults::cRecMenuRecordingSearchResults(string searchString, cRecording **searchResults, int numResults) {
+ SetMenuWidth(80);
+ this->searchString = searchString;
+ this->searchResults = searchResults;
+ this->numResults = numResults;
+ string line1 = *cString::sprintf("%s %d %s %s:", tr("Found"), numResults, (numResults>1)?tr("recordings"):tr("recording"), tr("for"));
+ string line2 = *cString::sprintf("\"%s\"", searchString.c_str());
+ AddHeader(new cRecMenuItemInfo(line1, 2, line2));
+
+ if (searchResults && (numResults > 0)) {
+ for (int i=0; i<numResults; i++) {
+ AddMenuItem(new cRecMenuItemRecording(searchResults[i], (i==0)?true:false));
+ }
+ cRecMenuItem *buttons = new cRecMenuItemButtonYesNo(tr("Adapt Search"), tr("Close"), rmsRecordingSearch, rmsClose, false);
+ AddFooter(buttons);
+ } else {
+ cRecMenuItem *buttons = new cRecMenuItemButtonYesNo(tr("Adapt Search"), tr("Close"), rmsRecordingSearch, rmsClose, true);
+ AddMenuItem(buttons);
+ }
+}
+
+// --- cRecMenuRecordingSearchNotFound ---------------------------------------------------------
+cRecMenuRecordingSearchNotFound::cRecMenuRecordingSearchNotFound(string searchString) {
+ SetMenuWidth(50);
+ string line2 = *cString::sprintf("\"%s\"", searchString.c_str());
+ AddHeader(new cRecMenuItemInfo(tr("No recordings found for"), 2, line2));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+/******************************************************************************************
+* Favorites
+******************************************************************************************/
+
+// --- cRecMenuFavorites ---------------------------------------------------------
+
+cRecMenuFavorites::cRecMenuFavorites(vector<cTVGuideSearchTimer> favorites) {
+ SetMenuWidth(70);
+ this->favorites = favorites;
+
+ CreateFavoritesMenuItems();
+
+ string header;
+ if (numFavorites > 0) {
+ header = tr("Favorites");
+ } else {
+ header = tr("No Favorites available");
+ }
+ AddHeader(new cRecMenuItemInfo(header));
+ cRecMenuItem *button = new cRecMenuItemButton(tr("Close"), rmsClose, (numFavorites==0)?true:false);
+ if (numFavorites != 0)
+ AddFooter(button);
+ else
+ AddMenuItem(button);
+}
+
+void cRecMenuFavorites::CreateFavoritesMenuItems(void) {
+ bool active = true;
+ if (config.favWhatsOnNow) {
+ AddMenuItem(new cRecMenuItemFavoriteStatic(tr("What's on now"), rmsFavoritesNow, active));
+ active = false;
+ numFavorites++;
+ }
+ if (config.favWhatsOnNext) {
+ AddMenuItem(new cRecMenuItemFavoriteStatic(tr("What's on next"), rmsFavoritesNext, active));
+ active = false;
+ numFavorites++;
+ }
+ if (config.favUseTime1) {
+ string desc = *cString::sprintf("%s (%s)", config.descUser1.c_str(), NiceTime(config.favTime1).c_str());
+ AddMenuItem(new cRecMenuItemFavoriteStatic(desc, rmsFavoritesUser1, active));
+ active = false;
+ numFavorites++;
+ }
+ if (config.favUseTime2) {
+ string desc = *cString::sprintf("%s (%s)", config.descUser2.c_str(), NiceTime(config.favTime2).c_str());
+ AddMenuItem(new cRecMenuItemFavoriteStatic(desc, rmsFavoritesUser2, active));
+ active = false;
+ numFavorites++;
+ }
+ if (config.favUseTime3) {
+ string desc = *cString::sprintf("%s (%s)", config.descUser3.c_str(), NiceTime(config.favTime3).c_str());
+ AddMenuItem(new cRecMenuItemFavoriteStatic(desc, rmsFavoritesUser3, active));
+ active = false;
+ numFavorites++;
+ }
+ if (config.favUseTime4) {
+ string desc = *cString::sprintf("%s (%s)", config.descUser4.c_str(), NiceTime(config.favTime4).c_str());
+ AddMenuItem(new cRecMenuItemFavoriteStatic(desc, rmsFavoritesUser4, active));
+ active = false;
+ numFavorites++;
+ }
+
+ int numAdditionalFavs = favorites.size();
+ for (int i = 0; i < numAdditionalFavs; i++) {
+ AddMenuItem(new cRecMenuItemFavorite(favorites[i], rmsSearchTimerTest, active));
+ active = false;
+ numFavorites++;
+ }
+}
+
+string cRecMenuFavorites::NiceTime(int favTime) {
+ int hours = favTime/100;
+ int mins = favTime - hours * 100;
+ return *cString::sprintf("%02d:%02d", hours, mins);
+}
+
+cTVGuideSearchTimer cRecMenuFavorites::GetFavorite(void) {
+ cRecMenuItemFavorite *activeItem = dynamic_cast<cRecMenuItemFavorite*>(GetActive());
+ return activeItem->GetFavorite();
+}
diff --git a/recmenus.h b/recmenus.h
new file mode 100644
index 0000000..ad2bf8d
--- /dev/null
+++ b/recmenus.h
@@ -0,0 +1,451 @@
+#ifndef __TVGUIDE_RECMENUS_H
+#define __TVGUIDE_RECMENUS_H
+
+#define TEXTINPUTLENGTH 256
+
+#include "services/epgsearch.h"
+#include "recmenu.h"
+#include "recmanager.h"
+#include "timerconflict.h"
+#include "switchtimer.h"
+
+// --- cRecMenuMain ---------------------------------------------------------
+class cRecMenuMain : public cRecMenu {
+private:
+ int intcallback;
+ bool boolcallback;
+ int stringselectcallback;
+ char searchString[TEXTINPUTLENGTH];
+ int timecallback;
+ time_t daycallback;
+public:
+ cRecMenuMain(bool epgSearchAvailable, bool timerActive, bool switchTimerActive);
+ virtual ~cRecMenuMain(void) {};
+};
+
+/******************************************************************************************
+* Instant Timer Menus
+******************************************************************************************/
+
+// --- cRecMenuConfirmTimer ---------------------------------------------------------
+class cRecMenuConfirmTimer: public cRecMenu {
+public:
+ cRecMenuConfirmTimer(const cEvent *event);
+ virtual ~cRecMenuConfirmTimer(void) {};
+};
+
+// --- cRecMenuAskFolder ---------------------------------------------------------
+class cRecMenuAskFolder: public cRecMenu {
+private:
+ vector<string> folders;
+ eRecMenuState nextAction;
+public:
+ cRecMenuAskFolder(const cEvent *event, eRecMenuState nextAction);
+ virtual ~cRecMenuAskFolder(void) {};
+ string GetFolder(void);
+};
+
+// --- cRecMenuConfirmDeleteTimer ---------------------------------------------------------
+class cRecMenuConfirmDeleteTimer: public cRecMenu {
+public:
+ cRecMenuConfirmDeleteTimer(const cEvent *event);
+ virtual ~cRecMenuConfirmDeleteTimer(void) {};
+};
+
+// --- cRecMenuAskDeleteTimer ---------------------------------------------------------
+class cRecMenuAskDeleteTimer: public cRecMenu {
+public:
+ cRecMenuAskDeleteTimer(const cEvent *event);
+ virtual ~cRecMenuAskDeleteTimer(void) {};
+};
+
+// --- cRecMenuTimerConflicts ---------------------------------------------------------
+class cRecMenuTimerConflicts: public cRecMenu {
+public:
+ cRecMenuTimerConflicts(cTVGuideTimerConflicts *conflicts);
+ virtual ~cRecMenuTimerConflicts(void) {};
+ int GetTimerConflict(void);
+};
+
+// --- cRecMenuTimerConflict ---------------------------------------------------------
+class cRecMenuTimerConflict: public cRecMenu {
+private:
+ cTVGuideTimerConflict *conflict;
+public:
+ cRecMenuTimerConflict(cTVGuideTimerConflict *conflict);
+ virtual ~cRecMenuTimerConflict(void) {};
+ int GetTimerConflictIndex(void);
+};
+
+// --- cRecMenuNoTimerConflict ---------------------------------------------------------
+class cRecMenuNoTimerConflict: public cRecMenu {
+public:
+ cRecMenuNoTimerConflict(void);
+ virtual ~cRecMenuNoTimerConflict(void) {};
+};
+
+// --- cRecMenuRerunResults ---------------------------------------------------------
+class cRecMenuRerunResults: public cRecMenu {
+private:
+ const cEvent **reruns;
+ int numReruns;
+public:
+ cRecMenuRerunResults(const cEvent *original, const cEvent **reruns, int numReruns);
+ int GetTotalNumMenuItems(void);
+ virtual ~cRecMenuRerunResults(void) {
+ delete[] reruns;
+ };
+ const cEvent *GetEvent(void);
+};
+
+// --- cRecMenuNoRerunsFound ---------------------------------------------------------
+class cRecMenuNoRerunsFound: public cRecMenu {
+public:
+ cRecMenuNoRerunsFound(string searchString);
+ virtual ~cRecMenuNoRerunsFound(void) {};
+};
+
+// --- cRecMenuConfirmRerunUsed ---------------------------------------------------------
+class cRecMenuConfirmRerunUsed: public cRecMenu {
+public:
+ cRecMenuConfirmRerunUsed(const cEvent *original, const cEvent *replace);
+ virtual ~cRecMenuConfirmRerunUsed(void) {};
+};
+
+// --- cRecMenuEditTimer ---------------------------------------------------------
+class cRecMenuEditTimer: public cRecMenu {
+private:
+ cTimer *originalTimer;
+ bool timerActive;
+ time_t day;
+ int start;
+ int stop;
+ int prio;
+ int lifetime;
+ char folder[TEXTINPUTLENGTH];
+public:
+ cRecMenuEditTimer(cTimer *timer, eRecMenuState nextState);
+ virtual ~cRecMenuEditTimer(void) {};
+ cTimer GetTimer(void);
+ cTimer *GetOriginalTimer(void);
+};
+
+/******************************************************************************************
+* Series Timer Menus
+******************************************************************************************/
+
+// --- cRecMenuSeriesTimer ---------------------------------------------------------
+class cRecMenuSeriesTimer: public cRecMenu {
+ string folder;
+ bool timerActive;
+ int channel;
+ time_t tstart;
+ int start;
+ int stop;
+ int dayOfWeek;
+ int priority;
+ int lifetime;
+ void CalculateTimes(const cEvent *event);
+public:
+ cRecMenuSeriesTimer(cChannel *initialChannel, const cEvent *event, string folder);
+ virtual ~cRecMenuSeriesTimer(void) {};
+ cTimer *GetTimer(void);
+};
+
+// --- cRecMenuConfirmSeriesTimer ---------------------------------------------------------
+class cRecMenuConfirmSeriesTimer: public cRecMenu {
+public:
+ cRecMenuConfirmSeriesTimer(cTimer *seriesTimer);
+ virtual ~cRecMenuConfirmSeriesTimer(void) {};
+};
+
+/******************************************************************************************
+* SearchTimer Menus
+******************************************************************************************/
+
+// --- cRecMenuSearchTimer ---------------------------------------------------------
+class cRecMenuSearchTimer: public cRecMenu {
+private:
+ char searchString[TEXTINPUTLENGTH];
+public:
+ cRecMenuSearchTimer(const cEvent *event);
+ virtual ~cRecMenuSearchTimer(void) {};
+ string GetSearchString(void) { return searchString; };
+};
+
+// --- cRecMenuSearchTimerTemplates ---------------------------------------------------------
+class cRecMenuSearchTimerTemplates: public cRecMenu {
+private:
+ int numTemplates;
+ cTVGuideSearchTimer searchTimer;
+ vector<TVGuideEPGSearchTemplate> templates;
+public:
+ cRecMenuSearchTimerTemplates(cTVGuideSearchTimer searchTimer, vector<TVGuideEPGSearchTemplate> templates);
+ virtual ~cRecMenuSearchTimerTemplates(void) {};
+ cTVGuideSearchTimer GetSearchTimer(void) { return searchTimer; };
+ TVGuideEPGSearchTemplate GetTemplate(void);
+};
+
+// --- cRecMenuSearchTimers ---------------------------------------------------------
+class cRecMenuSearchTimers: public cRecMenu {
+private:
+ int numSearchTimers;
+ vector<cTVGuideSearchTimer> searchTimers;
+ void SetMenuItems(void);
+public:
+ cRecMenuSearchTimers(vector<cTVGuideSearchTimer> searchTimers);
+ virtual ~cRecMenuSearchTimers(void) {};
+ cTVGuideSearchTimer GetSearchTimer(void);
+};
+
+// --- cRecMenuSearchTimerEdit ---------------------------------------------------------
+class cRecMenuSearchTimerEdit: public cRecMenu {
+private:
+ bool advancedOptions;
+ cTVGuideSearchTimer searchTimer;
+ char searchString[TEXTINPUTLENGTH];
+ bool timerActive;
+ int mode;
+ bool useTitle;
+ bool useSubtitle;
+ bool useDescription;
+ bool useChannel;
+ int startChannel;
+ int stopChannel;
+ bool useTime;
+ int startTime;
+ int stopTime;
+ bool useDayOfWeek;
+ int dayOfWeek;
+ int priority;
+ int lifetime;
+ bool useEpisode;
+ char directory[TEXTINPUTLENGTH];
+ int marginStart;
+ int marginStop;
+ bool useVPS;
+ bool avoidRepeats;
+ int allowedRepeats;
+ bool compareTitle;
+ bool compareSubtitle;
+ bool compareSummary;
+ bool useInFavorites;
+ void CreateMenuItems(void);
+public:
+ cRecMenuSearchTimerEdit(cTVGuideSearchTimer searchTimer, bool advancedOptions);
+ virtual ~cRecMenuSearchTimerEdit(void) {};
+ cTVGuideSearchTimer GetSearchTimer(void);
+};
+
+// --- cRecMenuSearchTimerDeleteConfirm ---------------------------------------------
+class cRecMenuSearchTimerDeleteConfirm: public cRecMenu {
+private:
+ cTVGuideSearchTimer searchTimer;
+public:
+ cRecMenuSearchTimerDeleteConfirm(cTVGuideSearchTimer searchTimer);
+ virtual ~cRecMenuSearchTimerDeleteConfirm(void) {};
+ cTVGuideSearchTimer GetSearchTimer(void);
+};
+
+// --- cRecMenuSearchTimerCreateConfirm ---------------------------------------------------------
+class cRecMenuSearchTimerCreateConfirm: public cRecMenu {
+private:
+public:
+ cRecMenuSearchTimerCreateConfirm(bool success);
+ virtual ~cRecMenuSearchTimerCreateConfirm(void) {};
+};
+
+// --- cRecMenuSearchTimerTemplatesCreate ---------------------------------------------------------
+class cRecMenuSearchTimerTemplatesCreate: public cRecMenu {
+private:
+ TVGuideEPGSearchTemplate templ;
+ cTVGuideSearchTimer searchTimer;
+public:
+ cRecMenuSearchTimerTemplatesCreate(TVGuideEPGSearchTemplate templ, cTVGuideSearchTimer searchTimer);
+ virtual ~cRecMenuSearchTimerTemplatesCreate(void) {};
+ cTVGuideSearchTimer GetSearchTimer(void) { return searchTimer; };
+ TVGuideEPGSearchTemplate GetTemplate(void) { return templ; };
+};
+
+// --- cRecMenuSearchTimerResults ---------------------------------------------------------
+class cRecMenuSearchTimerResults: public cRecMenu {
+private:
+ const cEvent **searchResults;
+ int numResults;
+ eRecMenuState action2;
+public:
+ cRecMenuSearchTimerResults(string searchString, const cEvent **searchResults, int numResults, string templateName = "", eRecMenuState action2 = rmsDisabled);
+ virtual ~cRecMenuSearchTimerResults(void) {
+ delete[] searchResults;
+ };
+ const cEvent *GetEvent(void);
+};
+
+// --- cRecMenuSearchTimerNothingFound ---------------------------------------------------------
+class cRecMenuSearchTimerNothingFound: public cRecMenu {
+public:
+ cRecMenuSearchTimerNothingFound(string searchString);
+ virtual ~cRecMenuSearchTimerNothingFound(void) {};
+};
+
+/******************************************************************************************
+* SwitchTimer Menus
+******************************************************************************************/
+
+// --- cRecMenuSwitchTimer ---------------------------------------------------------
+class cRecMenuSwitchTimer: public cRecMenu {
+private:
+ int switchMinsBefore;
+ int announceOnly;
+public:
+ cRecMenuSwitchTimer(void);
+ virtual ~cRecMenuSwitchTimer(void) {};
+ cSwitchTimer GetSwitchTimer(void);
+};
+
+// --- cRecMenuSwitchTimerConfirm ---------------------------------------------------------
+class cRecMenuSwitchTimerConfirm: public cRecMenu {
+private:
+public:
+ cRecMenuSwitchTimerConfirm(bool success);
+ virtual ~cRecMenuSwitchTimerConfirm(void) {};
+};
+
+// --- cRecMenuSwitchTimerDelete ---------------------------------------------------------
+class cRecMenuSwitchTimerDelete: public cRecMenu {
+private:
+public:
+ cRecMenuSwitchTimerDelete(void);
+ virtual ~cRecMenuSwitchTimerDelete(void) {};
+};
+
+/******************************************************************************************
+* Search Menus
+******************************************************************************************/
+
+// --- cRecMenuSearch ---------------------------------------------------------
+class cRecMenuSearch: public cRecMenu {
+private:
+ char searchString[TEXTINPUTLENGTH];
+ int mode;
+ int channelNr;
+ bool useTitle;
+ bool useSubTitle;
+ bool useDescription;
+public:
+ cRecMenuSearch(string searchString, bool withOptions);
+ virtual ~cRecMenuSearch(void) {};
+ string GetSearchString(void) { return searchString; };
+ Epgsearch_searchresults_v1_0 GetEPGSearchStruct(void);
+};
+
+// --- cRecMenuSearchResults ---------------------------------------------------------
+class cRecMenuSearchResults: public cRecMenu {
+private:
+ string searchString;
+ const cEvent **searchResults;
+ int numResults;
+public:
+ cRecMenuSearchResults(string searchString, const cEvent **searchResults, int numResults);
+ virtual ~cRecMenuSearchResults(void) {
+ delete[] searchResults;
+ };
+ string GetSearchString(void) { return searchString; };
+ const cEvent *GetEvent(void);
+};
+
+// --- cRecMenuSearchNothingFound ---------------------------------------------------------
+class cRecMenuSearchNothingFound: public cRecMenu {
+public:
+ cRecMenuSearchNothingFound(string searchString, bool tooShort = false);
+ virtual ~cRecMenuSearchNothingFound(void) {};
+};
+
+// --- cRecMenuSearchConfirmTimer ---------------------------------------------------------
+class cRecMenuSearchConfirmTimer: public cRecMenu {
+public:
+ cRecMenuSearchConfirmTimer(const cEvent *event);
+ virtual ~cRecMenuSearchConfirmTimer(void) {};
+};
+
+/******************************************************************************************
+* Timeline
+******************************************************************************************/
+
+// --- cRecMenuTimeline ---------------------------------------------------------
+class cRecMenuTimeline: public cRecMenu {
+private:
+ vector<cTimer*> timersToday;
+ int numTimersToday;
+ time_t today;
+ time_t timeStart;
+ time_t timeStop;
+ cRecMenuItemTimelineHeader *timelineHeader;
+ cRecMenuItem *timelineFooter;
+ void SetStartStop(void);
+ void GetTimersForDay(void);
+ void SetTimers(void);
+ void PrevDay(void);
+ void NextDay(void);
+ void ClearMenu(void);
+public:
+ cRecMenuTimeline(void);
+ virtual ~cRecMenuTimeline(void) {};
+ eRecMenuState ProcessKey(eKeys Key);
+ void SetHeaderTimer(void);
+ cTimer *GetTimer(void);
+};
+
+/******************************************************************************************
+* Recording Search Menus
+******************************************************************************************/
+
+// --- cRecMenuRecordingSearch ---------------------------------------------------------
+class cRecMenuRecordingSearch: public cRecMenu {
+private:
+ char searchString[TEXTINPUTLENGTH];
+public:
+ cRecMenuRecordingSearch(string search);
+ virtual ~cRecMenuRecordingSearch(void) {};
+ string GetSearchString(void) { return searchString; };
+};
+
+// --- cRecMenuRecordingSearchResults ---------------------------------------------------------
+class cRecMenuRecordingSearchResults: public cRecMenu {
+private:
+ string searchString;
+ cRecording **searchResults;
+ int numResults;
+public:
+ cRecMenuRecordingSearchResults(string searchString, cRecording **searchResults, int numResults);
+ virtual ~cRecMenuRecordingSearchResults(void) {
+ delete[] searchResults;
+ };
+ string GetSearchString(void) { return searchString; };
+};
+
+// --- cRecMenuRecordingSearchNotFound ---------------------------------------------------------
+class cRecMenuRecordingSearchNotFound: public cRecMenu {
+public:
+ cRecMenuRecordingSearchNotFound(string searchString);
+ virtual ~cRecMenuRecordingSearchNotFound(void) {};
+};
+
+/******************************************************************************************
+* Favorites
+******************************************************************************************/
+
+// --- cRecMenuFavorites ---------------------------------------------------------
+class cRecMenuFavorites: public cRecMenu {
+private:
+ vector<cTVGuideSearchTimer> favorites;
+ int numFavorites;
+ void CreateFavoritesMenuItems(void);
+ string NiceTime(int favTime);
+public:
+ cRecMenuFavorites(vector<cTVGuideSearchTimer> favorites);
+ virtual ~cRecMenuFavorites(void) {};
+ cTVGuideSearchTimer GetFavorite(void);
+};
+
+#endif //__TVGUIDE_RECMENUS_H \ No newline at end of file
diff --git a/recmenuview.c b/recmenuview.c
new file mode 100644
index 0000000..5f20197
--- /dev/null
+++ b/recmenuview.c
@@ -0,0 +1,846 @@
+#include "tvguidengosd.h"
+#include "recmenuview.h"
+
+cRecMenuView::cRecMenuView(void) {
+ active = false;
+ recMenuView = NULL;
+ recMenuViewBuffer = NULL;
+ recMenuViewBuffer2 = NULL;
+ event = NULL;
+ displayEvent = NULL;
+ activeMenu = NULL;
+ activeMenuBuffer = NULL;
+ activeMenuBuffer2 = NULL;
+ recManager = new cRecManager();
+ recManager->SetEPGSearchPlugin();
+ timerConflicts = NULL;
+}
+
+cRecMenuView::~cRecMenuView() {
+ if (activeMenu)
+ delete activeMenu;
+ if (activeMenuBuffer)
+ delete activeMenuBuffer;
+ if (activeMenuBuffer2)
+ delete activeMenuBuffer2;
+ if (recMenuView)
+ delete recMenuView;
+ if (recMenuViewBuffer)
+ delete recMenuViewBuffer;
+ if (recMenuViewBuffer2)
+ delete recMenuViewBuffer2;
+}
+
+void cRecMenuView::Init(cOsdView *recMenuView, cOsdView *recMenuViewBuffer, cOsdView *recMenuViewBuffer2) {
+ active = true;
+ this->recMenuView = recMenuView;
+ this->recMenuViewBuffer = recMenuViewBuffer;
+ this->recMenuViewBuffer2 = recMenuViewBuffer2;
+}
+
+void cRecMenuView::DisplayRecMenu(const cEvent *event) {
+ if (!event) {
+ return;
+ }
+ this->event = event;
+ activeMenu = new cRecMenuMain(recManager->EpgSearchAvailable(), recManager->CheckEventForTimer(event), SwitchTimers.EventInSwitchList(event));
+ DisplayMenu();
+}
+
+void cRecMenuView::DisplayFavorites(void) {
+ vector<cTVGuideSearchTimer> favorites;
+ recManager->GetFavorites(&favorites);
+ activeMenu = new cRecMenuFavorites(favorites);
+ DisplayMenu();
+}
+
+void cRecMenuView::Close(void) {
+ if (activeMenu) {
+ delete activeMenu;
+ activeMenu = NULL;
+ }
+ if (activeMenuBuffer) {
+ delete activeMenuBuffer;
+ activeMenuBuffer = NULL;
+ }
+ if (activeMenuBuffer2) {
+ delete activeMenuBuffer2;
+ activeMenuBuffer2 = NULL;
+ }
+ delete recMenuView;
+ recMenuView = NULL;
+ delete recMenuViewBuffer;
+ recMenuViewBuffer = NULL;
+ delete recMenuViewBuffer2;
+ recMenuViewBuffer2 = NULL;
+ active = false;
+}
+
+void cRecMenuView::Hide(void) {
+ if (recMenuViewBuffer)
+ recMenuViewBuffer->Deactivate(true);
+ else
+ recMenuView->Deactivate(true);
+}
+
+void cRecMenuView::Activate(void) {
+ if (recMenuViewBuffer)
+ recMenuViewBuffer->Activate();
+ else
+ recMenuView->Activate();
+}
+
+eOSState cRecMenuView::ProcessKey(eKeys Key) {
+ eOSState state = osContinue;
+ eRecMenuState nextState = rmsContinue;
+ if (!activeMenu)
+ return state;
+ nextState = activeMenu->ProcessKey(Key);
+ if ((nextState == rmsClose) || ((nextState == rmsNotConsumed)&&(Key == kBack))) {
+ if (activeMenuBuffer2) {
+ delete activeMenu;
+ activeMenu = activeMenuBuffer2;
+ activeMenuBuffer2 = NULL;
+ activeMenu->Show();
+ return osContinue;
+ } else if (activeMenuBuffer) {
+ delete activeMenu;
+ activeMenu = activeMenuBuffer;
+ activeMenuBuffer = NULL;
+ activeMenu->Show();
+ return osContinue;
+ } else {
+ Close();
+ return osEnd;
+ }
+ }
+ state = StateMachine(nextState);
+ if (activeMenu)
+ activeMenu->Flush();
+ return state;
+}
+
+void cRecMenuView::DisplayMenu(bool buffer, bool buffer2) {
+ if (!activeMenu)
+ return;
+ if (buffer) {
+ activeMenu->Init(recMenuViewBuffer);
+ } else if (buffer2) {
+ activeMenu->Init(recMenuViewBuffer2);
+ } else {
+ activeMenu->Init(recMenuView);
+ }
+ if (cRecMenuTimeline *timeline = dynamic_cast<cRecMenuTimeline*>(activeMenu)) {
+ timeline->SetHeaderTimer();
+ }
+ activeMenu->Draw();
+}
+
+void cRecMenuView::DisplaySearchTimerList(void) {
+ delete activeMenu;
+ vector<cTVGuideSearchTimer> searchTimers;
+ recManager->GetSearchTimers(&searchTimers);
+ activeMenu = new cRecMenuSearchTimers(searchTimers);
+ DisplayMenu();
+}
+
+bool cRecMenuView::DisplayTimerConflict(cTimer *timer) {
+ int timerID = 0;
+ for (cTimer *t = Timers.First(); t; t = Timers.Next(t)) {
+ if (t == timer)
+ return DisplayTimerConflict(timerID);
+ timerID++;
+ }
+ return false;
+}
+
+bool cRecMenuView::DisplayTimerConflict(int timerID) {
+ if (timerConflicts)
+ delete timerConflicts;
+ timerConflicts = recManager->CheckTimerConflict();
+ if (!timerConflicts)
+ return false;
+ int showTimerConflict = timerConflicts->GetCorrespondingConflict(timerID);
+ if (showTimerConflict > -1) {
+ timerConflicts->SetCurrentConflict(showTimerConflict);
+ cTVGuideTimerConflict *conflict = timerConflicts->GetCurrentConflict();
+ if (!conflict)
+ return false;
+ activeMenu = new cRecMenuTimerConflict(conflict);
+ DisplayMenu();
+ return true;
+ }
+ return false;
+}
+
+void cRecMenuView::DisplayFavoriteResults(string header, const cEvent **result, int numResults) {
+ if (numResults) {
+ activeMenuBuffer = activeMenu;
+ activeMenuBuffer->Hide();
+ activeMenu = new cRecMenuSearchTimerResults(header, result, numResults, "", rmsFavoritesRecord);
+ } else {
+ activeMenuBuffer = activeMenu;
+ activeMenuBuffer->Hide();
+ activeMenu = new cRecMenuSearchTimerNothingFound(header);
+ }
+ DisplayMenu(true);
+}
+
+eOSState cRecMenuView::StateMachine(eRecMenuState nextState) {
+ eOSState state = osContinue;
+ switch (nextState) {
+/***************************************************************************************
+* INSTANT RECORDING
+****************************************************************************************/
+ case rmsInstantRecord: {
+ //caller: main menu or folder chooser
+ //Creating timer for active Event, if no conflict, confirm and exit
+ string recFolder = "";
+ if (cRecMenuAskFolder *menu = dynamic_cast<cRecMenuAskFolder*>(activeMenu)) {
+ recFolder = menu->GetFolder();
+ }
+ delete activeMenu;
+ cTimer *timer = recManager->createTimer(event, recFolder);
+ if (!DisplayTimerConflict(timer)) {
+ activeMenu = new cRecMenuConfirmTimer(event);
+ DisplayMenu();
+ }
+ break; }
+ case rmsInstantRecordFolder: {
+ //caller: main menu
+ //Asking for Folder
+ delete activeMenu;
+ activeMenu = new cRecMenuAskFolder(event, rmsInstantRecord);
+ DisplayMenu();
+ break; }
+ case rmsDeleteTimer: {
+ //caller: main menu
+ //delete timer for active event
+ delete activeMenu;
+ if (recManager->IsRecorded(event)) {
+ activeMenu = new cRecMenuAskDeleteTimer(event);
+ } else {
+ recManager->DeleteTimer(event);
+ activeMenu = new cRecMenuConfirmDeleteTimer(event);
+ }
+ DisplayMenu();
+ break; }
+ case rmsDeleteTimerConfirmation: {
+ //delete running timer for active event
+ recManager->DeleteTimer(event);
+ delete activeMenu;
+ activeMenu = new cRecMenuConfirmDeleteTimer(event);
+ DisplayMenu();
+ break; }
+ case rmsEditTimer: {
+ //edit timer for active event
+ cTimer *timer = recManager->GetTimerForEvent(event);
+ if (timer) {
+ delete activeMenu;
+ activeMenu = new cRecMenuEditTimer(timer, rmsSaveTimer);
+ DisplayMenu();
+ }
+ break; }
+ case rmsSaveTimer: {
+ //caller: cRecMenuEditTimer
+ //save timer for active event
+ cTimer timerModified;
+ cTimer *originalTimer;
+ if (cRecMenuEditTimer *menu = dynamic_cast<cRecMenuEditTimer*>(activeMenu)) {
+ timerModified = menu->GetTimer();
+ originalTimer = menu->GetOriginalTimer();
+ } else break;
+ recManager->SaveTimer(originalTimer, timerModified);
+ Close();
+ state = osEnd;
+ break; }
+ case rmsIgnoreTimerConflict:
+ //caller: cRecMenuTimerConflict
+ //Confirming created Timer
+ delete activeMenu;
+ activeMenu = new cRecMenuConfirmTimer(event);
+ DisplayMenu();
+ break;
+ case rmsTimerConflictShowInfo: {
+ //caller: cRecMenuTimerConflict
+ int timerIndex;
+ if (cRecMenuTimerConflict *menu = dynamic_cast<cRecMenuTimerConflict*>(activeMenu)) {
+ timerIndex = menu->GetTimerConflictIndex();
+ } else break;
+ int timerID = timerConflicts->GetCurrentConflictTimerID(timerIndex);
+ cTimer *t = Timers.Get(timerID);
+ if (t) {
+ displayEvent = t->Event();
+ if (displayEvent) {
+ state = osUser1;
+ }
+ }
+ break; }
+ case rmsDeleteTimerConflictMenu: {
+ //caller: cRecMenuTimerConflict
+ //delete timer out of current timer conflict
+ int timerIndex;
+ if (cRecMenuTimerConflict *menu = dynamic_cast<cRecMenuTimerConflict*>(activeMenu)) {
+ timerIndex = menu->GetTimerConflictIndex();
+ } else break;
+ int timerID = timerConflicts->GetCurrentConflictTimerID(timerIndex);
+ recManager->DeleteTimer(timerID);
+ delete activeMenu;
+ if (!DisplayTimerConflict(timerID)) {
+ activeMenu = new cRecMenuConfirmTimer(event);
+ DisplayMenu();
+ }
+ break; }
+ case rmsEditTimerConflictMenu: {
+ //caller: cRecMenuTimerConflict
+ //edit timer out of current timer conflict
+ int timerIndex;
+ if (cRecMenuTimerConflict *menu = dynamic_cast<cRecMenuTimerConflict*>(activeMenu)) {
+ timerIndex = menu->GetTimerConflictIndex();
+ } else break;
+ int timerID = timerConflicts->GetCurrentConflictTimerID(timerIndex);
+ cTimer *timer = Timers.Get(timerID);
+ if (timer) {
+ delete activeMenu;
+ activeMenu = new cRecMenuEditTimer(timer, rmsSaveTimerConflictMenu);
+ DisplayMenu();
+ }
+ break; }
+ case rmsSaveTimerConflictMenu: {
+ //caller: cRecMenuEditTimer
+ //save timer from current timer conflict
+ cTimer timerModified;
+ cTimer *originalTimer;
+ if (cRecMenuEditTimer *menu = dynamic_cast<cRecMenuEditTimer*>(activeMenu)) {
+ timerModified = menu->GetTimer();
+ originalTimer = menu->GetOriginalTimer();
+ } else break;
+ recManager->SaveTimer(originalTimer, timerModified);
+ delete activeMenu;
+ if (!DisplayTimerConflict(originalTimer)) {
+ activeMenu = new cRecMenuConfirmTimer(event);
+ DisplayMenu();
+ }
+ break; }
+/***************************************************************************************
+* SERIES TIMER
+****************************************************************************************/
+ case rmsSeriesTimer: {
+ //caller: main menu oder folder chooser
+ string recFolder = "";
+ if (cRecMenuAskFolder *menu = dynamic_cast<cRecMenuAskFolder*>(activeMenu)) {
+ recFolder = menu->GetFolder();
+ }
+ delete activeMenu;
+ cChannel *channel = Channels.GetByChannelID(event->ChannelID());
+ activeMenu = new cRecMenuSeriesTimer(channel, event, recFolder);
+ DisplayMenu();
+ break; }
+ case rmsSeriesTimerFolder:
+ //caller: main menu
+ //Asking for Folder
+ delete activeMenu;
+ activeMenu = new cRecMenuAskFolder(event, rmsSeriesTimer);
+ DisplayMenu();
+ break;
+ case rmsSeriesTimerCreate: {
+ //caller: cRecMenuSeriesTimer
+ cTimer *seriesTimer;
+ if (cRecMenuSeriesTimer *menu = dynamic_cast<cRecMenuSeriesTimer*>(activeMenu)) {
+ seriesTimer = menu->GetTimer();
+ } else break;
+ recManager->CreateSeriesTimer(seriesTimer);
+ delete activeMenu;
+ activeMenu = new cRecMenuConfirmSeriesTimer(seriesTimer);
+ DisplayMenu();
+ break; }
+/**********************************************************************************************
+ * SWITCH TIMER
+ ***********************************************************************************************/
+ case rmsSwitchTimer:
+ delete activeMenu;
+ activeMenu = new cRecMenuSwitchTimer();
+ DisplayMenu();
+ break;
+ case rmsSwitchTimerCreate: {
+ cSwitchTimer switchTimer;
+ if (cRecMenuSwitchTimer *menu = dynamic_cast<cRecMenuSwitchTimer*>(activeMenu)) {
+ switchTimer = menu->GetSwitchTimer();
+ } else break;
+ bool success = recManager->CreateSwitchTimer(event, switchTimer);
+ delete activeMenu;
+ activeMenu = new cRecMenuSwitchTimerConfirm(success);
+ DisplayMenu();
+ break; }
+ case rmsSwitchTimerDelete:
+ recManager->DeleteSwitchTimer(event);
+ delete activeMenu;
+ activeMenu = new cRecMenuSwitchTimerDelete();
+ DisplayMenu();
+ break;
+/**********************************************************************************************
+ * SEARCH TIMER
+ ***********************************************************************************************/
+ case rmsSearchTimer:
+ //Caller: main menu
+ //set search String for search timer
+ delete activeMenu;
+ activeMenu = new cRecMenuSearchTimer(event);
+ DisplayMenu();
+ break;
+ case rmsSearchTimerOptions: {
+ //Caller: cRecMenuSearchTimer, cRecMenuSearchTimerTemplates
+ //Choose to set options manually or by template
+ string searchString;
+ cTVGuideSearchTimer searchTimer;
+ bool reload = false;
+ if (cRecMenuSearchTimer *menu = dynamic_cast<cRecMenuSearchTimer*>(activeMenu)) {
+ searchString = menu->GetSearchString();
+ } else if (cRecMenuSearchTimerTemplatesCreate *menu = dynamic_cast<cRecMenuSearchTimerTemplatesCreate*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ reload = true;
+ } else break;
+ delete activeMenu;
+ if (searchString.size() < 4) {
+ activeMenu = new cRecMenuSearchTimer(event);
+ } else {
+ if (!reload) {
+ searchTimer.SetSearchString(searchString);
+ }
+ vector<TVGuideEPGSearchTemplate> epgSearchTemplates;
+ recManager->ReadEPGSearchTemplates(&epgSearchTemplates);
+ if (epgSearchTemplates.size() > 0) {
+ activeMenu = new cRecMenuSearchTimerTemplates(searchTimer, epgSearchTemplates);
+ } else {
+ activeMenu = new cRecMenuSearchTimerEdit(searchTimer, false);
+ }
+ }
+ DisplayMenu();
+ break; }
+ case rmsSearchTimers: {
+ //caller: main menu
+ DisplaySearchTimerList();
+ break; }
+ case rmsSearchTimerEdit:
+ case rmsSearchTimerEditAdvanced: {
+ //caller: cRecMenuSearchTimers, cRecMenuSearchTimerEdit, cRecMenuSearchTimerTemplates
+ cTVGuideSearchTimer searchTimer;
+ bool advancedOptions = false;
+ if (cRecMenuSearchTimers *menu = dynamic_cast<cRecMenuSearchTimers*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ } else if (cRecMenuSearchTimerEdit *menu = dynamic_cast<cRecMenuSearchTimerEdit*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ advancedOptions = (nextState == rmsSearchTimerEditAdvanced)?true:false;
+ } else if (cRecMenuSearchTimerTemplates *menu = dynamic_cast<cRecMenuSearchTimerTemplates*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ } else break;
+ delete activeMenu;
+ activeMenu = new cRecMenuSearchTimerEdit(searchTimer, advancedOptions);
+ DisplayMenu();
+ break; }
+ case rmsSearchTimerTest: {
+ //caller: cRecMenuSearchTimerEdit, cRecMenuSearchTimerTemplatesCreate, cRecMenuSearchTimers, cRecMenuFavorites
+ //show results of currently choosen search timer
+ cTVGuideSearchTimer searchTimer;
+ eRecMenuState recState = rmsDisabled;
+ if (cRecMenuSearchTimerEdit *menu = dynamic_cast<cRecMenuSearchTimerEdit*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ recState = rmsSearchTimerRecord;
+ } else if (cRecMenuSearchTimers *menu = dynamic_cast<cRecMenuSearchTimers*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ recState = rmsSearchTimerRecord;
+ } else if (cRecMenuSearchTimerTemplatesCreate *menu = dynamic_cast<cRecMenuSearchTimerTemplatesCreate*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ TVGuideEPGSearchTemplate tmpl = menu->GetTemplate();
+ searchTimer.SetTemplate(tmpl.templValue);
+ searchTimer.Parse(true);
+ } else if (cRecMenuFavorites *menu = dynamic_cast<cRecMenuFavorites*>(activeMenu)) {
+ searchTimer = menu->GetFavorite();
+ recState = rmsFavoritesRecord;
+ }
+ else break;
+ int numSearchResults = 0;
+ string searchString = searchTimer.BuildSearchString();
+ const cEvent **searchResult = recManager->PerformSearchTimerSearch(searchString, numSearchResults);
+ if (numSearchResults) {
+ activeMenuBuffer = activeMenu;
+ activeMenuBuffer->Hide();
+ activeMenu = new cRecMenuSearchTimerResults(searchTimer.SearchString(), searchResult, numSearchResults, "", recState);
+ } else {
+ activeMenuBuffer = activeMenu;
+ activeMenuBuffer->Hide();
+ activeMenu = new cRecMenuSearchTimerNothingFound(searchTimer.SearchString());
+ }
+ DisplayMenu(true);
+ break; }
+ case rmsSearchTimerSave: {
+ //caller: cRecMenuSearchTimerEdit, cRecMenuSearchTimerTemplatesCreate
+ //create new or modify existing search timer
+ cTVGuideSearchTimer searchTimer;
+ if (cRecMenuSearchTimerEdit *menu = dynamic_cast<cRecMenuSearchTimerEdit*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ } else if (cRecMenuSearchTimerTemplatesCreate *menu = dynamic_cast<cRecMenuSearchTimerTemplatesCreate*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ TVGuideEPGSearchTemplate tmpl = menu->GetTemplate();
+ searchTimer.SetTemplate(tmpl.templValue);
+ searchTimer.Parse(true);
+ } else break;
+ bool success = recManager->SaveSearchTimer(&searchTimer);
+ recManager->UpdateSearchTimers();
+ if (searchTimer.GetID() >= 0) {
+ //Timer modified, show list
+ DisplaySearchTimerList();
+ } else {
+ //new timer, confirm
+ delete activeMenu;
+ activeMenu = new cRecMenuSearchTimerCreateConfirm(success);
+ DisplayMenu();
+ }
+ break; }
+ case rmsSearchTimerCreateWithTemplate: {
+ //caller: cRecMenuSearchTimerTemplates
+ //create new search timer from template
+ TVGuideEPGSearchTemplate templ;
+ cTVGuideSearchTimer searchTimer;
+ if (cRecMenuSearchTimerTemplates *menu = dynamic_cast<cRecMenuSearchTimerTemplates*>(activeMenu)) {
+ templ = menu->GetTemplate();
+ searchTimer = menu->GetSearchTimer();
+ } else break;
+ delete activeMenu;
+ activeMenu = new cRecMenuSearchTimerTemplatesCreate(templ, searchTimer);
+ DisplayMenu();
+ break; }
+ case rmsSearchTimerDeleteConfirm: {
+ //caller: cRecMenuSearchTimers
+ //Ask for confirmation and if timers created by this search timer should alo be deleted
+ cTVGuideSearchTimer searchTimer;
+ if (cRecMenuSearchTimers *menu = dynamic_cast<cRecMenuSearchTimers*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ } else break;
+ activeMenuBuffer = activeMenu;
+ activeMenuBuffer->Hide();
+ activeMenu = new cRecMenuSearchTimerDeleteConfirm(searchTimer);
+ DisplayMenu(true);
+ break; }
+ case rmsSearchTimerDelete:
+ case rmsSearchTimerDeleteWithTimers: {
+ //caller: cRecMenuSearchTimerDeleteConfirm
+ //actually delete searchtimer
+ cTVGuideSearchTimer searchTimer;
+ if (cRecMenuSearchTimerDeleteConfirm *menu = dynamic_cast<cRecMenuSearchTimerDeleteConfirm*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ } else break;
+ bool delTimers = (nextState==rmsSearchTimerDeleteWithTimers)?true:false;
+ recManager->DeleteSearchTimer(&searchTimer, delTimers);
+ delete activeMenuBuffer;
+ activeMenuBuffer = NULL;
+ DisplaySearchTimerList();
+ break; }
+ case rmsSearchTimerRecord: {
+ //caller: cRecMenuSearchTimerResults
+ const cEvent *ev = NULL;
+ if (cRecMenuSearchTimerResults *menu = dynamic_cast<cRecMenuSearchTimerResults*>(activeMenu)) {
+ ev = menu->GetEvent();
+ } else break;
+ if (!ev)
+ break;
+ recManager->createTimer(ev, "");
+ activeMenuBuffer2 = activeMenu;
+ activeMenuBuffer2->Hide();
+ activeMenu = new cRecMenuSearchConfirmTimer(ev);
+ DisplayMenu(false, true);
+ break; }
+/**********************************************************************************************
+* SEARCH
+***********************************************************************************************/
+ case rmsSearch:
+ case rmsSearchWithOptions: {
+ //caller: main menu, cRecMenuSearch, cRecMenuSearchResults
+ bool withOptions = false;
+ string searchString = event->Title();
+ if (cRecMenuSearch *menu = dynamic_cast<cRecMenuSearch*>(activeMenu)) {
+ withOptions = true;
+ searchString = menu->GetSearchString();
+ } else if (cRecMenuSearchResults *menu = dynamic_cast<cRecMenuSearchResults*>(activeMenu)) {
+ searchString = menu->GetSearchString();
+ }
+ delete activeMenu;
+ activeMenu = new cRecMenuSearch(searchString, withOptions);
+ DisplayMenu();
+ break; }
+ case rmsSearchPerform: {
+ //caller: cRecMenuSearch
+ bool useBuffer = false;
+ Epgsearch_searchresults_v1_0 epgSearchData;
+ if (cRecMenuSearch *menu = dynamic_cast<cRecMenuSearch*>(activeMenu)) {
+ epgSearchData = menu->GetEPGSearchStruct();
+ } else break;
+ string searchString = epgSearchData.query;
+ if (searchString.size() < 3) {
+ activeMenu->Hide();
+ activeMenuBuffer = activeMenu;
+ useBuffer = true;
+ activeMenu = new cRecMenuSearchNothingFound(searchString, true);
+ } else {
+ int numSearchResults = 0;
+ const cEvent **searchResult = recManager->PerformSearch(epgSearchData, numSearchResults);
+ if (searchResult) {
+ delete activeMenu;
+ activeMenu = new cRecMenuSearchResults(searchString, searchResult, numSearchResults);
+ } else {
+ activeMenu->Hide();
+ activeMenuBuffer = activeMenu;
+ useBuffer = true;
+ activeMenu = new cRecMenuSearchNothingFound(searchString);
+ }
+ }
+ DisplayMenu(useBuffer);
+ break; }
+ case rmsSearchShowInfo: {
+ //caller: cRecMenuSearchResults, cRecMenuSearchTimerResults, cRecMenuRerunResults
+ if (cRecMenuSearchResults *menu = dynamic_cast<cRecMenuSearchResults*>(activeMenu)) {
+ displayEvent = menu->GetEvent();
+ } else if (cRecMenuSearchTimerResults *menu = dynamic_cast<cRecMenuSearchTimerResults*>(activeMenu)) {
+ displayEvent = menu->GetEvent();
+ } else if (cRecMenuRerunResults *menu = dynamic_cast<cRecMenuRerunResults*>(activeMenu)) {
+ displayEvent = menu->GetEvent();
+ } else break;
+ if (displayEvent) {
+ state = osUser1;
+ }
+ break;}
+ case rmsSearchRecord: {
+ //caller: cRecMenuSearchResults
+ const cEvent *ev = NULL;
+ if (cRecMenuSearchResults *menu = dynamic_cast<cRecMenuSearchResults*>(activeMenu)) {
+ ev = menu->GetEvent();
+ } else break;
+ if (!ev)
+ break;
+ recManager->createTimer(ev, "");
+ activeMenuBuffer = activeMenu;
+ activeMenuBuffer->Hide();
+ activeMenu = new cRecMenuSearchConfirmTimer(ev);
+ DisplayMenu(true);
+ break;}
+/**********************************************************************************************
+ * CHECK FOR TIMER CONFLICTS
+ ***********************************************************************************************/
+ case rmsTimerConflicts: {
+ //caller: main menu
+ //Show timer conflict
+ if (timerConflicts) {
+ delete timerConflicts;
+ }
+ timerConflicts = recManager->CheckTimerConflict();
+ delete activeMenu;
+ int numConflicts = timerConflicts->NumConflicts();
+ if (numConflicts > 0) {
+ activeMenu = new cRecMenuTimerConflicts(timerConflicts);
+ } else {
+ activeMenu = new cRecMenuNoTimerConflict();
+ }
+ DisplayMenu();
+ break; }
+ case rmsTimerConflict: {
+ //caller: cRecMenuTimerConflicts
+ //Show timer conflict
+ if (!timerConflicts)
+ break;
+ int timerConflict;
+ if (cRecMenuTimerConflicts *menu = dynamic_cast<cRecMenuTimerConflicts*>(activeMenu)) {
+ timerConflict = menu->GetTimerConflict();
+ } else break;
+ timerConflicts->SetCurrentConflict(timerConflict);
+ delete activeMenu;
+ activeMenu = new cRecMenuTimerConflict(timerConflicts->GetCurrentConflict());
+ DisplayMenu();
+ break; }
+ case rmsSearchRerunsTimerConflictMenu: {
+ //caller: cRecMenuTimerConflict
+ //Show reruns for timer from timer conflict
+ if (!timerConflicts)
+ break;
+ int timerConflict;
+ if (cRecMenuTimerConflict *menu = dynamic_cast<cRecMenuTimerConflict*>(activeMenu)) {
+ timerConflict = menu->GetTimerConflictIndex();
+ } else break;
+ int timerID = timerConflicts->GetCurrentConflictTimerID(timerConflict);
+ cTimer *timer = Timers.Get(timerID);
+ if (timer) {
+ const cEvent *event = timer->Event();
+ if (event) {
+ int numReruns = 0;
+ const cEvent **reruns = recManager->LoadReruns(event, numReruns);
+ if (reruns && (numReruns > 0)) {
+ activeMenuBuffer = activeMenu;
+ activeMenuBuffer->Hide();
+ activeMenu = new cRecMenuRerunResults(event, reruns, numReruns);
+ } else {
+ activeMenuBuffer = activeMenu;
+ activeMenuBuffer->Hide();
+ activeMenu = new cRecMenuNoRerunsFound((event->Title())?event->Title():"");
+ }
+ DisplayMenu(true);
+ }
+ }
+ break; }
+ case rmsTimerConflictRecordRerun: {
+ //caller: cRecMenuRerunResults
+ //buffer: cRecMenuTimerConflict
+ if (!activeMenuBuffer)
+ break;
+ if (!timerConflicts)
+ break;
+ const cEvent *replace;
+ int originalConflictIndex;
+ if (cRecMenuRerunResults *menu = dynamic_cast<cRecMenuRerunResults*>(activeMenu)) {
+ replace = menu->GetEvent();
+ } else break;
+ if (cRecMenuTimerConflict *menu = dynamic_cast<cRecMenuTimerConflict*>(activeMenuBuffer)) {
+ originalConflictIndex = menu->GetTimerConflictIndex();
+ } else break;
+ int originalTimerID = timerConflicts->GetCurrentConflictTimerID(originalConflictIndex);
+ cTimer *timerOriginal = Timers.Get(originalTimerID);
+ if (replace && timerOriginal) {
+ recManager->DeleteTimer(timerOriginal->Event());
+ recManager->createTimer(replace);
+ activeMenuBuffer->Show();
+ delete activeMenuBuffer;
+ activeMenuBuffer = NULL;
+ delete activeMenu;
+ activeMenu = new cRecMenuConfirmRerunUsed(timerOriginal->Event(), replace);
+ DisplayMenu();
+ }
+ break; }
+ /**********************************************************************************************
+ * TIMELINE
+ ***********************************************************************************************/
+ case rmsTimeline: {
+ delete activeMenu;
+ activeMenu = new cRecMenuTimeline();
+ DisplayMenu();
+ break; }
+ case rmsTimelineTimerEdit: {
+ cTimer *timer;
+ if (cRecMenuTimeline *menu = dynamic_cast<cRecMenuTimeline*>(activeMenu)) {
+ timer = menu->GetTimer();
+ } else break;
+ if (timer) {
+ delete activeMenu;
+ activeMenu = new cRecMenuEditTimer(timer, rmsTimelineTimerSave);
+ DisplayMenu();
+ }
+ break;}
+ case rmsTimelineTimerSave: {
+ cTimer timerModified;
+ cTimer *originalTimer;
+ if (cRecMenuEditTimer *menu = dynamic_cast<cRecMenuEditTimer*>(activeMenu)) {
+ timerModified = menu->GetTimer();
+ originalTimer = menu->GetOriginalTimer();
+ } else break;
+ recManager->SaveTimer(originalTimer, timerModified);
+ delete activeMenu;
+ activeMenu = new cRecMenuTimeline();
+ DisplayMenu();
+ break; }
+ case rmsTimelineTimerDelete: {
+ cTimer *timer;
+ if (cRecMenuEditTimer *menu = dynamic_cast<cRecMenuEditTimer*>(activeMenu)) {
+ timer = menu->GetOriginalTimer();
+ } else break;
+ recManager->DeleteTimer(timer);
+ delete activeMenu;
+ activeMenu = new cRecMenuTimeline();
+ DisplayMenu();
+ break; }
+ default:
+ break;
+/**********************************************************************************************
+ * RECORDINGS SEARCH
+ ***********************************************************************************************/
+ case rmsRecordingSearch: {
+ //caller: main menu or rmsRecordingSearchResult
+ string searchString = event->Title();
+ if (cRecMenuRecordingSearchResults *menu = dynamic_cast<cRecMenuRecordingSearchResults*>(activeMenu)) {
+ searchString = menu->GetSearchString();
+ };
+ delete activeMenu;
+ activeMenu = new cRecMenuRecordingSearch(searchString);
+ DisplayMenu();
+ break; }
+ case rmsRecordingSearchResult: {
+ //caller: cRecMenuRecordingSearch
+ string searchString;
+ if (cRecMenuRecordingSearch *menu = dynamic_cast<cRecMenuRecordingSearch*>(activeMenu)) {
+ searchString = menu->GetSearchString();
+ } else break;
+ delete activeMenu;
+ if (searchString.size() < 4) {
+ activeMenu = new cRecMenuRecordingSearch(searchString);
+ } else {
+ int numSearchResults = 0;
+ cRecording **searchResult = recManager->SearchForRecordings(searchString, numSearchResults);
+ if (numSearchResults == 0) {
+ activeMenu = new cRecMenuRecordingSearchNotFound(searchString);
+ } else {
+ activeMenu = new cRecMenuRecordingSearchResults(searchString, searchResult, numSearchResults);
+ }
+ }
+ DisplayMenu();
+ break; }
+ /**********************************************************************************************
+ * FAVORITES
+ *********************************************************************************************/
+ case rmsFavoritesRecord: {
+ //caller: cRecMenuSearchTimerResults
+ const cEvent *ev = NULL;
+ if (cRecMenuSearchTimerResults *menu = dynamic_cast<cRecMenuSearchTimerResults*>(activeMenu)) {
+ ev = menu->GetEvent();
+ } else break;
+ if (!ev)
+ break;
+ recManager->createTimer(ev, "");
+ activeMenuBuffer2 = activeMenu;
+ activeMenuBuffer2->Hide();
+ activeMenu = new cRecMenuSearchConfirmTimer(ev);
+ DisplayMenu(false, true);
+ break;}
+ case rmsFavoritesNow:
+ case rmsFavoritesNext: {
+ int numResults = 0;
+ bool nowOrNext;
+ string header;
+ if (nextState == rmsFavoritesNow) {
+ nowOrNext = true;
+ header = tr("What's on now");
+ } else {
+ nowOrNext = false;
+ header = tr("What's on next");
+ }
+ const cEvent **result = recManager->WhatsOnNow(nowOrNext, numResults);
+ DisplayFavoriteResults(header, result, numResults);
+ break; }
+ case rmsFavoritesUser1: {
+ int numResults = 0;
+ const cEvent **result = recManager->UserDefinedTime(1, numResults);
+ DisplayFavoriteResults(config.descUser1, result, numResults);
+ break; }
+ case rmsFavoritesUser2: {
+ int numResults = 0;
+ const cEvent **result = recManager->UserDefinedTime(2, numResults);
+ DisplayFavoriteResults(config.descUser2, result, numResults);
+ break; }
+ case rmsFavoritesUser3: {
+ int numResults = 0;
+ const cEvent **result = recManager->UserDefinedTime(3, numResults);
+ DisplayFavoriteResults(config.descUser3, result, numResults);
+ break; }
+ case rmsFavoritesUser4: {
+ int numResults = 0;
+ const cEvent **result = recManager->UserDefinedTime(4, numResults);
+ DisplayFavoriteResults(config.descUser4, result, numResults);
+ break; }
+ }
+ return state;
+}
diff --git a/recmenuview.h b/recmenuview.h
new file mode 100644
index 0000000..dfc526c
--- /dev/null
+++ b/recmenuview.h
@@ -0,0 +1,44 @@
+#ifndef __TVGUIDE_RECMENUVIEW_H
+#define __TVGUIDE_RECMENUVIEW_H
+
+#include "config.h"
+#include "libskindesigner/osdelements.h"
+#include "recmanager.h"
+#include "recmenus.h"
+
+class cRecMenuView {
+private:
+ bool active;
+ cOsdView *recMenuView;
+ cOsdView *recMenuViewBuffer;
+ cOsdView *recMenuViewBuffer2;
+ const cEvent *event;
+ const cEvent *displayEvent;
+ cRecManager *recManager;
+ cRecMenu *activeMenu;
+ cRecMenu *activeMenuBuffer;
+ cRecMenu *activeMenuBuffer2;
+ cTVGuideTimerConflicts *timerConflicts;
+ void DrawBackground(int menuWidth, int menuHeight, bool scrolling);
+ void DisplayMenu(bool buffer = false, bool buffer2 = false);
+ void DisplaySearchTimerList(void);
+ bool DisplayTimerConflict(cTimer *timer);
+ bool DisplayTimerConflict(int timerID);
+ void DisplayFavoriteResults(string header, const cEvent **result, int numResults);
+ eOSState StateMachine(eRecMenuState nextState);
+public:
+ cRecMenuView(void);
+ virtual ~cRecMenuView(void);
+ void Init(cOsdView *recMenuView, cOsdView *recMenuViewBuffer, cOsdView *recMenuViewBuffer2);
+ void DisplayRecMenu(const cEvent *event);
+ void DisplayFavorites(void);
+ void Close(void);
+ void Hide(void);
+ void Activate(void);
+ eOSState ProcessKey(eKeys Key);
+ void Flush(void) { activeMenu->Flush(); };
+ const cEvent *GetEvent(void) { return displayEvent; };
+ bool Active(void) { return active; };
+};
+
+#endif //__TVGUIDE_RECMENUVIEW_H
diff --git a/searchtimer.c b/searchtimer.c
new file mode 100644
index 0000000..6570c55
--- /dev/null
+++ b/searchtimer.c
@@ -0,0 +1,570 @@
+#include <string>
+#include <vector>
+#include <sstream>
+#include <algorithm>
+#include <vdr/channels.h>
+#include <vdr/device.h>
+#include "helpers.h"
+#include "searchtimer.h"
+
+// -- cTVGuideSearchTimer -----------------------------------------------------------------
+cTVGuideSearchTimer::cTVGuideSearchTimer(void) {
+ strTimer = "";
+ ID = -1;
+ searchString = "";
+ useTime = false;
+ startTime = 0000;
+ stopTime = 2359;
+ useChannel = false;
+ channelMin = Channels.GetByNumber(cDevice::CurrentChannel());
+ channelMax = Channels.GetByNumber(cDevice::CurrentChannel());
+ channelGroup = "";
+ useCase = false;
+ mode = 0;
+ useTitle = true;
+ useSubtitle = true;
+ useDescription = true;
+ useDuration = false;
+ minDuration = 0;
+ maxDuration = 2359;
+ useAsSearchTimer = true;
+ useDayOfWeek = false;
+ dayOfWeek = 0;
+ directory = "";
+ useEpisode = 0;
+ priority = 99;
+ lifetime = 99;
+ marginStart = 5;
+ marginStop = 5;
+ useVPS = false;
+ action = 0;
+ useExtEPGInfo = 0;
+ extEPGInfoValues = "";
+ avoidRepeats = 1;
+ allowedRepeats = 1;
+ compareTitle = 1;
+ compareSubtitle = 2;
+ compareSummary = 1;
+ catvaluesAvoidRepeat = 0;
+ repeatsWithinDays = 0;
+ delAfterDays = 0;
+ recordingsKeep = 0;
+ switchMinsBefore = 0;
+ pauseOnNrRecordings = 0;
+ blacklistMode = 0;
+ blacklists = "";
+ fuzzyTolerance = 0;
+ useInFavorites = 0;
+ menuTemplate = 0;
+ delMode = 0;
+ delAfterCountRecs = 0;
+ delAfterDaysOfFirstRec = 0;
+ useAsSearchTimerFrom = 0;
+ useAsSearchTimerTil = 0;
+ ignoreMissingEPGCats = 0;
+ unmuteSoundOnSwitch = 0;
+ compareSummaryMatchInPercent = 0;
+ contentsFilter = "";
+ compareDate = 0;
+}
+
+cTVGuideSearchTimer::~cTVGuideSearchTimer(void) {
+}
+
+bool cTVGuideSearchTimer::operator < (const cTVGuideSearchTimer& other) const {
+ std::string searchStringOther = other.SearchString();
+ searchStringOther = StrToLowerCase(searchStringOther);
+ std::string thisSearchString = StrToLowerCase(searchString);
+ int comp = thisSearchString.compare(searchStringOther);
+ if (comp < 0)
+ return true;
+ return false;
+}
+
+
+void cTVGuideSearchTimer::SetTemplate(std::string tmpl) {
+ std::stringstream searchTimerString;
+ searchTimerString << "0:";
+ searchTimerString << tmpl;
+ strTimer = searchTimerString.str();
+}
+
+int cTVGuideSearchTimer::DayOfWeek(void) {
+ int vdrDayOfWeek = 0;
+ if (dayOfWeek >= 0) {
+ vdrDayOfWeek = pow(2, (dayOfWeek+6)%7);
+ } else if (dayOfWeek < 0) {
+ int absDayOfWeek = abs(dayOfWeek);
+ for (int i=0; i < 7; i++) {
+ if (absDayOfWeek & (1 << i)) {
+ vdrDayOfWeek += pow(2, (i+6)%7);
+ }
+ }
+ }
+ return vdrDayOfWeek;
+}
+
+void cTVGuideSearchTimer::SetDayOfWeek(int VDRDayOfWeek) {
+ int epgSearchDayOfWeek = 0;
+ for (int i=0; i < 7; i++) {
+ if (VDRDayOfWeek & (1 << i)) {
+ epgSearchDayOfWeek += pow(2, (i+1)%7);
+ }
+ }
+ this->dayOfWeek = epgSearchDayOfWeek * (-1);
+}
+
+/*
+ 0 - unique search timer id
+ 1 - the search term
+ 2 - use time? 0/1
+ 3 - start time in HHMM
+ 4 - stop time in HHMM
+ 5 - use channel? 0 = no, 1 = Interval, 2 = Channel group, 3 = FTA only
+ 6 - if 'use channel' = 1 then channel id[|channel id] in VDR format,
+ one entry or min/max entry separated with |, if 'use channel' = 2
+ then the channel group name
+ 7 - match case? 0/1
+ 8 - search mode:
+ 0 - the whole term must appear as substring
+ 1 - all single terms (delimiters are blank,',', ';', '|' or '~')
+ must exist as substrings.
+ 2 - at least one term (delimiters are blank, ',', ';', '|' or '~')
+ must exist as substring.
+ 3 - matches exactly
+ 4 - regular expression
+ 9 - use title? 0/1
+ 10 - use subtitle? 0/1
+ 11 - use description? 0/1
+ 12 - use duration? 0/1
+ 13 - min duration in hhmm
+ 14 - max duration in hhmm
+ 15 - use as search timer? 0/1
+ 16 - use day of week? 0/1
+ 17 - day of week (0 = Sunday, 1 = Monday...;
+ -1 Sunday, -2 Monday, -4 Tuesday, ...; -7 Sun, Mon, Tue)
+ 18 - use series recording? 0/1
+ 19 - directory for recording
+ 20 - priority of recording
+ 21 - lifetime of recording
+ 22 - time margin for start in minutes
+ 23 - time margin for stop in minutes
+ 24 - use VPS? 0/1
+ 25 - action:
+ 0 = create a timer
+ 1 = announce only via OSD (no timer)
+ 2 = switch only (no timer)
+ 3 = announce via OSD and switch (no timer)
+ 4 = announce via mail
+ 26 - use extended EPG info? 0/1
+ 27 - extended EPG info values. This entry has the following format
+ (delimiter is '|' for each category, '#' separates id and value):
+ 1 - the id of the extended EPG info category as specified in
+ epgsearchcats.conf
+ 2 - the value of the extended EPG info category
+ (a ':' will be translated to "!^colon^!", e.g. in "16:9")
+ 28 - avoid repeats? 0/1
+ 29 - allowed repeats
+ 30 - compare title when testing for a repeat? 0/1
+ 31 - compare subtitle when testing for a repeat? 0/1/2
+ 0 - no
+ 1 - yes
+ 2 - yes, if present
+ 32 - compare description when testing for a repeat? 0/1
+ 33 - compare extended EPG info when testing for a repeat?
+ This entry is a bit field of the category IDs.
+ 34 - accepts repeats only within x days
+ 35 - delete a recording automatically after x days
+ 36 - but keep this number of recordings anyway
+ 37 - minutes before switch (if action = 2)
+ 38 - pause if x recordings already exist
+ 39 - blacklist usage mode (0 none, 1 selection, 2 all)
+ 40 - selected blacklist IDs separated with '|'
+ 41 - fuzzy tolerance value for fuzzy searching
+ 42 - use this search in favorites menu (0 no, 1 yes)
+ 43 - id of a menu search template
+ 44 - auto deletion mode (0 don't delete search timer, 1 delete after given
+ count of recordings, 2 delete after given days after first recording)
+ 45 - count of recordings after which to delete the search timer
+ 46 - count of days after the first recording after which to delete the search
+ timer
+ 47 - first day where the search timer is active (see parameter 16)
+ 48 - last day where the search timer is active (see parameter 16)
+ 49 - ignore missing EPG categories? 0/1
+ 50 - unmute sound if off when used as switch timer
+ 51 - percentage of match when comparing the summary of two events (with 'avoid repeats')
+ 52 - HEX representation of the content descriptors, each descriptor ID is represented with 2 chars
+ 53 - compare date when testing for a repeat? (0=no, 1=same day, 2=same week, 3=same month)
+*/
+bool cTVGuideSearchTimer::Parse(bool readTemplate) {
+ splitstring s(strTimer.c_str());
+ std::vector<std::string> values = s.split(':', 1);
+ int numValues = values.size();
+ if (numValues < 12)
+ return false;
+ for (int value = 0; value < numValues; value++) {
+ switch (value) {
+ case 0:
+ if (!readTemplate)
+ ID = atoi(values[value].c_str());
+ break;
+ case 1:
+ if (!readTemplate) {
+ std::string searchStringMasked = values[value];
+ std::replace(searchStringMasked.begin(), searchStringMasked.end(), '|', ':');
+ searchString = searchStringMasked;
+ }
+ break;
+ case 2:
+ useTime = atoi(values[value].c_str());
+ break;
+ case 3:
+ if (useTime) {
+ startTime = atoi(values[value].c_str());
+ }
+ break;
+ case 4:
+ if (useTime) {
+ stopTime = atoi(values[value].c_str());
+ }
+ break;
+ case 5:
+ useChannel = atoi(values[value].c_str());
+ break;
+ case 6:
+ if (useChannel == 0) {
+ channelMin = NULL;
+ channelMax = NULL;
+ } else if (useChannel == 1) {
+ char *channelMinbuffer = NULL;
+ char *channelMaxbuffer = NULL;
+ int channels = sscanf(values[value].c_str(), "%a[^|]|%a[^|]", &channelMinbuffer, &channelMaxbuffer);
+ channelMin = Channels.GetByChannelID(tChannelID::FromString(channelMinbuffer), true, true);
+ if (!channelMin) {
+ channelMin = channelMax = NULL;
+ useChannel = 0;
+ }
+ if (channels == 1)
+ channelMax = channelMin;
+ else {
+ channelMax = Channels.GetByChannelID(tChannelID::FromString(channelMaxbuffer), true, true);
+ if (!channelMax) {
+ channelMin = channelMax = NULL;
+ useChannel = 0;
+ }
+ }
+ free(channelMinbuffer);
+ free(channelMaxbuffer);
+ } else if (useChannel == 2) {
+ channelGroup = values[value];
+ }
+ break;
+ case 7:
+ useCase = atoi(values[value].c_str());
+ break;
+ case 8:
+ mode = atoi(values[value].c_str());
+ break;
+ case 9:
+ useTitle = atoi(values[value].c_str());
+ break;
+ case 10:
+ useSubtitle = atoi(values[value].c_str());
+ break;
+ case 11:
+ useDescription = atoi(values[value].c_str());
+ break;
+ case 12:
+ useDuration = atoi(values[value].c_str());
+ break;
+ case 13:
+ minDuration = atoi(values[value].c_str());
+ break;
+ case 14:
+ maxDuration = atoi(values[value].c_str());
+ break;
+ case 15:
+ useAsSearchTimer = atoi(values[value].c_str());
+ break;
+ case 16:
+ useDayOfWeek = atoi(values[value].c_str());
+ break;
+ case 17:
+ dayOfWeek = atoi(values[value].c_str());
+ break;
+ case 18:
+ useEpisode = atoi(values[value].c_str());
+ break;
+ case 19:
+ directory = values[value];
+ break;
+ case 20:
+ priority = atoi(values[value].c_str());
+ break;
+ case 21:
+ lifetime = atoi(values[value].c_str());
+ break;
+ case 22:
+ marginStart = atoi(values[value].c_str());
+ break;
+ case 23:
+ marginStop = atoi(values[value].c_str());
+ break;
+ case 24:
+ useVPS = atoi(values[value].c_str());
+ break;
+ case 25:
+ action = atoi(values[value].c_str());
+ break;
+ case 26:
+ useExtEPGInfo = atoi(values[value].c_str());
+ break;
+ case 27:
+ extEPGInfoValues = values[value];
+ break;
+ case 28:
+ avoidRepeats = atoi(values[value].c_str());
+ break;
+ case 29:
+ allowedRepeats = atoi(values[value].c_str());
+ break;
+ case 30:
+ compareTitle = atoi(values[value].c_str());
+ break;
+ case 31:
+ compareSubtitle = atoi(values[value].c_str());
+ break;
+ case 32:
+ compareSummary = atoi(values[value].c_str());
+ break;
+ case 33:
+ catvaluesAvoidRepeat = atol(values[value].c_str());
+ break;
+ case 34:
+ repeatsWithinDays = atoi(values[value].c_str());
+ break;
+ case 35:
+ delAfterDays = atoi(values[value].c_str());
+ break;
+ case 36:
+ recordingsKeep = atoi(values[value].c_str());
+ break;
+ case 37:
+ switchMinsBefore = atoi(values[value].c_str());
+ break;
+ case 38:
+ pauseOnNrRecordings = atoi(values[value].c_str());
+ break;
+ case 39:
+ blacklistMode = atoi(values[value].c_str());
+ break;
+ case 40:
+ blacklists = values[value];
+ break;
+ case 41:
+ fuzzyTolerance = atoi(values[value].c_str());
+ break;
+ case 42:
+ useInFavorites = atoi(values[value].c_str());
+ break;
+ case 43:
+ menuTemplate = atoi(values[value].c_str());
+ break;
+ case 44:
+ delMode = atoi(values[value].c_str());
+ break;
+ case 45:
+ delAfterCountRecs = atoi(values[value].c_str());
+ break;
+ case 46:
+ delAfterDaysOfFirstRec = atoi(values[value].c_str());
+ break;
+ case 47:
+ useAsSearchTimerFrom = atol(values[value].c_str());
+ break;
+ case 48:
+ useAsSearchTimerTil = atol(values[value].c_str());
+ break;
+ case 49:
+ ignoreMissingEPGCats = atoi(values[value].c_str());
+ break;
+ case 50:
+ unmuteSoundOnSwitch = atoi(values[value].c_str());
+ break;
+ case 51:
+ compareSummaryMatchInPercent = atoi(values[value].c_str());
+ break;
+ case 52:
+ contentsFilter = values[value];
+ break;
+ case 53:
+ compareDate = atoi(values[value].c_str());
+ break;
+ default:
+ break;
+ }
+ }
+ return true;
+}
+
+std::string cTVGuideSearchTimer::BuildSearchString(void) {
+ std::stringstream search;
+ // 0 - 2
+ if (ID > -1)
+ search << ID << ":";
+ else
+ search << ":";
+ std::string searchStringMasked = searchString;
+ std::replace(searchStringMasked.begin(), searchStringMasked.end(), ':', '|');
+ search << searchStringMasked << ":";
+ search << useTime << ":";
+
+ // 3 - 6
+ if (useTime) {
+ search << *cString::sprintf("%04d", startTime) << ":";
+ search << *cString::sprintf("%04d", stopTime) << ":";
+ } else {
+ search << "::";
+ }
+
+ search << useChannel << ":";
+ if (useChannel == 1) {
+ if (channelMin && channelMax) {
+ if (channelMin->Number() < channelMax->Number())
+ search << std::string(channelMin->GetChannelID().ToString()) << "|" << std::string(channelMax->GetChannelID().ToString()) << ":";
+ else
+ search << std::string(channelMin->GetChannelID().ToString()) << ":";
+ } else {
+ search << "0:";
+ }
+ } else if (useChannel == 2) {
+ search << channelGroup << ":";
+ } else {
+ search << "0:";
+ }
+ // 7 - 14
+ search << useCase << ":";
+ search << mode << ":";
+ search << useTitle << ":";
+ search << useSubtitle << ":";
+ search << useDescription << ":";
+ search << useDuration << ":";
+ if (useDuration) {
+ search << *cString::sprintf("%04d", minDuration) << ":";
+ search << *cString::sprintf("%04d", maxDuration) << ":";
+ } else {
+ search << "::";
+ }
+ //15 - 53
+ search << useAsSearchTimer << ":";
+ search << useDayOfWeek << ":";
+ search << dayOfWeek << ":";
+ search << useEpisode << ":";
+ search << directory << ":";
+ search << priority << ":";
+ search << lifetime << ":";
+ search << marginStart << ":";
+ search << marginStop << ":";
+ search << useVPS << ":";
+ search << action << ":";
+ search << useExtEPGInfo << ":";
+ search << extEPGInfoValues << ":";
+ search << avoidRepeats << ":";
+ search << allowedRepeats << ":";
+ search << compareTitle << ":";
+ search << compareSubtitle << ":";
+ search << compareSummary << ":";
+ search << catvaluesAvoidRepeat << ":";
+ search << repeatsWithinDays << ":";
+ search << delAfterDays << ":";
+ search << recordingsKeep << ":";
+ search << switchMinsBefore << ":";
+ search << pauseOnNrRecordings << ":";
+ search << blacklistMode << ":";
+ search << blacklists << ":";
+ search << fuzzyTolerance << ":";
+ search << useInFavorites << ":";
+ search << menuTemplate << ":";
+ search << delMode << ":";
+ search << delAfterCountRecs << ":";
+ search << delAfterDaysOfFirstRec << ":";
+ search << useAsSearchTimerFrom << ":";
+ search << useAsSearchTimerTil << ":";
+ search << ignoreMissingEPGCats << ":";
+ search << unmuteSoundOnSwitch << ":";
+ search << compareSummaryMatchInPercent << ":";
+ search << contentsFilter << ":";
+ search << compareDate;
+
+ strTimer = search.str();
+ return strTimer;
+}
+
+bool cTVGuideSearchTimer::Active(void) {
+ if (useAsSearchTimer)
+ return true;
+ return false;
+}
+
+
+int cTVGuideSearchTimer::GetNumTimers(void) {
+ int numTimers = 0;
+ if (ID < 0)
+ return numTimers;
+ for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer)) {
+ char* searchID = GetAuxValue(timer, "s-id");
+ if (!searchID) continue;
+ if (ID == atoi(searchID))
+ numTimers++;
+ free(searchID);
+ }
+ return numTimers;
+}
+
+int cTVGuideSearchTimer::GetNumRecordings(void) {
+ int numRecordings = 0;
+ if (ID < 0)
+ return numRecordings;
+ for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
+ if (recording->IsEdited())
+ continue;
+ if (!recording->Info())
+ continue;
+ char* searchID = GetAuxValue(recording, "s-id");
+ if (!searchID) continue;
+ if (ID == atoi(searchID))
+ numRecordings++;
+ free(searchID);
+ }
+ return numRecordings;
+}
+
+void cTVGuideSearchTimer::GetSearchModes(std::vector<std::string> *searchModes) {
+ searchModes->push_back(tr("whole term must appear"));
+ searchModes->push_back(tr("all terms must exist"));
+ searchModes->push_back(tr("one term must exist"));
+ searchModes->push_back(tr("exact match"));
+ searchModes->push_back(tr("regular expression"));
+}
+
+
+void cTVGuideSearchTimer::Dump(void) {
+ esyslog("tvguide searchtimer: strTimer: %s", strTimer.c_str());
+ esyslog("tvguide searchtimer: ID: %d", ID);
+ esyslog("tvguide searchtimer: searchString: %s", searchString.c_str());
+ esyslog("tvguide searchtimer: useTime: %d", useTime);
+ esyslog("tvguide searchtimer: startTime: %d", startTime);
+ esyslog("tvguide searchtimer: stopTime: %d", stopTime);
+ esyslog("tvguide searchtimer: useChannel: %d", useChannel);
+ if (channelMin)
+ esyslog("tvguide searchtimer: channelMin: %s", channelMin->Name());
+ if (channelMax)
+ esyslog("tvguide searchtimer: channelMax: %s", channelMax->Name());
+ esyslog("tvguide searchtimer: channelGroup: %s", channelGroup.c_str());
+ esyslog("tvguide searchtimer: useCase: %d", useCase);
+ esyslog("tvguide searchtimer: mode: %d", mode);
+ esyslog("tvguide searchtimer: useTitle: %d", useTitle);
+ esyslog("tvguide searchtimer: useSubtitle: %d", useSubtitle);
+ esyslog("tvguide searchtimer: useDescription: %d", useDescription);
+}
+
diff --git a/searchtimer.h b/searchtimer.h
new file mode 100644
index 0000000..f797ebd
--- /dev/null
+++ b/searchtimer.h
@@ -0,0 +1,136 @@
+#ifndef __TVGUIDE_SEARCHTIMER_H
+#define __TVGUIDE_SEARCHTIMER_H
+
+
+class cTVGuideSearchTimer {
+private:
+ std::string strTimer;
+ int ID;
+ std::string searchString;
+ int useTime;
+ int startTime;
+ int stopTime;
+ int useChannel;
+ cChannel *channelMin;
+ cChannel *channelMax;
+ std::string channelGroup;
+ int useCase;
+ int mode;
+ int useTitle;
+ int useSubtitle;
+ int useDescription;
+ int useDuration;
+ int minDuration;
+ int maxDuration;
+ int useAsSearchTimer;
+ int useDayOfWeek;
+ int dayOfWeek;
+ int useEpisode;
+ std::string directory;
+ int priority;
+ int lifetime;
+ int marginStart;
+ int marginStop;
+ int useVPS;
+ int action;
+ int useExtEPGInfo;
+ std::string extEPGInfoValues;
+ int avoidRepeats;
+ int allowedRepeats;
+ int compareTitle;
+ int compareSubtitle;
+ int compareSummary;
+ unsigned long catvaluesAvoidRepeat;
+ int repeatsWithinDays;
+ int delAfterDays;
+ int recordingsKeep;
+ int switchMinsBefore;
+ int pauseOnNrRecordings;
+ int blacklistMode;
+ std::string blacklists;
+ int fuzzyTolerance;
+ int useInFavorites;
+ int menuTemplate;
+ int delMode;
+ int delAfterCountRecs;
+ int delAfterDaysOfFirstRec;
+ int useAsSearchTimerFrom;
+ int useAsSearchTimerTil;
+ int ignoreMissingEPGCats;
+ int unmuteSoundOnSwitch;
+ int compareSummaryMatchInPercent;
+ std::string contentsFilter;
+ int compareDate;
+public:
+ cTVGuideSearchTimer(void);
+ virtual ~cTVGuideSearchTimer(void);
+ bool operator < (const cTVGuideSearchTimer& other) const;
+ void SetEPGSearchString(std::string strTimer) { this->strTimer = strTimer; };
+ void SetTemplate(std::string tmpl);
+ bool Parse(bool readTemplate = false);
+ std::string BuildSearchString(void);
+ int GetID(void) { return ID; };
+ //GETTER
+ std::string SearchString(void) const { return searchString; };
+ bool Active(void);
+ bool UseTitle(void) { return useTitle; };
+ bool UseSubtitle(void) { return useSubtitle; };
+ bool UseDescription(void) { return useDescription; };
+ int SearchMode(void) { return mode; };
+ bool UseChannel(void) { return useChannel; };
+ int StartChannel(void) { return (channelMin)?channelMin->Number():0; };
+ int StopChannel(void) { return (channelMax)?channelMax->Number():0; };
+ bool UseTime(void) { return useTime; };
+ int StartTime(void) { return startTime; };
+ int StopTime(void) { return stopTime; };
+ bool UseDayOfWeek(void) { return useDayOfWeek; };
+ int DayOfWeek(void);
+ int UseEpisode(void) { return useEpisode; };
+ std::string Directory(void) { return directory; };
+ int Priority(void) { return priority; };
+ int Lifetime(void) { return lifetime; };
+ int MarginStart(void) { return marginStart; };
+ int MarginStop(void) { return marginStop; };
+ bool UseVPS(void) { return useVPS; };
+ bool AvoidRepeats(void) { return avoidRepeats; };
+ int AllowedRepeats(void) { return allowedRepeats; };
+ bool CompareTitle(void) { return compareTitle; };
+ bool CompareSubtitle(void) { return compareSubtitle; };
+ bool CompareSummary(void) { return compareSummary; };
+ bool UseInFavorites(void) { return useInFavorites; };
+ //SETTER
+ void SetSearchString(std::string searchString) { this->searchString = searchString; };
+ void SetActive(bool active) { useAsSearchTimer = active; };
+ void SetSearchMode(int searchMode) { mode = searchMode; };
+ void SetUseTitle(bool useTitle) { this->useTitle = useTitle; };
+ void SetUseSubtitle(bool useSubtitle) { this->useSubtitle = useSubtitle; };
+ void SetUseDesription(bool useDescription) { this->useDescription = useDescription; };
+ void SetUseChannel(bool useChannel) { this->useChannel = useChannel; };
+ void SetStartChannel(int startChannel) { channelMin = Channels.GetByNumber(startChannel); };
+ void SetStopChannel(int stopChannel) { channelMax = Channels.GetByNumber(stopChannel); };
+ void SetUseTime(bool useTime) { this->useTime = useTime; };
+ void SetStartTime(int startTime) { this->startTime = startTime; };
+ void SetStopTime(int stopTime) { this->stopTime = stopTime; };
+ void SetUseDayOfWeek(bool useDayOfWeek) { this->useDayOfWeek = useDayOfWeek; };
+ void SetDayOfWeek(int VDRDayOfWeek);
+ void SetUseEpisode(int useEpisode) { this->useEpisode = useEpisode; };
+ void SetDirectory(std::string directory) { this-> directory = directory; };
+ void SetPriority(int priority) { this->priority = priority; };
+ void SetLifetime(int lifetime) { this->lifetime = lifetime; };
+ void SetMarginStart(int marginStart) { this->marginStart = marginStart; };
+ void SetMarginStop(int marginStop) { this->marginStop = marginStop; };
+ void SetUseVPS(bool useVPS) { this->useVPS = useVPS; };
+ void SetAvoidRepeats(bool avoidRepeats) { this->avoidRepeats = avoidRepeats; };
+ void SetAllowedRepeats(int allowedRepeats) { this->allowedRepeats = allowedRepeats; };
+ void SetCompareTitle(bool compareTitle) { this->compareTitle = compareTitle; };
+ void SetCompareSubtitle(bool compareSubtitle) { this->compareSubtitle = compareSubtitle; };
+ void SetCompareSummary(bool compareSummary) { this->compareSummary = compareSummary; };
+ void SetUseInFavorites(bool useInFavorites) { this->useInFavorites = useInFavorites; };
+ //COMMON
+ int GetNumTimers(void);
+ int GetNumRecordings(void);
+ void GetSearchModes(std::vector<std::string> *searchModes);
+ void Dump(void);
+};
+
+#endif //__TVGUIDE_SEARCHTIMER_H
diff --git a/services/epgsearch.h b/services/epgsearch.h
new file mode 100644
index 0000000..2669da4
--- /dev/null
+++ b/services/epgsearch.h
@@ -0,0 +1,202 @@
+/* -*- c++ -*-
+Copyright (C) 2004-2013 Christian Wieninger
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+
+The author can be reached at cwieninger@gmx.de
+
+The project's page is at http://winni.vdr-developer.org/epgsearch
+*/
+
+#ifndef EPGSEARCHSERVICES_INC
+#define EPGSEARCHSERVICES_INC
+
+#include <string>
+#include <list>
+#include <memory>
+#include <set>
+#include <vdr/osdbase.h>
+
+// Data structure for service "Epgsearch-search-v1.0"
+struct Epgsearch_search_v1_0
+{
+// in
+ char* query; // search term
+ int mode; // search mode (0=phrase, 1=and, 2=or, 3=regular expression)
+ int channelNr; // channel number to search in (0=any)
+ bool useTitle; // search in title
+ bool useSubTitle; // search in subtitle
+ bool useDescription; // search in description
+// out
+ cOsdMenu* pResultMenu; // pointer to the menu of results
+};
+
+// Data structure for service "Epgsearch-exttimeredit-v1.0"
+struct Epgsearch_exttimeredit_v1_0
+{
+// in
+ cTimer* timer; // pointer to the timer to edit
+ bool bNew; // flag that indicates, if this is a new timer or an existing one
+ const cEvent* event; // pointer to the event corresponding to this timer (may be NULL)
+// out
+ cOsdMenu* pTimerMenu; // pointer to the menu of results
+};
+
+// Data structure for service "Epgsearch-enablesearchtimers-v1.0"
+struct Epgsearch_enablesearchtimers_v1_0
+{
+// in
+ bool enable; // enable search timer thread?
+};
+
+// Data structure for service "Epgsearch-updatesearchtimers-v1.0"
+struct Epgsearch_updatesearchtimers_v1_0
+{
+// in
+ bool showMessage; // inform via osd when finished?
+};
+
+// Data structure for service "Epgsearch-osdmessage-v1.0"
+struct Epgsearch_osdmessage_v1_0
+{
+// in
+ char* message; // the message to display
+ eMessageType type;
+};
+
+// Data structure for service "EpgsearchMenu-v1.0"
+struct EpgSearchMenu_v1_0
+{
+// in
+// out
+ cOsdMenu* Menu; // pointer to the menu
+};
+
+// Data structure for service "Epgsearch-lastconflictinfo-v1.0"
+struct Epgsearch_lastconflictinfo_v1_0
+{
+// in
+// out
+ time_t nextConflict; // next conflict date, 0 if none
+ int relevantConflicts; // number of relevant conflicts
+ int totalConflicts; // total number of conflicts
+};
+
+// Data structure for service "Epgsearch-searchresults-v1.0"
+struct Epgsearch_searchresults_v1_0
+{
+// in
+ char* query; // search term
+ int mode; // search mode (0=phrase, 1=and, 2=or, 3=regular expression)
+ int channelNr; // channel number to search in (0=any)
+ bool useTitle; // search in title
+ bool useSubTitle; // search in subtitle
+ bool useDescription; // search in description
+// out
+
+ class cServiceSearchResult : public cListObject
+ {
+ public:
+ const cEvent* event;
+ cServiceSearchResult(const cEvent* Event) : event(Event) {}
+ };
+
+ cList<cServiceSearchResult>* pResultList; // pointer to the results
+};
+
+// Data structure for service "Epgsearch-switchtimer-v1.0"
+struct Epgsearch_switchtimer_v1_0
+{
+// in
+ const cEvent* event;
+ int mode; // mode (0=query existence, 1=add/modify, 2=delete)
+// in/out
+ int switchMinsBefore;
+ int announceOnly;
+// out
+ bool success; // result
+};
+
+// Data structures for service "Epgsearch-services-v1.0"
+class cServiceHandler
+{
+ public:
+ virtual std::list<std::string> SearchTimerList() = 0;
+ // returns a list of search timer entries in the same format as used in epgsearch.conf
+ virtual int AddSearchTimer(const std::string&) = 0;
+ // adds a new search timer and returns its ID (-1 on error)
+ virtual bool ModSearchTimer(const std::string&) = 0;
+ // edits an existing search timer and returns success
+ virtual bool DelSearchTimer(int) = 0;
+ // deletes search timer with given ID and returns success
+ virtual std::list<std::string> QuerySearchTimer(int) = 0;
+ // returns the search result of the searchtimer with given ID in the same format as used in SVDRP command 'QRYS' (->MANUAL)
+ virtual std::list<std::string> QuerySearch(std::string) = 0;
+ // returns the search result of the searchtimer with given settings in the same format as used in SVDRP command 'QRYS' (->MANUAL)
+ virtual std::list<std::string> ExtEPGInfoList() = 0;
+ // returns a list of extended EPG categories in the same format as used in epgsearchcats.conf
+ virtual std::list<std::string> ChanGrpList() = 0;
+ // returns a list of channel groups maintained by epgsearch
+ virtual std::list<std::string> BlackList() = 0;
+ // returns a list of blacklists in the same format as used in epgsearchblacklists.conf
+ virtual std::set<std::string> DirectoryList() = 0;
+ // List of all recording directories used in recordings, timers, search timers or in epgsearchdirs.conf
+ virtual ~cServiceHandler() {}
+ // Read a setup value
+ virtual std::string ReadSetupValue(const std::string& entry) = 0;
+ // Write a setup value
+ virtual bool WriteSetupValue(const std::string& entry, const std::string& value) = 0;
+};
+
+struct Epgsearch_services_v1_0
+{
+// in/out
+ std::auto_ptr<cServiceHandler> handler;
+};
+
+// Data structures for service "Epgsearch-services-v1.1"
+class cServiceHandler_v1_1 : public cServiceHandler
+{
+ public:
+ // Get timer conflicts
+ virtual std::list<std::string> TimerConflictList(bool relOnly=false) = 0;
+ // Check if a conflict check is advised
+ virtual bool IsConflictCheckAdvised() = 0;
+};
+
+struct Epgsearch_services_v1_1
+{
+// in/out
+ std::auto_ptr<cServiceHandler_v1_1> handler;
+};
+
+// Data structures for service "Epgsearch-services-v1.2"
+class cServiceHandler_v1_2 : public cServiceHandler_v1_1
+{
+ public:
+ // List of all recording directories used in recordings, timers (and optionally search timers or in epgsearchdirs.conf)
+ virtual std::set<std::string> ShortDirectoryList() = 0;
+ // Evaluate an expression against an event
+ virtual std::string Evaluate(const std::string& expr, const cEvent* event) = 0;
+};
+
+struct Epgsearch_services_v1_2
+{
+// in/out
+ std::auto_ptr<cServiceHandler_v1_2> handler;
+};
+
+#endif
diff --git a/services/remotetimers.h b/services/remotetimers.h
new file mode 100644
index 0000000..cd86b7a
--- /dev/null
+++ b/services/remotetimers.h
@@ -0,0 +1,33 @@
+#ifndef REMOTETIMERSERVICES_INC
+#define REMOTETIMERSERVICES_INC
+
+#include <vdr/epg.h>
+#include <vdr/timers.h>
+
+// RemoteTimers services
+struct RemoteTimers_Event_v1_0 {
+ //in
+ const cEvent *event;
+ //out
+ cTimer *timer;
+ cString errorMsg;
+};
+
+struct RemoteTimers_GetMatch_v1_0 {
+ //in
+ const cEvent *event;
+ //out
+ cTimer *timer;
+ int timerMatch;
+ int timerType;
+ bool isRemote;
+};
+
+struct RemoteTimers_Timer_v1_0 {
+ //in+out
+ cTimer *timer;
+ //out
+ cString errorMsg;
+};
+
+#endif //REMOTETIMERSERVICES_INC \ No newline at end of file
diff --git a/services/scraper2vdr.h b/services/scraper2vdr.h
new file mode 100644
index 0000000..703d077
--- /dev/null
+++ b/services/scraper2vdr.h
@@ -0,0 +1,194 @@
+#ifndef __SCRAPER2VDRSERVICES_H
+#define __SCRAPER2VDRSERVICES_H
+
+#include <vdr/epg.h>
+#include <vdr/recording.h>
+
+enum tvType {
+ tSeries,
+ tMovie,
+ tNone,
+};
+
+/*********************************************************************
+* Helper Structures
+*********************************************************************/
+class cTvMedia {
+public:
+ cTvMedia(void) {
+ path = "";
+ width = height = 0;
+ };
+ std::string path;
+ int width;
+ int height;
+};
+
+class cEpisode {
+public:
+ cEpisode(void) {
+ number = 0;
+ season = 0;
+ name = "";
+ firstAired = "";
+ guestStars = "";
+ overview = "";
+ rating = 0.0;
+ };
+ int number;
+ int season;
+ std::string name;
+ std::string firstAired;
+ std::string guestStars;
+ std::string overview;
+ float rating;
+ cTvMedia episodeImage;
+};
+
+class cActor {
+public:
+ cActor(void) {
+ name = "";
+ role = "";
+ };
+ std::string name;
+ std::string role;
+ cTvMedia actorThumb;
+};
+
+/*********************************************************************
+* Data Structures for Service Calls
+*********************************************************************/
+
+// Data structure for service "GetEventType"
+class ScraperGetEventType {
+public:
+ ScraperGetEventType(void) {
+ event = NULL;
+ recording = NULL;
+ type = tNone;
+ movieId = 0;
+ seriesId = 0;
+ episodeId = 0;
+ };
+// in
+ const cEvent *event; // check type for this event
+ const cRecording *recording; // or for this recording
+//out
+ tvType type; //typeSeries or typeMovie
+ int movieId;
+ int seriesId;
+ int episodeId;
+};
+
+//Data structure for full series and episode information
+class cMovie {
+public:
+ cMovie(void) {
+ title = "";
+ originalTitle = "";
+ tagline = "";
+ overview = "";
+ adult = false;
+ collectionName = "";
+ budget = 0;
+ revenue = 0;
+ genres = "";
+ homepage = "";
+ releaseDate = "";
+ runtime = 0;
+ popularity = 0.0;
+ voteAverage = 0.0;
+ };
+//IN
+ int movieId; // movieId fetched from ScraperGetEventType
+//OUT
+ std::string title;
+ std::string originalTitle;
+ std::string tagline;
+ std::string overview;
+ bool adult;
+ std::string collectionName;
+ int budget;
+ int revenue;
+ std::string genres;
+ std::string homepage;
+ std::string releaseDate;
+ int runtime;
+ float popularity;
+ float voteAverage;
+ cTvMedia poster;
+ cTvMedia fanart;
+ cTvMedia collectionPoster;
+ cTvMedia collectionFanart;
+ std::vector<cActor> actors;
+};
+
+//Data structure for full series and episode information
+class cSeries {
+public:
+ cSeries(void) {
+ seriesId = 0;
+ episodeId = 0;
+ name = "";
+ overview = "";
+ firstAired = "";
+ network = "";
+ genre = "";
+ rating = 0.0;
+ status = "";
+ };
+//IN
+ int seriesId; // seriesId fetched from ScraperGetEventType
+ int episodeId; // episodeId fetched from ScraperGetEventType
+//OUT
+ std::string name;
+ std::string overview;
+ std::string firstAired;
+ std::string network;
+ std::string genre;
+ float rating;
+ std::string status;
+ cEpisode episode;
+ std::vector<cActor> actors;
+ std::vector<cTvMedia> posters;
+ std::vector<cTvMedia> banners;
+ std::vector<cTvMedia> fanarts;
+ cTvMedia seasonPoster;
+};
+
+// Data structure for service "GetPosterBanner"
+class ScraperGetPosterBanner {
+public:
+ ScraperGetPosterBanner(void) {
+ type = tNone;
+ };
+// in
+ const cEvent *event; // check type for this event
+//out
+ tvType type; //typeSeries or typeMovie
+ cTvMedia poster;
+ cTvMedia banner;
+};
+
+// Data structure for service "GetPoster"
+class ScraperGetPoster {
+public:
+// in
+ const cEvent *event; // check type for this event
+ const cRecording *recording; // or for this recording
+//out
+ cTvMedia poster;
+};
+
+// Data structure for service "GetPosterThumb"
+class ScraperGetPosterThumb {
+public:
+// in
+ const cEvent *event; // check type for this event
+ const cRecording *recording; // or for this recording
+//out
+ cTvMedia poster;
+};
+
+#endif //__SCRAPER2VDRSERVICES_H \ No newline at end of file
diff --git a/setup.c b/setup.c
new file mode 100644
index 0000000..9e6487c
--- /dev/null
+++ b/setup.c
@@ -0,0 +1,158 @@
+#include "setup.h"
+
+cTvGuideSetup::cTvGuideSetup() {
+ tmpConfig = config;
+ displayModeItems[0] = "vertical";
+ displayModeItems[1] = "horizontal";
+ jumpMode[0] = tr("x channels back / forward");
+ jumpMode[1] = tr("previous / next channel group");
+ numMode[0] = tr("Timely Jump");
+ numMode[1] = tr("Jump to specific channel");
+ blueMode[0] = tr("Blue: Channel Switch, Ok: Detailed EPG");
+ blueMode[1] = tr("Blue: Detailed EPG, Ok: Channel Switch");
+ blueMode[2] = tr("Blue: Favorites / Switch, Ok: Detailed EPG");
+ strn0cpy(description1, tmpConfig.descUser1.c_str(), sizeof(description1));
+ strn0cpy(description2, tmpConfig.descUser2.c_str(), sizeof(description2));
+ strn0cpy(description3, tmpConfig.descUser3.c_str(), sizeof(description3));
+ strn0cpy(description4, tmpConfig.descUser4.c_str(), sizeof(description4));
+ recFolderMode[0] = tr("Always use root video folder");
+ recFolderMode[1] = tr("Select from folder list");
+ recFolderMode[2] = tr("Use fixed folder");
+ strn0cpy(fixedFolder, tmpConfig.instRecFixedFolder.c_str(), sizeof(fixedFolder));
+ useSubtitleRerunTexts[0] = tr("never");
+ useSubtitleRerunTexts[1] = tr("if exists");
+ useSubtitleRerunTexts[2] = tr("always");
+ Setup();
+}
+
+cTvGuideSetup::~cTvGuideSetup() {
+}
+
+void cTvGuideSetup::Setup(void) {
+ int current = Current();
+ Clear();
+
+ cString indent = " ";
+
+ Add(new cMenuEditBoolItem(tr("Show Main Menu Entry"), &tmpConfig.showMainMenuEntry));
+ Add(new cMenuEditBoolItem(tr("Replace VDR Schedules Menu"), &tmpConfig.replaceOriginalSchedule));
+ Add(new cMenuEditStraItem(tr("Display Mode"), &tmpConfig.displayMode, 2, displayModeItems));
+ Add(new cMenuEditIntItem(tr("Channels to display in horizontal View"), &tmpConfig.channelsPerPageHorizontal, 3, 12));
+ Add(new cMenuEditIntItem(tr("Channels to display in vertical View"), &tmpConfig.channelsPerPageVertical, 3, 12));
+ Add(new cMenuEditIntItem(tr("Number of Hours to display"), &tmpConfig.displayHours, 3, 12));
+ Add(new cMenuEditIntItem(tr("Big Step (Keys 1 / 3) in hours"), &tmpConfig.bigStepHours, 1, 12));
+ Add(new cMenuEditIntItem(tr("Huge Step (Keys 4 / 6) in hours"), &tmpConfig.hugeStepHours, 13, 48));
+ Add(new cMenuEditBoolItem(tr("Hide last Channel Group"), &tmpConfig.hideLastChannelGroup));
+ Add(new cMenuEditStraItem(tr("Channel Jump Mode (Keys Green / Yellow)"), &tmpConfig.channelJumpMode, 2, jumpMode));
+ Add(new cMenuEditBoolItem(tr("Close TVGuide after channel switch"), &tmpConfig.closeOnSwitch));
+ Add(new cMenuEditStraItem(tr("Functionality of numeric Keys"), &tmpConfig.numKeyMode, 2, numMode));
+ Add(new cMenuEditStraItem(tr("Keys Blue and OK"), &tmpConfig.blueKeyMode, 3, blueMode));
+ Add(new cMenuEditIntItem(tr("Maximum number of reruns to display"), &tmpConfig.rerunAmount, 1, 100));
+ Add(new cMenuEditIntItem(tr("Minimum timely distance of rerun (in hours)"), &tmpConfig.rerunDistance, 0, 1000));
+ Add(new cMenuEditIntItem(tr("Limit Channel Numbers for reruns"), &tmpConfig.rerunMaxChannel, 0, 1000, tr("no limit")));
+
+ Add(new cMenuEditStraItem(tr("Folder for instant Recordings"), &tmpConfig.instRecFolderMode, 3, recFolderMode));
+ if (tmpConfig.instRecFolderMode == eFolderFixed) {
+ Add(new cMenuEditStrItem(cString::sprintf("%s%s", *indent, tr("Folder")), fixedFolder, sizeof(fixedFolder), trVDR(FileNameChars)));
+ }
+ if (pRemoteTimers)
+ Add(new cMenuEditBoolItem(tr("Use Remotetimers"), &tmpConfig.useRemoteTimers));
+
+ Add(new cMenuEditBoolItem(tr("Use \"What's on now\" in favorites"), &tmpConfig.favWhatsOnNow));
+ Add(new cMenuEditBoolItem(tr("Use \"What's on next\" in favorites"), &tmpConfig.favWhatsOnNext));
+ Add(new cMenuEditBoolItem(tr("Use user defined time 1 in favorites"), &tmpConfig.favUseTime1));
+ if (tmpConfig.favUseTime1) {
+ Add(new cMenuEditStrItem(cString::sprintf("%s%s", *indent, tr("Description")), description1, sizeof(description1), trVDR(FileNameChars)));
+ Add(new cMenuEditTimeItem(cString::sprintf("%s%s", *indent, tr("Time")), &tmpConfig.favTime1));
+ }
+ Add(new cMenuEditBoolItem(tr("Use user defined time 2 in favorites"), &tmpConfig.favUseTime2));
+ if (tmpConfig.favUseTime2) {
+ Add(new cMenuEditStrItem(cString::sprintf("%s%s", *indent, tr("Description")), description2, sizeof(description2), trVDR(FileNameChars)));
+ Add(new cMenuEditTimeItem(cString::sprintf("%s%s", *indent, tr("Time")), &tmpConfig.favTime2));
+ }
+ Add(new cMenuEditBoolItem(tr("Use user defined time 3 in favorites"), &tmpConfig.favUseTime3));
+ if (tmpConfig.favUseTime3) {
+ Add(new cMenuEditStrItem(cString::sprintf("%s%s", *indent, tr("Description")), description3, sizeof(description3), trVDR(FileNameChars)));
+ Add(new cMenuEditTimeItem(cString::sprintf("%s%s", *indent, tr("Time")), &tmpConfig.favTime3));
+ }
+ Add(new cMenuEditBoolItem(tr("Use user defined time 4 in favorites"), &tmpConfig.favUseTime4));
+ if (tmpConfig.favUseTime4) {
+ Add(new cMenuEditStrItem(cString::sprintf("%s%s", *indent, tr("Description")), description4, sizeof(description4), trVDR(FileNameChars)));
+ Add(new cMenuEditTimeItem(cString::sprintf("%s%s", *indent, tr("Time")), &tmpConfig.favTime4));
+ }
+ Add(new cMenuEditBoolItem(tr("Limit channels in favorites"), &tmpConfig.favLimitChannels));
+ if (tmpConfig.favLimitChannels) {
+ Add(new cMenuEditChanItem(tr("Start Channel"), &tmpConfig.favStartChannel));
+ Add(new cMenuEditChanItem(tr("Stop Channel"), &tmpConfig.favStopChannel));
+ }
+
+ SetCurrent(Get(current));
+ Display();
+}
+
+void cTvGuideSetup::Store(void) {
+ config = tmpConfig;
+ SetupStore("showMainMenuEntry", config.showMainMenuEntry);
+ SetupStore("replaceOriginalSchedule", config.replaceOriginalSchedule);
+ SetupStore("displayMode", config.displayMode);
+ SetupStore("channelsPerPageHorizontal", config.channelsPerPageHorizontal);
+ SetupStore("channelsPerPageVertical", config.channelsPerPageVertical);
+ SetupStore("displayHours", config.displayHours);
+ SetupStore("bigStepHours", config.bigStepHours);
+ SetupStore("hugeStepHours", config.hugeStepHours);
+ SetupStore("hideLastChannelGroup", config.hideLastChannelGroup);
+ SetupStore("channelJumpMode", config.channelJumpMode);
+ SetupStore("closeOnSwitch", config.closeOnSwitch);
+ SetupStore("numKeyMode", config.numKeyMode);
+ SetupStore("blueKeyMode", config.blueKeyMode);
+ SetupStore("rerunAmount", config.rerunAmount);
+ SetupStore("rerunDistance", config.rerunDistance);
+ SetupStore("rerunMaxChannel", config.rerunMaxChannel);
+ SetupStore("useRemoteTimers", config.useRemoteTimers);
+ SetupStore("instRecFolderMode", config.instRecFolderMode);
+ SetupStore("instRecFixedFolder", config.instRecFixedFolder.c_str());
+ SetupStore("favWhatsOnNow", config.favWhatsOnNow);
+ SetupStore("favWhatsOnNext", config.favWhatsOnNext);
+ SetupStore("favUseTime1", config.favUseTime1);
+ SetupStore("favUseTime2", config.favUseTime2);
+ SetupStore("favUseTime3", config.favUseTime3);
+ SetupStore("favUseTime4", config.favUseTime4);
+ SetupStore("favTime1", config.favTime1);
+ SetupStore("favTime2", config.favTime2);
+ SetupStore("favTime3", config.favTime3);
+ SetupStore("favTime4", config.favTime4);
+ SetupStore("descUser1", config.descUser1.c_str());
+ SetupStore("descUser2", config.descUser2.c_str());
+ SetupStore("descUser3", config.descUser3.c_str());
+ SetupStore("descUser4", config.descUser4.c_str());
+ SetupStore("favLimitChannels", config.favLimitChannels);
+ SetupStore("favStartChannel", config.favStartChannel);
+ SetupStore("favStopChannel", config.favStopChannel);
+}
+
+eOSState cTvGuideSetup::ProcessKey(eKeys Key) {
+ int tmpFavUseTime1 = tmpConfig.favUseTime1;
+ int tmpFavUseTime2 = tmpConfig.favUseTime2;
+ int tmpFavUseTime3 = tmpConfig.favUseTime3;
+ int tmpFavUseTime4 = tmpConfig.favUseTime4;
+ int tmpFavLimitChannels = tmpConfig.favLimitChannels;
+ int tmpFolderMode = tmpConfig.instRecFolderMode;
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ if (Key == kOk) {
+ tmpConfig.descUser1 = description1;
+ tmpConfig.descUser2 = description2;
+ tmpConfig.descUser3 = description3;
+ tmpConfig.descUser4 = description4;
+ tmpConfig.instRecFixedFolder = fixedFolder;
+ Store();
+ } else if ((Key == kLeft)||(Key == kRight)) {
+ if ((tmpFavUseTime1 != tmpConfig.favUseTime1) ||
+ (tmpFavUseTime2 != tmpConfig.favUseTime2) ||
+ (tmpFavUseTime3 != tmpConfig.favUseTime3) ||
+ (tmpFavUseTime4 != tmpConfig.favUseTime4) ||
+ (tmpFavLimitChannels != tmpConfig.favLimitChannels) ||
+ (tmpFolderMode != tmpConfig.instRecFolderMode) )
+ Setup();
+ }
+ return state;
+}
diff --git a/setup.h b/setup.h
new file mode 100644
index 0000000..faffc2b
--- /dev/null
+++ b/setup.h
@@ -0,0 +1,30 @@
+#ifndef __TVGUIDE_SETUP_H
+#define __TVGUIDE_SETUP_H
+
+#include <vdr/plugin.h>
+#include "config.h"
+
+extern cTVGuideConfig config;
+
+class cTvGuideSetup : public cMenuSetupPage {
+ public:
+ cTvGuideSetup(void);
+ virtual ~cTvGuideSetup();
+ private:
+ cTVGuideConfig tmpConfig;
+ const char * displayModeItems[2];
+ const char * jumpMode[2];
+ const char * numMode[2];
+ const char * blueMode[3];
+ char description1[256];
+ char description2[256];
+ char description3[256];
+ char description4[256];
+ const char * recFolderMode[3];
+ char fixedFolder[256];
+ const char *useSubtitleRerunTexts[3];
+ void Setup(void);
+ virtual eOSState ProcessKey(eKeys Key);
+ virtual void Store(void);
+};
+#endif //__TVGUIDE_SETUP_H
diff --git a/switchtimer.c b/switchtimer.c
new file mode 100644
index 0000000..f56c469
--- /dev/null
+++ b/switchtimer.c
@@ -0,0 +1,113 @@
+#include "switchtimer.h"
+
+cSwitchTimers SwitchTimers;
+
+// -- cSwitchTimer -----------------------------------------------------------------
+cSwitchTimer::cSwitchTimer(void) {
+ eventID = 0;
+ startTime = 0;
+ switchMinsBefore = 2;
+ announceOnly = 0;
+}
+
+cSwitchTimer::cSwitchTimer(const cEvent* Event) {
+ eventID = 0;
+ startTime = 0;
+ if (Event) {
+ eventID = Event->EventID();
+ channelID = Event->ChannelID();
+ startTime = Event->StartTime();
+ }
+}
+
+bool cSwitchTimer::Parse(const char *s) {
+ char *line;
+ char *pos;
+ char *pos_next;
+ int parameter = 1;
+ int valuelen;
+#define MAXVALUELEN (10 * MaxFileName)
+
+ char value[MAXVALUELEN];
+ startTime=0;
+
+ pos = line = strdup(s);
+ pos_next = pos + strlen(pos);
+ if (*pos_next == '\n') *pos_next = 0;
+ while (*pos) {
+ while (*pos == ' ')
+ pos++;
+ if (*pos) {
+ if (*pos != ':') {
+ pos_next = strchr(pos, ':');
+ if (!pos_next)
+ pos_next = pos + strlen(pos);
+ valuelen = pos_next - pos + 1;
+ if (valuelen > MAXVALUELEN)
+ valuelen = MAXVALUELEN;
+ strn0cpy(value, pos, valuelen);
+ pos = pos_next;
+ switch (parameter) {
+ case 1:
+ channelID = tChannelID::FromString(value);
+ break;
+ case 2:
+ eventID = atoi(value);
+ break;
+ case 3:
+ startTime = atol(value);
+ break;
+ default:
+ break;
+ }
+ }
+ parameter++;
+ }
+ if (*pos)
+ pos++;
+ }
+ free(line);
+ return (parameter >= 3) ? true : false;
+}
+
+bool cSwitchTimers::EventInSwitchList(const cEvent* event) {
+ if (!event) return false;
+ cMutexLock SwitchTimersLock(this);
+ cSwitchTimer* switchTimer = SwitchTimers.First();
+ while (switchTimer) {
+ if (switchTimer->eventID == event->EventID())
+ return true;
+ switchTimer = SwitchTimers.Next(switchTimer);
+ }
+ return false;
+}
+
+bool cSwitchTimers::ChannelInSwitchList(const cChannel* channel) {
+ if (!channel) return false;
+ cMutexLock SwitchTimersLock(this);
+ cSwitchTimer* switchTimer = SwitchTimers.First();
+ while (switchTimer) {
+ if (switchTimer->channelID == channel->GetChannelID())
+ return true;
+ switchTimer = SwitchTimers.Next(switchTimer);
+ }
+ return false;
+}
+
+void cSwitchTimers::DeleteSwitchTimer(const cEvent *event) {
+ if (!event) return;
+ cMutexLock SwitchTimersLock(this);
+ cSwitchTimer* switchTimer = SwitchTimers.First();
+ cSwitchTimer* delTimer = NULL;
+
+ while (switchTimer) {
+ if (switchTimer->eventID == event->EventID()) {
+ delTimer = switchTimer;
+ break;
+ }
+ switchTimer = SwitchTimers.Next(switchTimer);
+ }
+ if (delTimer) {
+ SwitchTimers.Del(delTimer, true);
+ }
+}
diff --git a/switchtimer.h b/switchtimer.h
new file mode 100644
index 0000000..09ebe88
--- /dev/null
+++ b/switchtimer.h
@@ -0,0 +1,31 @@
+#ifndef __TVGUIDE_SWITCHTIMER_H
+#define __TVGUIDE_SWITCHTIMER_H
+
+#include <vdr/plugin.h>
+
+class cSwitchTimer : public cListObject {
+public:
+ tEventID eventID;
+ time_t startTime;
+ tChannelID channelID;
+ int switchMinsBefore;
+ int announceOnly;
+ cSwitchTimer(void);
+ cSwitchTimer(const cEvent* Event);
+ bool Parse(const char *s);
+ void SetEventID(tEventID eventID) { this->eventID = eventID; };
+ void SetStartTime(time_t startTime) { this->startTime = startTime; };
+};
+
+class cSwitchTimers : public cConfig<cSwitchTimer>, public cMutex {
+public:
+ cSwitchTimers(void) {}
+ ~cSwitchTimers(void) {}
+ bool EventInSwitchList(const cEvent* event);
+ bool ChannelInSwitchList(const cChannel* channel);
+ void DeleteSwitchTimer(const cEvent *event);
+};
+
+extern cSwitchTimers SwitchTimers;
+
+#endif //__TVGUIDE_SWITCHTIMER_H
diff --git a/templates/plug-tvguideng-detail.xml b/templates/plug-tvguideng-detail.xml
new file mode 100644
index 0000000..37d4af1
--- /dev/null
+++ b/templates/plug-tvguideng-detail.xml
@@ -0,0 +1,237 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE displayplugin SYSTEM "../../../dtd/displayplugin.dtd">
+
+<displayplugin x="0" y="0" width="100%" height="100%" fadetime="0" scaletvx="0" scaletvy="0" scaletvwidth="100%" scaletvheight="100%">
+
+ <viewelement name="background">
+ </viewelement>
+
+ <!-- Available Variables scrollbar:
+ {height} height of scrollbar in tenth of a percent of total height
+ {offset} offset in tenth of a percent of total height
+ -->
+ <scrollbar>
+ </scrollbar>
+
+ <!-- Available Variables Footer:
+ {red1} true if red button is button 1
+ {red2} true if red button is button 2
+ {red3} true if red button is button 3
+ {red4} true if red button is button 4
+ {green1} true if green button is button 1
+ {green2} true if green button is button 2
+ {green3} true if green button is button 3
+ {green4} true if green button is button 4
+ {yellow1} true if yellow button is button 1
+ {yellow2} true if yellow button is button 2
+ {yellow3} true if yellow button is button 3
+ {yellow4} true if yellow button is button 4
+ {blue1} true if blue button is button 1
+ {blue2} true if blue button is button 2
+ {blue3} true if blue button is button 3
+ {blue4} true if blue button is button 4
+ {red} label of red button
+ {green} label of green button
+ {yellow} label of yellow button
+ {blue} label of blue button
+ -->
+ <viewelement name="footer">
+ </viewelement>
+
+ <!-- Available Variables time:
+ {time} timestring in hh:mm
+ {sec} current seconds
+ {min} current minutes
+ {hour} current hours
+ {hmins} current "hourminutes" to display an hour hand
+ -->
+ <viewelement name="time">
+ </viewelement>
+
+
+ <!-- Available Variables in detailheader elements:
+ {title} title of event
+ {shorttext} shorttext of event
+ {start} event start time in hh::mm
+ {stop} event stop time
+ {day} Day of event as three letter abrivation
+ {date} date of current event in dd.mm.yy
+ {daynumeric} day as number
+ {month} month as number
+ {year} year as number
+ {running} true if event is currently running
+ {elapsed} elapsed time of event, if not running 0
+ {duration} duration of event
+ {durationhours} duration, full hours
+ {durationminutes} duration, rest of minutes
+ {vps} vps description string
+ {channelname} Channelname of event
+ {channelnumber} Channelnumber of event
+ {channellogoexists} true if a channel logo exists
+ {channelid} ChannelID as path to display channel logo
+ {ismovie} true if event is scraped as a movie
+ {isseries} true if event is scraped as a series
+ {posteravailable} true if a poster is available
+ {posterwidth} width of scraped poster
+ {posterheight} height of scraped poster
+ {posterpath} absolute path of scraped poster
+ {banneravailable} true if a banner is available
+ {bannerwidth} width of banner
+ {bannerheight} height of banner
+ {bannerpath} path of banner
+ {epgpicavailable} true if a epg picture is available
+ {epgpicpath} path of epg picture
+ -->
+ <viewelement name="header">
+ </viewelement>
+
+ <!-- Available Variables in tab elements:
+ {title} title of event
+ {shorttext} shorttext of event
+ {description} description of event
+ {start} event start time in hh::mm
+ {stop} event stop time
+ {day} Day of event as three letter abrivation
+ {date} date of current event in dd.mm.yy
+ {daynumeric} day as number
+ {month} month as number
+ {year} year as number
+ {running} true if event is currently running
+ {elapsed} elapsed time of event, if not running 0
+ {duration} duration of event
+ {durationhours} duration, full hours
+ {durationminutes} duration, rest of minutes
+ {vps} vps description string
+ {channellogoexists} true if a channel logo exists
+ {channelid} ChannelID as path to display channel logo
+ {hasreruns} true if reruns of this event are found
+ {reruns[]} array with reruns
+ {reruns[title]} title of rerun
+ {reruns[shorttext]} shorttext of rerun
+ {reruns[date]} date of rerun in dd:mm
+ {reruns[day]} short dayname of rerun
+ {reruns[start]} start time of rerun in hh:mm
+ {reruns[stop]} stop time of rerun in hh:mm
+ {reruns[channelname]} name of channel on which rerun occurs
+ {reruns[channelnumber]} number of channel on which rerun occurs
+ {reruns[channelid]} id of channel on which rerun occurs to display channel logo
+ {reruns[channellogoexists]} true if channel logo exists
+ {epgpic1avaialble} true if first epg picture is available
+ {epgpic2avaialble} true if first epg picture is available
+ {epgpic3avaialble} true if first epg picture is available
+ {epgpic1path} path of first epg picture
+ {epgpic2path} path of second epg picture
+ {epgpic3path} path of third epg picture
+
+ {ismovie} true if event is scraped as a movie
+ Available variables for movies:
+ {movietitle} movie title from themoviedb
+ {movieoriginalTitle} movie original title from themoviedb
+ {movietagline} movie tagline from themoviedb
+ {movieoverview} movie overview from themoviedb
+ {movieadult} true if movie is rated as adult
+ {moviebudget} movie budget from themoviedb in $
+ {movierevenue} movie revenue from themoviedb in $
+ {moviegenres} movie genres from themoviedb
+ {moviehomepage} movie homepage from themoviedb
+ {moviereleasedate} movie release date from themoviedb
+ {movieruntime} movie runtime from themoviedb
+ {moviepopularity} movie popularity from themoviedb
+ {movievoteaverage} movie vote average from themoviedb
+ {posterwidth} width of scraped poster
+ {posterheight} height of scraped poster
+ {posterpath} absolute path of scraped poster
+ {fanartwidth} width of scraped fanart
+ {fanartheight} height of scraped fanart
+ {fanartpath} absolute path of scraped fanart
+ {movieiscollection} true if movie is part of a collection
+ {moviecollectionName} name of movie collection
+ {collectionposterwidth} width of scraped collection poster
+ {collectionposterheight} height of scraped collection poster
+ {collectionposterpath} absolute path of scraped collection poster
+ {collectionfanartwidth} width of scraped collection fanart
+ {collectionfanartheight} height of scraped collection fanart
+ {collectionfanartpath} absolute path of scraped collection fanart
+ {actors[]} array with movie actors
+ {actors[name]} real name of actor
+ {actors[role]} actor role
+ {actors[thumb]} absolute path of scraped actor thumb
+ {actors[thumbwidth]} width of scraped actor thumb
+ {actors[thumbheight]} height of scraped actor thumb
+
+ {isseries} true if event is scraped as a series
+ Available variables for series:
+ {seriesname} name of series
+ {seriesoverview} series overview
+ {seriesfirstaired} first aired date
+ {seriesnetwork} network which produces series
+ {seriesgenre} series genre
+ {seriesrating} series thetvdb rating
+ {seriesstatus} status of series (running / finished)
+ {episodetitle} title of episode
+ {episodenumber} number of episode
+ {episodeseason} season of episode
+ {episodefirstaired} first aired date of episode
+ {episodegueststars} guest stars of episode
+ {episodeoverview} episode overview
+ {episoderating} user rating for episode
+ {episodeimagewidth} episode image width
+ {episodeimageheight} episode image height
+ {episodeimagepath} episode image path
+ {seasonposterwidth} episode season poster width
+ {seasonposterheight} episode season poster height
+ {seasonposterpath} episode season poster path
+ {seriesposter1width} width of 1st poster
+ {seriesposter1height} height of 1st poster
+ {seriesposter1path} path of 1st poster
+ {seriesposter2width} width of 2nd poster
+ {seriesposter2height} height of 2nd poster
+ {seriesposter2path} path of 2nd poster
+ {seriesposter3width} width of 3rd poster
+ {seriesposter3height} height of 3rd poster
+ {seriesposter3path} path of 3rd poster
+ {seriesfanart1width} width of 1st fanart
+ {seriesfanart1height} height of 1st fanart
+ {seriesfanart1path} path of 1st fanart
+ {seriesfanart2width} width of 2nd fanart
+ {seriesfanart2height} height of 2nd fanart
+ {seriesfanart2path} path of 2nd fanart
+ {seriesfanart3width} width of 3rd fanart
+ {seriesfanart3height} height of 3rd fanart
+ {seriesfanart3path} path of 3rd fanart
+ {seriesbanner1width} width of 1st banner
+ {seriesbanner1height} height of 1st banner
+ {seriesbanner1path} path of 1st banner
+ {seriesbanner2width} width of 2nd banner
+ {seriesbanner2height} height of 2nd banner
+ {seriesbanner2path} path of 2nd banner
+ {seriesbanner3width} width of 3rd banner
+ {seriesbanner3height} height of 3rd banner
+ {seriesbanner3path} path of 3rd fanart
+ {actors[]} array with movie actors
+ {actors[name]} real name of actor
+ {actors[role]} actor role
+ {actors[thumb]} absolute path of scraped actor thumb
+ {actors[thumbwidth]} width of scraped actor thumb
+ {actors[thumbheight]} height of scraped actor thumb
+ -->
+
+ <!-- a tab is one scrolling area, just position and draw as inside a normal area -->
+ <!-- just define as many tabs as needed -->
+
+ <!-- TAB EPGINFO -->
+ <tab name="EPG Info" x="0" y="0" width="100%" height="100%" layer="1" scrollheight="{areaheight}/4">
+ </tab>
+
+ <!-- Available Variables tablabels:
+ {currenttab} name of currently active tab
+ {prevtab} name of prev tab
+ {nexttab} name of next tab
+ {tabs[]} array with available tab labels
+ {tabs[title]} title of tab
+ {tabs[current]} true if tab is displayed currently
+ -->
+ <tablabels>
+ </tablabels>
+
+</displayplugin>
diff --git a/templates/plug-tvguideng-recmenu.xml b/templates/plug-tvguideng-recmenu.xml
new file mode 100644
index 0000000..46222ec
--- /dev/null
+++ b/templates/plug-tvguideng-recmenu.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE displayplugin SYSTEM "../../../dtd/displayplugin.dtd">
+
+<displayplugin x="0" y="0" width="100%" height="100%" fadetime="0" scaletvx="0" scaletvy="0" scaletvwidth="100%" scaletvheight="100%">
+ <!-- Available Variables Background:
+ {menuwidth} menuwidth in percent of screenwidth
+ {menuheight} menuheight in percent of screenheight
+ {hasscrollbar} true if menu needs a scrollbar
+ -->
+ <viewelement name="background">
+ </viewelement>
+
+ <!-- Available Variables Scrollbar:
+ {menuwidth} menuwidth in percent of screenwidth
+ {posy} y position of scrollbar start in percent of screenheight
+ {totalheight} height of complete scrollbar in percent of screenheight
+ {height} height in tenth of a percent of total height
+ {offset} offset in tenth of a percent
+ -->
+ <viewelement name="scrollbar">
+ </viewelement>
+
+ <grid name="recmenu" x="0" y="0" width="100%" height="100%">
+ <!-- Background
+ {current} true if item is currently selected
+ -->
+ <!-- info item
+ {info} true
+ {lines} number of lines to display (max. 4)
+ {line1} text of line 1
+ {line2} text of line 1
+ {line3} text of line 1
+ {line4} text of line 1
+ -->
+ <!-- button
+ {button} true
+ {buttontext} text to display on button
+ -->
+ <!-- yes / no button
+ {buttonyesno} true
+ {yes} true if button is set to yes
+ {textyes} text to display on yes button
+ {textno} text to display on no button
+ -->
+ <!-- Int Selector
+ {intselector} true
+ {text} title of selector
+ {value} current value of selector, integer
+ -->
+ <!-- Bool Selector
+ {boolselector} true
+ {text} title of selector
+ {value} current value of selector, true or false
+ -->
+ <!-- String Selector
+ {stringselector} true
+ {text} title of selector
+ {value} current value of selector, string
+ -->
+ <!-- Text Input
+ {textinput} true
+ {editmode} true if currently in edit mode
+ {text} title of selector
+ {value} current value of selector, string
+ -->
+ <!-- Time Selector
+ {timeselector} true
+ {text} title of selector
+ {value} current value of selector, hh:mm
+ -->
+ <!-- Day Selector
+ {dayselector} true
+ {text} title of selector
+ {value} current value of selector, dd.mm
+ -->
+ <!-- Channel Selector
+ {channelselector} true
+ {text} title of selector
+ {channelnumber} number of currently selected channel, 0 for "all channels"
+ {channelname} name of channel or "all channels"
+ {channelid} id of channel
+ {channellogoexisis} true if channel logo exists
+ -->
+ <!-- Weekday Selector
+ {weekdayselector} true
+ {text} title of selector
+ {dayselected} number of currently selected day (0 - 6)
+ {day0abbr} ... {day6abbr} localized one character abbrevation for weekdays from Monday to Sunday
+ {day0set} ... {day6set} true if according weekday from Monday to Sunday is set
+ -->
+ <!-- Directory Selector
+ {directoryselector} true
+ {text} title of selector
+ {folder} current folder of selector, string
+ -->
+ <!-- Timerconflict Header
+ {timerconflictheader} true
+ {text} title of Timerconflict Header
+ {conflictstart} start of conflict in hh:mm
+ {conflictstop} end of conflict in hh:mm
+ {overlapstart} start of overlap in hh:mm
+ {overlapstop} end of overlap in hh:mm
+ {overlapstartpercent} start of overlap in percent of total conflict time width
+ {overlapwidthpercent} width of overlap in percent of total conflict time width
+ -->
+ <!-- Timerconflict
+ {timerconflict} true
+ {timertitle} title of timer
+ {channelname} name of channel
+ {channelid} channel ID
+ {transponder} transponder of channel
+ {starttime} start of timer in hh:mm
+ {stoptime} end of timer in hh:mm
+ {date} date of timer in dd.mm.yy
+ {weekday} weekday of timer, 3 letter abrivation
+ {infoactive} true if info icon is active
+ {deleteactive} true if delete icon is active
+ {editactive} true if edit icon is active
+ {searchactive} true if search icon is active
+ {timerstartpercent} start of timer in percent of total conflict time width
+ {timerwidthpercent} end of timer in percent of total conflict time width
+ {overlapstartpercent} start of overlap in percent of total conflict time width
+ {overlapwidthpercent} width of overlap in percent of total conflict time width
+ -->
+ <!-- Event
+ {event} true
+ {title} title of event
+ {shorttext} shorttext of event
+ {starttime} start of event in hh:mm
+ {stoptime} end of event in hh:mm
+ {date} date of event in dd.mm.yy
+ {weekday} weekday of event, 3 letter abrivation
+ {channelnumber} number of channel
+ {channelname} name of channel
+ {channelid} id of channel
+ {channellogoexisis} true if channel logo exists
+ {hastimer} true if event has a timer
+ -->
+ <!-- Recording
+ {recording} true
+ {recname} title of recording
+ {recstarttime} start of recording in hh:mm
+ {recdate} date of recording in dd.mm.yy
+ {recduration} duration of recording in min
+ {channelnumber} number of channel
+ {channelname} name of channel
+ {channelid} id of channel
+ {channellogoexisis} true if channel logo exists
+ -->
+ <!-- Searchtimer
+ {searchtimer} true
+ {timeractive} true if searchtimer is active
+ {searchstring} searchtimer search string
+ {activetimers} number of active timers caused by this searchtimer
+ {recordingsdone} number of recordings done by this searchtimer
+ {searchactive} true if search icon is active
+ {editactive} true if edit icon is active
+ {deleteactive} true if delete icon is active
+ -->
+ <!-- Timeline Header
+ {timelineheader} true
+ {date} date of current day in weekdayname dd.mm.yyyy
+ {timerset} true if timer info is set
+ {channelname} name of channel of timer
+ {channelid} channel ID of channel of timer
+ {channellogoexisis} true if channel logo exists
+ {channelnumber} number of channel of timer
+ {channeltransponder} transponder of channel of timer
+ {timerstart} start of timer in hh:mm
+ {timerstop} end of timer in hh:mm
+ {eventtitle} title of according event
+ {eventshorttext} short text of according event
+ {eventstart} start time of according event
+ {eventstop} end time of according event
+ -->
+ <!-- Timeline Timer
+ {timelinetimer} true
+ {timerstart} start of timer in tenth percent of complete 24h width
+ {timerwidth} width of timer in tenth percent of complete 24h width
+ -->
+ <!-- Favorites
+ {favorite} true
+ {favdesc} description of favorite
+ -->
+ </grid>
+</displayplugin>
diff --git a/templates/plug-tvguideng-root.xml b/templates/plug-tvguideng-root.xml
new file mode 100644
index 0000000..9e2c329
--- /dev/null
+++ b/templates/plug-tvguideng-root.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE displayplugin SYSTEM "../../../dtd/displayplugin.dtd">
+
+<displayplugin x="0" y="0" width="100%" height="100%" fadetime="0" scaletvx="0" scaletvy="0" scaletvwidth="100%" scaletvheight="100%">
+
+ <viewelement name="background_hor">
+ </viewelement>
+
+ <viewelement name="background_ver">
+ </viewelement>
+
+ <!-- Tokens available in Header
+ {isdummy} true if active element is a dummy element
+ {title} title of event of active grid
+ {shorttext} shorttext of event of active grid
+ {description} detailed description of event of active grid
+ {start} event start time in hh::mm
+ {stop} event stop time
+ {day} day of event of active grid
+ {date} date of event of active grid in dd.mm.yy
+ {daynumeric} day as number
+ {month} month as number
+ {year} year as number
+ {running} true if event is currently running
+ {elapsed} elapsed time of event, if not running 0
+ {duration} duration of event
+ {durationhours} duration, full hours
+ {durationminutes} duration, rest of minutes
+ {channelname} Channel Name
+ {channelnumber} Channel Number
+ {channelid} ChannelID as path to display channel logo
+ {channellogoexists} true if channel logo exists
+ {hasposter} true if a scraped poster is available for this element
+ {posterwidth} width of scraped poster
+ {posterheight} height of scraped poster
+ {posterpath} absolute path of scraped poster
+ -->
+ <viewelement name="header">
+ </viewelement>
+
+ <!-- Available Variables Footer:
+ {red1} true if red button is button 1
+ {red2} true if red button is button 2
+ {red3} true if red button is button 3
+ {red4} true if red button is button 4
+ {green1} true if green button is button 1
+ {green2} true if green button is button 2
+ {green3} true if green button is button 3
+ {green4} true if green button is button 4
+ {yellow1} true if yellow button is button 1
+ {yellow2} true if yellow button is button 2
+ {yellow3} true if yellow button is button 3
+ {yellow4} true if yellow button is button 4
+ {blue1} true if blue button is button 1
+ {blue2} true if blue button is button 2
+ {blue3} true if blue button is button 3
+ {blue4} true if blue button is button 4
+ {red} label of red button
+ {green} label of green button
+ {yellow} label of yellow button
+ {blue} label of blue button
+ -->
+ <viewelement name="footer">
+ </viewelement>
+
+ <!-- Available Variables time:
+ {time} timestring in hh:mm
+ {sec} current seconds
+ {min} current minutes
+ {hour} current hours
+ {hmins} current "hourminutes" to display an hour hand
+ {day} day in digits
+ {dayleadingzero} day in digits with leading 0
+ {dayname} Full name of the day
+ {daynameshort} Short 3 char name of the day
+ {month} month in digits with leading 0
+ {monthname} Full name of the month
+ {monthnameshort} 3 letter abbrivation of month name
+ {year} year in yyyy
+ -->
+ <viewelement name="time">
+ </viewelement>
+
+ <!-- Tokens available in datetimeline
+ {weekday} weekday of current display
+ {date} date of current display
+ -->
+ <viewelement name="datetimeline_hor">
+ </viewelement>
+
+ <viewelement name="datetimeline_ver">
+ </viewelement>
+
+ <!-- Tokens available in timeindicator
+ {percenttotal} position of current time indicator in tenth of a percent of complete time shown
+ -->
+ <viewelement name="timeindicator_hor">
+ </viewelement>
+
+ <viewelement name="timeindicator_ver">
+ </viewelement>
+
+ <!-- Tokens available in timeline
+ {timestring} time of grid in hh:mm
+ -->
+ <grid name="timeline_hor" x="0" y="0" width="100%" height="100%">
+ </grid>
+
+ <grid name="timeline_ver" x="0" y="0" width="100%" height="100%">
+ </grid>
+
+ <!-- Tokens available in channels
+ {name} name of channel
+ {number} number of channel
+ {channelid} id of channel to display channel logo
+ {channellogoexists} true if channel logo exists
+ -->
+ <grid name="channels_hor" x="0" y="0" width="100%" height="100%">
+ </grid>
+
+ <grid name="channels_ver" x="0" y="0" width="100%" height="100%">
+ </grid>
+
+ <!-- Tokens available in channelgroups
+ {color} alternates grid by grid from true to false
+ {group} name of channel group
+ -->
+ <grid name="channelgroups_hor" x="0" y="0" width="100%" height="100%">
+ </grid>
+
+ <grid name="channelgroups_ver" x="0" y="0" width="100%" height="100%">
+ </grid>
+
+ <!-- Tokens available in schedules
+ {color} alternates grid by grid from true to false
+ {dummy} true if grid is a dummy grid
+ {timer} true if a timer is set for the according event
+ {switchtimer} true if a switchtimer is set for the according event
+ {title} title of grid
+ {shorttext} shorttext of grid
+ {start} start time in hh:mm
+ {stop} stop time in hh:dd
+ -->
+ <grid name="schedules_hor" x="0" y="0" width="100%" height="100%">
+ </grid>
+
+ <grid name="schedules_ver" x="0" y="0" width="100%" height="100%">
+ </grid>
+
+ <!-- Tokens available in channeljump
+ {channel} current user input for channel jump
+ -->
+ <viewelement name="channeljump">
+ </viewelement>
+
+</displayplugin>
diff --git a/timeline.c b/timeline.c
new file mode 100644
index 0000000..143a6a6
--- /dev/null
+++ b/timeline.c
@@ -0,0 +1,153 @@
+#include "timeline.h"
+
+// --- cTimelineElement -------------------------------------------------------------
+
+cTimelineElement::cTimelineElement(time_t elementTime) {
+ init = true;
+ this->elementTime = elementTime;
+}
+
+cTimelineElement::~cTimelineElement() {
+
+}
+
+bool cTimelineElement::IsNew(void) {
+ if (init) {
+ init = false;
+ return true;
+ }
+ return init;
+}
+
+bool cTimelineElement::IsFullHour(void) {
+ if (elementTime%3600 == 0)
+ return true;
+ return false;
+}
+
+// --- cTimeline -------------------------------------------------------------
+
+cTimeline::cTimeline(cViewGrid *timelineGrid, cViewElement *timelineDate, cViewElement *timeIndicator, cTimeManager *timeManager) {
+ this->timelineGrid = timelineGrid;
+ this->timelineDate = timelineDate;
+ this->timeIndicator = timeIndicator;
+ this->timeManager = timeManager;
+ steps = config.displayHours * 2;
+ stepDuration = 30 * 60;
+ weekday = "";
+ timeIndicatorShown = true;
+}
+
+cTimeline::~cTimeline(void) {
+ Clear();
+ delete timelineDate;
+ delete timeIndicator;
+ delete timelineGrid;
+}
+
+void cTimeline::Init(void) {
+ time_t startTime = timeManager->GetStart();
+ time_t elementTime = startTime;
+ for (int i=0; i < steps; i++) {
+ cTimelineElement *e = new cTimelineElement(elementTime);
+ grids.Add(e);
+ elementTime += stepDuration;
+ }
+}
+
+void cTimeline::Clear(void) {
+ timelineGrid->Clear();
+ grids.Clear();
+}
+
+void cTimeline::ScrollForward(int stepMinutes) {
+ int numSteps = stepMinutes / 30;
+ for (int i=0; i<numSteps; i++) {
+ cTimelineElement *e = grids.First();
+ timelineGrid->Delete(e->Id());
+ grids.Del(e);
+ }
+ time_t newStartTime = timeManager->GetEnd() - stepMinutes * 60;
+ for (int i=0; i<numSteps; i++) {
+ cTimelineElement *e = new cTimelineElement(newStartTime);
+ grids.Add(e);
+ newStartTime += stepDuration;
+ }
+}
+
+void cTimeline::ScrollBack(int stepMinutes) {
+ time_t startTime = timeManager->GetStart();
+ time_t endTime = timeManager->GetEnd();
+ int numSteps = stepMinutes / 30;
+ for (int i=0; i<numSteps; i++) {
+ cTimelineElement *e = grids.Last();
+ timelineGrid->Delete(e->Id());
+ grids.Del(e);
+ }
+ time_t newStartTime = timeManager->GetStart() + (numSteps-1) * 30 * 60;
+ for (int i=0; i<numSteps; i++) {
+ cTimelineElement *e = new cTimelineElement(newStartTime);
+ grids.Ins(e);
+ newStartTime -= stepDuration;
+ }
+}
+
+void cTimeline::Draw(void) {
+ DrawDate();
+ DrawTimeIndicator();
+ double x, y, width, height;
+ if (config.displayMode == eHorizontal) {
+ x = 0.0;
+ y = 0.0;
+ width = (double)1 / (double)steps;
+ height = 1.0;
+ } else {
+ x = 0.0;
+ y = 0.0;
+ width = 1.0;
+ height = (double)1 / (double)steps;
+ }
+ for (cTimelineElement *e = grids.First(); e; e = grids.Next(e)) {
+ if (e->IsNew()) {
+ timelineGrid->ClearTokens();
+ timelineGrid->AddIntToken("fullhour", e->IsFullHour());
+ timelineGrid->AddStringToken("timestring", e->ToString());
+ timelineGrid->SetGrid(e->Id(), x, y, width, height);
+ } else {
+ timelineGrid->MoveGrid(e->Id(), x, y, width, height);
+ }
+ if (config.displayMode == eHorizontal)
+ x += width;
+ else
+ y += height;
+ }
+ timelineGrid->Display();
+}
+
+void cTimeline::DrawDate(void) {
+ string weekdayNew = *(timeManager->GetWeekday());
+ if (!weekdayNew.compare(weekday))
+ return;
+ weekday = weekdayNew;
+ timelineDate->Clear();
+ timelineDate->ClearTokens();
+ string date = *(timeManager->GetDate());
+ timelineDate->AddStringToken("weekday", weekday);
+ timelineDate->AddStringToken("date", date);
+ timelineDate->Display();
+}
+
+void cTimeline::DrawTimeIndicator(void) {
+ if (timeManager->NowVisible()) {
+ timeIndicatorShown = true;
+ int distance = (timeManager->GetNow() - timeManager->GetStart()) / 60;
+ int percentTotal = distance*1000/(config.displayHours*60);
+ timeIndicator->Clear();
+ timeIndicator->ClearTokens();
+ timeIndicator->AddIntToken("percenttotal", percentTotal);
+ timeIndicator->Display();
+ } else if (timeIndicatorShown) {
+ timeIndicatorShown = false;
+ timeIndicator->Clear();
+ }
+}
diff --git a/timeline.h b/timeline.h
new file mode 100644
index 0000000..c6914d2
--- /dev/null
+++ b/timeline.h
@@ -0,0 +1,50 @@
+#ifndef __TVGUIDE_TIMELINE_H
+#define __TVGUIDE_TIMELINE_H
+
+#include <set>
+#include <vdr/tools.h>
+#include "libskindesigner/osdelements.h"
+#include "config.h"
+#include "timemanager.h"
+
+// --- cTimelineElement -------------------------------------------------------------
+
+class cTimelineElement : public cListObject {
+private:
+ bool init;
+ time_t elementTime;
+public:
+ cTimelineElement(time_t elementTime);
+ virtual ~cTimelineElement(void);
+ long Id(void) { return elementTime; };
+ bool IsNew(void);
+ bool IsFullHour(void);
+ string ToString(void) { return *TimeString(elementTime); };
+};
+
+// --- cTimeline -------------------------------------------------------------
+
+class cTimeline {
+private:
+ cViewGrid *timelineGrid;
+ cViewElement *timelineDate;
+ cViewElement *timeIndicator;
+ cTimeManager *timeManager;
+ cList<cTimelineElement> grids;
+ int steps;
+ int stepDuration;
+ string weekday;
+ bool timeIndicatorShown;
+ void DrawDate(void);
+ void DrawTimeIndicator(void);
+public:
+ cTimeline(cViewGrid *timelineGrid, cViewElement *timelineDate, cViewElement *timeIndicator, cTimeManager *timeManager);
+ virtual ~cTimeline(void);
+ void Init(void);
+ void Clear(void);
+ void ScrollForward(int stepMinutes);
+ void ScrollBack(int stepMinutes);
+ void Draw(void);
+};
+
+#endif //__TVGUIDE_TIMELINE_H
diff --git a/timemanager.c b/timemanager.c
new file mode 100644
index 0000000..51c02be
--- /dev/null
+++ b/timemanager.c
@@ -0,0 +1,172 @@
+#include <time.h>
+#include <vdr/tools.h>
+#include "timemanager.h"
+
+cTimeManager::cTimeManager(void) {
+ displayHours = 4;
+ displaySeconds = displayHours * 3600;
+ timeFormat = e24Hours;
+}
+
+cTimeManager::~cTimeManager(void) {
+}
+
+cString cTimeManager::PrintTime(time_t displayTime) {
+ struct tm *ts;
+ ts = localtime(&displayTime);
+ cString strTime = cString::sprintf("%d.%d-%d:%d.%d", ts->tm_mday, ts->tm_mon+1, ts->tm_hour, ts->tm_min, ts->tm_sec);
+ return strTime;
+}
+
+
+void cTimeManager::Now() {
+ t = time(0);
+ tStart = t;
+ tStart = GetRounded();
+ tEnd = tStart + displaySeconds;
+}
+
+void cTimeManager::AddMinutes(int step) {
+ tStart += step*60;
+ tEnd += step*60;
+}
+
+bool cTimeManager::DelMinutes(int step) {
+ if ((tStart - step*60)+30*60 < t) {
+ return true;
+ }
+ tStart -= step*60;
+ tEnd -= step*60;
+ return false;
+}
+
+void cTimeManager::SetTime(time_t newTime) {
+ tStart = newTime;
+ tEnd = tStart + displaySeconds;
+}
+
+time_t cTimeManager::GetPrevPrimetime(time_t current) {
+ tm *st = localtime(&current);
+ if (st->tm_hour < 21) {
+ current -= 24 * 60* 60;
+ st = localtime(&current);
+ }
+ st->tm_hour = 20;
+ st->tm_min = 0;
+ time_t primeTime = mktime(st);
+ return primeTime;
+}
+
+time_t cTimeManager::GetNextPrimetime(time_t current){
+ tm *st = localtime(&current);
+ if (st->tm_hour > 19) {
+ current += 24 * 60* 60;
+ st = localtime(&current);
+ }
+ st->tm_hour = 20;
+ st->tm_min = 0;
+ time_t primeTime = mktime(st);
+ return primeTime;
+}
+
+bool cTimeManager::TooFarInPast(time_t current) {
+ if (current < t) {
+ return true;
+ }
+ return false;
+}
+
+cString cTimeManager::GetCurrentTime() {
+ char buf[25];
+ t = time(0);
+ tm *st = localtime(&t);
+ if (timeFormat == e12Hours) {
+ strftime(buf, sizeof(buf), "%I:%M %p", st);
+ } else if (timeFormat == e24Hours)
+ strftime(buf, sizeof(buf), "%H:%M", st);
+ return buf;
+
+}
+
+cString cTimeManager::GetDate() {
+ char text[6];
+ tm *st = localtime(&tStart);
+ snprintf(text, sizeof(text), "%d.%d", st->tm_mday, st->tm_mon+1);
+ return text;
+}
+
+cString cTimeManager::GetWeekday() {
+ return WeekDayName(tStart);
+}
+
+bool cTimeManager::IsStart(int activeStart) {
+ if (tStart <= t && activeStart <= t)
+ return true;
+ return false;
+}
+
+time_t cTimeManager::GetRounded() {
+ tm *rounded = localtime ( &tStart );
+ rounded->tm_sec = 0;
+ if (rounded->tm_min > 29)
+ rounded->tm_min = 30;
+ else
+ rounded->tm_min = 0;
+ return mktime(rounded);
+}
+
+bool cTimeManager::NowVisible(void) {
+ if (t > tStart)
+ return true;
+ return false;
+}
+
+void cTimeManager::Debug() {
+ esyslog("tvguideng: now %s, tStart: %s, tEnd: %s", *TimeString(t), *TimeString(tStart), *TimeString(tEnd));
+}
+
+// --- cTimeInterval -------------------------------------------------------------
+
+cTimeInterval::cTimeInterval(time_t start, time_t stop) {
+ this->start = start;
+ this->stop = stop;
+}
+
+cTimeInterval::~cTimeInterval(void) {
+}
+
+cTimeInterval *cTimeInterval::Intersect(cTimeInterval *interval) {
+ time_t startIntersect, stopIntersect;
+
+ if ((stop <= interval->Start()) || (interval->Stop() <= start)) {
+ return NULL;
+ }
+
+ if (start <= interval->Start()) {
+ startIntersect = interval->Start();
+ } else {
+ startIntersect = start;
+ }
+ if (stop <= interval->Stop()) {
+ stopIntersect = stop;
+ } else {
+ stopIntersect = interval->Stop();
+ }
+ return new cTimeInterval(startIntersect, stopIntersect);
+}
+
+cTimeInterval *cTimeInterval::Union(cTimeInterval *interval) {
+ time_t startUnion, stopUnion;
+
+ if (start <= interval->Start()) {
+ startUnion = start;
+ } else {
+ startUnion = interval->Start();
+ }
+ if (stop <= interval->Stop()) {
+ stopUnion = interval->Stop();
+ } else {
+ stopUnion = stop;
+ }
+ return new cTimeInterval(startUnion, stopUnion);
+} \ No newline at end of file
diff --git a/timemanager.h b/timemanager.h
new file mode 100644
index 0000000..fb6b5b2
--- /dev/null
+++ b/timemanager.h
@@ -0,0 +1,61 @@
+#ifndef __TVGUIDE_TIMEMANAGER_H
+#define __TVGUIDE_TIMEMANAGER_H
+
+#include <vdr/tools.h>
+
+enum eTimeFormat {
+ e12Hours,
+ e24Hours
+};
+
+// --- cTimeManager -------------------------------------------------------------
+
+class cTimeManager {
+ private:
+ time_t t;
+ time_t tStart;
+ time_t tEnd;
+ int displayHours;
+ int displaySeconds;
+ eTimeFormat timeFormat;
+ public:
+ cTimeManager(void);
+ virtual ~cTimeManager(void);
+ static cString PrintTime(time_t displayTime);
+ void Now();
+ time_t GetNow() { return t; };
+ void AddMinutes(int step);
+ bool DelMinutes(int step);
+ void SetTime(time_t newTime);
+ time_t Get() {return t;};
+ time_t GetStart() {return tStart;};
+ time_t GetEnd() {return tEnd;};
+ cString GetCurrentTime();
+ cString GetDate();
+ cString GetWeekday();
+ time_t GetPrevPrimetime(time_t current);
+ time_t GetNextPrimetime(time_t current);
+ bool TooFarInPast(time_t current);
+ bool IsStart(int activeStart);
+ time_t GetRounded();
+ bool NowVisible(void);
+ int GetDisplaySeconds(void) {return displaySeconds; };
+ void Debug();
+};
+
+// --- cTimeInterval -------------------------------------------------------------
+
+class cTimeInterval {
+ private:
+ time_t start;
+ time_t stop;
+ public:
+ cTimeInterval(time_t start, time_t stop);
+ virtual ~cTimeInterval(void);
+ time_t Start(void) { return start; };
+ time_t Stop(void) { return stop; };
+ cTimeInterval *Intersect(cTimeInterval *interval);
+ cTimeInterval *Union(cTimeInterval *interval);
+};
+
+#endif //__TVGUIDE_TIMEMANAGER_H \ No newline at end of file
diff --git a/timerconflict.c b/timerconflict.c
new file mode 100644
index 0000000..9f01427
--- /dev/null
+++ b/timerconflict.c
@@ -0,0 +1,177 @@
+#include <string>
+#include <vector>
+#include <vdr/timers.h>
+#include "helpers.h"
+#include "timemanager.h"
+#include "timerconflict.h"
+
+cTVGuideTimerConflict::cTVGuideTimerConflict(void) {
+ time = 0;
+ timeStart = 0;
+ timeStop = 0;
+ overlapStart = 0;
+ overlapStop = 0;
+ percentPossible = 0;
+ timerID = 0;
+}
+
+cTVGuideTimerConflict::~cTVGuideTimerConflict(void) {
+
+}
+
+bool cTVGuideTimerConflict::timerInvolved(int involvedID) {
+ int numConflicts = timerIDs.size();
+ for (int i=0; i<numConflicts; i++) {
+ if (timerIDs[i] == involvedID)
+ return true;
+ }
+ return false;
+}
+
+// --- cTVGuideTimerConflicts------------------------------------
+
+cTVGuideTimerConflicts::cTVGuideTimerConflicts(void) {
+ numConflicts = 0;
+ currentConflict = -1;
+}
+
+cTVGuideTimerConflicts::~cTVGuideTimerConflicts(void) {
+ for(std::vector<cTVGuideTimerConflict*>::const_iterator it = conflicts.begin(); it != conflicts.end(); it++) {
+ cTVGuideTimerConflict *conf = *it;
+ delete conf;
+ }
+ conflicts.clear();
+}
+
+void cTVGuideTimerConflicts::AddConflict(std::string epgSearchConflictLine) {
+ /* TIMERCONFLICT FORMAT:
+ The result list looks like this for example when we have 2 timer conflicts at one time:
+ 1190232780:152|30|50#152#45:45|10|50#152#45
+ '1190232780' is the time of the conflict in seconds since 1970-01-01.
+ It's followed by list of timers that have a conflict at this time:
+ '152|30|50#1 int editTimer(cTimer *timer, bool active, int prio, int start, int stop);
+ 52#45' is the description of the first conflicting timer. Here:
+ '152' is VDR's timer id of this timer as returned from VDR's LSTT command
+ '30' is the percentage of recording that would be done (0...100)
+ '50#152#45' is the list of concurrent timers at this conflict
+ '45|10|50#152#45' describes the next conflict
+ */
+ cTVGuideTimerConflict *conflict = new cTVGuideTimerConflict();
+ splitstring s(epgSearchConflictLine.c_str());
+ std::vector<std::string> flds = s.split(':');
+ if (flds.size() < 2)
+ return;
+ conflict->time = atoi(flds[0].c_str());
+ splitstring s2(flds[1].c_str());
+ std::vector<std::string> flds2 = s2.split('|');
+ if (flds2.size() < 3)
+ return;
+ conflict->timerID = atoi(flds2[0].c_str());
+ conflict->percentPossible = atoi(flds2[1].c_str());
+ splitstring s3(flds2[2].c_str());
+ std::vector<std::string> flds3 = s3.split('#');
+ std::vector<int> timerIDs;
+ for (int k = 0; k < flds3.size(); k++) {
+ timerIDs.push_back(atoi(flds3[k].c_str()) - 1);
+ }
+ conflict->timerIDs = timerIDs;
+ conflicts.push_back(conflict);
+}
+
+void cTVGuideTimerConflicts::CalculateConflicts(void) {
+ numConflicts = conflicts.size();
+ time_t startTime = 0;
+ time_t endTime = 0;
+ for (int i=0; i < numConflicts; i++) {
+ cTimeInterval *unionSet = NULL;
+ int numTimers = conflicts[i]->timerIDs.size();
+ for (int j=0; j < numTimers; j++) {
+ const cTimer *timer = Timers.Get(conflicts[i]->timerIDs[j]);
+ if (timer) {
+ if (!unionSet) {
+ unionSet = new cTimeInterval(timer->StartTime(), timer->StopTime());
+ } else {
+ cTimeInterval *timerInterval = new cTimeInterval(timer->StartTime(), timer->StopTime());
+ cTimeInterval *newUnion = unionSet->Union(timerInterval);
+ delete unionSet;
+ delete timerInterval;
+ unionSet = newUnion;
+ }
+ }
+ }
+ conflicts[i]->timeStart = unionSet->Start();
+ conflicts[i]->timeStop = unionSet->Stop();
+ delete unionSet;
+
+ cTimeInterval *intersect = NULL;
+ for (int j=0; j < numTimers; j++) {
+ const cTimer *timer = Timers.Get(conflicts[i]->timerIDs[j]);
+ if (timer) {
+ if (!intersect) {
+ intersect = new cTimeInterval(timer->StartTime(), timer->StopTime());
+ } else {
+ cTimeInterval *timerInterval = new cTimeInterval(timer->StartTime(), timer->StopTime());
+ cTimeInterval *newIntersect = intersect->Intersect(timerInterval);
+ if (newIntersect) {
+ delete intersect;
+ intersect = newIntersect;
+ }
+ delete timerInterval;
+ }
+ }
+ }
+ conflicts[i]->overlapStart = intersect->Start();
+ conflicts[i]->overlapStop = intersect->Stop();
+ delete intersect;
+ }
+}
+
+cTVGuideTimerConflict *cTVGuideTimerConflicts::GetCurrentConflict(void) {
+ if (currentConflict < 0)
+ return NULL;
+ if (currentConflict > (numConflicts-1))
+ return NULL;
+ return conflicts[currentConflict];
+}
+
+int cTVGuideTimerConflicts::GetCurrentConflictTimerID(int timerIndex) {
+ if (currentConflict < 0)
+ return -1;
+ if (currentConflict > (numConflicts-1))
+ return -1;
+ int numTimersInConflict = conflicts[currentConflict]->timerIDs.size();
+ if (timerIndex > (numTimersInConflict - 1))
+ return -1;
+ return conflicts[currentConflict]->timerIDs[timerIndex];
+}
+
+int cTVGuideTimerConflicts::GetCorrespondingConflict(int timerID) {
+ int conflictIndex = -1;
+ if (numConflicts > 0) {
+ for (int i=0; i<numConflicts; i++) {
+ if (conflicts[i]->timerInvolved(timerID)) {
+ conflictIndex = i;
+ break;
+ }
+ }
+ }
+ return conflictIndex;
+}
+
+cTVGuideTimerConflict *cTVGuideTimerConflicts::GetConflict(int conflictIndex) {
+ if (conflictIndex < 0)
+ return NULL;
+ if (conflictIndex > (numConflicts-1))
+ return NULL;
+ return conflicts[conflictIndex];
+}
+
+std::vector<cTVGuideTimerConflict*> cTVGuideTimerConflicts::GetConflictsBetween(time_t start, time_t stop) {
+ std::vector<cTVGuideTimerConflict*> conflictsFound;
+ for (int i=0; i < numConflicts; i++) {
+ if ((conflicts[i]->timeStart > start) && (conflicts[i]->timeStart < stop)||
+ (conflicts[i]->timeStop > start) && (conflicts[i]->timeStop < stop))
+ conflictsFound.push_back(conflicts[i]);
+ }
+ return conflictsFound;
+} \ No newline at end of file
diff --git a/timerconflict.h b/timerconflict.h
new file mode 100644
index 0000000..d3def89
--- /dev/null
+++ b/timerconflict.h
@@ -0,0 +1,38 @@
+#ifndef __TVGUIDE_TIMERCONFLICT_H
+#define __TVGUIDE_TIMERCONFLICT_H
+
+class cTVGuideTimerConflict {
+public:
+ cTVGuideTimerConflict(void);
+ virtual ~cTVGuideTimerConflict(void);
+ time_t time;
+ time_t timeStart;
+ time_t timeStop;
+ time_t overlapStart;
+ time_t overlapStop;
+ int percentPossible;
+ int timerID;
+ std::vector<int> timerIDs;
+ bool timerInvolved(int involvedID);
+};
+
+class cTVGuideTimerConflicts {
+private:
+ std::vector<cTVGuideTimerConflict*> conflicts;
+ int numConflicts;
+ int currentConflict;
+public:
+ cTVGuideTimerConflicts(void);
+ virtual ~cTVGuideTimerConflicts(void);
+ void AddConflict(std::string epgSearchConflictLine);
+ void CalculateConflicts(void);
+ int NumConflicts(void) {return numConflicts; };
+ void SetCurrentConflict(int current) { currentConflict = current; };
+ cTVGuideTimerConflict *GetCurrentConflict(void);
+ int GetCurrentConflictTimerID(int timerIndex);
+ int GetCorrespondingConflict(int timerID);
+ cTVGuideTimerConflict *GetConflict(int conflictIndex);
+ std::vector<cTVGuideTimerConflict*> GetConflictsBetween(time_t start, time_t stop);
+};
+
+#endif //__TVGUIDE_RECMMANAGER_H \ No newline at end of file
diff --git a/tvguideng.c b/tvguideng.c
new file mode 100644
index 0000000..8f9b5ba
--- /dev/null
+++ b/tvguideng.c
@@ -0,0 +1,156 @@
+/*
+ * tvguideng.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include <vdr/plugin.h>
+#include "libskindesigner/services.h"
+#define DEFINE_CONFIG 1
+#include "config.h"
+#include "setup.h"
+#include "tvguidengosd.h"
+
+static const char *VERSION = "0.0.1";
+static const char *DESCRIPTION = "TV Guide for Skindesigner Skins";
+static const char *MAINMENUENTRY = "TV Guide NG";
+
+class cPluginTvguideng : public cPlugin {
+private:
+ // Add any member variables or functions you may need here.
+public:
+ cPluginTvguideng(void);
+ virtual ~cPluginTvguideng();
+ virtual const char *Version(void) { return VERSION; }
+ virtual const char *Description(void) { return DESCRIPTION; }
+ virtual const char *CommandLineHelp(void);
+ virtual bool ProcessArgs(int argc, char *argv[]);
+ virtual bool Initialize(void);
+ virtual bool Start(void);
+ virtual void Stop(void);
+ virtual void Housekeeping(void);
+ virtual void MainThreadHook(void);
+ virtual cString Active(void);
+ virtual time_t WakeupTime(void);
+ virtual const char *MainMenuEntry(void) { return (config.showMainMenuEntry)?MAINMENUENTRY: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);
+ };
+
+cPluginTvguideng::cPluginTvguideng(void) {
+}
+
+cPluginTvguideng::~cPluginTvguideng() {
+}
+
+const char *cPluginTvguideng::CommandLineHelp(void) {
+ return NULL;
+}
+
+bool cPluginTvguideng::ProcessArgs(int argc, char *argv[]) {
+ return true;
+}
+
+bool cPluginTvguideng::Initialize(void) {
+ return true;
+}
+
+bool cPluginTvguideng::Start(void) {
+ RegisterPlugin reg;
+ reg.name = "tvguideng";
+ reg.SetView(viRootView, "root.xml");
+ reg.SetViewElement(viRootView, verBackgroundHor, "background_hor");
+ reg.SetViewElement(viRootView, verBackgroundVer, "background_ver");
+ reg.SetViewElement(viRootView, verHeader, "header");
+ reg.SetViewElement(viRootView, verTime, "time");
+ reg.SetViewElement(viRootView, verFooter, "footer");
+ reg.SetViewElement(viRootView, verDateTimelineHor, "datetimeline_hor");
+ reg.SetViewElement(viRootView, verDateTimelineVer, "datetimeline_ver");
+ reg.SetViewElement(viRootView, verTimeIndicatorHor, "timeindicator_hor");
+ reg.SetViewElement(viRootView, verTimeIndicatorVer, "timeindicator_ver");
+ reg.SetViewElement(viRootView, verChannelJump, "channeljump");
+ reg.SetViewGrid(viRootView, vgChannelsHor, "channels_hor");
+ reg.SetViewGrid(viRootView, vgChannelsVer, "channels_ver");
+ reg.SetViewGrid(viRootView, vgSchedulesHor, "schedules_hor");
+ reg.SetViewGrid(viRootView, vgSchedulesVer, "schedules_ver");
+ reg.SetViewGrid(viRootView, vgTimelineHor, "timeline_hor");
+ reg.SetViewGrid(viRootView, vgTimelineVer, "timeline_ver");
+ reg.SetViewGrid(viRootView, vgChannelGroupsHor, "channelgroups_hor");
+ reg.SetViewGrid(viRootView, vgChannelGroupsVer, "channelgroups_ver");
+ //Detail View
+ reg.SetSubView(viRootView, viDetailView, "detail.xml");
+ reg.SetViewElement(viDetailView, vedBackground, "background");
+ reg.SetViewElement(viDetailView, vedHeader, "header");
+ reg.SetViewElement(viDetailView, vedFooter, "footer");
+ reg.SetViewElement(viDetailView, vedTime, "time");
+ //Search & Recording Menus
+ reg.SetSubView(viRootView, viRecMenu, "recmenu.xml");
+ reg.SetViewElement(viRecMenu, vemBackground, "background");
+ reg.SetViewElement(viRecMenu, vemScrollbar, "scrollbar");
+ reg.SetViewGrid(viRecMenu, vgRecordingMenu, "recmenu");
+ static cPlugin *pSkinDesigner = cPluginManager::GetPlugin("skindesigner");
+ if (pSkinDesigner) {
+ pSkinDesigner->Service("RegisterPlugin", &reg);
+ } else {
+ esyslog("tvguideng: skindesigner not available");
+ }
+
+ return true;
+}
+
+void cPluginTvguideng::Stop(void) {
+}
+
+void cPluginTvguideng::Housekeeping(void) {
+}
+
+void cPluginTvguideng::MainThreadHook(void) {
+}
+
+cString cPluginTvguideng::Active(void) {
+ return NULL;
+}
+
+time_t cPluginTvguideng::WakeupTime(void) {
+ return 0;
+}
+
+cOsdObject *cPluginTvguideng::MainMenuAction(void) {
+ return new cTVGuideOSD();
+}
+
+cMenuSetupPage *cPluginTvguideng::SetupMenu(void) {
+ return new cTvGuideSetup();
+}
+
+bool cPluginTvguideng::SetupParse(const char *Name, const char *Value) {
+ return config.SetupParse(Name, Value);
+}
+
+bool cPluginTvguideng::Service(const char *Id, void *Data) {
+ if (strcmp(Id, "MainMenuHooksPatch-v1.0::osSchedule") == 0 && config.replaceOriginalSchedule != 0) {
+ if (Data == NULL)
+ return true;
+ cOsdObject **guide = (cOsdObject**) Data;
+ if (guide)
+ *guide = MainMenuAction();
+ return true;
+ }
+ return false;
+}
+
+const char **cPluginTvguideng::SVDRPHelpPages(void) {
+ return NULL;
+}
+
+cString cPluginTvguideng::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode) {
+ return NULL;
+}
+
+VDRPLUGINCREATOR(cPluginTvguideng); // Don't touch this!
diff --git a/tvguidengosd.c b/tvguidengosd.c
new file mode 100644
index 0000000..c49f816
--- /dev/null
+++ b/tvguidengosd.c
@@ -0,0 +1,475 @@
+#include "tvguidengosd.h"
+
+cTVGuideOSD::cTVGuideOSD(void) {
+ timeManager = NULL;
+ epgGrid = NULL;
+ channelJumper = NULL;
+ detailView = NULL;
+ recMenuView = NULL;
+}
+
+cTVGuideOSD::~cTVGuideOSD(void) {
+ if (recMenuView && recMenuView->Active()) {
+ recMenuView->Close();
+ }
+ if (timeManager)
+ delete timeManager;
+ if (epgGrid)
+ delete epgGrid;
+ if (channelJumper)
+ delete channelJumper;
+ if (detailView)
+ delete detailView;
+ if (recMenuView)
+ delete recMenuView;
+}
+
+void cTVGuideOSD::Show(void) {
+ bool skinDesignerAvailable = InitSkindesignerInterface("tvguideng");
+ if (!skinDesignerAvailable) {
+ return;
+ esyslog("tvguideng: skindesigner not available");
+ }
+
+ cOsdView *rootView = GetOsdView(viRootView);
+ if (!rootView) {
+ esyslog("tvguideng: used skindesigner skin does not support tvguideng");
+ return;
+ }
+ SwitchTimers.Load(AddDirectory(cPlugin::ConfigDirectory("epgsearch"), "epgsearchswitchtimers.conf"));
+ recMenuView = new cRecMenuView();
+ pRemoteTimers = cPluginManager::CallFirstService("RemoteTimers::RefreshTimers-v1.0", NULL);
+ if (pRemoteTimers) {
+ isyslog("tvguideng: remotetimers-plugin is available");
+ }
+ if (config.useRemoteTimers && pRemoteTimers) {
+ cString errorMsg;
+ if (!pRemoteTimers->Service("RemoteTimers::RefreshTimers-v1.0", &errorMsg)) {
+ esyslog("tvguideng: %s", *errorMsg);
+ }
+ }
+
+ timeManager = new cTimeManager();
+ timeManager->Now();
+
+ epgGrid = new cEpgGrid(rootView, timeManager);
+
+ cChannel *startChannel = Channels.GetByNumber(cDevice::CurrentChannel());
+
+ epgGrid->Init(startChannel);
+ epgGrid->DrawHeader();
+ epgGrid->DrawTime();
+ epgGrid->DrawFooter();
+ epgGrid->DrawChannelHeaders();
+ epgGrid->DrawTimeline();
+ epgGrid->DrawChannelgroups();
+ epgGrid->DrawGrid();
+ epgGrid->Flush();
+}
+
+eOSState cTVGuideOSD::ProcessKey(eKeys Key) {
+ if (!epgGrid) {
+ return osEnd;
+ }
+
+ eOSState state = osContinue;
+
+ if (recMenuView->Active() && !detailView) {
+ state = recMenuView->ProcessKey(Key);
+ if (state == osEnd) {
+ epgGrid->SetTimers();
+ epgGrid->Activate();
+ epgGrid->DrawGrid();
+ epgGrid->Flush();
+ } else if (state == osUser1) {
+ DetailView(recMenuView->GetEvent());
+ }
+ state = osContinue;
+ if (epgGrid->DrawTime())
+ epgGrid->Flush();
+ } else if (detailView) {
+ //Detail View Key Handling
+ switch (Key & ~k_Repeat) {
+ case kBack:
+ CloseDetailedView();
+ break;
+ case kUp:
+ detailView->Up();
+ detailView->Flush();
+ break;
+ case kDown:
+ detailView->Down();
+ detailView->Flush();
+ break;
+ case kLeft:
+ detailView->Left();
+ detailView->Flush();
+ break;
+ case kRight:
+ detailView->Right();
+ detailView->Flush();
+ break;
+ case kOk:
+ CloseDetailedView();
+ break;
+ case kBlue:
+ state = ChannelSwitch();
+ break;
+ default:
+ break;
+ }
+ if (detailView && state != osEnd && detailView->DrawTime()) {
+ detailView->Flush();
+ }
+ } else {
+
+ //EPG Grid is active
+ switch (Key & ~k_Repeat) {
+ case kBack:
+ state=osEnd;
+ break;
+ case kUp:
+ KeyUp();
+ break;
+ case kDown:
+ KeyDown();
+ break;
+ case kLeft:
+ KeyLeft();
+ break;
+ case kRight:
+ KeyRight();
+ break;
+ case kOk:
+ state = KeyOk(epgGrid->GetCurrentEvent());
+ break;
+ case k0 ... k9:
+ NumericKey(Key - k0);
+ break;
+ case kRed:
+ KeyRed();
+ break;
+ case kGreen:
+ KeyGreen();
+ break;
+ case kYellow:
+ KeyYellow();
+ break;
+ case kBlue:
+ state = KeyBlue(epgGrid->GetCurrentEvent());
+ break;
+ case kNone:
+ if (channelJumper) CheckTimeout();
+ break;
+ default:
+ break;
+ }
+ if (state != osEnd && epgGrid->DrawTime())
+ epgGrid->Flush();
+ }
+ return state;
+}
+
+void cTVGuideOSD::KeyLeft(void) {
+ if (config.displayMode == eHorizontal) {
+ TimeBack();
+ } else if (config.displayMode == eVertical) {
+ ChannelsBack();
+ }
+}
+
+void cTVGuideOSD::KeyRight(void) {
+ if (config.displayMode == eHorizontal) {
+ TimeForward();
+ } else if (config.displayMode == eVertical) {
+ ChannelsForward();
+ }
+}
+
+void cTVGuideOSD::KeyUp(void) {
+ if (config.displayMode == eHorizontal) {
+ ChannelsBack();
+ } else if (config.displayMode == eVertical) {
+ TimeBack();
+ }
+}
+
+void cTVGuideOSD::KeyDown(void) {
+ if (config.displayMode == eHorizontal) {
+ ChannelsForward();
+ } else if (config.displayMode == eVertical) {
+ TimeForward();
+ }
+}
+
+void cTVGuideOSD::TimeForward(void) {
+ bool scrolled = epgGrid->TimeForward();
+ if (!scrolled) {
+ epgGrid->UpdateActive();
+ } else {
+ epgGrid->DrawGrid();
+ }
+ epgGrid->DrawHeader();
+ epgGrid->Flush();
+}
+
+void cTVGuideOSD::TimeBack(void) {
+ bool scrolled = epgGrid->TimeBack();
+ if (!scrolled) {
+ epgGrid->UpdateActive();
+ } else {
+ epgGrid->DrawGrid();
+ }
+ epgGrid->DrawHeader();
+ epgGrid->Flush();
+}
+
+void cTVGuideOSD::ChannelsForward(void) {
+ bool scrolled = epgGrid->ChannelForward();
+ if (!scrolled) {
+ epgGrid->UpdateActive();
+ } else {
+ epgGrid->DrawChannelHeaders();
+ epgGrid->DrawChannelgroups();
+ epgGrid->DrawGrid();
+ }
+ if (config.channelJumpMode == eGroupJump)
+ epgGrid->DrawFooter();
+ epgGrid->DrawHeader();
+ epgGrid->Flush();
+}
+
+void cTVGuideOSD::ChannelsBack(void) {
+ bool scrolled = epgGrid->ChannelBack();
+ if (!scrolled) {
+ epgGrid->UpdateActive();
+ } else {
+ epgGrid->DrawChannelHeaders();
+ epgGrid->DrawChannelgroups();
+ epgGrid->DrawGrid();
+ }
+ if (config.channelJumpMode == eGroupJump)
+ epgGrid->DrawFooter();
+ epgGrid->DrawHeader();
+ epgGrid->Flush();
+}
+
+void cTVGuideOSD::NumericKey(int key) {
+ if (config.numKeyMode == eTimeJump) {
+ NumericKeyTimeJump(key);
+ } else if (config.numKeyMode == eChannelJump) {
+ NumericKeyChannelJump(key);
+ }
+}
+
+void cTVGuideOSD::NumericKeyTimeJump(int key) {
+ switch (key) {
+ case 1: {
+ bool tooFarInPast = timeManager->DelMinutes(config.bigStepHours*60);
+ if (tooFarInPast)
+ return;
+ }
+ break;
+ case 3: {
+ timeManager->AddMinutes(config.bigStepHours*60);
+ }
+ break;
+ case 4: {
+ bool tooFarInPast = timeManager->DelMinutes(config.hugeStepHours*60);
+ if (tooFarInPast)
+ return;
+ }
+ break;
+ case 6: {
+ timeManager->AddMinutes(config.hugeStepHours*60);
+ }
+ break;
+ case 7: {
+ cTimeManager primeChecker;
+ primeChecker.Now();
+ time_t prevPrime = primeChecker.GetPrevPrimetime(timeManager->GetStart());
+ if (primeChecker.TooFarInPast(prevPrime))
+ return;
+ timeManager->SetTime(prevPrime);
+ }
+ break;
+ case 9: {
+ cTimeManager primeChecker;
+ time_t nextPrime = primeChecker.GetNextPrimetime(timeManager->GetStart());
+ timeManager->SetTime(nextPrime);
+ }
+ break;
+ default:
+ return;
+ }
+ epgGrid->RebuildEpgGrid();
+ epgGrid->DrawGrid();
+ epgGrid->DrawHeader();
+ epgGrid->DrawTimeline();
+ epgGrid->Flush();
+}
+
+void cTVGuideOSD::NumericKeyChannelJump(int key) {
+ if (!channelJumper) {
+ channelJumper = epgGrid->GetChannelJumper();
+ }
+ channelJumper->Set(key);
+ channelJumper->Draw();
+ epgGrid->Flush();
+}
+
+void cTVGuideOSD::CheckTimeout(void) {
+ if (!channelJumper)
+ return;
+ if (!channelJumper->TimeOut())
+ return;
+ int newChannelNum = channelJumper->GetChannel();
+ delete channelJumper;
+ channelJumper = NULL;
+ const cChannel *newChannel = Channels.GetByNumber(newChannelNum);
+ if (!newChannel) {
+ return;
+ }
+ epgGrid->Clear();
+ epgGrid->Init(newChannel);
+ epgGrid->DrawHeader();
+ epgGrid->DrawTime();
+ epgGrid->DrawFooter();
+ epgGrid->DrawChannelHeaders();
+ epgGrid->DrawTimeline();
+ epgGrid->DrawChannelgroups();
+ epgGrid->DrawGrid();
+ epgGrid->Flush();
+}
+
+void cTVGuideOSD::KeyGreen(void) {
+ const cChannel *prevStart = NULL;
+ if (config.channelJumpMode == eNumJump) {
+ prevStart = epgGrid->GetPrevChannelNumJump();
+ } else if (config.channelJumpMode == eGroupJump) {
+ if (epgGrid->IsFirstGroup()) {
+ return;
+ }
+ prevStart = epgGrid->GetPrevChannelGroupJump();
+ }
+ epgGrid->Clear();
+ epgGrid->Init(prevStart);
+ epgGrid->DrawHeader();
+ epgGrid->DrawTime();
+ epgGrid->DrawFooter();
+ epgGrid->DrawChannelHeaders();
+ epgGrid->DrawChannelgroups();
+ if (config.channelJumpMode == eGroupJump)
+ epgGrid->DrawFooter();
+ epgGrid->DrawTimeline();
+ epgGrid->DrawGrid();
+ epgGrid->Flush();
+}
+
+void cTVGuideOSD::KeyYellow(void) {
+ const cChannel *nextStart = NULL;
+ if (config.channelJumpMode == eNumJump) {
+ nextStart = epgGrid->GetNextChannelNumJump();
+ } else if (config.channelJumpMode == eGroupJump) {
+ if (epgGrid->IsLastGroup()) {
+ return;
+ }
+ if (config.hideLastChannelGroup && epgGrid->IsSecondLastGroup()) {
+ return;
+ }
+ nextStart = epgGrid->GetNextChannelGroupJump();
+ }
+ epgGrid->Clear();
+ epgGrid->Init(nextStart);
+ epgGrid->DrawHeader();
+ epgGrid->DrawTime();
+ epgGrid->DrawFooter();
+ epgGrid->DrawChannelHeaders();
+ epgGrid->DrawChannelgroups();
+ if (config.channelJumpMode == eGroupJump)
+ epgGrid->DrawFooter();
+ epgGrid->DrawTimeline();
+ epgGrid->DrawGrid();
+ epgGrid->Flush();
+}
+
+eOSState cTVGuideOSD::KeyBlue(const cEvent *e) {
+ if (config.blueKeyMode == eBlueKeySwitch) {
+ return ChannelSwitch();
+ } else if (config.blueKeyMode == eBlueKeyEPG) {
+ DetailView(e);
+ } else if (config.blueKeyMode == eBlueKeyFavorites) {
+ Favorites();
+ }
+ return osContinue;
+}
+
+eOSState cTVGuideOSD::KeyOk(const cEvent *e) {
+ if (config.blueKeyMode == eBlueKeySwitch) {
+ DetailView(e);
+ } else if (config.blueKeyMode == eBlueKeyEPG) {
+ return ChannelSwitch();
+ } else if (config.blueKeyMode == eBlueKeyFavorites) {
+ DetailView(e);
+ }
+ return osContinue;
+}
+
+eOSState cTVGuideOSD::ChannelSwitch(void) {
+ const cChannel *currentChannel = epgGrid->GetCurrentChannel();
+ if (!currentChannel) {
+ return osContinue;
+ }
+ cDevice::PrimaryDevice()->SwitchChannel(currentChannel, true);
+ if (config.closeOnSwitch) {
+ return osEnd;
+ }
+ return osContinue;
+}
+
+void cTVGuideOSD::DetailView(const cEvent *e) {
+ if (!e)
+ return;
+ epgGrid->Deactivate(true);
+ if (recMenuView->Active())
+ recMenuView->Hide();
+ cOsdView *dV = GetOsdView(viRootView, viDetailView);
+ detailView = new cDetailView(dV, e);
+ detailView->Draw();
+ detailView->DrawTime();
+ detailView->Flush();
+}
+
+void cTVGuideOSD::CloseDetailedView(void) {
+ delete detailView;
+ detailView = NULL;
+ epgGrid->Activate();
+ epgGrid->Flush();
+ if (recMenuView->Active()) {
+ recMenuView->Activate();
+ recMenuView->Flush();
+ }
+}
+
+void cTVGuideOSD::KeyRed(void) {
+ const cEvent *e = epgGrid->GetCurrentEvent();
+ if (!e)
+ return;
+ epgGrid->Deactivate(false);
+ cOsdView *recView = GetOsdView(viRootView, viRecMenu);
+ cOsdView *recViewBuffer = GetOsdView(viRootView, viRecMenu);
+ cOsdView *recViewBuffer2 = GetOsdView(viRootView, viRecMenu);
+ recMenuView->Init(recView, recViewBuffer, recViewBuffer2);
+ recMenuView->DisplayRecMenu(e);
+ recMenuView->Flush();
+}
+
+void cTVGuideOSD::Favorites(void) {
+ epgGrid->Deactivate(false);
+ cOsdView *recView = GetOsdView(viRootView, viRecMenu);
+ cOsdView *recViewBuffer = GetOsdView(viRootView, viRecMenu);
+ cOsdView *recViewBuffer2 = GetOsdView(viRootView, viRecMenu);
+ recMenuView->Init(recView, recViewBuffer, recViewBuffer2);
+ recMenuView->DisplayFavorites();
+ recMenuView->Flush();
+}
diff --git a/tvguidengosd.h b/tvguidengosd.h
new file mode 100644
index 0000000..9fc8a00
--- /dev/null
+++ b/tvguidengosd.h
@@ -0,0 +1,93 @@
+#ifndef __TVGUIDENGOSD_H
+#define __TVGUIDENGOSD_H
+
+#include <vdr/osdbase.h>
+#include <vdr/plugin.h>
+#include "libskindesigner/skindesignerosdbase.h"
+#include "libskindesigner/osdelements.h"
+#include "config.h"
+#include "timemanager.h"
+#include "epggrid.h"
+#include "channeljump.h"
+#include "detailview.h"
+#include "recmenuview.h"
+
+enum eViews {
+ viRootView,
+ viDetailView,
+ viRecMenu
+};
+
+enum eViewElementsRoot {
+ verBackgroundHor,
+ verBackgroundVer,
+ verHeader,
+ verFooter,
+ verTime,
+ verDateTimelineHor,
+ verDateTimelineVer,
+ verTimeIndicatorHor,
+ verTimeIndicatorVer,
+ verChannelJump
+};
+
+enum eViewGrids {
+ vgChannelsHor,
+ vgChannelsVer,
+ vgSchedulesHor,
+ vgSchedulesVer,
+ vgChannelGroupsHor,
+ vgChannelGroupsVer,
+ vgTimelineHor,
+ vgTimelineVer,
+ vgRecordingMenu
+};
+
+enum eViewElementsDetail {
+ vedBackground,
+ vedHeader,
+ vedFooter,
+ vedTime
+};
+
+enum eViewElementsRecMenu {
+ vemBackground,
+ vemScrollbar
+};
+
+class cTVGuideOSD : public cSkindesignerOsdObject {
+private:
+ cTimeManager *timeManager;
+ cEpgGrid *epgGrid;
+ cChannelJump *channelJumper;
+ cDetailView *detailView;
+ cRecMenuView *recMenuView;
+ void KeyLeft(void);
+ void KeyRight(void);
+ void KeyUp(void);
+ void KeyDown(void);
+ void TimeForward(void);
+ void TimeBack(void);
+ void ChannelsForward(void);
+ void ChannelsBack(void);
+ void NumericKey(int key);
+ void NumericKeyTimeJump(int key);
+ void NumericKeyChannelJump(int key);
+ void CheckTimeout(void);
+ void KeyGreen(void);
+ void KeyYellow(void);
+ eOSState KeyBlue(const cEvent *e);
+ eOSState KeyOk(const cEvent *e);
+ void KeyRed(void);
+ void DetailView(const cEvent *e);
+ void CloseDetailedView(void);
+ eOSState ChannelSwitch(void);
+ void Favorites(void);
+public:
+ cTVGuideOSD(void);
+ virtual ~cTVGuideOSD(void);
+ virtual void Show(void);
+ virtual eOSState ProcessKey(eKeys Key);
+};
+
+#endif //__TVGUIDENGOSD_H