From 3cc8e78e1bf00e16b49520ac416b74b5fd73c906 Mon Sep 17 00:00:00 2001 From: louis Date: Thu, 12 Mar 2015 17:22:16 +0100 Subject: Version 0.0.1 --- .gitignore | 8 + COPYING | 340 +++++++ HISTORY | 6 + Makefile | 146 +++ README | 182 ++++ channelepg.c | 444 ++++++++++ channelepg.h | 61 ++ channelgroups.c | 196 ++++ channelgroups.h | 54 ++ channeljump.c | 47 + channeljump.h | 25 + config.c | 87 ++ config.h | 90 ++ detailview.c | 621 +++++++++++++ detailview.h | 37 + dummyelement.c | 26 + dummyelement.h | 24 + epgelement.c | 52 ++ epgelement.h | 28 + epggrid.c | 774 ++++++++++++++++ epggrid.h | 73 ++ gridelement.c | 56 ++ gridelement.h | 51 ++ helpers.c | 514 +++++++++++ helpers.h | 87 ++ libskindesigner/osdelements.c | 203 +++++ libskindesigner/osdelements.h | 91 ++ libskindesigner/services.h | 131 +++ libskindesigner/skindesignerosdbase.c | 209 +++++ libskindesigner/skindesignerosdbase.h | 84 ++ po/de_DE.po | 604 +++++++++++++ recmanager.c | 710 +++++++++++++++ recmanager.h | 64 ++ recmenu.c | 429 +++++++++ recmenu.h | 59 ++ recmenuitem.c | 1573 +++++++++++++++++++++++++++++++++ recmenuitem.h | 495 +++++++++++ recmenus.c | 1143 ++++++++++++++++++++++++ recmenus.h | 451 ++++++++++ recmenuview.c | 846 ++++++++++++++++++ recmenuview.h | 44 + searchtimer.c | 570 ++++++++++++ searchtimer.h | 136 +++ services/epgsearch.h | 202 +++++ services/remotetimers.h | 33 + services/scraper2vdr.h | 194 ++++ setup.c | 158 ++++ setup.h | 30 + switchtimer.c | 113 +++ switchtimer.h | 31 + templates/plug-tvguideng-detail.xml | 237 +++++ templates/plug-tvguideng-recmenu.xml | 186 ++++ templates/plug-tvguideng-root.xml | 156 ++++ timeline.c | 153 ++++ timeline.h | 50 ++ timemanager.c | 172 ++++ timemanager.h | 61 ++ timerconflict.c | 177 ++++ timerconflict.h | 38 + tvguideng.c | 156 ++++ tvguidengosd.c | 475 ++++++++++ tvguidengosd.h | 93 ++ 62 files changed, 14586 insertions(+) create mode 100644 .gitignore create mode 100644 COPYING create mode 100644 HISTORY create mode 100644 Makefile create mode 100644 README create mode 100644 channelepg.c create mode 100644 channelepg.h create mode 100644 channelgroups.c create mode 100644 channelgroups.h create mode 100644 channeljump.c create mode 100644 channeljump.h create mode 100644 config.c create mode 100644 config.h create mode 100644 detailview.c create mode 100644 detailview.h create mode 100644 dummyelement.c create mode 100644 dummyelement.h create mode 100644 epgelement.c create mode 100644 epgelement.h create mode 100644 epggrid.c create mode 100644 epggrid.h create mode 100644 gridelement.c create mode 100644 gridelement.h create mode 100644 helpers.c create mode 100644 helpers.h create mode 100644 libskindesigner/osdelements.c create mode 100644 libskindesigner/osdelements.h create mode 100644 libskindesigner/services.h create mode 100644 libskindesigner/skindesignerosdbase.c create mode 100644 libskindesigner/skindesignerosdbase.h create mode 100644 po/de_DE.po create mode 100644 recmanager.c create mode 100644 recmanager.h create mode 100644 recmenu.c create mode 100644 recmenu.h create mode 100644 recmenuitem.c create mode 100644 recmenuitem.h create mode 100644 recmenus.c create mode 100644 recmenus.h create mode 100644 recmenuview.c create mode 100644 recmenuview.h create mode 100644 searchtimer.c create mode 100644 searchtimer.h create mode 100644 services/epgsearch.h create mode 100644 services/remotetimers.h create mode 100644 services/scraper2vdr.h create mode 100644 setup.c create mode 100644 setup.h create mode 100644 switchtimer.c create mode 100644 switchtimer.h create mode 100644 templates/plug-tvguideng-detail.xml create mode 100644 templates/plug-tvguideng-recmenu.xml create mode 100644 templates/plug-tvguideng-root.xml create mode 100644 timeline.c create mode 100644 timeline.h create mode 100644 timemanager.c create mode 100644 timemanager.h create mode 100644 timerconflict.c create mode 100644 timerconflict.h create mode 100644 tvguideng.c create mode 100644 tvguidengosd.c create mode 100644 tvguidengosd.h 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. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/HISTORY b/HISTORY new file mode 100644 index 0000000..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='' -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 + +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 /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 + /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::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 +#include +#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 grids; + set 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::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::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 +#include +#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 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 +#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 +#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 + +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::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::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* 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("reruns[title]", r->event->Title() ? r->event->Title() : "")); + rerun.insert(pair("reruns[shorttext]", r->event->ShortText() ? r->event->ShortText() : "")); + rerun.insert(pair("reruns[start]", *(r->event->GetTimeString()))); + rerun.insert(pair("reruns[start]", *(r->event->GetTimeString()))); + rerun.insert(pair("reruns[stop]", *(r->event->GetEndTimeString()))); + rerun.insert(pair("reruns[date]", *ShortDateString(r->event->StartTime()))); + rerun.insert(pair("reruns[day]", *WeekDayName(r->event->StartTime()))); + string channelID = *(r->event->ChannelID().ToString()); + rerun.insert(pair("reruns[channelid]", channelID)); + bool logoExists = tabs->ChannelLogoExists(channelID); + rerun.insert(pair("reruns[channellogoexists]", logoExists ? "1" : "0")); + + if (channel) { + stringstream channelNumber; + channelNumber << channel->Number(); + rerun.insert(pair("reruns[channelname]", channel->ShortName(true))); + rerun.insert(pair("reruns[channelnumber]", channelNumber.str())); + } else { + rerun.insert(pair("reruns[channelname]", "")); + rerun.insert(pair("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::iterator act = movie.actors.begin(); act != movie.actors.end(); act++) { + map< string, string > actor; + actor.insert(pair("actors[name]", (*act).name)); + actor.insert(pair("actors[role]", (*act).role)); + actor.insert(pair("actors[thumb]", (*act).actorThumb.path)); + stringstream actWidth, actHeight; + actWidth << (*act).actorThumb.width; + actHeight << (*act).actorThumb.height; + actor.insert(pair("actors[thumbwidth]", actWidth.str())); + actor.insert(pair("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::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::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::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::iterator act = series.actors.begin(); act != series.actors.end(); act++) { + map< string, string > actor; + actor.insert(pair("actors[name]", (*act).name)); + actor.insert(pair("actors[role]", (*act).role)); + actor.insert(pair("actors[thumb]", (*act).actorThumb.path)); + stringstream actWidth, actHeight; + actWidth << (*act).actorThumb.width; + actHeight << (*act).actorThumb.height; + actor.insert(pair("actors[thumbwidth]", actWidth.str())); + actor.insert(pair("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 +#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::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 channels; + cGridElement *active; + set 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 +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 *folders, cList *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 epgSearchDirs = epgSearch->handler->DirectoryList(); + std::set::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 *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 *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& 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, ""); + char* endaux = strstr(descr, ""); + if (!beginaux || !endaux) { + free(descr); + return NULL; + } + + beginaux += 11; // strlen(""); + 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, "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 +#include +#include +#include +#include + +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 *folders, cList *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 flds; +public: + splitstring(const char *s) : std::string(s) { }; + std::vector& 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(key, value)); +} + +void cOsdElement::AddIntToken(string key, int value) { + intTokens.insert(pair(key, value)); +} + +void cOsdElement::AddLoopToken(string loopName, map &tokens) { + map > >::iterator hitLoop = loopTokens.find(loopName); + if (hitLoop == loopTokens.end()) { + vector > tokenVector; + tokenVector.push_back(tokens); + loopTokens.insert(pair > >(loopName, tokenVector)); + } else { + vector > *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 +#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 &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 +#include +#include + +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 *stringTokens, map *intTokens, map > > *loopTokens, int Index, bool Current, bool Selectable); + virtual bool SetPluginText(map *stringTokens, map *intTokens, map > > *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 *intTokens); + virtual void SetViewElementStringTokens(map *stringTokens); + virtual void SetViewElementLoopTokens(map > > *loopTokens); + virtual void InitGrids(int viewGridID); + virtual void SetGrid(int viewGridID, long gridID, double x, double y, double width, double height, map *intTokens, map *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 *intTokens); + virtual void SetTabStringTokens(map *stringTokens); + virtual void SetTabLoopTokens(map > > *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(key, templateName)); + } + void SetView(int key, string templateName) { + views.insert(pair(key, templateName)); + } + void SetSubView(int view, int subView, string templateName) { + pair sub = make_pair(subView, templateName); + subViews.insert(pair >(view, sub)); + } + void SetViewElement(int view, int viewElement, string name) { + map< int, map >::iterator hit = viewElements.find(view); + if (hit == viewElements.end()) { + map vE; + vE.insert(pair(viewElement, name)); + viewElements.insert(pair >(view, vE)); + } else { + (hit->second).insert(pair(viewElement, name)); + } + } + void SetViewGrid(int view, int viewGrid, string name) { + map< int, map >::iterator hit = viewGrids.find(view); + if (hit == viewGrids.end()) { + map vG; + vG.insert(pair(viewGrid, name)); + viewGrids.insert(pair >(view, vG)); + } else { + (hit->second).insert(pair(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 > subViews; //subviews of standalone views as view -> (subview, templatename) multimap + map< int, map > viewElements; //viewelements as key -> (viewelement, viewelementname) hashmap + map< int, map > 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(key, value)); +} + +void cSkindesignerOsdItem::AddIntToken(string key, int value) { + intTokens.insert(pair(key, value)); +} + +void cSkindesignerOsdItem::AddLoopToken(string loopName, map &tokens) { + map > >::iterator hitLoop = loopTokens.find(loopName); + if (hitLoop == loopTokens.end()) { + vector > tokenVector; + tokenVector.push_back(tokens); + loopTokens.insert(pair > >(loopName, tokenVector)); + } else { + vector > *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(key, value)); +} + +void cSkindesignerOsdMenu::AddIntToken(string key, int value) { + intTokens.insert(pair(key, value)); +} + +void cSkindesignerOsdMenu::AddLoopToken(string loopName, map &tokens) { + map > >::iterator hitLoop = loopTokens.find(loopName); + if (hitLoop == loopTokens.end()) { + vector > tokenVector; + tokenVector.push_back(tokens); + loopTokens.insert(pair > >(loopName, tokenVector)); + } else { + vector > *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(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 +#include +#include +#include +#include +#include +#include +#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 &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 &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: \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 +#include +#include +#include + +#include +#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 conflicts = epgSearch->handler->TimerConflictList(); + int numConflicts = conflicts.size(); + if (numConflicts == 0) + return conflictList; + for (std::list::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 *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 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::iterator it=results.begin(); it != results.end(); ++it) { + try { + splitstring s(it->c_str()); + std::vector 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 *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 *searchTimer) { + if (!epgSearchAvailable) { + return; + } + Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1; + if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) { + std::list searchTimerList; + searchTimerList = epgSearch->handler->SearchTimerList(); + for(std::list::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* 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 *favorites) { + if (!epgSearchAvailable) { + return; + } + Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1; + if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) { + std::list searchTimerList; + searchTimerList = epgSearch->handler->SearchTimerList(); + for(std::list::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 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 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 +#include +#include +#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 *epgTemplates); + void GetSearchTimers(std::vector *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 *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 +#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 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 +#include +#include + +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 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; dayAddStringToken(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 +#include +#include +#include +#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 strings; + int numValues; +public: + cRecMenuItemSelect(string text, + vector 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 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 +#include +#include +#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; iGetConflict(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::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(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 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= 0 && tmplActive < templates.size()) + templ = templates[tmplActive]; + return templ; +} + +// --- cRecMenuSearchTimers --------------------------------------------------------- +cRecMenuSearchTimers::cRecMenuSearchTimers(std::vector 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(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 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(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 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 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(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(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; iSetInactive(); + } +} + +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(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 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(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 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 templates; +public: + cRecMenuSearchTimerTemplates(cTVGuideSearchTimer searchTimer, vector templates); + virtual ~cRecMenuSearchTimerTemplates(void) {}; + cTVGuideSearchTimer GetSearchTimer(void) { return searchTimer; }; + TVGuideEPGSearchTemplate GetTemplate(void); +}; + +// --- cRecMenuSearchTimers --------------------------------------------------------- +class cRecMenuSearchTimers: public cRecMenu { +private: + int numSearchTimers; + vector searchTimers; + void SetMenuItems(void); +public: + cRecMenuSearchTimers(vector 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 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 favorites; + int numFavorites; + void CreateFavoritesMenuItems(void); + string NiceTime(int favTime); +public: + cRecMenuFavorites(vector 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 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(activeMenu)) { + timeline->SetHeaderTimer(); + } + activeMenu->Draw(); +} + +void cRecMenuView::DisplaySearchTimerList(void) { + delete activeMenu; + vector 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(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(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(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(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(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(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(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(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(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(activeMenu)) { + searchString = menu->GetSearchString(); + } else if (cRecMenuSearchTimerTemplatesCreate *menu = dynamic_cast(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 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(activeMenu)) { + searchTimer = menu->GetSearchTimer(); + } else if (cRecMenuSearchTimerEdit *menu = dynamic_cast(activeMenu)) { + searchTimer = menu->GetSearchTimer(); + advancedOptions = (nextState == rmsSearchTimerEditAdvanced)?true:false; + } else if (cRecMenuSearchTimerTemplates *menu = dynamic_cast(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(activeMenu)) { + searchTimer = menu->GetSearchTimer(); + recState = rmsSearchTimerRecord; + } else if (cRecMenuSearchTimers *menu = dynamic_cast(activeMenu)) { + searchTimer = menu->GetSearchTimer(); + recState = rmsSearchTimerRecord; + } else if (cRecMenuSearchTimerTemplatesCreate *menu = dynamic_cast(activeMenu)) { + searchTimer = menu->GetSearchTimer(); + TVGuideEPGSearchTemplate tmpl = menu->GetTemplate(); + searchTimer.SetTemplate(tmpl.templValue); + searchTimer.Parse(true); + } else if (cRecMenuFavorites *menu = dynamic_cast(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(activeMenu)) { + searchTimer = menu->GetSearchTimer(); + } else if (cRecMenuSearchTimerTemplatesCreate *menu = dynamic_cast(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(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(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(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(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(activeMenu)) { + withOptions = true; + searchString = menu->GetSearchString(); + } else if (cRecMenuSearchResults *menu = dynamic_cast(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(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(activeMenu)) { + displayEvent = menu->GetEvent(); + } else if (cRecMenuSearchTimerResults *menu = dynamic_cast(activeMenu)) { + displayEvent = menu->GetEvent(); + } else if (cRecMenuRerunResults *menu = dynamic_cast(activeMenu)) { + displayEvent = menu->GetEvent(); + } else break; + if (displayEvent) { + state = osUser1; + } + break;} + case rmsSearchRecord: { + //caller: cRecMenuSearchResults + const cEvent *ev = NULL; + if (cRecMenuSearchResults *menu = dynamic_cast(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(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(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(activeMenu)) { + replace = menu->GetEvent(); + } else break; + if (cRecMenuTimerConflict *menu = dynamic_cast(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(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(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(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(activeMenu)) { + searchString = menu->GetSearchString(); + }; + delete activeMenu; + activeMenu = new cRecMenuRecordingSearch(searchString); + DisplayMenu(); + break; } + case rmsRecordingSearchResult: { + //caller: cRecMenuRecordingSearch + string searchString; + if (cRecMenuRecordingSearch *menu = dynamic_cast(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(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 +#include +#include +#include +#include +#include +#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 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 *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 *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 +#include +#include +#include +#include + +// 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* 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 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 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 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 ExtEPGInfoList() = 0; + // returns a list of extended EPG categories in the same format as used in epgsearchcats.conf + virtual std::list ChanGrpList() = 0; + // returns a list of channel groups maintained by epgsearch + virtual std::list BlackList() = 0; + // returns a list of blacklists in the same format as used in epgsearchblacklists.conf + virtual std::set 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 handler; +}; + +// Data structures for service "Epgsearch-services-v1.1" +class cServiceHandler_v1_1 : public cServiceHandler +{ + public: + // Get timer conflicts + virtual std::list 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 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 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 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 +#include + +// 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 +#include + +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 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 actors; + std::vector posters; + std::vector banners; + std::vector 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 +#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 + +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, 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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; iDelete(e->Id()); + grids.Del(e); + } + time_t newStartTime = timeManager->GetEnd() - stepMinutes * 60; + for (int i=0; iGetStart(); + time_t endTime = timeManager->GetEnd(); + int numSteps = stepMinutes / 30; + for (int i=0; iDelete(e->Id()); + grids.Del(e); + } + time_t newStartTime = timeManager->GetStart() + (numSteps-1) * 30 * 60; + for (int i=0; iIsNew()) { + 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 +#include +#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 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 +#include +#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(¤t); + if (st->tm_hour < 21) { + current -= 24 * 60* 60; + st = localtime(¤t); + } + st->tm_hour = 20; + st->tm_min = 0; + time_t primeTime = mktime(st); + return primeTime; +} + +time_t cTimeManager::GetNextPrimetime(time_t current){ + tm *st = localtime(¤t); + if (st->tm_hour > 19) { + current += 24 * 60* 60; + st = localtime(¤t); + } + st->tm_hour = 20; + st->tm_min = 0; + time_t primeTime = mktime(st); + return primeTime; +} + +bool 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 + +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 +#include +#include +#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::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 flds = s.split(':'); + if (flds.size() < 2) + return; + conflict->time = atoi(flds[0].c_str()); + splitstring s2(flds[1].c_str()); + std::vector 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 flds3 = s3.split('#'); + std::vector 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; itimerInvolved(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 cTVGuideTimerConflicts::GetConflictsBetween(time_t start, time_t stop) { + std::vector 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 timerIDs; + bool timerInvolved(int involvedID); +}; + +class cTVGuideTimerConflicts { +private: + std::vector 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 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 +#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", ®); + } 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 +#include +#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 -- cgit v1.2.3