diff options
author | louis <louis.braun@gmx.de> | 2015-03-12 17:22:16 +0100 |
---|---|---|
committer | louis <louis.braun@gmx.de> | 2015-03-12 17:22:16 +0100 |
commit | 3cc8e78e1bf00e16b49520ac416b74b5fd73c906 (patch) | |
tree | 4425c4eb4b9833d927aae2f65d0f3a99d3792f83 | |
download | vdr-plugin-tvguideng-3cc8e78e1bf00e16b49520ac416b74b5fd73c906.tar.gz vdr-plugin-tvguideng-3cc8e78e1bf00e16b49520ac416b74b5fd73c906.tar.bz2 |
Version 0.0.10.0.1
62 files changed, 14586 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..19526e0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.dependencies +*.o +*.so +*.orig +*.rej +*.mo +*.pot +*.tgz @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. @@ -0,0 +1,6 @@ +VDR Plugin 'tvguideng' Revision History +--------------------------------------- + +2015-01-26: Version 0.0.1 + +- Initial revision. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..128e041 --- /dev/null +++ b/Makefile @@ -0,0 +1,146 @@ +# +# Makefile for a Video Disk Recorder plugin +# +# $Id$ + +# The official name of this plugin. +# This name will be used in the '-P...' option of VDR to load the plugin. +# By default the main source file also carries this name. + +PLUGIN = tvguideng + +### The version number of this plugin (taken from the main source file): + +VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g') + +### The directory environment: + +# Use package data if installed...otherwise assume we're under the VDR source directory: +PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc)) +LIBDIR = $(call PKGCFG,libdir) +LOCDIR = $(call PKGCFG,locdir) +PLGCFG = $(call PKGCFG,plgcfg) +# +TMPDIR ?= /tmp + +### The compiler options: + +export CFLAGS = $(call PKGCFG,cflags) +export CXXFLAGS = $(call PKGCFG,cxxflags) + +### The version number of VDR's plugin API: + +APIVERSION = $(call PKGCFG,apiversion) + +### Allow user defined options to overwrite defaults: + +-include $(PLGCFG) + +### The name of the distribution archive: + +ARCHIVE = $(PLUGIN)-$(VERSION) +PACKAGE = vdr-$(ARCHIVE) + +### The name of the shared object file: + +SOFILE = libvdr-$(PLUGIN).so + +### Includes and Defines (add further entries here): + +INCLUDES += + +DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' + +### The object files (add further files here): + +OBJS = $(PLUGIN).o \ + libskindesigner/skindesignerosdbase.o \ + libskindesigner/osdelements.o \ + helpers.o \ + config.o \ + setup.o \ + timemanager.o \ + recmanager.o \ + searchtimer.o \ + switchtimer.o \ + timerconflict.o \ + gridelement.o \ + dummyelement.o \ + epgelement.o \ + timeline.o \ + channeljump.o \ + channelgroups.o \ + channelepg.o \ + epggrid.o \ + detailview.o \ + recmenuview.o \ + recmenu.o \ + recmenuitem.o \ + recmenus.o \ + tvguidengosd.o + +### The main target: + +all: $(SOFILE) i18n + +### Implicit rules: + +%.o: %.c + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $< + +### Dependencies: + +MAKEDEP = $(CXX) -MM -MG +DEPFILE = .dependencies +$(DEPFILE): Makefile + @$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ + +-include $(DEPFILE) + +### Internationalization (I18N): + +PODIR = po +I18Npo = $(wildcard $(PODIR)/*.po) +I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file)))) +I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file)))))) +I18Npot = $(PODIR)/$(PLUGIN).pot + +%.mo: %.po + msgfmt -c -o $@ $< + +$(I18Npot): $(wildcard *.c) + xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='<see README>' -o $@ `ls $^` + +%.po: $(I18Npot) + msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $< + @touch $@ + +$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo + install -D -m644 $< $@ + +.PHONY: i18n +i18n: $(I18Nmo) $(I18Npot) + +install-i18n: $(I18Nmsgs) + +### Targets: + +$(SOFILE): $(OBJS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) -o $@ + +install-lib: $(SOFILE) + install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION) + +install: install-lib install-i18n + +dist: $(I18Npo) clean + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @mkdir $(TMPDIR)/$(ARCHIVE) + @cp -a * $(TMPDIR)/$(ARCHIVE) + @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @echo Distribution package created as $(PACKAGE).tgz + +clean: + @-rm -f $(PODIR)/*.mo $(PODIR)/*.pot + @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~ @@ -0,0 +1,182 @@ +This is a "plugin" for the Video Disk Recorder (VDR). + +Written by: Louis Braun <louis DOT braun AT gmx DOT de> + +Project's homepage: http://projects.vdr-developer.org/projects/plg-tvguideng + +Latest version available at: git://projects.vdr-developer.org/vdr-plugin-tvguideng.git + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +See the file COPYING for more information. + +Requirements +------------ + +- Clone Git Repository: git clone git://projects.vdr-developer.org/vdr-plugin-tvguideng.git +- VDR version >= 2.0.0 +- Skindesigner Plugin Version >= 0.3.0 + + IMPORTANT: this Plugin will NOT WORK WITHOUT installed skindesigner Plugin. The + used Skindesigner Skin has to provide Templates for TVGuideNG, otherwise the Plugin + will not work. + +- Installed epgsearch Plugin for extended search & recording features. +- Installed remotetimers Plugin for creating timers on a vdr server remotely from a + vdr client. + + +Description +----------- + +"TvGuideNG" is a highly customizable 2D EPG viewer plugin. The "Search & Recordings" +Menü provided by the red button allows to search in the EPG and manage timers, +search timers, series timers and switch timers in an convenient way. + +Installation +------------ + +Just install the Plugin as usual. The Plugin has no start parameters + +Usage +----- + +Remote Control Keys: + +Up/Down/Left/Right: Navigation in the EPG grid +Ok: Detailed EPG View of the selected grid + 2nd Ok closes the detailed view +Red: Search & Recording Menu +Green / Yellow: Jump channels back / forward +Blue: Depends on configured Mode: + Default: Favorites Menu, Switch to channel in + detailed EPG View + Optional: Switch to currently selected channel + Optional: Open detailed EPG view (OK switches + then to channel) + +Numeric Keys: If Setup Option "Functionality of numeric Keys" is configured +to "Jump to specific channel", the numeric keys are used for channel number +input. If "Timely Jump" is configured, the keys are used as follows: + +1 / 3: Big jump (default 3h) back / forward in time +4 / 6: huge jump (default 24h) back / forward in time +7 / 9: jump to previous / next prime time (8pm) + +Exit: closes plugin + +In Search & Recording menu the following options are available: + +- Instant Record: create a timer for the currently selected program. An + check for timer conflicts will be performed, if a timer conflict occurs, + the involved timers can be adapted to solve the conflict. Alternatively + reruns can be searched and used to solve timer conflicts. +- Delete / Edit Timer: if a timer already exists for the currently selected + broadcast, this timer can be deleted or edited. +- Timer Timeline: displays active timers per day +- Create Search Timer: create an search timer with epgsearch. The most + important options can be defined directly in the menu, expert options + are available via the "advanced options" button. +- Manage Search Timers: edit, delete and check results of search timers +- Create Series Timer: create a periodical timer for a dedicated time on + dedicated days. +- Create Switch Timer: create a switch timer for the currently selected + program. +- Search: search with epgsearch +- Check for Timer Conflicts: check for timer conflicts with epgsearch. +- Search in Recordings: check if a recording already exists for the currently + selected program. + +The search & recordings menu is intended to present a convenient interface +to different VDR core recording features and additional epgsearch features. +The main goal is to reach better usability for "non expert users". + +The navigation through the search & recording menu can be done easily with +up/down, left/right and the ok key. In scrolling lists, with left/right +(after the first/last option of the displayed list element is selected) you +jump to the next page of the list. Pressing up on the first / down on the +last list item jumpps to the end / start of the list. + +In the Plugin Setup Menu in Section "Recording Menus and Favorites" you can +choose three alternatives for the option "Folder for instant Recordings": +- "Always use root video folder": the recording will always be placed in the + video root folder +- "Select from folder list": before creating a timer, you'll get asked in which + directory the recording should be placed. If the epgsearch plugin is not + available, the entries from VDRs <VDRCONFIG>/folders.conf will be used. If + the epgsearch plugin is available, the more sophisticated epgsearch mechanism + will be used to identify the possible folders (folders from + <VDRCONFIG>/folders.conf and epgsearchfolders.conf and all already existing + folders in the video directory). +- "Use fixed folder": the configured folder will always be used. If this string + is a epgsearch variable ("%variable%"), epgsearch will be used to replace + the variable with a concrete folder. All values defined in the epgsearch config + file "epgsearchuservars.conf" can be used. These variables use the definitions + from "epgsearchcats.conf". For a detailed description see the epgsearch Manual. + With that and a with an EPG with detailed information about movies and series + it is possible to create sophisticated paths for timers automatically (for + instance with series season and episode in the filename). + +For a search timer also all folders and the defined dedicated folder can be used. + +Favorites Menu: if configured, the blue key provides a favorite menu. In this +menu the current scheduling ("What's on now") and the next upcomming Shows +("What's on next") can be listed. Via the Plugin Setup Menu up to four user +defined times can be configured with a dedicated description (for instance +"Primetime" for 8:15 pm). These user defined times are also shown as options in +the favorites menu. Finally all search timers with the option "use as favorite" +enabled are displayed in the favorites menu. By selecting this entry, all hits +for this search timer are shown. + +Setup Options +------------- + +* General Settings: + +- Show Main Menu Entry + Display "tvguide" in main menu +- Replace VDR Schedules Menu + If set to "yes", the original VDR schedules menu will be replaced by tvguide +- Display Mode + Choose between horizontal or vertical display of the channels. All further + width / height settings are dependend of this setting +- Number of Channels to display +- Time to display in hours: period of time to display in main EPG view +- Channel Jump Mode (Keys Green / Yellow) + select between jumping x channels back/forward or to the previous / next + channel group +- Keys Blue and OK + Blue: Favorites in normal view, channel switch in det. EPG View, Ok: det. EPG View + or + Blue: Channel Switch, Ok: Detailed EPG + or + Blue: Detailed EPG, Ok: Channel Switch +- Close TVGuide after channel switch: If set to "no", tvguide will not be closed + after a channel switch. +- Functionality of numeric Keys: see chapter Usage / Remote Control Keys +- Hide last Channel Group + If set to yes, the channels of the last channel group will not be displayed +- Big Step (Keys 1 / 3) in hours + Hours to jump vertically with keys 1 / 3 +- Huge Step (Keys 4 / 6) in hours + Hours to jump vertically with keys 4 / 6 + + +- Folder for instant Recordings: (see chapter "Usage" for detailed description) + - Always use root video folder + - Select from folder list + - Use fixed folder +- Use Remotetimers: Timers are handled not locally but on a VDR Server defined + with the remotetimers plugin. +- Use "What's on now" in favorites +- Use "What's on next" in favorites +- Use user defined time 1 (to 4) in favorites + - Description (1 - 4) + - Time (1 - 4) +- Limit channels in favorites: if set to yes, only the configured channel range + is used to display favorite results (except for search timer favorites) + - start channel + - stop channel +
\ No newline at end of file diff --git a/channelepg.c b/channelepg.c new file mode 100644 index 0000000..e93eb6a --- /dev/null +++ b/channelepg.c @@ -0,0 +1,444 @@ +#include "channelepg.h" + +cChannelEpg::cChannelEpg(int position, const cChannel *channel, cTimeManager *timeManager) { + init = true; + this->channel = channel; + this->position = position; + this->timeManager = timeManager; + channelsPerPage = (config.displayMode == eHorizontal) ? config.channelsPerPageHorizontal : config.channelsPerPageVertical; + SetTimer(); + SetSwitchTimer(); + schedulesLock = new cSchedulesLock(false, 100); +} + +cChannelEpg::~cChannelEpg(void) { + grids.Clear(); + delete schedulesLock; +} + +void cChannelEpg::ClearGrids(void) { + grids.Clear(); +} + +bool cChannelEpg::ReadGrids(void) { + schedules = cSchedules::Schedules(*schedulesLock); + const cSchedule *Schedule = NULL; + Schedule = schedules->GetSchedule(channel); + if (!Schedule) { + AddDummyGrid(timeManager->GetStart(), timeManager->GetEnd()); + return true; + } + bool eventFound = false; + bool dummyAtStart = false; + const cEvent *startEvent = Schedule->GetEventAround(timeManager->GetStart()); + if (startEvent != NULL) { + eventFound = true; + } else { + for (int i=1; i<6; i++) { + startEvent = Schedule->GetEventAround(timeManager->GetStart()+i*5*60); + if (startEvent) { + eventFound = true; + dummyAtStart = true; + break; + } + } + } + if (eventFound) { + if (dummyAtStart) { + AddDummyGrid(timeManager->GetStart(), startEvent->StartTime()); + } + bool dummyNeeded = true; + bool toFarInFuture = false; + time_t endLast = timeManager->GetStart(); + const cEvent *event = startEvent; + const cEvent *eventLast = NULL; + for (; event; event = Schedule->Events()->Next(event)) { + if (endLast < event->StartTime()) { + if (dummyAtStart) { + dummyAtStart = false; + } else { + //gap, dummy needed + time_t endTime = event->StartTime(); + if (endTime > timeManager->GetEnd()) { + endTime = timeManager->GetEnd(); + toFarInFuture = true; + } + AddDummyGrid(endLast, endTime); + } + } + if (toFarInFuture) { + break; + } + if (event->StartTime() >= timeManager->GetEnd()) { + dummyNeeded = false; + break; + } + AddEpgGrid(event); + endLast = event->EndTime(); + if (event->EndTime() > timeManager->GetEnd()) { + dummyNeeded = false; + break; + } + eventLast = event; + } + if (dummyNeeded) { + AddDummyGrid(eventLast->EndTime(), timeManager->GetEnd()); + } + return true; + } else { + AddDummyGrid(timeManager->GetStart(), timeManager->GetEnd()); + return true; + } + return false; +} + +cGridElement *cChannelEpg::GetActive(void) { + cTimeManager t; + t.Now(); + for (cGridElement *grid = grids.First(); grid; grid = grids.Next(grid)) { + if (grid->Match(t.Get())) + return grid; + } + return grids.First(); +} + +cGridElement *cChannelEpg::GetNext(cGridElement *activeGrid) { + if (activeGrid == NULL) + return NULL; + cGridElement *next = grids.Next(activeGrid); + if (next) + return next; + return NULL; +} + +cGridElement *cChannelEpg::GetPrev(cGridElement *activeGrid) { + if (activeGrid == NULL) + return NULL; + cGridElement *prev = grids.Prev(activeGrid); + if (prev) + return prev; + return NULL; +} + +cGridElement *cChannelEpg::GetNeighbor(cGridElement *activeGrid) { + if (!activeGrid) + return NULL; + cGridElement *neighbor = NULL; + int overlap = 0; + int overlapNew = 0; + cGridElement *grid = NULL; + grid = grids.First(); + if (grid) { + for (; grid; grid = grids.Next(grid)) { + if ( (grid->StartTime() == activeGrid->StartTime()) ) { + neighbor = grid; + break; + } + overlapNew = activeGrid->CalcOverlap(grid); + if (overlapNew > overlap) { + neighbor = grid; + overlap = overlapNew; + } + } + } + if (!neighbor) + neighbor = grids.First(); + return neighbor; +} + +bool cChannelEpg::IsFirst(cGridElement *grid) { + if (grid == grids.First()) + return true; + return false; +} + +void cChannelEpg::AddNewGridsAtStart(void) { + cGridElement *firstGrid = NULL; + firstGrid = grids.First(); + if (firstGrid == NULL) + return; + //if first event is long enough, nothing to do. + if (firstGrid->StartTime() <= timeManager->GetStart()) { + return; + } + //if not, i have to add new ones to the list + schedules = cSchedules::Schedules(*schedulesLock); + const cSchedule *Schedule = NULL; + Schedule = schedules->GetSchedule(channel); + if (!Schedule) { + if (firstGrid->IsDummy()) { + firstGrid->SetStartTime(timeManager->GetStart()); + firstGrid->SetEndTime(timeManager->GetEnd()); + } + return; + } + bool dummyNeeded = true; + for (const cEvent *event = Schedule->GetEventAround(firstGrid->StartTime()-60); event; event = Schedule->Events()->Prev(event)) { + if (!event) + break; + if (event->EndTime() < timeManager->GetStart()) { + break; + } + cGridElement *grid = AddEpgGrid(event, firstGrid); + firstGrid = grid; + if (event->StartTime() <= timeManager->GetStart()) { + dummyNeeded = false; + break; + } + } + if (dummyNeeded) { + firstGrid = grids.First(); + if (firstGrid->IsDummy()) { + firstGrid->SetStartTime(timeManager->GetStart()); + if (firstGrid->EndTime() >= timeManager->GetEnd()) + firstGrid->SetEndTime(timeManager->GetEnd()); + } else { + cGridElement *grid = AddDummyGrid(timeManager->GetStart(), firstGrid->StartTime(), firstGrid); + } + } +} + +void cChannelEpg::AddNewGridsAtEnd(void) { + cGridElement *lastGrid = NULL; + lastGrid = grids.Last(); + if (lastGrid == NULL) + return; + //if last event is long enough, nothing to do. + if (lastGrid->EndTime() >= timeManager->GetEnd()) { + return; + } + //if not, i have to add new ones to the list + schedules = cSchedules::Schedules(*schedulesLock); + const cSchedule *Schedule = NULL; + Schedule = schedules->GetSchedule(channel); + if (!Schedule) { + if (lastGrid->IsDummy()) { + lastGrid->SetStartTime(timeManager->GetStart()); + lastGrid->SetEndTime(timeManager->GetEnd()); + } + return; + } + bool dummyNeeded = true; + for (const cEvent *event = Schedule->GetEventAround(lastGrid->EndTime()+60); event; event = Schedule->Events()->Next(event)) { + if (!event) + break; + if (event->StartTime() > timeManager->GetEnd()) { + break; + } + cGridElement *grid = AddEpgGrid(event); + if (event->EndTime() > timeManager->GetEnd()) { + dummyNeeded = false; + break; + } + } + if (dummyNeeded) { + lastGrid = grids.Last(); + if (lastGrid->IsDummy()) { + lastGrid->SetEndTime(timeManager->GetEnd()); + if (lastGrid->StartTime() <= timeManager->GetStart()) + lastGrid->SetStartTime(timeManager->GetStart()); + } else { + cGridElement *grid = AddDummyGrid(lastGrid->EndTime(), timeManager->GetEnd()); + } + } +} + +void cChannelEpg::ClearOutdatedStart(void) { + deletedElements.clear(); + cGridElement *firstGrid = NULL; + while (true) { + firstGrid = grids.First(); + if (!firstGrid) + break; + if (firstGrid->EndTime() <= timeManager->GetStart()) { + deletedElements.insert(firstGrid->Id()); + grids.Del(firstGrid); + firstGrid = NULL; + } else { + if (firstGrid->IsDummy()) { + firstGrid->SetStartTime(timeManager->GetStart()); + cGridElement *next = GetNext(firstGrid); + if (next) { + firstGrid->SetEndTime(next->StartTime()); + } else { + firstGrid->SetEndTime(timeManager->GetEnd()); + } + } + break; + } + } +} + +void cChannelEpg::ClearOutdatedEnd(void) { + deletedElements.clear(); + cGridElement *lastGrid = NULL; + while (true) { + lastGrid = grids.Last(); + if (!lastGrid) + break; + if (lastGrid->StartTime() >= timeManager->GetEnd()) { + deletedElements.insert(lastGrid->Id()); + grids.Del(lastGrid); + lastGrid = NULL; + } else { + if (lastGrid->IsDummy()) { + lastGrid->SetEndTime(timeManager->GetEnd()); + cGridElement *prev = GetPrev(lastGrid); + if (prev) { + lastGrid->SetStartTime(prev->EndTime()); + } else { + lastGrid->SetStartTime(timeManager->GetStart()); + } + } + break; + } + } +} + +void cChannelEpg::SetTimers(void) { + SetTimer(); + SetSwitchTimer(); + for (cGridElement *grid = grids.First(); grid; grid = grids.Next(grid)) { + bool gridHadTimer = grid->HasTimer(); + grid->SetTimer(); + if (gridHadTimer != grid->HasTimer()) { + grid->Dirty(); + } + bool gridHadSwitchTimer = grid->HasSwitchTimer(); + grid->SetSwitchTimer(); + if (gridHadSwitchTimer != grid->HasSwitchTimer()) + grid->Dirty(); + } +} + +void cChannelEpg::DrawHeader(cViewGrid *channelsGrid) { + + double x, y, width, height; + + if (config.displayMode == eHorizontal) { + x = 0.0; + width = 1.0; + height = (double)1 / (double)channelsPerPage; + y = height * (double)position; + } else { + y = 0.0; + height = 1.0; + width = (double)1 / (double)channelsPerPage; + x = width * (double)position; + } + + int id = channel->Number(); + if (init) { + channelsGrid->ClearTokens(); + channelsGrid->AddIntToken("number", id); + channelsGrid->AddStringToken("name", channel->Name()); + string channelId = *(channel->GetChannelID().ToString()); + bool channelLogoExisis = channelsGrid->ChannelLogoExists(channelId); + channelsGrid->AddStringToken("channelid", channelId); + channelsGrid->AddIntToken("channellogoexists", channelLogoExisis); + channelsGrid->SetGrid(id, x, y, width, height); + init = false; + } else { + channelsGrid->MoveGrid(id, x, y, width, height); + } +} + +void cChannelEpg::DrawGrids(cViewGrid *epgGrid) { + int displaySeconds = timeManager->GetDisplaySeconds(); + + double x, y, width, height; + if (config.displayMode == eHorizontal) { + x = 0.0; + width = 0.0; + height = (double)1 / (double)channelsPerPage; + y = height * (double)position; + } else { + y = 0.0; + height = 0.0; + width = (double)1 / (double)channelsPerPage; + x = width * (double)position; + } + time_t startTime = timeManager->GetStart(); + time_t stopTime = timeManager->GetEnd(); + + for (cGridElement *grid = grids.First(); grid; grid = grids.Next(grid)) { + time_t gridStart = grid->StartTime(); + time_t gridStop = grid->EndTime(); + if (grid->IsNew()) { + epgGrid->ClearTokens(); + epgGrid->AddIntToken("color", grid->Id() % 2); + epgGrid->AddIntToken("dummy", grid->IsDummy()); + epgGrid->AddIntToken("timer", grid->HasTimer()); + epgGrid->AddIntToken("switchtimer", grid->HasSwitchTimer()); + epgGrid->AddStringToken("title", grid->Title()); + epgGrid->AddStringToken("shorttext", grid->ShortText()); + epgGrid->AddStringToken("start", *TimeString(gridStart)); + epgGrid->AddStringToken("stop", *TimeString(gridStop)); + } + + if (gridStart < startTime) { + gridStart = startTime; + } + if (gridStop > stopTime) { + gridStop = stopTime; + } + if (config.displayMode == eHorizontal) { + int xGrid = gridStart - startTime; + x = (double)xGrid / (double)displaySeconds; + int duration = gridStop - gridStart; + width = (double)duration / (double)displaySeconds; + } else { + int yGrid = gridStart - startTime; + y = (double)yGrid / (double)displaySeconds; + int duration = gridStop - gridStart; + height = (double)duration / (double)displaySeconds; + } + + if (grid->IsNew()) { + epgGrid->SetGrid(grid->Id(), x, y, width, height); + grid->InitFinished(); + } else { + epgGrid->MoveGrid(grid->Id(), x, y, width, height); + } + if (grid->Active()) + epgGrid->SetCurrent(grid->Id(), true); + + } +} + +void cChannelEpg::DeleteOutdated(cViewGrid *epgGrid) { + for (set<long>::iterator it = deletedElements.begin(); it != deletedElements.end(); it++) { + epgGrid->Delete(*it); + } +} + +void cChannelEpg::DeleteGridViews(cViewGrid *epgGrid) { + for (cGridElement *ge = grids.First(); ge; ge = grids.Next(ge)) { + epgGrid->Delete(ge->Id()); + } +} + +cGridElement *cChannelEpg::AddEpgGrid(const cEvent *event, cGridElement *after) { + cGridElement *grid = new cEpgElement(event, this); + if (!after) + grids.Add(grid); + else + grids.Ins(grid, after); + return grid; +} + +cGridElement *cChannelEpg::AddDummyGrid(time_t start, time_t end, cGridElement *after) { + cGridElement *dummy = new cDummyElement(start, end, this); + if (!after) + grids.Add(dummy); + else + grids.Ins(dummy, after); + return dummy; +} + +void cChannelEpg::Debug(void) { + esyslog("tvguideng: channel %s", channel->Name()); + for (cGridElement *grid = grids.First(); grid; grid = grids.Next(grid)) { + grid->Debug(); + } +}
\ No newline at end of file diff --git a/channelepg.h b/channelepg.h new file mode 100644 index 0000000..2a575ce --- /dev/null +++ b/channelepg.h @@ -0,0 +1,61 @@ +#ifndef __TVGUIDE_CHANNELEPG_H +#define __TVGUIDE_CHANNELEPG_H + +#include <set> +#include <vdr/tools.h> +#include "libskindesigner/osdelements.h" +#include "config.h" +#include "gridelement.h" +#include "epgelement.h" +#include "dummyelement.h" +#include "timemanager.h" +#include "switchtimer.h" + +// --- cChannelEpg ------------------------------------------------------------- + +class cChannelEpg : public cListObject { +private: + bool init; + int channelsPerPage; + cTimeManager *timeManager; + int position; + const cChannel *channel; + cList<cGridElement> grids; + set<long> deletedElements; + cSchedulesLock *schedulesLock; + const cSchedules *schedules; + bool hasTimer; + bool hasSwitchTimer; + cGridElement *AddEpgGrid(const cEvent *event, cGridElement *after = NULL); + cGridElement *AddDummyGrid(time_t start, time_t end, cGridElement *after = NULL); +public: + cChannelEpg(int position, const cChannel *channel, cTimeManager *timeManager); + virtual ~cChannelEpg(void); + void SetPosition(int newPos) { position = newPos; }; + bool ReadGrids(void); + const cChannel *Channel(void) { return channel; }; + const char* Name(void) { return channel->Name(); }; + int GetChannelNumber(void) {return channel->Number();} + cGridElement * GetActive(void); + cGridElement * GetNext(cGridElement *activeGrid); + cGridElement * GetPrev(cGridElement *activeGrid); + cGridElement * GetNeighbor(cGridElement *activeGrid); + bool IsFirst(cGridElement *grid); + void AddNewGridsAtStart(void); + void AddNewGridsAtEnd(void); + void ClearOutdatedStart(void); + void ClearOutdatedEnd(void); + void SetTimers(void); + void SetTimer(void) { hasTimer = channel->HasTimer(); }; + bool HasTimer(void) { return hasTimer; }; + void SetSwitchTimer() {hasSwitchTimer = SwitchTimers.ChannelInSwitchList(channel);}; + bool HasSwitchTimer() { return hasSwitchTimer; }; + void ClearGrids(void); + void DrawHeader(cViewGrid *channelsGrid); + void DrawGrids(cViewGrid *epgGrid); + void DeleteOutdated(cViewGrid *epgGrid); + void DeleteGridViews(cViewGrid *epgGrid); + void Debug(void); +}; + +#endif //__TVGUIDE_CHANNELEPG_H diff --git a/channelgroups.c b/channelgroups.c new file mode 100644 index 0000000..4f68037 --- /dev/null +++ b/channelgroups.c @@ -0,0 +1,196 @@ +#include "config.h" +#include "channelgroups.h" + +// --- cChannelGroup ------------------------------------------------------------- + +cChannelGroup::cChannelGroup(string name, int id) { + this->id = id; + channelStart = 0; + channelStop = 0; + this->name = name; +} + +cChannelGroup::~cChannelGroup(void) { +} + +void cChannelGroup::Debug(void) { + dsyslog("tvguideng: id %d channel group %s, start %d, stop %d", id, name.c_str(), channelStart, channelStop); +} + +// --- cChannelgroups ------------------------------------------------------------- + +cChannelgroups::cChannelgroups(cViewGrid *channelgroupGrid) { + this->channelgroupGrid = channelgroupGrid; +} + +cChannelgroups::~cChannelgroups(void) { + Clear(); +} + +void cChannelgroups::Init(void) { + bool setStart = false; + int lastChannelNumber = 0; + int id = 0; + const cChannel *first = Channels.First(); + if (!first->GroupSep()) { + channelGroups.push_back(cChannelGroup(tr("Main Program"), id++)); + setStart = true; + } + for (const cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { + if (setStart && (channelGroups.size() > 0)) { + channelGroups[channelGroups.size()-1].SetChannelStart(channel->Number()); + setStart = false; + } + if (channel->GroupSep()) { + if (channelGroups.size() > 0) { + channelGroups[channelGroups.size()-1].SetChannelStop(lastChannelNumber); + } + channelGroups.push_back(cChannelGroup(channel->Name(), id++)); + setStart = true; + } else { + lastChannelNumber = channel->Number(); + } + } + if (channelGroups.size() > 0) { + channelGroups[channelGroups.size()-1].SetChannelStop(lastChannelNumber); + } + + for (vector<cChannelGroup>::iterator it = channelGroups.begin(); it != channelGroups.end(); it++) { + (*it).Debug(); + } +} + +void cChannelgroups::Clear(void) { + channelgroupGrid->Clear(); +} + +void cChannelgroups::Draw(const cChannel *start, const cChannel *stop) { + if (!start || !stop) + return; + + int group = GetGroup(start); + int groupLast = group; + int fields = 1; + double offset = 0.0; + + for (const cChannel *channel = Channels.Next(start); channel; channel = Channels.Next(channel)) { + if (channel->GroupSep()) + continue; + if (config.hideLastChannelGroup && IsInLastGroup(channel)) { + SetGroup(group, fields, offset); + break; + } + group = GetGroup(channel); + if (group != groupLast) { + offset = SetGroup(groupLast, fields, offset); + fields = 0; + } + fields++; + groupLast = group; + if (channel == stop) { + SetGroup(group, fields, offset); + break; + } + } + channelgroupGrid->Display(); +} + +double cChannelgroups::SetGroup(int groupId, int fields, double offset) { + int channelsPerPage; + double x, y, width, height; + if (config.displayMode == eHorizontal) { + channelsPerPage = config.channelsPerPageHorizontal; + x = 0.0; + y = offset; + width = 1.0; + height = (double)fields / (double)channelsPerPage; + offset += height; + } else { + channelsPerPage = config.channelsPerPageVertical; + x = offset; + y = 0.0; + width = (double)fields / (double)channelsPerPage; + height = 1.0; + offset += width; + } + string groupName = channelGroups[groupId].GetName(); + channelgroupGrid->ClearTokens(); + channelgroupGrid->AddIntToken("color", groupId % 2); + channelgroupGrid->AddStringToken("group", groupName); + channelgroupGrid->SetGrid(groupId, x, y, width, height); + + return offset; +} + +int cChannelgroups::GetGroup(const cChannel *channel) { + if (!channel) + return -1; + int channelNumber = channel->Number(); + for (vector<cChannelGroup>::iterator group = channelGroups.begin(); group != channelGroups.end(); group++) { + if ( (*group).StartChannel() <= channelNumber && (*group).StopChannel() >= channelNumber ) { + return (*group).GetId(); + } + } + return -1; +} + +string cChannelgroups::GetPrevGroupName(int group) { + if (group <= 0 || group > channelGroups.size()) + return ""; + return channelGroups[group-1].GetName(); +} + +string cChannelgroups::GetNextGroupName(int group) { + if (group < 0 || group >= channelGroups.size() - 1) + return ""; + return channelGroups[group+1].GetName(); +} + +int cChannelgroups::GetPrevGroupFirstChannel(int group) { + if (group <= 0 || group > channelGroups.size()) + return -1; + return channelGroups[group-1].StartChannel(); +} + +int cChannelgroups::GetNextGroupFirstChannel(int group) { + if (group < 0 || group >= channelGroups.size()) + return -1; + return channelGroups[group+1].StartChannel(); +} + +bool cChannelgroups::IsInFirstGroup(const cChannel *channel) { + if (channelGroups.size() == 0) + return false; + int channelNumber = channel->Number(); + if (channelNumber >= channelGroups[0].StartChannel() && channelNumber <= channelGroups[0].StopChannel()) + return true; + return false; +} + +bool cChannelgroups::IsInLastGroup(const cChannel *channel) { + size_t numGroups = channelGroups.size(); + if (numGroups == 0) + return false; + int channelNumber = channel->Number(); + if (channelNumber >= channelGroups[numGroups-1].StartChannel() && channelNumber <= channelGroups[numGroups-1].StopChannel()) { + return true; + } + return false; +} + +bool cChannelgroups::IsInSecondLastGroup(const cChannel *channel) { + size_t numGroups = channelGroups.size(); + if (numGroups < 2) + return false; + int channelNumber = channel->Number(); + if (channelNumber >= channelGroups[numGroups-2].StartChannel() && channelNumber <= channelGroups[numGroups-2].StopChannel()) + return true; + return false; +} + +int cChannelgroups::GetLastValidChannel(void) { + if (config.hideLastChannelGroup && channelGroups.size() > 1) { + return channelGroups[channelGroups.size()-2].StopChannel(); + } + return Channels.MaxNumber(); +} diff --git a/channelgroups.h b/channelgroups.h new file mode 100644 index 0000000..8742ab4 --- /dev/null +++ b/channelgroups.h @@ -0,0 +1,54 @@ +#ifndef __TVGUIDE_CHANNELGROUPS_H +#define __TVGUIDE_CHANNELGROUPS_H + +#include <set> +#include <vdr/tools.h> +#include "libskindesigner/osdelements.h" +#include "config.h" + +// --- cChannelGroup ------------------------------------------------------------- + +class cChannelGroup : public cListObject { +private: + int id; + int channelStart; + int channelStop; + string name; +public: + cChannelGroup(string name, int id); + virtual ~cChannelGroup(void); + int GetId(void) { return id; }; + void SetChannelStart(int start) { channelStart = start; }; + int StartChannel(void) { return channelStart; }; + void SetChannelStop(int stop) { channelStop = stop; }; + int StopChannel(void) { return channelStop; }; + string GetName(void) { return name; }; + void Debug(void); +}; + + +// --- cChannelgroups ------------------------------------------------------------- + +class cChannelgroups { +private: + cViewGrid *channelgroupGrid; + vector<cChannelGroup> channelGroups; + double SetGroup(int groupId, int fields, double offset); +public: + cChannelgroups(cViewGrid *channelgroupGrid); + virtual ~cChannelgroups(void); + void Init(void); + void Clear(void); + void Draw(const cChannel *start, const cChannel *stop); + int GetGroup(const cChannel *channel); + string GetPrevGroupName(int group); + string GetNextGroupName(int group); + int GetPrevGroupFirstChannel(int group); + int GetNextGroupFirstChannel(int group); + bool IsInFirstGroup(const cChannel *channel); + bool IsInLastGroup(const cChannel *channel); + bool IsInSecondLastGroup(const cChannel *channel); + int GetLastValidChannel(void); +}; + +#endif //__TVGUIDE_TIMELINE_H diff --git a/channeljump.c b/channeljump.c new file mode 100644 index 0000000..aeb5d40 --- /dev/null +++ b/channeljump.c @@ -0,0 +1,47 @@ +#include <vdr/channels.h>
+#include "channeljump.h"
+
+cChannelJump::cChannelJump(cViewElement *channelJump, int lastValidChannel) {
+ this->channelJump = channelJump;
+ channel = 0;
+ maxChannels = lastValidChannel;
+ startTime = 0;
+ timeout = Setup.ChannelEntryTimeout;
+}
+
+cChannelJump::~cChannelJump(void) {
+ channelJump->Clear();
+ delete channelJump;
+}
+
+void cChannelJump::Draw(void) {
+ channelJump->Clear();
+ channelJump->ClearTokens();
+ string channelString = *BuildChannelString();
+ channelJump->AddStringToken("channel", channelString);
+ channelJump->Display();
+}
+
+void cChannelJump::Set(int key) {
+ startTime = cTimeMs::Now();
+ if (channel == 0) {
+ channel = key;
+ return;
+ }
+ int newChannel = channel * 10 + key;
+ if (newChannel <= maxChannels)
+ channel = newChannel;
+}
+
+cString cChannelJump::BuildChannelString(void) {
+ if (channel*10 <= maxChannels)
+ return cString::sprintf("%d-", channel);
+ else
+ return cString::sprintf("%d", channel);
+}
+
+ bool cChannelJump::TimeOut(void) {
+ if ((cTimeMs::Now() - startTime) > timeout)
+ return true;
+ return false;
+ }
\ No newline at end of file diff --git a/channeljump.h b/channeljump.h new file mode 100644 index 0000000..54e05c2 --- /dev/null +++ b/channeljump.h @@ -0,0 +1,25 @@ +#ifndef __TVGUIDE_CHANNELJUMP_H
+#define __TVGUIDE_CHANNELJUMP_H
+
+#include "libskindesigner/osdelements.h"
+
+// --- cChannelJump -------------------------------------------------------------
+
+class cChannelJump {
+private:
+ cViewElement *channelJump;
+ int channel;
+ int maxChannels;
+ int startTime;
+ int timeout;
+ cString BuildChannelString(void);
+public:
+ cChannelJump(cViewElement *channelJump, int lastValidChannel);
+ virtual ~cChannelJump(void);
+ void Set(int key);
+ bool TimeOut(void);
+ void Draw(void);
+ int GetChannel(void) { return channel; };
+};
+
+#endif //__TVGUIDE_CHANNELJUMP_H
\ No newline at end of file diff --git a/config.c b/config.c new file mode 100644 index 0000000..10ab9e2 --- /dev/null +++ b/config.c @@ -0,0 +1,87 @@ +#include <vdr/plugin.h> +#include "config.h" + +cTVGuideConfig::cTVGuideConfig(void) { + showMainMenuEntry = 1; + replaceOriginalSchedule = 0; + displayMode = eHorizontal; + channelsPerPageHorizontal = 8; + channelsPerPageVertical = 6; + displayHours = 4; + timeStep = 60; + bigStepHours = 3; + hugeStepHours = 24; + hideLastChannelGroup = 0; + channelJumpMode = eGroupJump; + closeOnSwitch = 0; + numKeyMode = eChannelJump; + blueKeyMode = eBlueKeyFavorites; + //settings for rerun display + rerunAmount = 10; + rerunDistance = 2; + rerunMaxChannel = 0; + useRemoteTimers = 1; + instRecFolderMode = eFolderRoot; + instRecFixedFolder = ""; + favWhatsOnNow = 1; + favWhatsOnNext = 1; + favUseTime1 = 0; + favUseTime2 = 0; + favUseTime3 = 0; + favUseTime4 = 0; + favTime1 = 0; + favTime2 = 0; + favTime3 = 0; + favTime4 = 0; + descUser1 = ""; + descUser2 = ""; + descUser3 = ""; + descUser4 = ""; + favLimitChannels = 0; + favStartChannel = 0; + favStopChannel = 0; + useSubtitleRerun = 1; +} + +bool cTVGuideConfig::SetupParse(const char *Name, const char *Value) { + + if (!strcasecmp(Name, "showMainMenuEntry")) showMainMenuEntry = atoi(Value); + else if (!strcasecmp(Name, "replaceOriginalSchedule")) replaceOriginalSchedule = atoi(Value); + else if (!strcasecmp(Name, "displayMode")) displayMode = atoi(Value); + else if (!strcasecmp(Name, "channelsPerPageHorizontal")) channelsPerPageHorizontal = atoi(Value); + else if (!strcasecmp(Name, "channelsPerPageVertical")) channelsPerPageVertical = atoi(Value); + else if (!strcasecmp(Name, "displayHours")) displayHours = atoi(Value); + else if (!strcasecmp(Name, "bigStepHours")) bigStepHours = atoi(Value); + else if (!strcasecmp(Name, "hugeStepHours")) hugeStepHours = atoi(Value); + else if (!strcasecmp(Name, "hideLastChannelGroup")) hideLastChannelGroup = atoi(Value); + else if (!strcasecmp(Name, "channelJumpMode")) channelJumpMode = atoi(Value); + else if (!strcasecmp(Name, "closeOnSwitch")) closeOnSwitch = atoi(Value); + else if (!strcasecmp(Name, "numKeyMode")) numKeyMode = atoi(Value); + else if (!strcasecmp(Name, "blueKeyMode")) blueKeyMode = atoi(Value); + else if (!strcasecmp(Name, "rerunAmount")) rerunAmount = atoi(Value); + else if (!strcasecmp(Name, "rerunDistance")) rerunDistance = atoi(Value); + else if (!strcasecmp(Name, "rerunMaxChannel")) rerunMaxChannel = atoi(Value); + else if (!strcasecmp(Name, "useRemoteTimers")) useRemoteTimers = atoi(Value); + else if (!strcasecmp(Name, "instRecFolderMode")) instRecFolderMode = atoi(Value); + else if (!strcasecmp(Name, "instRecFixedFolder")) instRecFixedFolder = atoi(Value); + else if (!strcasecmp(Name, "favWhatsOnNow")) favWhatsOnNow = atoi(Value); + else if (!strcasecmp(Name, "favWhatsOnNext")) favWhatsOnNext = atoi(Value); + else if (!strcasecmp(Name, "favUseTime1")) favUseTime1 = atoi(Value); + else if (!strcasecmp(Name, "favUseTime2")) favUseTime2 = atoi(Value); + else if (!strcasecmp(Name, "favUseTime3")) favUseTime3 = atoi(Value); + else if (!strcasecmp(Name, "favUseTime4")) favUseTime4 = atoi(Value); + else if (!strcasecmp(Name, "favTime1")) favTime1 = atoi(Value); + else if (!strcasecmp(Name, "favTime2")) favTime2 = atoi(Value); + else if (!strcasecmp(Name, "favTime3")) favTime3 = atoi(Value); + else if (!strcasecmp(Name, "favTime4")) favTime4 = atoi(Value); + else if (!strcasecmp(Name, "descUser1")) descUser1 = Value; + else if (!strcasecmp(Name, "descUser2")) descUser2 = Value; + else if (!strcasecmp(Name, "descUser3")) descUser3 = Value; + else if (!strcasecmp(Name, "descUser4")) descUser4 = Value; + else if (!strcasecmp(Name, "favLimitChannels")) favLimitChannels = atoi(Value); + else if (!strcasecmp(Name, "favStartChannel")) favStartChannel = atoi(Value); + else if (!strcasecmp(Name, "favStopChannel")) favStopChannel = atoi(Value); + else return false; + + return true; +}
\ No newline at end of file diff --git a/config.h b/config.h new file mode 100644 index 0000000..019ca65 --- /dev/null +++ b/config.h @@ -0,0 +1,90 @@ +#ifndef __TVGUIDE_CONFIG_H +#define __TVGUIDE_CONFIG_H + +#include "string" +#include <vdr/plugin.h> + +using namespace std; + +enum eDisplayMode { + eVertical, + eHorizontal +}; + +enum eChannelJumpMode { + eNumJump, + eGroupJump +}; + +enum eNumKeyMode { + eTimeJump, + eChannelJump +}; + +enum eBlueKeyMode { + eBlueKeySwitch = 0, + eBlueKeyEPG, + eBlueKeyFavorites +}; + +enum eInstRecFolderMode { + eFolderRoot = 0, + eFolderSelect, + eFolderFixed +}; + +class cTVGuideConfig { +private: +public: + cTVGuideConfig(void); + ~cTVGuideConfig() {}; + int showMainMenuEntry; + int replaceOriginalSchedule; + int displayMode; + int channelsPerPageHorizontal; + int channelsPerPageVertical; + int displayHours; + int timeStep; + int bigStepHours; + int hugeStepHours; + int hideLastChannelGroup; + int channelJumpMode; + int closeOnSwitch; + int numKeyMode; + int blueKeyMode; + int rerunAmount; + int rerunDistance; + int rerunMaxChannel; + int useRemoteTimers; + int instRecFolderMode; + string instRecFixedFolder; + int favWhatsOnNow; + int favWhatsOnNext; + int favUseTime1; + int favUseTime2; + int favUseTime3; + int favUseTime4; + int favTime1; + int favTime2; + int favTime3; + int favTime4; + string descUser1; + string descUser2; + string descUser3; + string descUser4; + int favLimitChannels; + int favStartChannel; + int favStopChannel; + int useSubtitleRerun; + bool SetupParse(const char *Name, const char *Value); +}; + +#ifdef DEFINE_CONFIG + cTVGuideConfig config; + cPlugin *pRemoteTimers = NULL; +#else + extern cTVGuideConfig config; + extern cPlugin *pRemoteTimers; +#endif + +#endif //__TVGUIDE_CONFIG_H
\ No newline at end of file diff --git a/detailview.c b/detailview.c new file mode 100644 index 0000000..72c4eb6 --- /dev/null +++ b/detailview.c @@ -0,0 +1,621 @@ +#include "helpers.h"
+#include "tvguidengosd.h"
+#include "detailview.h"
+#include "services/scraper2vdr.h"
+#include "services/epgsearch.h"
+
+cDetailView::cDetailView(cOsdView *detailView, const cEvent *event) {
+ init = true;
+ lastSecond = -1;
+ this->detailView = detailView;
+ this->event = event;
+ back = detailView->GetViewElement(vedBackground);
+ header = detailView->GetViewElement(vedHeader);
+ footer = detailView->GetViewElement(vedFooter);
+ watch = detailView->GetViewElement(vedTime);
+ tabs = detailView->GetViewTabs();
+}
+
+cDetailView::~cDetailView() {
+ delete back;
+ delete header;
+ delete footer;
+ delete watch;
+ delete detailView;
+}
+
+void cDetailView::Draw(void) {
+ if (!event) {
+ return;
+ }
+ if (init) {
+ DrawBackground();
+ DrawHeader();
+ DrawFooter();
+ Flush();
+ SetTabTokens();
+ tabs->Init();
+ init = false;
+ }
+ tabs->Display();
+}
+
+void cDetailView::Left(void) {
+ tabs->Left();
+ tabs->Display();
+}
+
+void cDetailView::Right(void) {
+ tabs->Right();
+ tabs->Display();
+}
+
+void cDetailView::Up(void) {
+ tabs->Up();
+ tabs->Display();
+}
+
+void cDetailView::Down(void) {
+ tabs->Down();
+ tabs->Display();
+}
+
+void cDetailView::DrawBackground(void) {
+ back->Display();
+}
+
+void cDetailView::DrawHeader(void) {
+ if (!event)
+ return;
+ header->ClearTokens();
+ static cPlugin *pScraper = GetScraperPlugin();
+ if (!pScraper) {
+ header->AddIntToken("ismovie", false);
+ header->AddIntToken("isseries", false);
+ header->AddIntToken("posteravailable", false);
+ header->AddIntToken("banneravailable", false);
+ } else {
+ ScraperGetEventType getType;
+ getType.event = event;
+ if (!pScraper->Service("GetEventType", &getType)) {
+ header->AddIntToken("ismovie", false);
+ header->AddIntToken("isseries", false);
+ header->AddIntToken("posteravailable", false);
+ header->AddIntToken("banneravailable", false);
+ } else {
+ if (getType.type == tMovie) {
+ cMovie movie;
+ movie.movieId = getType.movieId;
+ pScraper->Service("GetMovie", &movie);
+ header->AddIntToken("ismovie", true);
+ header->AddIntToken("isseries", false);
+ header->AddIntToken("posteravailable", true);
+ header->AddIntToken("banneravailable", false);
+ header->AddStringToken("posterpath", movie.poster.path);
+ header->AddIntToken("posterwidth", movie.poster.width);
+ header->AddIntToken("posterheight", movie.poster.height);
+ } else if (getType.type == tSeries) {
+ cSeries series;
+ series.seriesId = getType.seriesId;
+ series.episodeId = getType.episodeId;
+ pScraper->Service("GetSeries", &series);
+ header->AddIntToken("ismovie", false);
+ header->AddIntToken("isseries", true);
+ vector<cTvMedia>::iterator poster = series.posters.begin();
+ if (poster != series.posters.end()) {
+ header->AddIntToken("posterwidth", (*poster).width);
+ header->AddIntToken("posterheight", (*poster).height);
+ header->AddStringToken("posterpath", (*poster).path);
+ header->AddIntToken("posteravailable", true);
+ } else {
+ header->AddIntToken("posterwidth", 0);
+ header->AddIntToken("posterheight", 0);
+ header->AddStringToken("posterpath", "");
+ header->AddIntToken("posteravailable", false);
+ }
+ vector<cTvMedia>::iterator banner = series.banners.begin();
+ if (banner != series.banners.end()) {
+ header->AddIntToken("bannerwidth", (*banner).width);
+ header->AddIntToken("bannerheight", (*banner).height);
+ header->AddStringToken("bannerpath", (*banner).path);
+ header->AddIntToken("banneravailable", true);
+ } else {
+ header->AddIntToken("bannerwidth", 0);
+ header->AddIntToken("bannerheight", 0);
+ header->AddStringToken("bannerpath", "");
+ header->AddIntToken("banneravailable", false);
+ }
+ } else {
+ header->AddIntToken("ismovie", false);
+ header->AddIntToken("isseries", false);
+ header->AddIntToken("posteravailable", false);
+ header->AddIntToken("banneravailable", false);
+ }
+ }
+ }
+
+ header->AddStringToken("title", event->Title() ? event->Title() : "");
+ header->AddStringToken("shorttext", event->ShortText() ? event->ShortText() : "");
+ header->AddStringToken("start", *(event->GetTimeString()));
+ header->AddStringToken("stop", *(event->GetEndTimeString()));
+
+ time_t startTime = event->StartTime();
+ header->AddStringToken("day", *WeekDayName(startTime));
+ header->AddStringToken("date", *ShortDateString(startTime));
+ struct tm * sStartTime = localtime(&startTime);
+ header->AddIntToken("year", sStartTime->tm_year + 1900);
+ header->AddIntToken("daynumeric", sStartTime->tm_mday);
+ header->AddIntToken("month", sStartTime->tm_mon+1);
+
+ const cChannel *channel = Channels.GetByChannelID(event->ChannelID());
+ if (channel) {
+ header->AddStringToken("channelname", channel->Name() ? channel->Name() : "");
+ header->AddIntToken("channelnumber", channel->Number());
+ } else {
+ header->AddStringToken("channelname", "");
+ header->AddIntToken("channelnumber", 0);
+ }
+ string channelID = *(channel->GetChannelID().ToString());
+ header->AddStringToken("channelid", channelID);
+ header->AddIntToken("channellogoexists", header->ChannelLogoExists(channelID));
+
+ bool isRunning = false;
+ time_t now = time(NULL);
+ if ((now >= event->StartTime()) && (now <= event->EndTime()))
+ isRunning = true;
+ header->AddIntToken("running", isRunning);
+ if (isRunning) {
+ header->AddIntToken("elapsed", (now - event->StartTime())/60);
+ } else {
+ header->AddIntToken("elapsed", 0);
+ }
+ header->AddIntToken("duration", event->Duration() / 60);
+ header->AddIntToken("durationhours", event->Duration() / 3600);
+ header->AddStringToken("durationminutes", *cString::sprintf("%.2d", (event->Duration() / 60)%60));
+ if (event->Vps())
+ header->AddStringToken("vps", *event->GetVpsString());
+ else
+ header->AddStringToken("vps", "");
+
+ stringstream epgImageName;
+ epgImageName << event->EventID();
+ string epgImagePath = header->GetEpgImagePath();
+
+ bool epgPicAvailable = FileExists(epgImagePath, epgImageName.str(), "jpg");
+ if (epgPicAvailable) {
+ header->AddIntToken("epgpicavailable", true);
+ header->AddStringToken("epgpicpath", *cString::sprintf("%s%s.jpg", epgImagePath.c_str(), epgImageName.str().c_str()));
+ } else {
+ epgImageName << "_0";
+ epgPicAvailable = FileExists(epgImagePath, epgImageName.str(), "jpg");
+ if (epgPicAvailable) {
+ header->AddIntToken("epgpicavailable", true);
+ header->AddStringToken("epgpicpath", *cString::sprintf("%s%s.jpg", epgImagePath.c_str(), epgImageName.str().c_str()));
+ } else {
+ header->AddIntToken("epgpicavailable", false);
+ header->AddStringToken("epgpicpath", "");
+ }
+ }
+
+ header->Display();
+}
+
+void cDetailView::DrawFooter(void) {
+ string textGreen = "";
+ string textYellow = "";
+ string textRed = tr("Search & Record");
+ string textBlue = tr("Switch");
+
+ int colorKeys[4] = { Setup.ColorKey0, Setup.ColorKey1, Setup.ColorKey2, Setup.ColorKey3 };
+
+ footer->Clear();
+ footer->ClearTokens();
+
+ footer->AddStringToken("red", textRed);
+ footer->AddStringToken("green", textGreen);
+ footer->AddStringToken("yellow", textYellow);
+ footer->AddStringToken("blue", textBlue);
+
+ for (int button = 1; button < 5; button++) {
+ string red = *cString::sprintf("red%d", button);
+ string green = *cString::sprintf("green%d", button);
+ string yellow = *cString::sprintf("yellow%d", button);
+ string blue = *cString::sprintf("blue%d", button);
+ bool isRed = false;
+ bool isGreen = false;
+ bool isYellow = false;
+ bool isBlue = false;
+ switch (colorKeys[button-1]) {
+ case 0:
+ isRed = true;
+ break;
+ case 1:
+ isGreen = true;
+ break;
+ case 2:
+ isYellow = true;
+ break;
+ case 3:
+ isBlue = true;
+ break;
+ default:
+ break;
+ }
+ footer->AddIntToken(red, isRed);
+ footer->AddIntToken(green, isGreen);
+ footer->AddIntToken(yellow, isYellow);
+ footer->AddIntToken(blue, isBlue);
+ }
+
+ footer->Display();
+}
+
+bool cDetailView::DrawTime(void) {
+ time_t t = time(0); // get time now
+ struct tm * now = localtime(&t);
+ int sec = now->tm_sec;
+ if (sec == lastSecond)
+ return false;
+
+ int min = now->tm_min;
+ int hour = now->tm_hour;
+ int hourMinutes = hour%12 * 5 + min / 12;
+
+ watch->ClearTokens();
+ watch->AddIntToken("sec", sec);
+ watch->AddIntToken("min", min);
+ watch->AddIntToken("hour", hour);
+ watch->AddIntToken("hmins", hourMinutes);
+ watch->AddStringToken("time", *TimeString(t));
+ watch->Display();
+
+ lastSecond = sec;
+ return true;
+}
+
+void cDetailView::SetTabTokens(void) {
+ tabs->ClearTokens();
+
+ tabs->AddStringToken("title", event->Title() ? event->Title() : "");
+ tabs->AddStringToken("shorttext", event->ShortText() ? event->ShortText() : "");
+ tabs->AddStringToken("description", event->Description() ? event->Description() : "");
+ tabs->AddStringToken("start", *(event->GetTimeString()));
+ tabs->AddStringToken("stop", *(event->GetEndTimeString()));
+ time_t startTime = event->StartTime();
+ tabs->AddStringToken("day", *WeekDayName(startTime));
+ tabs->AddStringToken("date", *ShortDateString(startTime));
+ struct tm * sStartTime = localtime(&startTime);
+ tabs->AddIntToken("year", sStartTime->tm_year + 1900);
+ tabs->AddIntToken("daynumeric", sStartTime->tm_mday);
+ tabs->AddIntToken("month", sStartTime->tm_mon+1);
+
+ string channelID = *(event->ChannelID().ToString());
+ tabs->AddStringToken("channelid", channelID);
+ tabs->AddIntToken("channellogoexists", tabs->ChannelLogoExists(channelID));
+
+ bool isRunning = false;
+ time_t now = time(NULL);
+ if ((now >= event->StartTime()) && (now <= event->EndTime()))
+ isRunning = true;
+ tabs->AddIntToken("running", isRunning);
+ if (isRunning) {
+ tabs->AddIntToken("elapsed", (now - event->StartTime())/60);
+ } else {
+ tabs->AddIntToken("elapsed", 0);
+ }
+ tabs->AddIntToken("duration", event->Duration() / 60);
+ tabs->AddIntToken("durationhours", event->Duration() / 3600);
+ tabs->AddStringToken("durationminutes", *cString::sprintf("%.2d", (event->Duration() / 60)%60));
+ if (event->Vps())
+ tabs->AddStringToken("vps", *event->GetVpsString());
+ else
+ tabs->AddStringToken("vps", "");
+
+
+ bool hasReruns = LoadReruns();
+ tabs->AddIntToken("hasreruns", hasReruns);
+
+ SetScraperTokens();
+ SetEpgPictures(event->EventID());
+}
+
+bool cDetailView::LoadReruns(void) {
+ if (!event)
+ return false;
+
+ cPlugin *epgSearchPlugin = cPluginManager::GetPlugin("epgsearch");
+ if (!epgSearchPlugin)
+ return false;
+
+ if (isempty(event->Title()))
+ return false;
+
+ int maxNumReruns = config.rerunAmount;
+ int rerunDistance = config.rerunDistance * 3600;
+ int rerunNaxChannel = config.rerunMaxChannel;
+
+ Epgsearch_searchresults_v1_0 data;
+ string strQuery = (event->Title()) ? event->Title() : "";
+ data.query = (char *)strQuery.c_str();
+ data.mode = 0;
+ data.channelNr = 0;
+ data.useTitle = true;
+ data.useSubTitle = true;
+ data.useDescription = false;
+
+ bool foundRerun = false;
+ if (epgSearchPlugin->Service("Epgsearch-searchresults-v1.0", &data)) {
+ cList<Epgsearch_searchresults_v1_0::cServiceSearchResult>* list = data.pResultList;
+ if (list && (list->Count() > 1)) {
+ foundRerun = true;
+ int i = 0;
+ for (Epgsearch_searchresults_v1_0::cServiceSearchResult *r = list->First(); r && i < maxNumReruns; r = list->Next(r)) {
+ time_t eventStart = event->StartTime();
+ time_t rerunStart = r->event->StartTime();
+ cChannel *channel = Channels.GetByChannelID(r->event->ChannelID(), true, true);
+ //check for identical event
+ if ((event->ChannelID() == r->event->ChannelID()) && (eventStart == rerunStart))
+ continue;
+ //check for timely distance
+ if (rerunDistance > 0) {
+ if (rerunStart - eventStart < rerunDistance) {
+ continue;
+ }
+ }
+ //check for maxchannel
+ if (rerunNaxChannel > 0) {
+ if (channel && channel->Number() > rerunNaxChannel) {
+ continue;
+ }
+ }
+ i++;
+ map< string, string > rerun;
+ rerun.insert(pair<string, string>("reruns[title]", r->event->Title() ? r->event->Title() : ""));
+ rerun.insert(pair<string, string>("reruns[shorttext]", r->event->ShortText() ? r->event->ShortText() : ""));
+ rerun.insert(pair<string, string>("reruns[start]", *(r->event->GetTimeString())));
+ rerun.insert(pair<string, string>("reruns[start]", *(r->event->GetTimeString())));
+ rerun.insert(pair<string, string>("reruns[stop]", *(r->event->GetEndTimeString())));
+ rerun.insert(pair<string, string>("reruns[date]", *ShortDateString(r->event->StartTime())));
+ rerun.insert(pair<string, string>("reruns[day]", *WeekDayName(r->event->StartTime())));
+ string channelID = *(r->event->ChannelID().ToString());
+ rerun.insert(pair<string, string>("reruns[channelid]", channelID));
+ bool logoExists = tabs->ChannelLogoExists(channelID);
+ rerun.insert(pair<string, string>("reruns[channellogoexists]", logoExists ? "1" : "0"));
+
+ if (channel) {
+ stringstream channelNumber;
+ channelNumber << channel->Number();
+ rerun.insert(pair<string, string>("reruns[channelname]", channel->ShortName(true)));
+ rerun.insert(pair<string, string>("reruns[channelnumber]", channelNumber.str()));
+ } else {
+ rerun.insert(pair<string, string>("reruns[channelname]", ""));
+ rerun.insert(pair<string, string>("reruns[channelnumber]", ""));
+ }
+ tabs->AddLoopToken("reruns", rerun);
+ }
+ delete list;
+ }
+ }
+ return foundRerun;
+}
+
+void cDetailView::SetScraperTokens(void) {
+ static cPlugin *pScraper = GetScraperPlugin();
+ if (!pScraper || !event) {
+ tabs->AddIntToken("ismovie", false);
+ tabs->AddIntToken("isseries", false);
+ return;
+ }
+
+ ScraperGetEventType getType;
+ getType.event = event;
+ getType.recording = NULL;
+ if (!pScraper->Service("GetEventType", &getType)) {
+ tabs->AddIntToken("ismovie", false);
+ tabs->AddIntToken("isseries", false);
+ return;
+ }
+
+ if (getType.type == tMovie) {
+ cMovie movie;
+ movie.movieId = getType.movieId;
+ pScraper->Service("GetMovie", &movie);
+ tabs->AddIntToken("ismovie", true);
+ tabs->AddIntToken("isseries", false);
+
+ tabs->AddStringToken("movietitle", movie.title);
+ tabs->AddStringToken("movieoriginalTitle", movie.originalTitle);
+ tabs->AddStringToken("movietagline", movie.tagline);
+ tabs->AddStringToken("movieoverview", movie.overview);
+ tabs->AddStringToken("moviegenres", movie.genres);
+ tabs->AddStringToken("moviehomepage", movie.homepage);
+ tabs->AddStringToken("moviereleasedate", movie.releaseDate);
+ stringstream pop;
+ pop << movie.popularity;
+ tabs->AddStringToken("moviepopularity", pop.str());
+ stringstream vote;
+ vote << movie.voteAverage;
+ tabs->AddStringToken("movievoteaverage", pop.str());
+ tabs->AddStringToken("posterpath", movie.poster.path);
+ tabs->AddStringToken("fanartpath", movie.fanart.path);
+ tabs->AddStringToken("collectionposterpath", movie.collectionPoster.path);
+ tabs->AddStringToken("collectionfanartpath", movie.collectionFanart.path);
+
+ tabs->AddIntToken("movieadult", movie.adult);
+ tabs->AddIntToken("moviebudget", movie.budget);
+ tabs->AddIntToken("movierevenue", movie.revenue);
+ tabs->AddIntToken("movieruntime", movie.runtime);
+ tabs->AddIntToken("posterwidth", movie.poster.width);
+ tabs->AddIntToken("posterheight", movie.poster.height);
+ tabs->AddIntToken("fanartwidth", movie.fanart.width);
+ tabs->AddIntToken("fanartheight", movie.fanart.height);
+ tabs->AddIntToken("collectionposterwidth", movie.collectionPoster.width);
+ tabs->AddIntToken("collectionposterheight", movie.collectionPoster.height);
+ tabs->AddIntToken("collectionfanartwidth", movie.collectionFanart.width);
+ tabs->AddIntToken("collectionfanartheight", movie.collectionFanart.height);
+
+ for (vector<cActor>::iterator act = movie.actors.begin(); act != movie.actors.end(); act++) {
+ map< string, string > actor;
+ actor.insert(pair<string, string>("actors[name]", (*act).name));
+ actor.insert(pair<string, string>("actors[role]", (*act).role));
+ actor.insert(pair<string, string>("actors[thumb]", (*act).actorThumb.path));
+ stringstream actWidth, actHeight;
+ actWidth << (*act).actorThumb.width;
+ actHeight << (*act).actorThumb.height;
+ actor.insert(pair<string, string>("actors[thumbwidth]", actWidth.str()));
+ actor.insert(pair<string, string>("actors[thumbheight]", actHeight.str()));
+ tabs->AddLoopToken("actors", actor);
+ }
+
+ } else if (getType.type == tSeries) {
+ cSeries series;
+ series.seriesId = getType.seriesId;
+ series.episodeId = getType.episodeId;
+ pScraper->Service("GetSeries", &series);
+ tabs->AddIntToken("ismovie", false);
+ tabs->AddIntToken("isseries", true);
+ //Series Basics
+ tabs->AddStringToken("seriesname", series.name);
+ tabs->AddStringToken("seriesoverview", series.overview);
+ tabs->AddStringToken("seriesfirstaired", series.firstAired);
+ tabs->AddStringToken("seriesnetwork", series.network);
+ tabs->AddStringToken("seriesgenre", series.genre);
+ stringstream rating;
+ rating << series.rating;
+ tabs->AddStringToken("seriesrating", rating.str());
+ tabs->AddStringToken("seriesstatus", series.status);
+ //Episode Information
+ tabs->AddIntToken("episodenumber", series.episode.number);
+ tabs->AddIntToken("episodeseason", series.episode.season);
+ tabs->AddStringToken("episodetitle", series.episode.name);
+ tabs->AddStringToken("episodefirstaired", series.episode.firstAired);
+ tabs->AddStringToken("episodegueststars", series.episode.guestStars);
+ tabs->AddStringToken("episodeoverview", series.episode.overview);
+ stringstream eprating;
+ eprating << series.episode.rating;
+ tabs->AddStringToken("episoderating", eprating.str());
+ tabs->AddIntToken("episodeimagewidth", series.episode.episodeImage.width);
+ tabs->AddIntToken("episodeimageheight", series.episode.episodeImage.height);
+ tabs->AddStringToken("episodeimagepath", series.episode.episodeImage.path);
+ //Seasonposter
+ tabs->AddIntToken("seasonposterwidth", series.seasonPoster.width);
+ tabs->AddIntToken("seasonposterheight", series.seasonPoster.height);
+ tabs->AddStringToken("seasonposterpath", series.seasonPoster.path);
+
+ //Posters
+ int current = 1;
+ for(vector<cTvMedia>::iterator poster = series.posters.begin(); poster != series.posters.end(); poster++) {
+ stringstream labelWidth, labelHeight, labelPath;
+ labelWidth << "seriesposter" << current << "width";
+ labelHeight << "seriesposter" << current << "height";
+ labelPath << "seriesposter" << current << "path";
+
+ tabs->AddIntToken(labelWidth.str(), (*poster).width);
+ tabs->AddIntToken(labelHeight.str(), (*poster).height);
+ tabs->AddStringToken(labelPath.str(), (*poster).path);
+ current++;
+ }
+ if (current < 3) {
+ for (; current < 4; current++) {
+ stringstream labelWidth, labelHeight, labelPath;
+ labelWidth << "seriesposter" << current << "width";
+ labelHeight << "seriesposter" << current << "height";
+ labelPath << "seriesposter" << current << "path";
+
+ tabs->AddIntToken(labelWidth.str(), 0);
+ tabs->AddIntToken(labelHeight.str(), 0);
+ tabs->AddStringToken(labelPath.str(), "");
+ }
+ }
+
+ //Banners
+ current = 1;
+ for(vector<cTvMedia>::iterator banner = series.banners.begin(); banner != series.banners.end(); banner++) {
+ stringstream labelWidth, labelHeight, labelPath;
+ labelWidth << "seriesbanner" << current << "width";
+ labelHeight << "seriesbanner" << current << "height";
+ labelPath << "seriesbanner" << current << "path";
+
+ tabs->AddIntToken(labelWidth.str(), (*banner).width);
+ tabs->AddIntToken(labelHeight.str(), (*banner).height);
+ tabs->AddStringToken(labelPath.str(), (*banner).path);
+ current++;
+ }
+ if (current < 3) {
+ for (; current < 4; current++) {
+ stringstream labelWidth, labelHeight, labelPath;
+ labelWidth << "seriesbanner" << current << "width";
+ labelHeight << "seriesbanner" << current << "height";
+ labelPath << "seriesbanner" << current << "path";
+
+ tabs->AddIntToken(labelWidth.str(), 0);
+ tabs->AddIntToken(labelHeight.str(), 0);
+ tabs->AddStringToken(labelPath.str(), "");
+ }
+ }
+
+ //Fanarts
+ current = 1;
+ for(vector<cTvMedia>::iterator fanart = series.fanarts.begin(); fanart != series.fanarts.end(); fanart++) {
+ stringstream labelWidth, labelHeight, labelPath;
+ labelWidth << "seriesfanart" << current << "width";
+ labelHeight << "seriesfanart" << current << "height";
+ labelPath << "seriesfanart" << current << "path";
+
+ tabs->AddIntToken(labelWidth.str(), (*fanart).width);
+ tabs->AddIntToken(labelHeight.str(), (*fanart).height);
+ tabs->AddStringToken(labelPath.str(), (*fanart).path);
+ current++;
+ }
+ if (current < 3) {
+ for (; current < 4; current++) {
+ stringstream labelWidth, labelHeight, labelPath;
+ labelWidth << "seriesfanart" << current << "width";
+ labelHeight << "seriesfanart" << current << "height";
+ labelPath << "seriesfanart" << current << "path";
+
+ tabs->AddIntToken(labelWidth.str(), 0);
+ tabs->AddIntToken(labelHeight.str(), 0);
+ tabs->AddStringToken(labelPath.str(), "");
+ }
+ }
+
+ //Actors
+ for (vector<cActor>::iterator act = series.actors.begin(); act != series.actors.end(); act++) {
+ map< string, string > actor;
+ actor.insert(pair<string, string>("actors[name]", (*act).name));
+ actor.insert(pair<string, string>("actors[role]", (*act).role));
+ actor.insert(pair<string, string>("actors[thumb]", (*act).actorThumb.path));
+ stringstream actWidth, actHeight;
+ actWidth << (*act).actorThumb.width;
+ actHeight << (*act).actorThumb.height;
+ actor.insert(pair<string, string>("actors[thumbwidth]", actWidth.str()));
+ actor.insert(pair<string, string>("actors[thumbheight]", actHeight.str()));
+ tabs->AddLoopToken("actors", actor);
+ }
+
+ } else {
+ tabs->AddIntToken("ismovie", false);
+ tabs->AddIntToken("isseries", false);
+ }
+}
+
+void cDetailView::SetEpgPictures(int eventId) {
+ string epgImagePath = tabs->GetEpgImagePath();
+ for (int i=0; i<3; i++) {
+ stringstream picName;
+ picName << eventId << "_" << i;
+ bool epgPicAvailable = FileExists(epgImagePath, picName.str(), "jpg");
+ stringstream available;
+ stringstream path;
+ available << "epgpic" << i+1 << "avaialble";
+ path << "epgpic" << i+1 << "path";
+ if (epgPicAvailable) {
+ tabs->AddIntToken(available.str(), true);
+ tabs->AddStringToken(path.str(), *cString::sprintf("%s%s.jpg", epgImagePath.c_str(), picName.str().c_str()));
+ } else {
+ tabs->AddIntToken(available.str(), false);
+ tabs->AddStringToken(path.str(), "");
+ }
+ }
+}
\ No newline at end of file diff --git a/detailview.h b/detailview.h new file mode 100644 index 0000000..cb3cd3f --- /dev/null +++ b/detailview.h @@ -0,0 +1,37 @@ +#ifndef __TVGUIDE_DETAILVIEW_H
+#define __TVGUIDE_DETAILVIEW_H
+
+#include "config.h"
+#include "libskindesigner/osdelements.h"
+
+class cDetailView {
+private:
+ bool init;
+ int lastSecond;
+ cOsdView *detailView;
+ const cEvent *event;
+ cViewElement *back;
+ cViewElement *header;
+ cViewElement *footer;
+ cViewElement *watch;
+ cViewTab *tabs;
+ void DrawBackground(void);
+ void DrawHeader(void);
+ void DrawFooter(void);
+ void SetTabTokens(void);
+ bool LoadReruns(void);
+ void SetScraperTokens(void);
+ void SetEpgPictures(int eventId);
+public:
+ cDetailView(cOsdView *detailView, const cEvent *event);
+ virtual ~cDetailView(void);
+ void Draw(void);
+ void Left(void);
+ void Right(void);
+ void Up(void);
+ void Down(void);
+ bool DrawTime(void);
+ void Flush(void) { detailView->Display(); };
+};
+
+#endif //__TVGUIDE_DETAILVIEW_H
diff --git a/dummyelement.c b/dummyelement.c new file mode 100644 index 0000000..b33c744 --- /dev/null +++ b/dummyelement.c @@ -0,0 +1,26 @@ +#include "dummyelement.h" + +cDummyElement::cDummyElement(time_t start, time_t end, cChannelEpg *owner) : cGridElement(owner) { + this->start = start; + this->end = end; + dummy = true; +} + +cDummyElement::~cDummyElement(void) { +} + +time_t cDummyElement::Duration(void) { + //max Duration 5h + int dur = end - start; + if (dur > 18000) + return 18000; + return dur; +}; + +const char *cDummyElement::Title(void) { + return tr("No EPG available"); +} + +void cDummyElement::Debug(void) { + esyslog("tvguideng: dummyelement %ld, %s - %s, Channel %s", id, *TimeString(start), *TimeString(end), Channel()->Name()); +} diff --git a/dummyelement.h b/dummyelement.h new file mode 100644 index 0000000..b2de4fb --- /dev/null +++ b/dummyelement.h @@ -0,0 +1,24 @@ +#ifndef __TVGUIDE_DUMMYGRID_H +#define __TVGUIDE_DUMMYGRID_H + +#include "gridelement.h" + +// --- cDummyGrid ------------------------------------------------------------- + +class cDummyElement : public cGridElement { +private: + time_t start; + time_t end; + time_t Duration(void); +public: + cDummyElement(time_t start, time_t end, cChannelEpg *owner); + virtual ~cDummyElement(void); + time_t StartTime(void) { return start; }; + time_t EndTime(void) { return end; }; + void SetStartTime(time_t start) { this->start = start; }; + void SetEndTime(time_t end) { this->end = end; }; + const char *Title(void); + void Debug(void); +}; + +#endif //__TVGUIDE_DUMMYGRID_H
\ No newline at end of file diff --git a/epgelement.c b/epgelement.c new file mode 100644 index 0000000..1ef5542 --- /dev/null +++ b/epgelement.c @@ -0,0 +1,52 @@ +#include "switchtimer.h"
+#include "services/remotetimers.h"
+#include "epgelement.h"
+
+cEpgElement::cEpgElement(const cEvent *event, cChannelEpg *owner) : cGridElement(owner) {
+ this->event = event;
+ hasTimer = false;
+ SetTimer();
+ hasSwitchTimer = false;
+ SetSwitchTimer();
+ dummy = false;
+}
+
+cEpgElement::~cEpgElement(void) {
+}
+
+void cEpgElement::SetTimer() {
+ if (config.useRemoteTimers && pRemoteTimers) {
+ RemoteTimers_Event_v1_0 rt;
+ rt.event = event;
+ if (pRemoteTimers->Service("RemoteTimers::GetTimerByEvent-v1.0", &rt))
+ hasTimer = true;
+ else
+ hasTimer = false;
+ } else if (owner->HasTimer()) {
+ hasTimer = event->HasTimer();
+ } else {
+ hasTimer = false;
+ }
+}
+
+void cEpgElement::SetSwitchTimer() {
+ if (owner->HasSwitchTimer()) {
+ hasSwitchTimer = SwitchTimers.EventInSwitchList(event);
+ } else {
+ hasSwitchTimer = false;
+ }
+}
+
+const char *cEpgElement::Title(void) {
+ return event->Title();
+}
+
+const char *cEpgElement::ShortText(void) {
+ if (event->ShortText())
+ return event->ShortText();
+ return "";
+}
+
+void cEpgElement::Debug() {
+ esyslog("tvguideng: epgelement %ld: \"%s\" %s - %s, channel %s, timer: %d", id, event->Title(), *(event->GetTimeString()), *(event->GetEndTimeString()), Channel()->Name(), hasTimer);
+}
diff --git a/epgelement.h b/epgelement.h new file mode 100644 index 0000000..3a7b48d --- /dev/null +++ b/epgelement.h @@ -0,0 +1,28 @@ +#ifndef __TVGUIDE_EPGGRID_H
+#define __TVGUIDE_EPGGRID_H
+
+#include <vdr/epg.h>
+#include "gridelement.h"
+#include "channelepg.h"
+
+// --- cEpgGrid -------------------------------------------------------------
+
+class cEpgElement : public cGridElement {
+private:
+ const cEvent *event;
+ time_t Duration(void) { return event->Duration(); };
+public:
+ cEpgElement(const cEvent *event, cChannelEpg *owner);
+ virtual ~cEpgElement(void);
+ const cEvent *GetEvent(void) {return event;};
+ time_t StartTime(void) { return event->StartTime(); };
+ time_t EndTime(void) { return event->EndTime(); };
+ void SetTimer(void);
+ void SetSwitchTimer(void);
+ const char *Title(void);
+ const char *ShortText(void);
+ const cEvent *Event(void) { return event; };
+ void Debug(void);
+};
+
+#endif //__TVGUIDE_EPGGRID_H
\ No newline at end of file diff --git a/epggrid.c b/epggrid.c new file mode 100644 index 0000000..79cdae7 --- /dev/null +++ b/epggrid.c @@ -0,0 +1,774 @@ +#include "tvguidengosd.h" +#include "epggrid.h" +#include "helpers.h" +#include "services/scraper2vdr.h" + +cEpgGrid::cEpgGrid(cOsdView *rootView, cTimeManager *timeManager) { + lastSecond = -1; + channelGroupLast = -1; + active = NULL; + oldActiveGridId = -1; + + this->rootView = rootView; + this->timeManager = timeManager; + + channelsPerPage = (config.displayMode == eHorizontal) ? config.channelsPerPageHorizontal : config.channelsPerPageVertical; + + back = rootView->GetViewElement(config.displayMode == eHorizontal ? verBackgroundHor : verBackgroundVer); + back->Display(); + + header = rootView->GetViewElement(verHeader); + footer = rootView->GetViewElement(verFooter); + watch = rootView->GetViewElement(verTime); + + channelsGrid = rootView->GetViewGrid(config.displayMode == eHorizontal ? vgChannelsHor : vgChannelsVer); + epgGrid = rootView->GetViewGrid(config.displayMode == eHorizontal ? vgSchedulesHor : vgSchedulesVer); + + cViewElement *timelineDate = rootView->GetViewElement(config.displayMode == eHorizontal ? verDateTimelineHor : verDateTimelineVer); + cViewElement *timeIndicator = rootView->GetViewElement(config.displayMode == eHorizontal ? verTimeIndicatorHor : verTimeIndicatorVer); + cViewGrid *timelineGrid = rootView->GetViewGrid(config.displayMode == eHorizontal ? vgTimelineHor : vgTimelineVer); + timeline = new cTimeline(timelineGrid, timelineDate, timeIndicator, timeManager); + + cViewGrid *channelgroupsGrid = rootView->GetViewGrid(config.displayMode == eHorizontal ? vgChannelGroupsHor : vgChannelGroupsVer); + channelGroups = new cChannelgroups(channelgroupsGrid); + channelGroups->Init(); +} + +cEpgGrid::~cEpgGrid(void) { + delete back; + delete header; + delete footer; + channels.Clear(); + delete channelsGrid; + delete epgGrid; + delete timeline; + delete watch; + delete rootView; +} + +void cEpgGrid::Init(const cChannel *startChannel) { + if (!startChannel) + return; + timeline->Init(); + int numBack = channelsPerPage / 2; + int offset = 0; + const cChannel *newStartChannel = startChannel; + for (; newStartChannel ; newStartChannel = Channels.Prev(newStartChannel)) { + if (newStartChannel && !newStartChannel->GroupSep()) { + offset++; + } + if (offset == numBack) + break; + } + if (!newStartChannel) + newStartChannel = Channels.First(); + offset--; + if (offset < 0) + offset = 0; + + CreateChannels(newStartChannel, offset); +} + +void cEpgGrid::CreateChannels(const cChannel *startChannel, int activeChannel) { + int pos = 0; + bool foundEnough = false; + channels.Clear(); + if (!startChannel) + return; + for (const cChannel *channel = startChannel; channel; channel = Channels.Next(channel)) { + if (config.hideLastChannelGroup && channelGroups->IsInLastGroup(channel)) { + break; + } + if (!channel->GroupSep()) { + cChannelEpg *channelEpg = new cChannelEpg(pos, channel, timeManager); + if (channelEpg->ReadGrids()) { + channels.Add(channelEpg); + if (pos == activeChannel) { + active = channelEpg->GetActive(); + if (active) { + active->SetActive(); + } + } + pos++; + } else { + delete channelEpg; + } + } + if (pos == channelsPerPage) { + foundEnough = true; + break; + } + } + if (!foundEnough) { + int numCurrent = channels.Count(); + int numBack = channelsPerPage - numCurrent; + int newChannelNumber = channels.First()->GetChannelNumber() - numBack; + const cChannel *newStart = Channels.GetByNumber(newChannelNumber); + CreateChannels(newStart, pos+1); + } +} + +void cEpgGrid::Clear(void) { + timeline->Clear(); + channelsGrid->Clear(); + channels.Clear(); + header->Clear(); + footer->Clear(); + active = NULL; + oldActiveGridId = -1; + epgGrid->Clear(); +} + +void cEpgGrid::SetTimers(void) { + for (cChannelEpg *channelEpg = channels.First(); channelEpg; channelEpg = channels.Next(channelEpg)) { + channelEpg->SetTimers(); + } +} + + +void cEpgGrid::RebuildEpgGrid(void) { + if (!active) + return; + int activeChannelNumber = active->Channel()->Number(); + oldActiveGridId = -1; + active = NULL; + epgGrid->Clear(); + timeline->Clear(); + timeline->Init(); + for (cChannelEpg *channelEpg = channels.First(); channelEpg; channelEpg = channels.Next(channelEpg)) { + channelEpg->ClearGrids(); + channelEpg->ReadGrids(); + if (channelEpg->Channel()->Number() == activeChannelNumber) { + active = channelEpg->GetActive(); + active->SetActive(); + } + } +} + + +void cEpgGrid::SetActiveGrid(cGridElement *newActive) { + if (!newActive || !active) { + return; + } + oldActiveGridId = active->Id(); + if (newActive->Id() == active->Id()) { + return; + } + active->SetInActive(); + active = newActive; + active->SetActive(); +} + +bool cEpgGrid::TimeForward(void) { + bool scrolled = false; + if (!active) { + return false; + } + bool actionDone = false; + int minutesLeft = (timeManager->GetEnd() - active->EndTime())/60; + if ( minutesLeft < 30 ) { + actionDone = true; + ScrollForward(); + scrolled = true; + } + cGridElement *next = (cGridElement*)active->Next(); + if (next) { + if ( (next->EndTime() < timeManager->GetEnd()) || ( (timeManager->GetEnd() - next->StartTime())/60 > 30 ) ) { + SetActiveGrid(next); + actionDone = true; + } + } + if (!actionDone) { + ScrollForward(); + scrolled = true; + } + return scrolled; +} + +bool cEpgGrid::TimeBack(void) { + bool scrolled = false; + if (!active) { + return false; + } + + if (timeManager->IsStart(active->StartTime())) { + oldActiveGridId = active->Id(); + return false; + } + + bool actionDone = false; + int minutesLeft = (active->StartTime() - timeManager->GetStart())/60; + if ( minutesLeft < 30 ) { + actionDone = true; + ScrollBack(); + scrolled = true; + } + cGridElement *prev = (cGridElement*)active->Prev(); + if (prev) { + if ( (prev->StartTime() > timeManager->GetStart()) + || ( (prev->EndTime() - timeManager->GetStart())/60 > 30 ) + || ( prev->IsFirst()) ) { + SetActiveGrid(prev); + actionDone = true; + } + } + if (!actionDone) { + ScrollBack(); + scrolled = true; + } + return scrolled; +} + +bool cEpgGrid::ChannelForward(void) { + if (!active) { + return false; + } + //Scrolling in Grid + cChannelEpg *channelNext = channels.Next(active->Owner()); + if (!channelNext && config.hideLastChannelGroup) { + const cChannel *next = SeekChannelForward(1); + if (next == active->Channel()) + return false; + } + + if (channelNext) { + cGridElement *neighbor = channelNext->GetNeighbor(active); + if (neighbor) { + SetActiveGrid(neighbor); + } + return false; + } + //end of grid reached, scrolling half page forward + int numJumpForward = channelsPerPage/2; + const cChannel *currentChannel = active->Channel(); + const cChannel *newLastChannel = SeekChannelForward(numJumpForward); + if (!newLastChannel) + return false; + //insert new channels at end + int numInserted = 0; + for (const cChannel *channel = (const cChannel*)currentChannel->Next(); channel ; channel = Channels.Next(channel)) { + if (channel->GroupSep()) { + continue; + } + cChannelEpg *channelEpg = new cChannelEpg(0, channel, timeManager); + if (channelEpg->ReadGrids()) { + channels.Add(channelEpg); + numInserted++; + } + if (numInserted == 1) { + cGridElement *neighbor = channelEpg->GetNeighbor(active); + if (neighbor) { + SetActiveGrid(neighbor); + } + } + if (channel->Number() == newLastChannel->Number()) + break; + } + if (!numInserted) + return false; + //delete first channels + deletedChannels.clear(); + for (int i=0; i < numInserted; i++) { + cChannelEpg *first = channels.First(); + first->DeleteGridViews(epgGrid); + deletedChannels.insert(first->Channel()->Number()); + channels.Del(first); + } + //renumber channels + int newPos = 0; + for (cChannelEpg *channelEpg = channels.First(); channelEpg; channelEpg = channels.Next(channelEpg)) { + channelEpg->SetPosition(newPos); + newPos++; + } + + return true; +} + +bool cEpgGrid::ChannelBack(void) { + if (!active) { + return false; + } + //Scrolling in Grid + cChannelEpg *channelPrev = channels.Prev(active->Owner()); + if (channelPrev) { + cGridElement *neighbor = channelPrev->GetNeighbor(active); + if (neighbor) { + SetActiveGrid(neighbor); + } + return false; + } + //start of grid reached, scrolling half page back + int numJumpBack = channelsPerPage/2; + const cChannel *currentChannel = active->Channel(); + const cChannel *newFirstChannel = SeekChannelBack(numJumpBack); + if (!newFirstChannel) + return false; + //insert new channels at start + int numInserted = 0; + for (const cChannel *channel = (const cChannel*)currentChannel->Prev(); channel ; channel = Channels.Prev(channel)) { + if (channel->GroupSep()) { + continue; + } + cChannelEpg *channelEpg = new cChannelEpg(0, channel, timeManager); + if (channelEpg->ReadGrids()) { + channels.Ins(channelEpg, channels.First()); + numInserted++; + } + if (numInserted == 1) { + cGridElement *neighbor = channelEpg->GetNeighbor(active); + if (neighbor) { + SetActiveGrid(neighbor); + } + } + if (channel->Number() == newFirstChannel->Number()) + break; + } + if (!numInserted) + return false; + //delete last channels + deletedChannels.clear(); + for (int i=0; i < numInserted; i++) { + cChannelEpg *last = channels.Last(); + last->DeleteGridViews(epgGrid); + deletedChannels.insert(last->Channel()->Number()); + channels.Del(last); + } + //renumber channels + int newPos = 0; + for (cChannelEpg *channelEpg = channels.First(); channelEpg; channelEpg = channels.Next(channelEpg)) { + channelEpg->SetPosition(newPos); + newPos++; + } + return true; +} + +const cChannel *cEpgGrid::GetNextChannelNumJump(void) { + const cChannel *newFirst = SeekChannelForward(channelsPerPage); + if (!newFirst) + newFirst = Channels.Last(); + return newFirst; +} + +const cChannel *cEpgGrid::GetPrevChannelNumJump(void) { + const cChannel *newFirst = SeekChannelBack(channelsPerPage); + if (!newFirst) + newFirst = Channels.First(); + return newFirst; +} + +const cChannel *cEpgGrid::GetNextChannelGroupJump(void) { + if (!active) { + return Channels.Last(); + } + int currentGroup = channelGroups->GetGroup(active->Channel()); + int nextChannelNumber = channelGroups->GetNextGroupFirstChannel(currentGroup); + const cChannel *next = Channels.GetByNumber(nextChannelNumber); + if (next) + return next; + return Channels.Last(); +} + +const cChannel *cEpgGrid::GetPrevChannelGroupJump(void) { + if (!active) { + return Channels.First(); + } + int currentGroup = channelGroups->GetGroup(active->Channel()); + int prevChannelNumber = channelGroups->GetPrevGroupFirstChannel(currentGroup); + const cChannel *prev = Channels.GetByNumber(prevChannelNumber); + if (prev) + return prev; + return Channels.First(); +} + +bool cEpgGrid::IsFirstGroup(void) { + if (!active) { + return true; + } + return channelGroups->IsInFirstGroup(active->Channel()); +} + +bool cEpgGrid::IsLastGroup(void) { + if (!active) { + return true; + } + return channelGroups->IsInLastGroup(active->Channel()); +} + +bool cEpgGrid::IsSecondLastGroup(void) { + if (!active) { + return true; + } + return channelGroups->IsInSecondLastGroup(active->Channel()); +} + +const cChannel *cEpgGrid::GetCurrentChannel(void) { + if (!active) { + return NULL; + } + return active->Channel(); +} + +int cEpgGrid::GetLastValidChannel(void) { + return channelGroups->GetLastValidChannel(); +} + +const cEvent *cEpgGrid::GetCurrentEvent(void) { + if (!active) + return NULL; + return active->Event(); +} + +void cEpgGrid::DrawChannelHeaders(void) { + for (set<int>::iterator it = deletedChannels.begin(); it != deletedChannels.end(); it++) { + channelsGrid->Delete(*it); + } + for (cChannelEpg *channel = channels.First(); channel; channel = channels.Next(channel)) { + channel->DrawHeader(channelsGrid); + } + channelsGrid->Display(); +} + +void cEpgGrid::DrawChannelgroups(void) { + channelGroups->Clear(); + channelGroups->Draw(channels.First()->Channel(), channels.Last()->Channel()); +} + +void cEpgGrid::DrawTimeline(void) { + timeline->Draw(); +} + +void cEpgGrid::DrawGrid(void) { + if (!active) { + return; + } + epgGrid->SetCurrent(oldActiveGridId, false); + for (cChannelEpg *channel = channels.First(); channel; channel = channels.Next(channel)) { + channel->DeleteOutdated(epgGrid); + channel->DrawGrids(epgGrid); + } + epgGrid->Display(); +} + +void cEpgGrid::UpdateActive(void) { + if (!active) { + return; + } + if (oldActiveGridId == active->Id()) + return; + epgGrid->SetCurrent(oldActiveGridId, false); + epgGrid->SetCurrent(active->Id(), true); + epgGrid->Display(); +} + +void cEpgGrid::DrawHeader(void) { + if (!active) + return; + if (oldActiveGridId == active->Id()) + return; + + int isDummy = 0; + string title = active->Title(); + string shorttext = ""; + string description = ""; + string start = ""; + string stop = ""; + string day = ""; + string date = ""; + int daynumeric = 0; + int month = 0; + int year = 0; + int running = 0; + int elapsed = 0; + int duration = 0; + int durationhours = 0; + string durationminutes = ""; + string channelname = ""; + int channelnumber = 0; + string channelid = ""; + int channellogoexists = 0; + int hasposter = 0; + int posterwidth = -1; + int posterheight = -1; + string posterpath = ""; + + header->ClearTokens(); + header->Clear(); + if (active->IsDummy()) { + isDummy = 1; + start = *(TimeString(active->StartTime())); + stop = *(TimeString(active->EndTime())); + } else { + const cEvent *event = active->Event(); + if (event) { + shorttext = event->ShortText() ? event->ShortText() : ""; + description = event->Description() ? event->Description() : ""; + start = *(event->GetTimeString()); + stop = *(event->GetEndTimeString()); + time_t startTime = event->StartTime(); + day = *WeekDayName(startTime); + date = *ShortDateString(startTime); + struct tm * sStartTime = localtime(&startTime); + year = sStartTime->tm_year + 1900; + daynumeric = sStartTime->tm_mday; + month = sStartTime->tm_mon+1; + time_t now = time(NULL); + if ((now >= event->StartTime()) && (now <= event->EndTime())) + running = 1; + if (running) { + elapsed = (now - event->StartTime())/60; + } + duration = event->Duration() / 60; + durationhours = event->Duration() / 3600; + durationminutes = *cString::sprintf("%.2d", (event->Duration() / 60)%60); + + static cPlugin *pScraper = GetScraperPlugin(); + if (pScraper) { + ScraperGetPoster call; + call.event = event; + if (pScraper->Service("GetPoster", &call)) { + hasposter = FileExists(call.poster.path); + posterwidth = call.poster.width; + posterheight = call.poster.height; + posterpath = call.poster.path; + } + } + } + const cChannel *channel = active->Channel(); + if (channel) { + channelname = channel->Name() ? channel->Name() : ""; + channelid = *(channel->GetChannelID().ToString()); + channelnumber = channel->Number(); + channellogoexists = header->ChannelLogoExists(channelid); + } + } + header->AddIntToken("isdummy", isDummy); + header->AddStringToken("title", title); + header->AddStringToken("shorttext", shorttext); + header->AddStringToken("description", description); + header->AddStringToken("start", start); + header->AddStringToken("stop", stop); + header->AddStringToken("day", day); + header->AddStringToken("date", date); + header->AddIntToken("daynumeric", daynumeric); + header->AddIntToken("month", month); + header->AddIntToken("year", year); + header->AddIntToken("running", running); + header->AddIntToken("elapsed", elapsed); + header->AddIntToken("duration", duration); + header->AddIntToken("durationhours", durationhours); + header->AddStringToken("durationminutes", durationminutes); + header->AddStringToken("channelname", channelname); + header->AddStringToken("channelid", channelid); + header->AddIntToken("channelnumber", channelnumber); + header->AddIntToken("channellogoexists", channellogoexists); + header->AddIntToken("hasposter", hasposter); + header->AddIntToken("posterwidth", posterwidth); + header->AddIntToken("posterheight", posterheight); + header->AddStringToken("posterpath", posterpath); + header->Display(); +} + +void cEpgGrid::DrawFooter(void) { + if (!active) + return; + + string textGreen = ""; + string textYellow = ""; + string textRed = tr("Search & Record"); + string textBlue = ""; + if (config.channelJumpMode == eNumJump) { + textGreen = *cString::sprintf("%d %s", channelsPerPage, tr("Channels back")); + textYellow = *cString::sprintf("%d %s", channelsPerPage, tr("Channels forward")); + } else if (config.channelJumpMode == eGroupJump) { + int currentGroup = channelGroups->GetGroup(active->Channel()); + if (currentGroup == channelGroupLast) { + return; + } + channelGroupLast = currentGroup; + textGreen = channelGroups->GetPrevGroupName(currentGroup); + textYellow = channelGroups->GetNextGroupName(currentGroup); + } + if (config.blueKeyMode == eBlueKeySwitch) { + textBlue = tr("Switch"); + } else if (config.blueKeyMode == eBlueKeyEPG) { + textBlue = tr("Detailed EPG"); + } else if (config.blueKeyMode == eBlueKeyFavorites) { + textBlue = tr("Favorites"); + } + + int colorKeys[4] = { Setup.ColorKey0, Setup.ColorKey1, Setup.ColorKey2, Setup.ColorKey3 }; + + footer->Clear(); + footer->ClearTokens(); + + footer->AddStringToken("red", textRed); + footer->AddStringToken("green", textGreen); + footer->AddStringToken("yellow", textYellow); + footer->AddStringToken("blue", textBlue); + + for (int button = 1; button < 5; button++) { + string red = *cString::sprintf("red%d", button); + string green = *cString::sprintf("green%d", button); + string yellow = *cString::sprintf("yellow%d", button); + string blue = *cString::sprintf("blue%d", button); + bool isRed = false; + bool isGreen = false; + bool isYellow = false; + bool isBlue = false; + switch (colorKeys[button-1]) { + case 0: + isRed = true; + break; + case 1: + isGreen = true; + break; + case 2: + isYellow = true; + break; + case 3: + isBlue = true; + break; + default: + break; + } + footer->AddIntToken(red, isRed); + footer->AddIntToken(green, isGreen); + footer->AddIntToken(yellow, isYellow); + footer->AddIntToken(blue, isBlue); + } + + footer->Display(); +} + +bool cEpgGrid::DrawTime(void) { + time_t t = time(0); // get time now + struct tm * now = localtime(&t); + int sec = now->tm_sec; + if (sec == lastSecond) + return false; + + int min = now->tm_min; + int hour = now->tm_hour; + int hourMinutes = hour%12 * 5 + min / 12; + + char monthname[20]; + char monthshort[10]; + strftime(monthshort, sizeof(monthshort), "%b", now); + strftime(monthname, sizeof(monthname), "%B", now); + + watch->Clear(); + watch->ClearTokens(); + watch->AddIntToken("sec", sec); + watch->AddIntToken("min", min); + watch->AddIntToken("hour", hour); + watch->AddIntToken("hmins", hourMinutes); + watch->AddIntToken("year", now->tm_year + 1900); + watch->AddIntToken("day", now->tm_mday); + watch->AddStringToken("time", *TimeString(t)); + watch->AddStringToken("monthname", monthname); + watch->AddStringToken("monthnameshort", monthshort); + watch->AddStringToken("month", *cString::sprintf("%02d", now->tm_mon + 1)); + watch->AddStringToken("dayleadingzero", *cString::sprintf("%02d", now->tm_mday)); + watch->AddStringToken("dayname", *WeekDayNameFull(now->tm_wday)); + watch->AddStringToken("daynameshort", *WeekDayName(now->tm_wday)); + watch->Display(); + + lastSecond = sec; + return true; +} + +cChannelJump *cEpgGrid::GetChannelJumper(void) { + cViewElement *channelJump = rootView->GetViewElement(verChannelJump); + int lastValidChannel = GetLastValidChannel(); + return new cChannelJump(channelJump, lastValidChannel); +} + +/******************************************************************************************************* +* Private Functions +*******************************************************************************************************/ + +void cEpgGrid::ScrollForward(void) { + timeManager->AddMinutes(config.timeStep); + timeline->ScrollForward(config.timeStep); + timeline->Draw(); + for (cChannelEpg *channel = channels.First(); channel; channel = channels.Next(channel)) { + channel->AddNewGridsAtEnd(); + channel->ClearOutdatedStart(); + } +} + +void cEpgGrid::ScrollBack(void) { + timeManager->DelMinutes(config.timeStep); + timeline->ScrollBack(config.timeStep); + timeline->Draw(); + for (cChannelEpg *channel = channels.First(); channel; channel = channels.Next(channel)) { + channel->AddNewGridsAtStart(); + channel->ClearOutdatedEnd(); + } +} + +const cChannel *cEpgGrid::SeekChannelForward(int num) { + if (!active) + return NULL; + const cChannel *currentChannel = active->Channel(); + const cChannel *destChannel; + int found = 0; + for (destChannel = currentChannel; destChannel ; destChannel = Channels.Next(destChannel)) { + if (destChannel->GroupSep()) { + continue; + } + if (config.hideLastChannelGroup && channelGroups->IsInLastGroup(destChannel)) { + destChannel = Channels.Prev(destChannel); + while (destChannel && destChannel->GroupSep()) { + destChannel = Channels.Prev(destChannel); + } + break; + } + if (found == num) + break; + found++; + } + if (!destChannel) + destChannel = Channels.Last(); + while (destChannel && destChannel->GroupSep()) { + destChannel = Channels.Prev(destChannel); + } + return destChannel; +} + +const cChannel *cEpgGrid::SeekChannelBack(int num) { + if (!active) + return NULL; + const cChannel *currentChannel = active->Channel(); + const cChannel *destChannel; + int found = 0; + for (destChannel = currentChannel; destChannel ; destChannel = Channels.Prev(destChannel)) { + if (destChannel->GroupSep()) { + continue; + } + if (found == num) + break; + found++; + } + if (!destChannel) + destChannel = Channels.First(); + while (destChannel && destChannel->GroupSep()) { + destChannel = Channels.Next(destChannel); + } + return destChannel; +} + +void cEpgGrid::Debug(void) { + esyslog("tvguideng: ------------ debugging EpgGrid -------------"); + timeManager->Debug(); + for (cChannelEpg *channel = channels.First(); channel; channel = channels.Next(channel)) { + channel->Debug(); + } + if (active) { + esyslog("tvguideng: Active Grid Element:"); + active->Debug(); + } else { + esyslog("tvguideng: No Active Grid Element"); + } + esyslog("tvguideng: -----------------------------------------------"); +}
\ No newline at end of file diff --git a/epggrid.h b/epggrid.h new file mode 100644 index 0000000..4514f5b --- /dev/null +++ b/epggrid.h @@ -0,0 +1,73 @@ +#ifndef __TVGUIDE_EPGGRID_H +#define __TVGUIDE_EPGGRID_H + +#include "config.h" +#include "channelepg.h" +#include "timeline.h" +#include "channelgroups.h" +#include "channeljump.h" +#include "libskindesigner/osdelements.h" + +class cEpgGrid { +private: + int lastSecond; + int channelsPerPage; + int channelGroupLast; + long oldActiveGridId; + cOsdView *rootView; + cTimeManager *timeManager; + cViewElement *back; + cViewElement *header; + cViewElement *watch; + cViewElement *footer; + cViewGrid *channelsGrid; + cViewGrid *epgGrid; + cTimeline *timeline; + cChannelgroups *channelGroups; + cList<cChannelEpg> channels; + cGridElement *active; + set<int> deletedChannels; + void SetActiveGrid(cGridElement *newActive); + void ScrollForward(void); + void ScrollBack(void); + const cChannel *SeekChannelForward(int num); + const cChannel *SeekChannelBack(int num); + void ClearOutdatedChannelHeaders(void); +public: + cEpgGrid(cOsdView *rootView, cTimeManager *timeManager); + virtual ~cEpgGrid(void); + void Deactivate(bool hide) { lastSecond = -1; rootView->Deactivate(hide); }; + void Activate(void) { DrawTime(); rootView->Activate(); }; + void Init(const cChannel *startChannel); + void CreateChannels(const cChannel *startChannel, int activeChannel); + void Clear(void); + void SetTimers(void); + void RebuildEpgGrid(void); + bool TimeForward(void); + bool TimeBack(void); + bool ChannelForward(void); + bool ChannelBack(void); + const cChannel *GetNextChannelNumJump(void); + const cChannel *GetPrevChannelNumJump(void); + const cChannel *GetNextChannelGroupJump(void); + const cChannel *GetPrevChannelGroupJump(void); + bool IsFirstGroup(void); + bool IsLastGroup(void); + bool IsSecondLastGroup(void); + const cChannel *GetCurrentChannel(void); + int GetLastValidChannel(void); + const cEvent *GetCurrentEvent(void); + void DrawChannelHeaders(void); + void DrawTimeline(void); + void DrawGrid(void); + void UpdateActive(void); + void DrawHeader(void); + void DrawFooter(void); + void DrawChannelgroups(void); + bool DrawTime(void); + void Flush(void) { rootView->Display(); }; + cChannelJump *GetChannelJumper(void); + void Debug(void); +}; + +#endif //__TVGUIDE_EPGGRID_H diff --git a/gridelement.c b/gridelement.c new file mode 100644 index 0000000..fe36128 --- /dev/null +++ b/gridelement.c @@ -0,0 +1,56 @@ +#include "gridelement.h"
+#include "channelepg.h"
+
+long cGridElement::idCounter;
+
+cGridElement::cGridElement(cChannelEpg *owner) {
+ id = idCounter;
+ idCounter++;
+ init = true;
+ active = false;
+ hasTimer = false;
+ hasSwitchTimer = false;
+ this->owner = owner;
+}
+
+cGridElement::~cGridElement(void) {
+}
+
+bool cGridElement::Match(time_t t) {
+ if ((StartTime() < t) && (EndTime() > t))
+ return true;
+ else
+ return false;
+}
+
+bool cGridElement::IsFirst(void) {
+ return owner->IsFirst(this);
+}
+
+int cGridElement::CalcOverlap(cGridElement *neighbor) {
+ int overlap = 0;
+ if (Intersects(neighbor)) {
+ if ((StartTime() <= neighbor->StartTime()) && (EndTime() <= neighbor->EndTime())) {
+ overlap = EndTime() - neighbor->StartTime();
+ } else if ((StartTime() >= neighbor->StartTime()) && (EndTime() >= neighbor->EndTime())) {
+ overlap = neighbor->EndTime() - StartTime();
+ } else if ((StartTime() >= neighbor->StartTime()) && (EndTime() <= neighbor->EndTime())) {
+ overlap = Duration();
+ } else if ((StartTime() <= neighbor->StartTime()) && (EndTime() >= neighbor->EndTime())) {
+ overlap = neighbor->EndTime() - neighbor->StartTime();
+ }
+ }
+ return overlap;
+}
+
+bool cGridElement::Intersects(cGridElement *neighbor) {
+ return ! ( (neighbor->EndTime() <= StartTime()) || (neighbor->StartTime() >= EndTime()) );
+}
+
+const cChannel *cGridElement::Channel(void) {
+ return owner->Channel();
+}
+
+cChannelEpg *cGridElement::Owner(void) {
+ return owner;
+}
diff --git a/gridelement.h b/gridelement.h new file mode 100644 index 0000000..1183047 --- /dev/null +++ b/gridelement.h @@ -0,0 +1,51 @@ +#ifndef __TVGUIDE_GRID_H
+#define __TVGUIDE_GRID_H
+
+#include <vdr/tools.h>
+#include <vdr/epg.h>
+
+class cChannelEpg;
+
+class cGridElement : public cListObject {
+protected:
+ static long idCounter;
+ long id;
+ bool init;
+ bool active;
+ bool hasTimer;
+ bool hasSwitchTimer;
+ cChannelEpg *owner;
+ bool Intersects(cGridElement *neighbor);
+ virtual time_t Duration(void) {};
+ bool dummy;
+public:
+ cGridElement(cChannelEpg *owner);
+ virtual ~cGridElement(void);
+ bool IsNew(void) { return init; };
+ void Dirty(void) { init = true; };
+ void InitFinished(void) { init = false; };
+ void SetActive(void) {active = true;};
+ void SetInActive(void) {active = false;};
+ bool Match(time_t t);
+ virtual time_t StartTime(void) {};
+ virtual time_t EndTime(void) {};
+ virtual void SetStartTime(time_t start) {};
+ virtual void SetEndTime(time_t end) {};
+ int CalcOverlap(cGridElement *neighbor);
+ virtual void SetTimer(void) {};
+ virtual void SetSwitchTimer(void) {};
+ long Id(void) { return id; };
+ bool Active(void) { return active; };
+ bool HasTimer(void) {return hasTimer;};
+ bool HasSwitchTimer(void) {return hasSwitchTimer;};
+ bool IsFirst(void);
+ bool IsDummy(void) { return dummy; };
+ const cChannel *Channel(void);
+ virtual const char *Title(void) {};
+ virtual const char *ShortText(void) { return ""; };
+ virtual const cEvent *Event(void) { return NULL; };
+ cChannelEpg *Owner(void);
+ virtual void Debug(void) {};
+};
+
+#endif //__TVGUIDE_GRID_H
\ No newline at end of file diff --git a/helpers.c b/helpers.c new file mode 100644 index 0000000..1a2ef4a --- /dev/null +++ b/helpers.c @@ -0,0 +1,514 @@ +#include <string> +#include <vector> +#include <sstream> +#include <algorithm> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <vdr/osd.h> +#include <vdr/plugin.h> +#include <vdr/skins.h> +#include "services/epgsearch.h" +#include "helpers.h" + +cPlugin *GetScraperPlugin(void) { + static cPlugin *pScraper = cPluginManager::GetPlugin("scraper2vdr"); + if( !pScraper ) // if it doesn't exit, try tvscraper + pScraper = cPluginManager::GetPlugin("tvscraper"); + return pScraper; +} + +bool FileExists(const string &fullpath) { + struct stat buffer; + return (stat (fullpath.c_str(), &buffer) == 0); +} + +bool FileExists(const string &path, const string &name, const string &ext) { + stringstream fileName; + fileName << path << name << "." << ext; + struct stat buffer; + return (stat (fileName.str().c_str(), &buffer) == 0); +} + +/**************************************************************************************** +* STOPWATCH +****************************************************************************************/ + +cStopWatch::cStopWatch(const char* message) { + start = cTimeMs::Now(); + last = start; + if (message) { + dsyslog("tvguideng: Starting StopWatch %s", message); + } +} + +void cStopWatch::Report(const char* message) { + dsyslog("tvguideng: %s - needed %d ms", message, (int)(cTimeMs::Now() - last)); + last = cTimeMs::Now(); +} + +void cStopWatch::Stop(const char* message) { + dsyslog("tvguideng: %s - needed %d ms", message, (int)(cTimeMs::Now() - start)); +} + +/**************************************************************************************** +* CUTTEXT +****************************************************************************************/ +std::string CutText(std::string text, int width, const cFont *font) { + if (width <= font->Size()) + return text.c_str(); + if (font->Width(text.c_str()) < width) + return text.c_str(); + cTextWrapper twText; + twText.Set(text.c_str(), font, width); + std::string cuttedTextNative = twText.GetLine(0); + std::stringstream sstrText; + sstrText << cuttedTextNative << "..."; + std::string cuttedText = sstrText.str(); + int actWidth = font->Width(cuttedText.c_str()); + if (actWidth > width) { + int overlap = actWidth - width; + int charWidth = font->Width("."); + if (charWidth == 0) + charWidth = 1; + int cutChars = overlap / charWidth; + if (cutChars > 0) { + cuttedTextNative = cuttedTextNative.substr(0, cuttedTextNative.length() - cutChars); + std::stringstream sstrText2; + sstrText2 << cuttedTextNative << "..."; + cuttedText = sstrText2.str(); + } + } + return cuttedText; +} + +/**************************************************************************************** +* StrToLowerCase +****************************************************************************************/ +std::string StrToLowerCase(std::string str) { + std::string lowerCase = str; + const int length = lowerCase.length(); + for(int i=0; i < length; ++i) { + lowerCase[i] = std::tolower(lowerCase[i]); + } + return lowerCase; +} + +/**************************************************************************************** +* GetDirectoryFromTimer +****************************************************************************************/ +std::string GetDirectoryFromTimer(std::string file) { + std::string dir = ""; + size_t found = file.find_last_of('~'); + if (found != std::string::npos) { + dir = file.substr(0, found); + } + return dir; +} + +/**************************************************************************************** +* GetDirectoryFromTimer +****************************************************************************************/ +void ReadRecordingDirectories(std::vector<std::string> *folders, cList<cNestedItem> *rootFolders, cString path) { + cPlugin *epgSearchPlugin = NULL; + epgSearchPlugin = cPluginManager::GetPlugin("epgsearch"); + if (epgSearchPlugin) { + Epgsearch_services_v1_0 *epgSearch = new Epgsearch_services_v1_0; + if (epgSearchPlugin->Service("Epgsearch-services-v1.0", epgSearch)) { + std::set<std::string> epgSearchDirs = epgSearch->handler->DirectoryList(); + std::set<std::string>::iterator it; + for (it = epgSearchDirs.begin(); it != epgSearchDirs.end(); it++) { + std::string newFolder = *it; + std::replace(newFolder.begin(), newFolder.end(), '/', '~'); + folders->push_back(newFolder); + } + } + } else { + cList<cNestedItem> *foldersLevel = NULL; + if (rootFolders) { + foldersLevel = rootFolders; + } else { + foldersLevel = &Folders; + } + for (cNestedItem *folder = foldersLevel->First(); folder; folder = foldersLevel->Next(folder)) { + std::string strFolder = *cString::sprintf("%s%s", *path, folder->Text()); + std::replace(strFolder.begin(), strFolder.end(), '/', '~'); + folders->push_back(strFolder); + cList<cNestedItem> *subItems = folder->SubItems(); + if (subItems) { + std::string strFolder2 = *cString::sprintf("%s%s", *path, folder->Text()); + std::replace(strFolder2.begin(), strFolder2.end(), '/', '~'); + ReadRecordingDirectories(folders, subItems, strFolder2.c_str()); + } + } + } +} + + +/**************************************************************************************** +* DrawRoundedCorners +****************************************************************************************/ +void DrawRoundedCorners(cPixmap *p, int posX, int posY, int width, int height, int radius, int borderWidth, tColor borderColor) { + if( height > 2*radius) { + p->DrawEllipse(cRect(posX, posY, radius, radius), borderColor, -2); + p->DrawEllipse(cRect(posX - borderWidth, posY - borderWidth, radius, radius), clrTransparent, -2); + + p->DrawEllipse(cRect(posX+width - radius, posY, radius, radius), borderColor, -1); + p->DrawEllipse(cRect(posX+width - radius + borderWidth, posY - borderWidth, radius, radius), clrTransparent, -1); + + p->DrawEllipse(cRect(posX, posY + height - radius, radius, radius), borderColor, -3); + p->DrawEllipse(cRect(posX - borderWidth, posY + height - radius + borderWidth, radius, radius), clrTransparent, -3); + + p->DrawEllipse(cRect(posX + width - radius, posY + height - radius, radius, radius), borderColor, -4); + p->DrawEllipse(cRect(posX + width - radius + borderWidth, posY + height - radius + borderWidth, radius, radius), clrTransparent, -4); + } +} + + +/**************************************************************************************** +* SPLTSTRING +****************************************************************************************/ +// split: receives a char delimiter; returns a vector of strings +// By default ignores repeated delimiters, unless argument rep == 1. +std::vector<std::string>& splitstring::split(char delim, int rep) { + if (!flds.empty()) flds.clear(); // empty vector if necessary + std::string work = data(); + std::string buf = ""; + int i = 0; + while (i < work.length()) { + if (work[i] != delim) + buf += work[i]; + else if (rep == 1) { + flds.push_back(buf); + buf = ""; + } else if (buf.length() > 0) { + flds.push_back(buf); + buf = ""; + } + i++; + } + if (!buf.empty()) + flds.push_back(buf); + return flds; +} + +/**************************************************************************************** +* FINDIGNORECASE +****************************************************************************************/ +int FindIgnoreCase(const std::string& expr, const std::string& query) +{ + const char *p = expr.c_str(); + const char *r = strcasestr(p, query.c_str()); + + if (!r) + return -1; + return r - p; +} + + +/**************************************************************************************** +* GetAuxValue +****************************************************************************************/ +char* GetAuxValue(const char* aux, const char* name) { + if (isempty(aux)) + return NULL; + + char* descr = strdup(aux); + char* beginaux = strstr(descr, "<epgsearch>"); + char* endaux = strstr(descr, "</epgsearch>"); + if (!beginaux || !endaux) { + free(descr); + return NULL; + } + + beginaux += 11; // strlen("<epgsearch>"); + endaux[0] = 0; + memmove(descr, beginaux, endaux - beginaux + 1); + + if (strcmp(name, "epgsearch") == 0) + return descr; // full aux + + int namelen = strlen(name); + char catname[100] = ""; + catname[0] = '<'; + memcpy(catname + 1, name, namelen); + catname[1 + namelen] = '>'; + catname[2 + namelen] = 0; + + char* cat = strcasestr(descr, catname); + if (!cat) { + free(descr); + return NULL; + } + + cat += namelen + 2; + char* end = strstr(cat, "</"); + if (!end) { + free(descr); + return NULL; + } + end[0] = 0; + + int catlen = end - cat + 1; + char* value = (char *) malloc(catlen); + memcpy(value, cat, catlen); + + free(descr); + return value; +} + +char* GetAuxValue(const cRecording *recording, const char* name) { + if (!recording || !recording->Info()) + return NULL; + return GetAuxValue(recording->Info()->Aux(), name); +} + +char* GetAuxValue(const cTimer *timer, const char* name) { + if (!timer || !timer->Aux()) + return NULL; + return GetAuxValue(timer->Aux(), name); +} + +/**************************************************************************************** +* FUZZYSEARCH +****************************************************************************************/ + +/****************************************************************************** +FUNCTION afuzzy_init() + Initialization of the fuzzy search routine. This applies to the consequent + calls of the afuzzy_CheckRTR (whole string matching) and afuzzy_CheckSUB + (substring match) routines. afuzzy_init() should be called for each + new pattern or error length. The search is case sensitive + +ARGUMENTS: + p Pattern + kerr Number of possible errors. Shouldn't exceed pattern length + UseFilter Use agrep filter algorithm that speeds up search. + fuzzy pointer to the structure that will be later passes to Check* + (the first 6 elements should be NULLs for the first call) + +RETURN VALUE: + none + +ALGORITHM + see. the article on agrep algorithms. + The only change is accounting transpositions as one edit operation . +******************************************************************************/ +void afuzzy_init(const char *p, int kerr, int UseFilter, AFUZZY *fuzzy) +{ + int cnt, p_len, i, j, l, d, m, dd; + char PatFilter[sizeof(Uint)*8 + 1]; + + fuzzy->k = kerr; + m = strlen(p); + fuzzy->FilterSet = 0; + memset(fuzzy->Map, 0 , sizeof(fuzzy->Map) ); + + if (fuzzy->S) + free(fuzzy->S); + if (fuzzy->R) + free(fuzzy->R); + if (fuzzy->R1) + free(fuzzy->R1); + if (fuzzy->RP) + free(fuzzy->RP); + if (fuzzy->RI) + free(fuzzy->RI); + if (fuzzy->FilterS) + free(fuzzy->FilterS); + + fuzzy->FilterS = NULL; + fuzzy->S = (Uint *)calloc(m + 1, sizeof(Uint)); + fuzzy->R = (Uint *)calloc(fuzzy->k + 1, sizeof(Uint)); + fuzzy->R1 = (Uint *)calloc(fuzzy->k + 1, sizeof(Uint)); + fuzzy->RI = (Uint *)calloc(fuzzy->k + 1, sizeof(Uint)); + fuzzy->RP = (Uint *)calloc(fuzzy->k + 1, sizeof(Uint)); + + for (i = 0, cnt = 0; i < m; i++) + { + l = fuzzy->Map[(unsigned char)p[i]]; + if (!l) + { + l = fuzzy->Map[(unsigned char)p[i]] = ++cnt; + fuzzy->S[l] = 0; + } + fuzzy->S[l] |= 1 << i; + } + + + for (d = 0; d <= fuzzy->k; d++) + fuzzy->RI[d] = (1 << d) - 1; + + fuzzy->mask_ok = (1 << (m - 1)); + fuzzy->r_size = sizeof(Uint) * (fuzzy->k + 1); + p_len = m; + + if (p_len > (int) sizeof(Uint)*8) + p_len = (int) sizeof(Uint)*8; + + /* If k is zero then no filter is needed! */ + if (fuzzy->k && (p_len >= 2*(fuzzy->k + 1)) ) + { + if (UseFilter) + { + fuzzy->FilterSet = 1; + memset(fuzzy->FilterMap, 0 , sizeof(fuzzy->FilterMap) ); + fuzzy->FilterS = (Uint *)calloc(m + 1, sizeof(Uint)); + + /* Not let's fill the interleaved pattern */ + dd = p_len / (fuzzy->k + 1); + p_len = dd * (fuzzy->k + 1); + + for (i = 0, cnt = 0; i < dd; i++) + for (j = 0; j < fuzzy->k + 1; j++, cnt++) + PatFilter[cnt] = (unsigned char)p[j*dd + i]; + PatFilter[p_len] = 0; + + for (i = 0, cnt = 0; i < p_len; i++) + { + l = fuzzy->FilterMap[(unsigned char)PatFilter[i]]; + if (!l) + { + l = fuzzy->FilterMap[(unsigned char)PatFilter[i]] = ++cnt; + fuzzy->FilterS[l] = 0; + } + fuzzy->FilterS[l] |= 1 << i; + } + fuzzy->filter_ok = 0; + for (i = p_len - fuzzy->k - 1; i <= p_len - 1; i++) /* k+1 times */ + fuzzy->filter_ok |= 1 << i; + + /* k+1 first bits set to 1 */ + fuzzy->filter_shift = (1 << (fuzzy->k + 2)) - 1; + } + } +} + +/****************************************************************************** +FUNCTION afuzzy_free() + Cleaning up after previous afuzzy_init() call. + +ARGUMENTS: + fuzzy pointer to the afuzzy parameters structure + +RETURN VALUE: + none +******************************************************************************/ +void afuzzy_free(AFUZZY *fuzzy) +{ + if (fuzzy->S) + { + free(fuzzy->S); + fuzzy->S = NULL; + } + if (fuzzy->R) + { + free(fuzzy->R); + fuzzy->R = NULL; + } + if (fuzzy->R1) + { + free(fuzzy->R1); + fuzzy->R1 = NULL; + } + if (fuzzy->RP) + { + free(fuzzy->RP); + fuzzy->RP = NULL; + } + if (fuzzy->RI) + { + free(fuzzy->RI); + fuzzy->RI = NULL; + } + if (fuzzy->FilterS) + { + free(fuzzy->FilterS); + fuzzy->FilterS = NULL; + } +} + + +/****************************************************************************** +FUNCTION afuzzy_CheckSUB() + Perform a fuzzy pattern substring matching. afuzzy_init() should be + called previously to initialize the pattern and error length. + Positive result means that some part of the string given matches the + pattern with no more than afuzzy->k errors (1 error = 1 letter + replacement or transposition) + +ARGUMENTS: + t the string to test + fuzzy pointer to the afuzzy parameters structure + +RETURN VALUE: + 0 - no match + > 0 - strings match + +ALGORITHM + ???????????????? +******************************************************************************/ +int afuzzy_checkSUB(const char *t, AFUZZY *fuzzy) +{ + register char c; + register int j, d; + + /* For eficciency this case should be little bit optimized */ + if (!fuzzy->k) + { + Uint R = 0, R1; + + for (j = 0; (c = t[j]) != '\0'; j++) + { + R1 = ( ((R<<1) | 1) & fuzzy->S[fuzzy->Map[(unsigned char)c]]); + R = R1; + + if (R1 & fuzzy->mask_ok) + return 1; + } /* end for (register int j = 0 ... */ + return 0; + } + + if (fuzzy->FilterSet && !afuzzy_checkFLT(t, fuzzy)) + return 0; + + memcpy(fuzzy->R, fuzzy->RI, fuzzy->r_size); /* R = RI */ + + for (j = 0; (c = t[j]); j++) + { + for (d = 0; d <= fuzzy->k; d++) + { + fuzzy->R1[d] = (((fuzzy->R[d]<<1) | 1) & + fuzzy->S[fuzzy->Map[(unsigned char)c]]); + if (d > 0) + fuzzy->R1[d] |= ((fuzzy->R[d-1] | fuzzy->R1[d-1])<<1) | 1 | + fuzzy->R[d-1]; + } + if (fuzzy->R1[fuzzy->k] & fuzzy->mask_ok) + return j; + + memcpy(fuzzy->R, fuzzy->R1, fuzzy->r_size); + + } /* end for (register int j = 0 ... */ + + return 0; +} + +static int afuzzy_checkFLT(const char *t, AFUZZY *fuzzy) +{ + register Uint FilterR = 0; + register Uint FilterR1; + register int j; + + for (j = 0; t[j] != '\0'; j++) + { + FilterR1 = ( ((FilterR<<(fuzzy->k+1)) | fuzzy->filter_shift) & + fuzzy->FilterS[fuzzy->FilterMap[(unsigned char)t[j]]]); + if (FilterR1 & fuzzy->filter_ok) + return 1; + FilterR = FilterR1; + } /* end for (register int j = 0 ... */ + + return 0; +} diff --git a/helpers.h b/helpers.h new file mode 100644 index 0000000..5f16be6 --- /dev/null +++ b/helpers.h @@ -0,0 +1,87 @@ +#ifndef __TVGUIDE_HELPERS_H +#define __TVGUIDE_HELPERS_H + +#include <string> +#include <vector> +#include <vdr/font.h> +#include <vdr/recording.h> +#include <vdr/plugin.h> + +using namespace std; + +cPlugin *GetScraperPlugin(void); + +bool FileExists(const string &fullpath); +bool FileExists(const string &path, const string &name, const string &ext); + +class cStopWatch { +private: + uint64_t start; + uint64_t last; +public: + cStopWatch(const char* message = NULL); + ~cStopWatch(void) {}; + void Report(const char* message); + void Stop(const char* message); +}; + +std::string CutText(std::string text, int width, const cFont *font); +std::string StrToLowerCase(std::string str); +std::string GetDirectoryFromTimer(std::string file); +void ReadRecordingDirectories(std::vector<std::string> *folders, cList<cNestedItem> *rootFolders, cString path); +void DrawRoundedCorners(cPixmap *p, int posX, int posY, int width, int height, int radius, int borderWidth, tColor borderColor); + +class splitstring : public std::string { + std::vector<std::string> flds; +public: + splitstring(const char *s) : std::string(s) { }; + std::vector<std::string>& split(char delim, int rep=0); +}; + +int FindIgnoreCase(const std::string& expr, const std::string& query); + +char* GetAuxValue(const char* aux, const char* name); +char* GetAuxValue(const cRecording *recording, const char* name); +char* GetAuxValue(const cTimer* timer, const char* name); + +#ifndef _AFUZZY_H +#define _AFUZZY_H + +// source from: +/* + Leonid Boitsov 2002. (itman@narod.ru) + C version of Stas Namin. + This code is a GPL software and is distributed under GNU + public licence without any warranty. +*/ + +typedef unsigned int Uint; + +#define MaxPatSize (sizeof(Uint) * 8) + +typedef struct +{ + Uint *R, + *R1, + *RP, + *S, + *RI; + Uint *FilterS; + + int Map[256]; + int FilterMap[256]; + int k; + Uint mask_ok; + Uint filter_ok; + Uint filter_shift; + int r_size; + int FilterSet; +} AFUZZY; + +void afuzzy_init(const char *p, int kerr, int UseFilter, AFUZZY *fuzzy); +void afuzzy_free(AFUZZY *fuzzy); +int afuzzy_checkSUB(const char *t, AFUZZY *fuzzy); +static int afuzzy_checkFLT(const char *t, AFUZZY *fuzzy); +#endif + +#endif // __TVGUIDE_HELPERS_H diff --git a/libskindesigner/osdelements.c b/libskindesigner/osdelements.c new file mode 100644 index 0000000..11915a8 --- /dev/null +++ b/libskindesigner/osdelements.c @@ -0,0 +1,203 @@ +#include "osdelements.h" + +/********************************************************************** +* cOsdElement +**********************************************************************/ +cOsdElement::cOsdElement(cSkinDisplayPlugin *view) { + this->view = view; +} + +cOsdElement::~cOsdElement() { +} + +void cOsdElement::ClearTokens(void) { + stringTokens.clear(); + intTokens.clear(); + loopTokens.clear(); +} + +void cOsdElement::AddStringToken(string key, string value) { + stringTokens.insert(pair<string,string>(key, value)); +} + +void cOsdElement::AddIntToken(string key, int value) { + intTokens.insert(pair<string,int>(key, value)); +} + +void cOsdElement::AddLoopToken(string loopName, map<string, string> &tokens) { + map<string, vector<map<string, string> > >::iterator hitLoop = loopTokens.find(loopName); + if (hitLoop == loopTokens.end()) { + vector<map<string, string> > tokenVector; + tokenVector.push_back(tokens); + loopTokens.insert(pair<string, vector<map<string, string> > >(loopName, tokenVector)); + } else { + vector<map<string, string> > *tokenVector = &hitLoop->second; + tokenVector->push_back(tokens); + } +} + +bool cOsdElement::ChannelLogoExists(string channelId) { + return view->ChannelLogoExists(channelId); +} + +string cOsdElement::GetEpgImagePath(void) { + return view->GetEpgImagePath(); +} + + +/********************************************************************** +* cViewElement +**********************************************************************/ +cViewElement::cViewElement(cSkinDisplayPlugin *view, int viewElementID) : cOsdElement(view) { + this->viewElementID = viewElementID; +} + +cViewElement::~cViewElement() { +} + +void cViewElement::Clear(void) { + if (!view) + return; + view->ClearViewElement(viewElementID); +} + +void cViewElement::Display(void) { + if (!view) + return; + view->SetViewElementIntTokens(&intTokens); + view->SetViewElementStringTokens(&stringTokens); + view->SetViewElementLoopTokens(&loopTokens); + view->DisplayViewElement(viewElementID); +} + +/********************************************************************** +* cViewGrid +**********************************************************************/ +cViewGrid::cViewGrid(cSkinDisplayPlugin *view, int viewGridID) : cOsdElement(view) { + this->viewGridID = viewGridID; +} + +cViewGrid::~cViewGrid() { +} + +void cViewGrid::SetGrid(long gridID, double x, double y, double width, double height) { + if (!view) + return; + view->SetGrid(viewGridID, gridID, x, y, width, height, &intTokens, &stringTokens); +} + +void cViewGrid::SetCurrent(long gridID, bool current) { + if (!view) + return; + view->SetGridCurrent(viewGridID, gridID, current); +} + +void cViewGrid::MoveGrid(long gridID, double x, double y, double width, double height) { + if (!view) + return; + view->SetGrid(viewGridID, gridID, x, y, width, height, NULL, NULL); +} + +void cViewGrid::Delete(long gridID) { + if (!view) + return; + view->DeleteGrid(viewGridID, gridID); +} + +void cViewGrid::Clear(void) { + if (!view) + return; + view->ClearGrids(viewGridID); +} + +void cViewGrid::Display(void) { + if (!view) + return; + view->DisplayGrids(viewGridID); +} + +/********************************************************************** +* cViewTab +**********************************************************************/ +cViewTab::cViewTab(cSkinDisplayPlugin *view) : cOsdElement(view) { +} + +cViewTab::~cViewTab() { +} + +void cViewTab::Init(void) { + view->SetTabIntTokens(&intTokens); + view->SetTabStringTokens(&stringTokens); + view->SetTabLoopTokens(&loopTokens); + view->SetTabs(); +} + +void cViewTab::Left(void) { + view->TabLeft(); +} + +void cViewTab::Right(void) { + view->TabRight(); +} + +void cViewTab::Up(void) { + view->TabUp(); +} + +void cViewTab::Down(void) { + view->TabDown(); +} + +void cViewTab::Display(void) { + if (!view) + return; + view->DisplayTabs(); +} + +/********************************************************************** +* cOsdView +**********************************************************************/ +cOsdView::cOsdView(cSkinDisplayPlugin *displayPlugin) { + this->displayPlugin = displayPlugin; +} + +cOsdView::~cOsdView() { + delete displayPlugin; +} + +void cOsdView::Deactivate(bool hide) { + if (!displayPlugin) + return; + displayPlugin->Deactivate(hide); +} + +void cOsdView::Activate(void) { + if (!displayPlugin) + return; + displayPlugin->Activate(); +} + +cViewElement *cOsdView::GetViewElement(int viewElementID) { + if (!displayPlugin) + return NULL; + return new cViewElement(displayPlugin, viewElementID); +} + +cViewGrid *cOsdView::GetViewGrid(int viewGridID) { + if (!displayPlugin) + return NULL; + displayPlugin->InitGrids(viewGridID); + return new cViewGrid(displayPlugin, viewGridID); +} + +cViewTab *cOsdView::GetViewTabs(void) { + if (!displayPlugin) + return NULL; + return new cViewTab(displayPlugin); +} + +void cOsdView::Display(void) { + if (!displayPlugin) + return; + displayPlugin->Flush(); +} diff --git a/libskindesigner/osdelements.h b/libskindesigner/osdelements.h new file mode 100644 index 0000000..057a1fd --- /dev/null +++ b/libskindesigner/osdelements.h @@ -0,0 +1,91 @@ +#ifndef __OSDELEMENTS_H +#define __OSDELEMENTS_H + +#include <vdr/plugin.h> +#include "services.h" + +/********************************************************************** +* cOsdElement +**********************************************************************/ + +class cOsdElement { +protected: + cSkinDisplayPlugin *view; + map < string, string > stringTokens; + map < string, int > intTokens; + map < string, vector< map< string, string > > > loopTokens; +public: + cOsdElement(cSkinDisplayPlugin *view); + virtual ~cOsdElement(); + void AddLoopToken(string loopName, map<string, string> &tokens); + void AddStringToken(string key, string value); + void AddIntToken(string key, int value); + void ClearTokens(void); + bool ChannelLogoExists(string channelId); + string GetEpgImagePath(void); +}; + +/********************************************************************** +* cViewElement +**********************************************************************/ +class cViewElement : public cOsdElement { +private: + int viewElementID; +public: + cViewElement(cSkinDisplayPlugin *view, int viewElementID); + virtual ~cViewElement(); + void Clear(void); + void Display(void); +}; + +/********************************************************************** +* cViewGrid +**********************************************************************/ +class cViewGrid : public cOsdElement { +private: + int viewGridID; +public: + cViewGrid(cSkinDisplayPlugin *view, int viewGridID); + virtual ~cViewGrid(); + void SetGrid(long gridID, double x, double y, double width, double height); + void SetCurrent(long gridID, bool current); + void MoveGrid(long gridID, double x, double y, double width, double height); + void Delete(long gridID); + void Clear(void); + void Display(void); +}; + +/********************************************************************** +* cViewTab +**********************************************************************/ +class cViewTab : public cOsdElement { +private: +public: + cViewTab(cSkinDisplayPlugin *view); + virtual ~cViewTab(); + void Init(void); + void Left(void); + void Right(void); + void Up(void); + void Down(void); + void Display(void); +}; + +/********************************************************************** +* cOsdView +**********************************************************************/ +class cOsdView { +private: + cSkinDisplayPlugin *displayPlugin; +public: + cOsdView(cSkinDisplayPlugin *displayPlugin); + virtual ~cOsdView(); + void Deactivate(bool hide); + void Activate(void); + cViewElement *GetViewElement(int viewElementID); + cViewGrid *GetViewGrid(int viewGridID); + cViewTab *GetViewTabs(void); + void Display(void); +}; + +#endif // __OSDELEMENTS_H
\ No newline at end of file diff --git a/libskindesigner/services.h b/libskindesigner/services.h new file mode 100644 index 0000000..8fa9c69 --- /dev/null +++ b/libskindesigner/services.h @@ -0,0 +1,131 @@ +#ifndef __SKINDESIGNERSERVICES_H +#define __SKINDESIGNERSERVICES_H + +using namespace std; + +#include <string> +#include <vector> +#include <map> + +enum eMenuType { + mtList, + mtText +}; + +class cSDDisplayMenu : public cSkinDisplayMenu { +public: + virtual void SetTitle(const char *Title); + virtual void SetPluginMenu(string name, int menu, int type, bool init); + virtual bool SetItemPlugin(map<string,string> *stringTokens, map<string,int> *intTokens, map<string,vector<map<string,string> > > *loopTokens, int Index, bool Current, bool Selectable); + virtual bool SetPluginText(map<string,string> *stringTokens, map<string,int> *intTokens, map<string,vector<map<string,string> > > *loopTokens); +}; + +class cSkinDisplayPlugin { +public: + cSkinDisplayPlugin(void); + virtual ~cSkinDisplayPlugin(void); + virtual void Deactivate(bool hide); + virtual void Activate(void); + virtual void ClearViewElement(int id); + virtual void DisplayViewElement(int id); + virtual void SetViewElementIntTokens(map<string,int> *intTokens); + virtual void SetViewElementStringTokens(map<string,string> *stringTokens); + virtual void SetViewElementLoopTokens(map<string,vector<map<string,string> > > *loopTokens); + virtual void InitGrids(int viewGridID); + virtual void SetGrid(int viewGridID, long gridID, double x, double y, double width, double height, map<string,int> *intTokens, map<string,string> *stringTokens); + virtual void SetGridCurrent(int viewGridID, long gridID, bool current); + virtual void DeleteGrid(int viewGridID, long gridID); + virtual void DisplayGrids(int viewGridID); + virtual void ClearGrids(int viewGridID); + virtual void SetTabIntTokens(map<string,int> *intTokens); + virtual void SetTabStringTokens(map<string,string> *stringTokens); + virtual void SetTabLoopTokens(map<string,vector<map<string,string> > > *loopTokens); + virtual void SetTabs(void); + virtual void TabLeft(void); + virtual void TabRight(void); + virtual void TabUp(void); + virtual void TabDown(void); + virtual void DisplayTabs(void); + virtual void Flush(void); + virtual bool ChannelLogoExists(string channelId); + virtual string GetEpgImagePath(void); +}; + +/********************************************************************* +* Data Structures for Service Calls +*********************************************************************/ + +// Data structure for service "RegisterPlugin" +class RegisterPlugin { +public: + RegisterPlugin(void) { + name = ""; + }; + void SetMenu(int key, string templateName) { + menus.insert(pair<int, string>(key, templateName)); + } + void SetView(int key, string templateName) { + views.insert(pair<int, string>(key, templateName)); + } + void SetSubView(int view, int subView, string templateName) { + pair<int, string> sub = make_pair(subView, templateName); + subViews.insert(pair<int, pair<int, string> >(view, sub)); + } + void SetViewElement(int view, int viewElement, string name) { + map< int, map<int, string> >::iterator hit = viewElements.find(view); + if (hit == viewElements.end()) { + map<int, string> vE; + vE.insert(pair<int, string >(viewElement, name)); + viewElements.insert(pair<int, map < int, string > >(view, vE)); + } else { + (hit->second).insert(pair<int, string >(viewElement, name)); + } + } + void SetViewGrid(int view, int viewGrid, string name) { + map< int, map<int, string> >::iterator hit = viewGrids.find(view); + if (hit == viewGrids.end()) { + map<int, string> vG; + vG.insert(pair<int, string >(viewGrid, name)); + viewGrids.insert(pair<int, map < int, string > >(view, vG)); + } else { + (hit->second).insert(pair<int, string >(viewGrid, name)); + } + } +// in + string name; //name of plugin + map< int, string > menus; //menus as key -> templatename hashmap + map< int, string> views; //standalone views as key -> templatename hashmap + multimap< int, pair <int, string> > subViews; //subviews of standalone views as view -> (subview, templatename) multimap + map< int, map <int, string> > viewElements; //viewelements as key -> (viewelement, viewelementname) hashmap + map< int, map <int, string> > viewGrids; //viewgrids as key -> (viewgrid, viewgridname) hashmap +//out +}; + +// Data structure for service "GetDisplayMenu" +class GetDisplayMenu { +public: + GetDisplayMenu(void) { + displayMenu = NULL; + }; +// in +//out + cSDDisplayMenu *displayMenu; +}; + +// Data structure for service "GetDisplayPlugin" +class GetDisplayPlugin { +public: + GetDisplayPlugin(void) { + pluginName = ""; + viewID = -1; + subViewID = -1; + displayPlugin = NULL; + }; +// in + string pluginName; + int viewID; + int subViewID; +//out + cSkinDisplayPlugin *displayPlugin; +}; +#endif //__SKINDESIGNERSERVICES_H diff --git a/libskindesigner/skindesignerosdbase.c b/libskindesigner/skindesignerosdbase.c new file mode 100644 index 0000000..4d40615 --- /dev/null +++ b/libskindesigner/skindesignerosdbase.c @@ -0,0 +1,209 @@ +#include "skindesignerosdbase.h" +#include "osdelements.h" + +/********************************************************************** +* cSkindesignerOsdObject +**********************************************************************/ + +cSkindesignerOsdObject::cSkindesignerOsdObject(void) { + pSkinDesigner = NULL; + pluginName = ""; +} + +cSkindesignerOsdObject::~cSkindesignerOsdObject() { +} + +bool cSkindesignerOsdObject::InitSkindesignerInterface(string pluginName) { + this->pluginName = pluginName; + pSkinDesigner = cPluginManager::GetPlugin("skindesigner"); + if (!pSkinDesigner) { + return false; + } + return true; +} + +cOsdView *cSkindesignerOsdObject::GetOsdView(int viewID, int subViewID) { + cSkinDisplayPlugin *displayPlugin = NULL; + cOsdView *view = NULL; + GetDisplayPlugin call; + call.pluginName = pluginName; + call.viewID = viewID; + call.subViewID = subViewID; + bool ok = pSkinDesigner->Service("GetDisplayPlugin", &call); + if (ok) { + displayPlugin = call.displayPlugin; + view = new cOsdView(displayPlugin); + } + return view; +} + +/********************************************************************** +* cSkindesignerOsdItem +**********************************************************************/ +cSkindesignerOsdItem::cSkindesignerOsdItem(eOSState State) : cOsdItem(State) { + sdDisplayMenu = NULL; +} + +cSkindesignerOsdItem::cSkindesignerOsdItem(const char *Text, eOSState State, bool Selectable) : cOsdItem(Text, State, Selectable) { + sdDisplayMenu = NULL; +} + +cSkindesignerOsdItem::~cSkindesignerOsdItem() { + +} + +void cSkindesignerOsdItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) { + if (sdDisplayMenu) { + if (!sdDisplayMenu->SetItemPlugin(&stringTokens, &intTokens, &loopTokens, Index, Current, Selectable)) { + DisplayMenu->SetItem(Text(), Index, Current, Selectable); + } + } else { + DisplayMenu->SetItem(Text(), Index, Current, Selectable); + } +} + +void cSkindesignerOsdItem::AddStringToken(string key, string value) { + stringTokens.insert(pair<string,string>(key, value)); +} + +void cSkindesignerOsdItem::AddIntToken(string key, int value) { + intTokens.insert(pair<string,int>(key, value)); +} + +void cSkindesignerOsdItem::AddLoopToken(string loopName, map<string, string> &tokens) { + map<string, vector<map<string, string> > >::iterator hitLoop = loopTokens.find(loopName); + if (hitLoop == loopTokens.end()) { + vector<map<string, string> > tokenVector; + tokenVector.push_back(tokens); + loopTokens.insert(pair<string, vector<map<string, string> > >(loopName, tokenVector)); + } else { + vector<map<string, string> > *tokenVector = &hitLoop->second; + tokenVector->push_back(tokens); + } +} + + +/********************************************************************** +* cSkindesignerOsdMenu +**********************************************************************/ +cSkindesignerOsdMenu::cSkindesignerOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4) : cOsdMenu(Title, c0, c1, c2, c3, c4) { + init = true; + displayText = false; + sdDisplayMenu = NULL; + pluginName = ""; + SetMenuCategory(mcPlugin); + SetSkinDesignerDisplayMenu(); +} + +cSkindesignerOsdMenu::~cSkindesignerOsdMenu() { + +} + +void cSkindesignerOsdMenu::SetPluginMenu(int menu, eMenuType type) { + if (type == mtList) + displayText = false; + else if (type == mtText) + displayText = true; + + if (sdDisplayMenu) { + sdDisplayMenu->SetPluginMenu(pluginName, menu, type, init); + } + init = false; +} + +bool cSkindesignerOsdMenu::SetSkinDesignerDisplayMenu(void) { + static cPlugin *pSkinDesigner = cPluginManager::GetPlugin("skindesigner"); + if (!pSkinDesigner) { + return false; + } + GetDisplayMenu call; + bool ok = pSkinDesigner->Service("GetDisplayMenu", &call); + if (ok && call.displayMenu) { + sdDisplayMenu = call.displayMenu; + return true; + } + return false; +} + +void cSkindesignerOsdMenu::ClearTokens(void) { + text = ""; + stringTokens.clear(); + intTokens.clear(); + loopTokens.clear(); +} + +void cSkindesignerOsdMenu::AddStringToken(string key, string value) { + stringTokens.insert(pair<string,string>(key, value)); +} + +void cSkindesignerOsdMenu::AddIntToken(string key, int value) { + intTokens.insert(pair<string,int>(key, value)); +} + +void cSkindesignerOsdMenu::AddLoopToken(string loopName, map<string, string> &tokens) { + map<string, vector<map<string, string> > >::iterator hitLoop = loopTokens.find(loopName); + if (hitLoop == loopTokens.end()) { + vector<map<string, string> > tokenVector; + tokenVector.push_back(tokens); + loopTokens.insert(pair<string, vector<map<string, string> > >(loopName, tokenVector)); + } else { + vector<map<string, string> > *tokenVector = &hitLoop->second; + tokenVector->push_back(tokens); + } +} + +void cSkindesignerOsdMenu::TextKeyLeft(void) { + if (!displayText) + return; + DisplayMenu()->Scroll(true, true); +} + +void cSkindesignerOsdMenu::TextKeyRight(void) { + if (!displayText) + return; + DisplayMenu()->Scroll(false, true); +} + +void cSkindesignerOsdMenu::TextKeyUp(void) { + if (!displayText) + return; + DisplayMenu()->Scroll(true, false); +} + +void cSkindesignerOsdMenu::TextKeyDown(void) { + if (!displayText) + return; + DisplayMenu()->Scroll(false, false); +} + +void cSkindesignerOsdMenu::Display(void) { + if (displayText) { + if (sdDisplayMenu) { + sdDisplayMenu->SetTitle(Title()); + if (sdDisplayMenu->SetPluginText(&stringTokens, &intTokens, &loopTokens)) { + sdDisplayMenu->Flush(); + } else { + DisplayMenu()->Clear(); + DisplayMenu()->SetTitle(Title()); + DisplayMenu()->SetText(text.c_str(), false); + DisplayMenu()->Flush(); + } + } else { + DisplayMenu()->Clear(); + DisplayMenu()->SetTitle(Title()); + DisplayMenu()->SetText(text.c_str(), false); + DisplayMenu()->Flush(); + } + return; + } + if (sdDisplayMenu) { + sdDisplayMenu->SetTitle(Title()); + for (cOsdItem *item = First(); item; item = Next(item)) { + cSkindesignerOsdItem *sdItem = dynamic_cast<cSkindesignerOsdItem*>(item); + if (sdItem) { + sdItem->SetDisplayMenu(sdDisplayMenu); + } + } + } + cOsdMenu::Display(); +} diff --git a/libskindesigner/skindesignerosdbase.h b/libskindesigner/skindesignerosdbase.h new file mode 100644 index 0000000..060ec11 --- /dev/null +++ b/libskindesigner/skindesignerosdbase.h @@ -0,0 +1,84 @@ +#ifndef __SKINDESIGNEROSDBASE_H +#define __SKINDESIGNEROSDBASE_H + +#include <string> +#include <vector> +#include <map> +#include <set> +#include <sstream> +#include <vdr/osdbase.h> +#include <vdr/plugin.h> +#include "services.h" + +class cOsdView; + +/********************************************************************** +* cSkindesignerOsdObject +**********************************************************************/ +class cSkindesignerOsdObject : public cOsdObject { +protected: + string pluginName; + cPlugin *pSkinDesigner; + bool InitSkindesignerInterface(string pluginName); + cOsdView *GetOsdView(int viewID, int subViewID = -1); +public: + cSkindesignerOsdObject(void); + virtual ~cSkindesignerOsdObject(); + virtual void Show(void) {}; +}; + +/********************************************************************** +* cSkindesignerOsdItem +**********************************************************************/ +class cSkindesignerOsdItem : public cOsdItem { +private: + cSDDisplayMenu *sdDisplayMenu; + map < string, string > stringTokens; + map < string, int > intTokens; + map < string, vector< map< string, string > > > loopTokens; +protected: +public: + cSkindesignerOsdItem(eOSState State = osUnknown); + cSkindesignerOsdItem(const char *Text, eOSState State = osUnknown, bool Selectable = true); + virtual ~cSkindesignerOsdItem(); + virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable); + void SetDisplayMenu(cSDDisplayMenu *sdDisplayMenu) { this->sdDisplayMenu = sdDisplayMenu; }; + void AddStringToken(string key, string value); + void AddIntToken(string key, int value); + void AddLoopToken(string loopName, map<string, string> &tokens); +}; + +/********************************************************************** +* cSkindesignerOsdMenu +**********************************************************************/ +class cSkindesignerOsdMenu : public cOsdMenu { +private: + bool init; + bool displayText; + string pluginName; + cSDDisplayMenu *sdDisplayMenu; + string text; + map < string, string > stringTokens; + map < string, int > intTokens; + map < string, vector< map< string, string > > > loopTokens; + bool SetSkinDesignerDisplayMenu(void); +protected: + void ClearTokens(void); + void SetPluginName(string name) {pluginName = name; }; + void SetPluginMenu(int menu, eMenuType type); + void SetText(string text) { this->text = text; }; + void AddStringToken(string key, string value); + void AddIntToken(string key, int value); + void AddLoopToken(string loopName, map<string, string> &tokens); + void TextKeyLeft(void); + void TextKeyRight(void); + void TextKeyUp(void); + void TextKeyDown(void); +public: + cSkindesignerOsdMenu(const char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0); + virtual ~cSkindesignerOsdMenu(); + virtual void Display(void); +}; + +#endif // __SKINDESIGNEROSDBASE_H + diff --git a/po/de_DE.po b/po/de_DE.po new file mode 100644 index 0000000..4ab8c7e --- /dev/null +++ b/po/de_DE.po @@ -0,0 +1,604 @@ +# VDR plugin language source file. +msgid "" +msgstr "" +"Project-Id-Version: vdr-tvguideng 0.0.1\n" +"Report-Msgid-Bugs-To: <see README>\n" +"POT-Creation-Date: 2015-03-08 17:24+0200\n" +"PO-Revision-Date: 2015-03-08 17:49+0200\n" +"Last-Translator: Louis\n" +"Language-Team: \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Main Program" +msgstr "Hauptprogramm" + +msgid "Search & Record" +msgstr "Suchen & Aufnehmen" + +msgid "Switch" +msgstr "Umschalten" + +msgid "No EPG available" +msgstr "Keine EPG Daten verfügbar" + +msgid "Channels back" +msgstr "Kanäle zurück" + +msgid "Channels forward" +msgstr "Kanäle vor" + +msgid "Detailed EPG" +msgstr "Detailiertes EPG" + +msgid "Favorites" +msgstr "Favoriten" + +msgid "all Channels" +msgstr "alle Kanäle" + +msgid "root video folder" +msgstr "Video Hauptverzeichnis" + +msgid "Timer Conflict" +msgstr "Timer Konflikt" + +msgid "unknown channel" +msgstr "unbekannter Kanal" + +msgid "Instant Record" +msgstr "Aufnahme" + +msgid "Delete Timer" +msgstr "Timer löschen" + +msgid "Edit Timer" +msgstr "Timer bearbeiten" + +msgid "Timer Timeline" +msgstr "Timer Tagesübersicht" + +msgid "Create Search Timer" +msgstr "Suchtimer anlegen" + +msgid "Search Timers" +msgstr "Suchtimer" + +msgid "Create Series Timer" +msgstr "Serientimer anlegen" + +msgid "Create Switch Timer" +msgstr "Umschalttimer anlegen" + +msgid "Delete Switch Timer" +msgstr "Umschalttimer löschen" + +msgid "Search" +msgstr "Suchen" + +msgid "Check for Timer Conflicts" +msgstr "Auf Timerkonflikte prüfen" + +msgid "Search in Recordings" +msgstr "In Aufnahmen suchen" + +msgid "Timer created" +msgstr "Timer angelegt" + +msgid "Timer NOT created" +msgstr "Timer NICHT angelegt" + +msgid "OK" +msgstr "OK" + +msgid "Set Folder for" +msgstr "Verzeichnis festlegen für" + +msgid "Timer deleted" +msgstr "Timer gelöscht" + +msgid "Timer" +msgstr "Timer" + +msgid "still recording - really delete?" +msgstr "Aufzeichnung läuft - wirklich löschen?" + +msgid "Yes" +msgstr "Ja" + +msgid "No" +msgstr "Nein" + +msgid "One" +msgstr "Ein" + +msgid "detected" +msgstr "gefunden" + +msgid "Timer Conflicts" +msgstr "Timerkonflikte" + +msgid "Show conflict" +msgstr "Konflikt zeigen" + +msgid "timers involved" +msgstr "Timer beteiligt" + +msgid "Ignore Conflicts" +msgstr "" + +msgid "Ignore Conflict" +msgstr "Konflikt ignorieren" + +msgid "No Timer Conflicts found" +msgstr "Keine Timerkonflikte gefunden" + +msgid "Close" +msgstr "Schließen" + +msgid "reruns for" +msgstr "Wiederholungen für" + +msgid "rerun for" +msgstr "Wiederholung für" + +msgid "found" +msgstr "gefunden" + +msgid "Ignore reruns" +msgstr "Wiederholungen ignorieren" + +msgid "No reruns found for Event" +msgstr "Keine Wiederholungen gefunden für" + +msgid "Timer for" +msgstr "Timer für" + +msgid "replaced by rerun" +msgstr "ersetzt durch Wiederholung" + +msgid "Timer Active" +msgstr "Timer aktiv" + +msgid "Priority" +msgstr "Priorität" + +msgid "Lifetime" +msgstr "Lebensdauer" + +msgid "Day" +msgstr "Tag" + +msgid "Timer start time" +msgstr "Timer Start Zeit" + +msgid "Timer stop time" +msgstr "Timer Stop Zeit" + +msgid "Timer File" +msgstr "Timer Datei" + +msgid "New Folder" +msgstr "Neues Verzeichnis" + +msgid "Save" +msgstr "Speichern" + +msgid "Cancel" +msgstr "Abbrechen" + +msgid "Create Series Timer based on" +msgstr "Serientimer anlegen basierend auf" + +msgid "Channel" +msgstr "Kanal" + +msgid "Series Timer start time" +msgstr "Serientimer Start Zeit" + +msgid "Series Timer stop time" +msgstr "Serientimer Stop Zeit" + +msgid "Days to record" +msgstr "Tage" + +msgid "Day to start" +msgstr "Beginnen am" + +msgid "Create Timer" +msgstr "Timer anlegen" + +msgid "Series Timer created" +msgstr "Serientimer angelegt" + +msgid "Start" +msgstr "Start" + +msgid "Stop" +msgstr "Stop" + +msgid "Configure Search Timer based on" +msgstr "Suchtimer konfigurieren basierend auf" + +msgid "Search Expression:" +msgstr "Suchausdruck:" + +msgid "Continue" +msgstr "Weiter" + +msgid "Configure Search Timer for Search String" +msgstr "Suchtimer konfigurieren für Suchbegriff" + +msgid "Manually configure Options" +msgstr "Optionen manuell konfigurieren" + +msgid "Use Template" +msgstr "Template benutzen" + +msgid "EPGSearch Search Timers" +msgstr "EPGSearch Suchtimer" + +msgid "No Search Timers Configured" +msgstr "Keine Suchtimer angelegt" + +msgid "Configure Search Timer Options" +msgstr "Suchtimer konfigurieren" + +msgid "Save Search Timer" +msgstr "Suchtimer speichern" + +msgid "Search String" +msgstr "Suchbegriff" + +msgid "Active" +msgstr "Aktiv" + +msgid "Search Mode" +msgstr "Suchmodus" + +msgid "Use Title" +msgstr "Titel benutzen" + +msgid "Use Subtitle" +msgstr "Untertitel benutzen" + +msgid "Use Description" +msgstr "Beschreibung benutzen" + +msgid "Limit Channels" +msgstr "Kanäle einschränken" + +msgid "Start Channel" +msgstr "Startkanal" + +msgid "Stop Channel" +msgstr "Endekanal" + +msgid "Use Time" +msgstr "Zeit benutzen" + +msgid "Start after" +msgstr "Start nach" + +msgid "Start before" +msgstr "Start vor" + +msgid "Display advanced Options" +msgstr "Erweiterte Optionen anzeigen" + +msgid "Limit Days of the Week" +msgstr "Wochentage beschränken" + +msgid "Select Days" +msgstr "Tage auswählen" + +msgid "Time margin for start in minutes" +msgstr "Zeit vor Start in Minuten" + +msgid "Time margin for stop in minutes" +msgstr "Zeit nach Ende in Minuten" + +msgid "Series Recording" +msgstr "Serienaufnahme" + +msgid "Folder" +msgstr "Verzeichnis" + +msgid "Use VPS" +msgstr "VPS benutzen" + +msgid "Avoid Repeats" +msgstr "Wiederholungen vermeiden" + +msgid "Number of allowed repeats" +msgstr "Anzahl erlaubter Wiederholungen" + +msgid "Compare Title" +msgstr "Titel verglechen" + +msgid "Compare Subtitle" +msgstr "Untertitel vergleichen" + +msgid "Compare Description" +msgstr "Beschreibung vergleichen" + +msgid "Use in Favorites" +msgstr "In Favoriten benutzen" + +msgid "Hide advanced Options" +msgstr "Erweiterte Optionen ausblenden" + +msgid "Display Results for Search Timer" +msgstr "Ergebnisse für Suchtimer anzeigen" + +msgid "Really delete Search Timer" +msgstr "Suchtimer wirklich löschen" + +msgid "Delete only Search Timer" +msgstr "Nur Suchtimer löschen" + +msgid "Delete Search Timer and created Timers" +msgstr "Suchtimer und erzeugte Timer löschen" + +msgid "Search Timer sucessfully created" +msgstr "Suchtimer erfolgreich angelegt" + +msgid "Search Timer update initialised" +msgstr "Suchtimer update initialisiert" + +msgid "Search Timer NOT sucessfully created" +msgstr "Suchtimer NICHT erfolgreich angelegt" + +msgid "Creating Search Timer" +msgstr "Suchtimer anlegen" + +msgid "Search Term" +msgstr "Suchbegriff" + +msgid "Using Template" +msgstr "Template" + +msgid "Use other Template" +msgstr "Anderes Template benutzen" + +msgid "search results for Favorite" +msgstr "Suchergebnisse für Favorit" + +msgid "search result for Favorite" +msgstr "Suchergebnis für Favorit" + +msgid "search results for Search Timer" +msgstr "Treffer für Suchtimer" + +msgid "search result for Search Timer" +msgstr "Treffer für Suchtimer" + +msgid "Nothing found for Search String" +msgstr "Keine Treffer für Suchbegriff" + +msgid "Configure Options for Switchtimer" +msgstr "Optionen für Umschalttimer konfigurieren" + +msgid "Minutes before switching" +msgstr "Minuten vor umschalten" + +msgid "switch" +msgstr "umschalten" + +msgid "announce only" +msgstr "nur ankündigen" + +msgid "ask for switch" +msgstr "vor umschalten fragen" + +msgid "Switch Mode" +msgstr "Umschaltmodus" + +msgid "Create" +msgstr "Anlegen" + +msgid "Switch Timer sucessfully created" +msgstr "Umschalttimer erfolgreich angelegt" + +msgid "Switch Timer NOT sucessfully created" +msgstr "Umschalttimer NICHT erfolgreich angelegt" + +msgid "Switch Timer deleted" +msgstr "Umschalttimer gelöscht" + +msgid "Channel to Search" +msgstr "Suche auf Kanal" + +msgid "Search in title" +msgstr "In Titel suchen" + +msgid "Search in Subtitle" +msgstr "In Untertitel suchen" + +msgid "Search in Description" +msgstr "In Beschreibung suchen" + +msgid "Show Search Options" +msgstr "Suchoptionen anzeigen" + +msgid "Perform Search" +msgstr "Suche ausführen" + +msgid "search results for" +msgstr "Suchergebnisse für" + +msgid "search result for" +msgstr "Suchergebnis für" + +msgid "Adapt Search" +msgstr "Suche anpassen" + +msgid "Search String has to have at least three letters" +msgstr "Suchausdruck muss mindestens drei Zeichen haben" + +msgid "No active Timers" +msgstr "Keine aktiven Timer" + +msgid "Found" +msgstr " " + +msgid "recordings" +msgstr "Aufnahmen gefunden" + +msgid "recording" +msgstr "Aufnahme gefunden" + +msgid "for" +msgstr "für" + +msgid "No recordings found for" +msgstr "Keine Aufnahmen gefunden für" + +msgid "No Favorites available" +msgstr "Keine Favoriten verfügbar" + +msgid "What's on now" +msgstr "Was läuft jetzt" + +msgid "What's on next" +msgstr "Was läuft als nächstes" + +msgid "whole term must appear" +msgstr "vollständiger Ausdruck" + +msgid "all terms must exist" +msgstr "alle Worte" + +msgid "one term must exist" +msgstr "ein Wort" + +msgid "exact match" +msgstr "exakt" + +msgid "regular expression" +msgstr "Regulärer Ausdruck" + +msgid "x channels back / forward" +msgstr "x Kanäle zurück / vor" + +msgid "previous / next channel group" +msgstr "vorherige / nächste Kanalgruppe" + +msgid "Timely Jump" +msgstr "Zeitsprung" + +msgid "Jump to specific channel" +msgstr "Sprung zu deinem bestimmten Kanal" + +msgid "Blue: Channel Switch, Ok: Detailed EPG" +msgstr "Blau: Umschalten, OK: Detailiertes EPG" + +msgid "Blue: Detailed EPG, Ok: Channel Switch" +msgstr "Blau: Detailiertes EPG, OK: Umschalten" + +msgid "Blue: Favorites / Switch, Ok: Detailed EPG" +msgstr "Blau: Favoriten / Umschalten, OK: Det. EPG" + +msgid "Always use root video folder" +msgstr "Immer root Video Verzeichnis benutzen" + +msgid "Select from folder list" +msgstr "Verzeichnis aus Liste auswählen" + +msgid "Use fixed folder" +msgstr "Festes Verzeichnis benutzen" + +msgid "never" +msgstr "nie" + +msgid "if exists" +msgstr "falls vorhanden" + +msgid "always" +msgstr "immer" + +msgid "Show Main Menu Entry" +msgstr "Hauptmenüeintrag anzeigen" + +msgid "Replace VDR Schedules Menu" +msgstr "VDR Programm Menü ersetzen" + +msgid "Display Mode" +msgstr "Anzeigemodus" + +msgid "Channels to display in horizontal View" +msgstr "Anzahl der angezeigten Kanäle horizontal" + +msgid "Channels to display in vertical View" +msgstr "Anzahl der angezeigten Kanäle vertikal" + +msgid "Number of Hours to display" +msgstr "Anzuzeigende Stunden" + +msgid "Big Step (Keys 1 / 3) in hours" +msgstr "Großer Sprung (Tasten 1 / 3) in Stunden" + +msgid "Huge Step (Keys 4 / 6) in hours" +msgstr "Sehr großer Sprung (Tasten 4 / 6) in Stunden" + +msgid "Hide last Channel Group" +msgstr "Letzte Kanalgruppe verstecken" + +msgid "Channel Jump Mode (Keys Green / Yellow)" +msgstr "Kanalsprung Modus (Tasten grün / gelb)" + +msgid "Close TVGuide after channel switch" +msgstr "TVGuide nach Umschalten schließen" + +msgid "Functionality of numeric Keys" +msgstr "Funktion der Nummerntasten" + +msgid "Keys Blue and OK" +msgstr "Tasten Blau und OK" + +msgid "Maximum number of reruns to display" +msgstr "Maximale Anzahl dargestellter Wiederholungen" + +msgid "Minimum timely distance of rerun (in hours)" +msgstr "Zeitlicher Mindestabstand von Wiederholungen (in Stunden)" + +msgid "Limit Channel Numbers for reruns" +msgstr "Kanäle für Wiederholungen beschränken" + +msgid "no limit" +msgstr "keine Beschränkung" + +msgid "Folder for instant Recordings" +msgstr "Verzeichnis für Direktaufnahmen" + +msgid "Use Remotetimers" +msgstr "Remotetimers verwenden" + +msgid "Use \"What's on now\" in favorites" +msgstr "\"Was läuft jetzt\" in Favoriten benutzen" + +msgid "Use \"What's on next\" in favorites" +msgstr "\"Was läuft als nächstes\" in Favoriten benutzen" + +msgid "Use user defined time 1 in favorites" +msgstr "Benutzerdef. Zeit 1 in Favoriten benutzen" + +msgid "Description" +msgstr "Beschreibung" + +msgid "Time" +msgstr "Zeit" + +msgid "Use user defined time 2 in favorites" +msgstr "Benutzerdef. Zeit 2 in Favoriten benutzen" + +msgid "Use user defined time 3 in favorites" +msgstr "Benutzerdef. Zeit 3 in Favoriten benutzen" + +msgid "Use user defined time 4 in favorites" +msgstr "Benutzerdef. Zeit 4 in Favoriten benutzen" + +msgid "Limit channels in favorites" +msgstr "Kanäle in Favoriten beschränken" diff --git a/recmanager.c b/recmanager.c new file mode 100644 index 0000000..fbbf890 --- /dev/null +++ b/recmanager.c @@ -0,0 +1,710 @@ +#define __STL_CONFIG_H + +#include <string> +#include <sstream> +#include <vector> +#include <algorithm> + +#include <vdr/menu.h> +#include "recmanager.h" + +static int CompareRecording(const void *p1, const void *p2) { + return (int)((*(cRecording **)p1)->Start() - (*(cRecording **)p2)->Start()); +} + +cRecManager::cRecManager(void) { + epgSearchPlugin = NULL; + epgSearchAvailable = false; +} + +cRecManager::~cRecManager(void) { +} + +void cRecManager::SetEPGSearchPlugin(void) { + epgSearchPlugin = cPluginManager::GetPlugin("epgsearch"); + if (epgSearchPlugin) { + epgSearchAvailable = true; + } +} + +bool cRecManager::RefreshRemoteTimers(void) { + cString errorMsg; + if (!pRemoteTimers->Service("RemoteTimers::RefreshTimers-v1.0", &errorMsg)) { + esyslog("tvguide: %s", *errorMsg); + return false; + } + return true; +} + +bool cRecManager::CheckEventForTimer(const cEvent *event) { + bool hasTimer = false; + + if (config.useRemoteTimers && pRemoteTimers) { + RemoteTimers_GetMatch_v1_0 rtMatch; + rtMatch.event = event; + pRemoteTimers->Service("RemoteTimers::GetMatch-v1.0", &rtMatch); + if (rtMatch.timerMatch == tmFull) + hasTimer = true; + } else + hasTimer = event->HasTimer(); + + return hasTimer; +} + +cTimer *cRecManager::GetTimerForEvent(const cEvent *event) { + cTimer *timer = NULL; + if (config.useRemoteTimers && pRemoteTimers) { + RemoteTimers_GetMatch_v1_0 rtMatch; + rtMatch.event = event; + pRemoteTimers->Service("RemoteTimers::GetMatch-v1.0", &rtMatch); + timer = rtMatch.timer; + } else + timer = Timers.GetMatch(event); + return timer; +} + +cTimer *cRecManager::createTimer(const cEvent *event, std::string path) { + cTimer *timer = NULL; + if (config.useRemoteTimers && pRemoteTimers) { + timer = createRemoteTimer(event, path); + } else { + timer = createLocalTimer(event, path); + } + return timer; +} + +cTimer *cRecManager::createLocalTimer(const cEvent *event, std::string path) { + cTimer *timer = new cTimer(event); + cTimer *t = Timers.GetTimer(timer); + if (t) { + t->OnOff(); + t->SetEventFromSchedule(); + delete timer; + timer = t; + isyslog("timer %s reactivated", *t->ToDescr()); + } else { + Timers.Add(timer); + isyslog("timer %s added (active)", *timer->ToDescr()); + } + SetTimerPath(timer, event, path); + Timers.SetModified(); + return timer; +} + +cTimer *cRecManager::createRemoteTimer(const cEvent *event, std::string path) { + cTimer *t = new cTimer(event); + SetTimerPath(t, event, path); + RemoteTimers_Timer_v1_0 rt; + rt.timer = t; + pRemoteTimers->Service("RemoteTimers::GetTimer-v1.0", &rt.timer); + if (rt.timer) { + rt.timer->OnOff(); + if (!pRemoteTimers->Service("RemoteTimers::ModTimer-v1.0", &rt)) + rt.timer = NULL; + } else { + rt.timer = t; + if (!pRemoteTimers->Service("RemoteTimers::NewTimer-v1.0", &rt)) + isyslog("%s", *rt.errorMsg); + } + RefreshRemoteTimers(); + return rt.timer; +} + +void cRecManager::SetTimerPath(cTimer *timer, const cEvent *event, std::string path) { + if (config.instRecFolderMode == eFolderFixed) { + Epgsearch_services_v1_2 *epgSearch = new Epgsearch_services_v1_2; + std::string recDir = config.instRecFixedFolder; + std::replace(recDir.begin(), recDir.end(), '/', '~'); + if (strchr(recDir.c_str(), '%') != NULL) { + if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) { + std::string newFileName = epgSearch->handler->Evaluate(recDir, event); + if (strchr(newFileName.c_str(), '%') == NULL) // only set directory to new value if all categories could have been replaced + timer->SetFile(newFileName.c_str()); + else + esyslog("tvguide: timer path not set because replacing variable was not successfull: %s", newFileName.c_str()); + } + } else { + cString newFileName; + if (recDir.size() > 0) { + newFileName = cString::sprintf("%s~%s", recDir.c_str(), timer->File()); + timer->SetFile(*newFileName); + } + } + return; + } + //Set choosen path + cString newFileName; + if (path.size() > 0) { + std::replace(path.begin(), path.end(), '/', '~'); + newFileName = cString::sprintf("%s~%s", path.c_str(), timer->File()); + } else { + newFileName = event->Title(); + } + timer->SetFile(*newFileName); +} + +void cRecManager::DeleteTimer(int timerID) { + cTimer *t = Timers.Get(timerID); + if (!t) + return; + DeleteTimer(t); +} + +void cRecManager::DeleteTimer(const cEvent *event) { + if (!event) + return; + if (config.useRemoteTimers && pRemoteTimers) { + DeleteRemoteTimer(event); + } else { + DeleteLocalTimer(event); + } +} + +void cRecManager::DeleteLocalTimer(const cEvent *event) { + cTimer *t = Timers.GetMatch(event); + if (!t) + return; + DeleteTimer(t); +} + + +void cRecManager::DeleteTimer(cTimer *timer) { + if (timer->Recording()) { + timer->Skip(); + cRecordControls::Process(time(NULL)); + } + isyslog("timer %s deleted", *timer->ToDescr()); + Timers.Del(timer, true); + Timers.SetModified(); +} + +void cRecManager::DeleteRemoteTimer(const cEvent *event) { + RemoteTimers_GetMatch_v1_0 rtMatch; + rtMatch.event = event; + pRemoteTimers->Service("RemoteTimers::GetMatch-v1.0", &rtMatch); + if (rtMatch.timer) { + RemoteTimers_Timer_v1_0 rt; + rt.timer = rtMatch.timer; + isyslog("remotetimer %s deleted", *rt.timer->ToDescr()); + if (!pRemoteTimers->Service("RemoteTimers::DelTimer-v1.0", &rt)) + isyslog("remotetimer error"); + RefreshRemoteTimers(); + } +} + +void cRecManager::SaveTimer(cTimer *timer, cTimer newTimerSettings) { + if (!timer) + return; + + bool active = newTimerSettings.HasFlags(tfActive); + int prio = newTimerSettings.Priority(); + int lifetime = newTimerSettings.Lifetime(); + time_t day = newTimerSettings.Day(); + int start = newTimerSettings.Start(); + int stop = newTimerSettings.Stop(); + std::string fileName = newTimerSettings.File(); + + timer->SetDay(day); + timer->SetStart(start); + timer->SetStop(stop); + timer->SetPriority(prio); + timer->SetLifetime(lifetime); + timer->SetFile(fileName.c_str()); + + if (timer->HasFlags(tfActive) && !active) + timer->ClrFlags(tfActive); + else if (!timer->HasFlags(tfActive) && active) + timer->SetFlags(tfActive); + + timer->SetEventFromSchedule(); + if (config.useRemoteTimers && pRemoteTimers) { + RemoteTimers_Timer_v1_0 rt; + rt.timer = timer; + if (!pRemoteTimers->Service("RemoteTimers::ModTimer-v1.0", &rt)) + rt.timer = NULL; + RefreshRemoteTimers(); + } else { + Timers.SetModified(); + } +} + + +bool cRecManager::IsRecorded(const cEvent *event) { + cTimer *timer = Timers.GetMatch(event); + if (!timer) + return false; + return timer->Recording(); +} + +cTVGuideTimerConflicts *cRecManager::CheckTimerConflict(void) { + cTVGuideTimerConflicts *conflictList = new cTVGuideTimerConflicts(); + if (!epgSearchAvailable) + return conflictList; + Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1; + if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) { + std::list<std::string> conflicts = epgSearch->handler->TimerConflictList(); + int numConflicts = conflicts.size(); + if (numConflicts == 0) + return conflictList; + for (std::list<std::string>::iterator it=conflicts.begin(); it != conflicts.end(); ++it) { + conflictList->AddConflict(*it); + } + } + delete epgSearch; + conflictList->CalculateConflicts(); + return conflictList; +} + +void cRecManager::CreateSeriesTimer(cTimer *seriesTimer) { + seriesTimer->SetEventFromSchedule(); + if (config.useRemoteTimers && pRemoteTimers) { + RemoteTimers_Timer_v1_0 rt; + rt.timer = seriesTimer; + if (!pRemoteTimers->Service("RemoteTimers::NewTimer-v1.0", &rt)) + isyslog("%s", *rt.errorMsg); + RefreshRemoteTimers(); + } else { + Timers.Add(seriesTimer); + Timers.SetModified(); + } +} + + +void cRecManager::ReadEPGSearchTemplates(std::vector<TVGuideEPGSearchTemplate> *epgTemplates) { + cString ConfigDir = cPlugin::ConfigDirectory("epgsearch"); + cString epgsearchConf = "epgsearchtemplates.conf"; + cString fileName = AddDirectory(*ConfigDir, *epgsearchConf); + if (access(fileName, F_OK) == 0) { + FILE *f = fopen(fileName, "r"); + if (f) { + char *s; + cReadLine ReadLine; + while ((s = ReadLine.Read(f)) != NULL) { + char *p = strchr(s, '#'); + if (p) + *p = 0; + stripspace(s); + try { + if (!isempty(s)) { + std::string templ = s; + int posID = templ.find_first_of(":"); + int posName = templ.find_first_of(":", posID+1); + std::string name = templ.substr(posID+1, posName - posID - 1); + std::string templValue = templ.substr(posName); + TVGuideEPGSearchTemplate tmp; + tmp.name = name; + tmp.templValue = templValue; + epgTemplates->push_back(tmp); + } + } catch (...){} + } + } + } +} + +const cEvent **cRecManager::PerformSearchTimerSearch(std::string epgSearchString, int &numResults) { + if (!epgSearchAvailable) + return NULL; + const cEvent **searchResults = NULL; + Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1; + if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) { + std::list<std::string> results = epgSearch->handler->QuerySearch(epgSearchString); + numResults = results.size(); + if (numResults > 0) { + searchResults = new const cEvent *[numResults]; + cSchedulesLock schedulesLock; + const cSchedules *schedules; + schedules = cSchedules::Schedules(schedulesLock); + const cEvent *event = NULL; + int index=0; + for (std::list<std::string>::iterator it=results.begin(); it != results.end(); ++it) { + try { + splitstring s(it->c_str()); + std::vector<std::string> flds = s.split(':', 1); + int eventID = atoi(flds[1].c_str()); + std::string channelID = flds[7]; + tChannelID chanID = tChannelID::FromString(channelID.c_str()); + cChannel *channel = Channels.GetByChannelID(chanID); + if (channel) { + const cSchedule *Schedule = NULL; + Schedule = schedules->GetSchedule(channel); + event = Schedule->GetEvent(eventID); + if (event) { + searchResults[index] = event; + } else + return NULL; + } else + return NULL; + index++; + } catch (...){} + } + } + } + return searchResults; +} + +const cEvent **cRecManager::PerformSearch(Epgsearch_searchresults_v1_0 data, int &numResults) { + if (epgSearchAvailable) { + if (epgSearchPlugin->Service("Epgsearch-searchresults-v1.0", &data)) { + cList<Epgsearch_searchresults_v1_0::cServiceSearchResult> *list = data.pResultList; + if (!list) + return NULL; + int numElements = list->Count(); + const cEvent **searchResults = NULL; + if (numElements > 0) { + searchResults = new const cEvent *[numElements]; + numResults = numElements; + int index = 0; + for (Epgsearch_searchresults_v1_0::cServiceSearchResult *r = list->First(); r ; r = list->Next(r)) { + searchResults[index] = r->event; + index++; + } + } + delete list; + return searchResults; + } + } + return NULL; +} + +void cRecManager::GetSearchTimers(std::vector<cTVGuideSearchTimer> *searchTimer) { + if (!epgSearchAvailable) { + return; + } + Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1; + if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) { + std::list<std::string> searchTimerList; + searchTimerList = epgSearch->handler->SearchTimerList(); + for(std::list<std::string>::iterator it = searchTimerList.begin(); it != searchTimerList.end(); it++) { + cTVGuideSearchTimer timer; + timer.SetEPGSearchString(it->c_str()); + if (timer.Parse()) + searchTimer->push_back(timer); + } + } + std::sort(searchTimer->begin(), searchTimer->end()); +} + +int cRecManager::CreateSearchTimer(std::string epgSearchString) { + int timerID = -1; + if (!epgSearchAvailable) + return timerID; + Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1; + if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) { + timerID = epgSearch->handler->AddSearchTimer(epgSearchString); + } + return timerID; +} + +bool cRecManager::SaveSearchTimer(cTVGuideSearchTimer *searchTimer) { + if (!epgSearchAvailable) + return false; + Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1; + if (searchTimer->GetID() > -1) { + if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) { + bool success = epgSearch->handler->ModSearchTimer(searchTimer->BuildSearchString()); + if (success) { + esyslog("tvguide: search timer with id %d sucessfully modified", searchTimer->GetID()); + return true; + } else { + esyslog("tvguide: error modifying search timer with id %d, build string %s", searchTimer->GetID(), searchTimer->BuildSearchString().c_str()); + return false; + } + } + } else { + if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) { + int timerID = epgSearch->handler->AddSearchTimer(searchTimer->BuildSearchString()); + if (timerID >=0) { + esyslog("tvguide: search timer with id %d sucessfully created", timerID); + return true; + } else { + esyslog("tvguide: error creating search timer, build string %s", searchTimer->BuildSearchString().c_str()); + return false; + } + } + } + return false; +} + +void cRecManager::DeleteSearchTimer(cTVGuideSearchTimer *searchTimer, bool delTimers) { + if (!epgSearchAvailable) + return; + int searchTimerID = searchTimer->GetID(); + if (delTimers) { + cTimer *timer = Timers.First(); + while(timer) { + if (!timer->Recording()) { + char* searchID = GetAuxValue(timer, "s-id"); + if (searchID) { + if (searchTimerID == atoi(searchID)) { + cTimer* timerNext = Timers.Next(timer); + DeleteTimer(timer); + timer = timerNext; + } else { + timer = Timers.Next(timer); + } + free(searchID); + } else { + timer = Timers.Next(timer); + } + } else { + timer = Timers.Next(timer); + } + } + } + Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1; + if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) { + bool success = epgSearch->handler->DelSearchTimer(searchTimerID); + if (success) { + esyslog("tvguide: search timer \"%s\" sucessfully deleted", searchTimer->SearchString().c_str()); + } else { + esyslog("tvguide: error deleting search timer \"%s\"", searchTimer->SearchString().c_str()); + } + } +} + +void cRecManager::UpdateSearchTimers(void) { + if (epgSearchAvailable) { + Epgsearch_updatesearchtimers_v1_0 data; + data.showMessage = false; + epgSearchPlugin->Service("Epgsearch-updatesearchtimers-v1.0", &data); + } +} + +// announceOnly: 0 = switch, 1 = announce only, 2 = ask for switch +bool cRecManager::CreateSwitchTimer(const cEvent *event, cSwitchTimer switchTimer) { + if (epgSearchAvailable && event) { + Epgsearch_switchtimer_v1_0 data; + data.event = event; + data.mode = 1; + data.switchMinsBefore = switchTimer.switchMinsBefore; + data.announceOnly = switchTimer.announceOnly; + data.success = false; + epgSearchPlugin->Service("Epgsearch-switchtimer-v1.0", &data); + cSwitchTimer *t = new cSwitchTimer(event); + SwitchTimers.Add(t); + return data.success; + } + return false; +} + +void cRecManager::DeleteSwitchTimer(const cEvent *event) { + SwitchTimers.DeleteSwitchTimer(event); + if (epgSearchAvailable) { + Epgsearch_switchtimer_v1_0 data; + data.event = event; + data.mode = 2; + data.switchMinsBefore = 0; + data.announceOnly = 0; + data.success = false; + epgSearchPlugin->Service("Epgsearch-switchtimer-v1.0", &data); + } +} + +cRecording **cRecManager::SearchForRecordings(std::string searchString, int &numResults) { + + cRecording **matchingRecordings = NULL; + int num = 0; + numResults = 0; + + for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) { + std::string s1 = recording->Name(); + std::string s2 = searchString; + if (s1.empty() || s2.empty()) continue; + + // tolerance for fuzzy searching: 90% of the shorter text length, but at least 1 + int tolerance = std::max(1, (int)std::min(s1.size(), s2.size()) / 10); + + bool match = FindIgnoreCase(s1, s2) >= 0 || FindIgnoreCase(s2, s1) >= 0; + + if (!match) { + AFUZZY af = { NULL, NULL, NULL, NULL, NULL, NULL, { 0 }, { 0 }, 0, 0, 0, 0, 0, 0 }; + if (s1.size() > 32) s1 = s1.substr(0, 32); + afuzzy_init(s1.c_str(), tolerance, 0, &af); + /* Checking substring */ + int res = afuzzy_checkSUB(s2.c_str(), &af); + afuzzy_free(&af); + match = (res > 0); + } + + if (!match) { + AFUZZY af = { NULL, NULL, NULL, NULL, NULL, NULL, { 0 }, { 0 }, 0, 0, 0, 0, 0, 0 }; + if (s2.size() > 32) s2 = s2.substr(0, 32); + afuzzy_init(s2.c_str(), tolerance, 0, &af); + /* Checking substring */ + int res = afuzzy_checkSUB(s1.c_str(), &af); + afuzzy_free(&af); + match = (res > 0); + } + + if (match) { + matchingRecordings = (cRecording **)realloc(matchingRecordings, (num + 1) * sizeof(cRecording *)); + matchingRecordings[num++] = recording; + } + } + if (num > 0) { + qsort(matchingRecordings, num, sizeof(cRecording *), CompareRecording); + numResults = num; + return matchingRecordings; + } + return NULL; +} + +const cEvent **cRecManager::LoadReruns(const cEvent *event, int &numResults) { + if (epgSearchAvailable && !isempty(event->Title())) { + Epgsearch_searchresults_v1_0 data; + std::string strQuery = event->Title(); + if (config.useSubtitleRerun > 0) { + if (config.useSubtitleRerun == 2 || !isempty(event->ShortText())) + strQuery += "~"; + if (!isempty(event->ShortText())) + strQuery += event->ShortText(); + data.useSubTitle = true; + } else { + data.useSubTitle = false; + } + data.query = (char *)strQuery.c_str(); + data.mode = 0; + data.channelNr = 0; + data.useTitle = true; + data.useDescription = false; + + if (epgSearchPlugin->Service("Epgsearch-searchresults-v1.0", &data)) { + cList<Epgsearch_searchresults_v1_0::cServiceSearchResult>* list = data.pResultList; + if (!list) + return NULL; + const cEvent **searchResults = NULL; + int numElements = list->Count(); + if (numElements > 0) { + searchResults = new const cEvent *[numElements]; + int index = 0; + for (Epgsearch_searchresults_v1_0::cServiceSearchResult *r = list->First(); r; r = list->Next(r)) { + if ((event->ChannelID() == r->event->ChannelID()) && (event->StartTime() == r->event->StartTime())) + continue; + searchResults[index] = r->event; + index++; + } + delete list; + numResults = index; + return searchResults; + } + } + } + return NULL; +} + +void cRecManager::GetFavorites(std::vector<cTVGuideSearchTimer> *favorites) { + if (!epgSearchAvailable) { + return; + } + Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1; + if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) { + std::list<std::string> searchTimerList; + searchTimerList = epgSearch->handler->SearchTimerList(); + for(std::list<std::string>::iterator it = searchTimerList.begin(); it != searchTimerList.end(); it++) { + cTVGuideSearchTimer timer; + timer.SetEPGSearchString(it->c_str()); + if (timer.Parse()) { + if (timer.UseInFavorites()) + favorites->push_back(timer); + } + } + } + +} + +const cEvent **cRecManager::WhatsOnNow(bool nowOrNext, int &numResults) { + std::vector<const cEvent*> tmpResults; + cSchedulesLock schedulesLock; + const cSchedules *schedules = cSchedules::Schedules(schedulesLock); + const cChannel *startChannel = NULL, *stopChannel = NULL; + if (config.favLimitChannels) { + startChannel = Channels.GetByNumber(config.favStartChannel); + stopChannel = Channels.GetByNumber(config.favStopChannel); + } + if (!startChannel) + startChannel = Channels.First(); + + for (const cChannel *channel = startChannel; channel; channel = Channels.Next(channel)) { + if (channel->GroupSep()) continue; + const cSchedule *Schedule = schedules->GetSchedule(channel); + if (!Schedule) continue; + + const cEvent *event = NULL; + if (nowOrNext) + event = Schedule->GetPresentEvent(); + else + event = Schedule->GetFollowingEvent(); + if (event) { + tmpResults.push_back(event); + } + if (stopChannel && (stopChannel->Number() <= channel->Number())) + break; + } + numResults = tmpResults.size(); + const cEvent **results = new const cEvent *[numResults]; + for (int i=0; i<numResults; i++) { + results[i] = tmpResults[i]; + } + + return results; +} + +const cEvent **cRecManager::UserDefinedTime(int userTime, int &numResults) { + std::vector<const cEvent*> tmpResults; + int favTime = 0; + if (userTime == 1) { + favTime = config.favTime1; + } else if (userTime == 2) { + favTime = config.favTime2; + } else if (userTime == 3) { + favTime = config.favTime3; + } else if (userTime == 4) { + favTime = config.favTime4; + } + + time_t now = time(0); + tm *midn = localtime(&now); + midn->tm_sec = 0; + midn->tm_min = 0; + midn->tm_hour = 0; + time_t midnight = mktime(midn); + int hours = favTime/100; + int mins = favTime - hours * 100; + time_t searchTime = midnight + hours*60*60 + mins*60; + if (searchTime < now) + searchTime += 24*60*60; + + cSchedulesLock schedulesLock; + const cSchedules *schedules = cSchedules::Schedules(schedulesLock); + const cChannel *startChannel = NULL, *stopChannel = NULL; + if (config.favLimitChannels) { + startChannel = Channels.GetByNumber(config.favStartChannel); + stopChannel = Channels.GetByNumber(config.favStopChannel); + } + if (!startChannel) + startChannel = Channels.First(); + + for (const cChannel *channel = startChannel; channel; channel = Channels.Next(channel)) { + if (channel->GroupSep()) continue; + const cSchedule *Schedule = schedules->GetSchedule(channel); + if (!Schedule) continue; + const cEvent *event = Schedule->GetEventAround(searchTime); + if (!event) continue; + //if event is more or less over (only 15mns left), take next + if ((event->EndTime() - searchTime) < 15*60) { + event = Schedule->Events()->Next(event); + } + if (event) + tmpResults.push_back(event); + if (stopChannel && (stopChannel->Number() <= channel->Number())) + break; + } + + numResults = tmpResults.size(); + const cEvent **results = new const cEvent *[numResults]; + for (int i=0; i<numResults; i++) { + results[i] = tmpResults[i]; + } + return results; +} diff --git a/recmanager.h b/recmanager.h new file mode 100644 index 0000000..cff14fd --- /dev/null +++ b/recmanager.h @@ -0,0 +1,64 @@ +#ifndef __TVGUIDE_RECMMANAGER_H +#define __TVGUIDE_RECMMANAGER_H + +#include <string> +#include <vector> +#include <vdr/plugin.h> +#include "config.h" +#include "services/epgsearch.h" +#include "services/remotetimers.h" +#include "searchtimer.h" +#include "switchtimer.h" +#include "timerconflict.h" +#include "helpers.h" + +struct TVGuideEPGSearchTemplate { +public: + std::string name; + std::string templValue; +}; + +// --- cRecManager ------------------------------------------------------------- +class cRecManager { +private: + cPlugin *epgSearchPlugin; + bool epgSearchAvailable; +public: + cRecManager(void); + void SetEPGSearchPlugin(void); + bool EpgSearchAvailable(void) {return epgSearchAvailable;}; + bool RefreshRemoteTimers(void); + bool CheckEventForTimer(const cEvent *event); + cTimer *GetTimerForEvent(const cEvent *event); + cTimer *createTimer(const cEvent *event, std::string path = ""); + cTimer *createLocalTimer(const cEvent *event, std::string path); + cTimer *createRemoteTimer(const cEvent *event, std::string path); + void SetTimerPath(cTimer *timer, const cEvent *event, std::string path); + void DeleteTimer(cTimer *timer); + void DeleteTimer(int timerID); + void DeleteTimer(const cEvent *event); + void DeleteLocalTimer(const cEvent *event); + void DeleteRemoteTimer(const cEvent *event); + void SaveTimer(cTimer *timer, cTimer newTimerSettings); + bool IsRecorded(const cEvent *event); + cTVGuideTimerConflicts *CheckTimerConflict(void); + void CreateSeriesTimer(cTimer *seriesTimer); + const cEvent **PerformSearchTimerSearch(std::string epgSearchString, int &numResults); + const cEvent **PerformSearch(Epgsearch_searchresults_v1_0 data, int &numResults); + void ReadEPGSearchTemplates(std::vector<TVGuideEPGSearchTemplate> *epgTemplates); + void GetSearchTimers(std::vector<cTVGuideSearchTimer> *timers); + int CreateSearchTimer(std::string epgSearchString); + bool SaveSearchTimer(cTVGuideSearchTimer *searchTimer); + void DeleteSearchTimer(cTVGuideSearchTimer *searchTimer, bool delTimers); + void UpdateSearchTimers(void); + bool CreateSwitchTimer(const cEvent *event, cSwitchTimer switchTimer); + void DeleteSwitchTimer(const cEvent *event); + cRecording **SearchForRecordings(std::string searchString, int &numResults); + const cEvent **LoadReruns(const cEvent *event, int &numResults); + void GetFavorites(std::vector<cTVGuideSearchTimer> *favorites); + const cEvent **WhatsOnNow(bool nowOrNext, int &numResults); + const cEvent **UserDefinedTime(int userTime, int &numResults); + virtual ~cRecManager (void); +}; + +#endif //__TVGUIDE_RECMMANAGER_H
\ No newline at end of file diff --git a/recmenu.c b/recmenu.c new file mode 100644 index 0000000..76a97ee --- /dev/null +++ b/recmenu.c @@ -0,0 +1,429 @@ +#include "recmenu.h"
+#include "tvguidengosd.h"
+
+// --- cRecMenu -------------------------------------------------------------
+
+cRecMenu::cRecMenu() {
+ menuWidth = 50;
+ menuHeight = 0;
+ maxMenuHeight = 98;
+ recMenuGrid = NULL;
+ osdView = NULL;
+ scrollBar = NULL;
+ back = NULL;
+ start = NULL;
+ stop = NULL;
+ itemCount = 0;
+ active = NULL;
+ header = NULL;
+ footer = NULL;
+ scrolling = false;
+}
+
+cRecMenu::~cRecMenu(void) {
+ menuItems.Clear();
+ if (scrollBar) {
+ scrollBar->Clear();
+ delete scrollBar;
+ scrollBar = NULL;
+ }
+ if (back) {
+ back->Clear();
+ delete back;
+ back = NULL;
+ }
+ if (recMenuGrid) {
+ recMenuGrid->Clear();
+ delete recMenuGrid;
+ }
+}
+
+/********************************************************************
+* Public Functions
+********************************************************************/
+
+void cRecMenu::Init(cOsdView *osdView) {
+ this->osdView = osdView;
+ recMenuGrid = osdView->GetViewGrid(vgRecordingMenu);
+ scrollBar = osdView->GetViewElement(vemScrollbar);
+ back = osdView->GetViewElement(vemBackground);
+ InitMenuItems();
+}
+
+void cRecMenu::Draw(void) {
+ DrawHeader();
+ double width = (double)menuWidth / (double)100;
+ double x = (double)(100 - menuWidth)/(double)200;
+ int totalHeight = GetHeight();
+ int yPerc = (100 - totalHeight) / 2;
+ if (header)
+ yPerc += header->GetHeight();
+ double y = (double)yPerc/(double)100;
+
+ for (cRecMenuItem *current = start; current; current = menuItems.Next(current)) {
+
+ double itemHeight = (double)(current->GetHeight())/(double)100;
+ if (current->IsNew()) {
+ current->SetTokens(recMenuGrid);
+ recMenuGrid->SetGrid(current->Id(), x, y, width, itemHeight);
+ } else {
+ recMenuGrid->MoveGrid(current->Id(), x, y, width, itemHeight);
+ }
+ if (current->Active()) {
+ recMenuGrid->SetCurrent(current->Id(), true);
+ }
+ y += itemHeight;
+ if (current == stop)
+ break;
+ }
+ DrawFooter();
+ recMenuGrid->Display();
+}
+
+eRecMenuState cRecMenu::ProcessKey(eKeys Key) {
+ eRecMenuState state = rmsContinue;
+ if (!active)
+ return state;
+
+ state = active->ProcessKey(Key);
+ if (state == rmsRefresh) {
+ //Refresh current
+ active->SetTokens(recMenuGrid);
+ active->SetNew();
+ Draw();
+ } else if (state == rmsNotConsumed) {
+ switch (Key & ~k_Repeat) {
+ case kUp:
+ if (ScrollUp(false))
+ Draw();
+ state = rmsConsumed;
+ break;
+ case kDown:
+ if (ScrollDown(false))
+ Draw();
+ state = rmsConsumed;
+ break;
+ case kLeft:
+ if (PageUp())
+ Draw();
+ state = rmsConsumed;
+ break;
+ case kRight:
+ if (PageDown())
+ Draw();
+ state = rmsConsumed;
+ break;
+ case kBack:
+ state = rmsClose;
+ break;
+ default:
+ break;
+ }
+ }
+ return state;
+}
+
+/********************************************************************
+* Protected Functions
+********************************************************************/
+void cRecMenu::AddMenuItem(cRecMenuItem *item, bool inFront) {
+ if (!inFront)
+ menuItems.Add(item);
+ else
+ menuItems.Ins(item);
+}
+
+void cRecMenu::AddHeader(cRecMenuItem *header) {
+ this->header = header;
+ maxMenuHeight -= header->GetHeight();
+}
+
+void cRecMenu::AddFooter(cRecMenuItem *footer) {
+ this->footer = footer;
+ maxMenuHeight -= footer->GetHeight();
+}
+
+int cRecMenu::GetNumActive(void) {
+ int num = 0;
+ for (cRecMenuItem *current = start; current; current = menuItems.Next(current)) {
+ if (current == active)
+ return num;
+ num++;
+ }
+}
+
+bool cRecMenu::ScrollUp(bool retry) {
+ if (active == start) {
+ bool scrolled = SeekBack(false);
+ if (scrolled && scrolling) DrawScrollbar();
+ }
+ if (footer && active == footer) {
+ recMenuGrid->SetCurrent(footer->Id(), false);
+ footer->SetInactive();
+ active = stop;
+ active->SetActive();
+ return true;
+ }
+ cRecMenuItem *prev = (cRecMenuItem*)active->Prev();
+ while (prev && !prev->Selectable()) {
+ prev = (cRecMenuItem*)prev->Prev();
+ }
+ if (prev) {
+ recMenuGrid->SetCurrent(active->Id(), false);
+ active->SetInactive();
+ active = prev;
+ active->SetActive();
+ return true;
+ } else {
+ SeekBack(false);
+ if (!retry)
+ ScrollUp(true);
+ }
+ return false;
+}
+
+bool cRecMenu::ScrollDown(bool retry) {
+ if (active == stop || retry) {
+ bool scrolled = SeekForward(false);
+ if (scrolled && scrolling) DrawScrollbar();
+ }
+ cRecMenuItem *next = (cRecMenuItem*)active->Next();
+ while (next && !next->Selectable()) {
+ if (next == stop) {
+ return ScrollDown(true);
+ }
+ next = (cRecMenuItem*)next->Next();
+ }
+ if (next) {
+ recMenuGrid->SetCurrent(active->Id(), false);
+ active->SetInactive();
+ active = next;
+ active->SetActive();
+ return true;
+ } else {
+ SeekForward(false);
+ if (!retry)
+ ScrollDown(true);
+ }
+ if (footer) {
+ recMenuGrid->SetCurrent(active->Id(), false);
+ active->SetInactive();
+ active = footer;
+ active->SetActive();
+ return true;
+ }
+ return false;
+}
+
+bool cRecMenu::PageUp(void) {
+ bool scrolled = SeekBack(true);
+ if (scrolled && scrolling) DrawScrollbar();
+ if (!scrolled) {
+ recMenuGrid->SetCurrent(active->Id(), false);
+ active->SetInactive();
+ active = start;
+ active->SetActive();
+ return true;
+ }
+ if (!active) {
+ active = stop;
+ active->SetActive();
+ }
+ return scrolled;
+}
+
+bool cRecMenu::PageDown(void) {
+ bool scrolled = SeekForward(true);
+ if (scrolled && scrolling) DrawScrollbar();
+ if (!scrolled) {
+ recMenuGrid->SetCurrent(active->Id(), false);
+ active->SetInactive();
+ active = stop;
+ active->SetActive();
+ return true;
+ }
+ if (!active) {
+ active = start;
+ active->SetActive();
+ }
+ return scrolled;
+}
+
+void cRecMenu::ClearMenuItems(void) {
+ menuItems.Clear();
+ back->Clear();
+ scrollBar->Clear();
+ recMenuGrid->Clear();
+ if (footer)
+ footer->SetNew();
+ active = NULL;
+}
+
+void cRecMenu::InitMenuItems(void) {
+ if (menuItems.Count() == 0)
+ return;
+ scrolling = false;
+ menuHeight = 0;
+ start = menuItems.First();
+ cRecMenuItem *current = start;
+ while (current) {
+ int itemHeight = current->GetHeight();
+ if (menuHeight + itemHeight > maxMenuHeight) {
+ scrolling = true;
+ break;
+ }
+ if (current->Active())
+ active = current;
+ itemCount++;
+ stop = current;
+ menuHeight += itemHeight;
+ current = menuItems.Next(current);
+ }
+ DrawBackground();
+ if (scrolling)
+ DrawScrollbar();
+}
+
+int cRecMenu::GetHeight(void) {
+ int totalHeight = menuHeight;
+ if (header)
+ totalHeight += header->GetHeight();
+ if (footer)
+ totalHeight += footer->GetHeight();
+ return totalHeight;
+}
+
+/********************************************************************
+* Private Functions
+********************************************************************/
+
+bool cRecMenu::SeekForward(bool page) {
+ int jumpStep = 0;
+ if (page)
+ jumpStep = itemCount;
+ else
+ jumpStep = itemCount/2;
+ int jump = 0;
+ cRecMenuItem *next = (cRecMenuItem*)stop->Next();
+ while (next && jump < jumpStep) {
+ stop = next;
+ menuHeight += next->GetHeight();
+ next = (cRecMenuItem*)next->Next();
+ jump++;
+ }
+ while (start && menuHeight > maxMenuHeight) {
+ if (active == start) {
+ active = NULL;
+ start->SetInactive();
+ }
+ menuHeight -= start->GetHeight();
+ recMenuGrid->Delete(start->Id());
+ start->SetNew();
+ start = (cRecMenuItem*)start->Next();
+ }
+ if (jump > 0)
+ return true;
+ return false;
+}
+
+bool cRecMenu::SeekBack(bool page) {
+ int jumpStep = 0;
+ if (page)
+ jumpStep = itemCount;
+ else
+ jumpStep = itemCount/2;
+ int jump = 0;
+ cRecMenuItem *prev = (cRecMenuItem*)start->Prev();
+ while (prev && jump < jumpStep) {
+ start = prev;
+ menuHeight += prev->GetHeight();
+ prev = (cRecMenuItem*)prev->Prev();
+ jump++;
+ }
+ while (stop && menuHeight > maxMenuHeight) {
+ if (active == stop) {
+ active = NULL;
+ stop->SetInactive();
+ }
+ menuHeight -= stop->GetHeight();
+ recMenuGrid->Delete(stop->Id());
+ stop->SetNew();
+ stop = (cRecMenuItem*)stop->Prev();
+ }
+ if (jump > 0)
+ return true;
+ return false;
+}
+
+void cRecMenu::DrawBackground(void) {
+ back->Clear();
+ back->ClearTokens();
+ back->AddIntToken("menuwidth", menuWidth + 2);
+ back->AddIntToken("menuheight", GetHeight() + 2);
+ back->AddIntToken("hasscrollbar", scrolling);
+ back->Display();
+}
+
+void cRecMenu::DrawScrollbar(void) {
+ if (menuItems.Count() == 0)
+ return;
+ int scrollBarHeight = (double)itemCount / (double)menuItems.Count() * 1000;
+ int startPos = 0;
+ for (cRecMenuItem *current = menuItems.First(); current; current = menuItems.Next(current)) {
+ if (start == current)
+ break;
+ startPos++;
+ }
+ int offset = (double)startPos / (double)menuItems.Count() * 1000;
+ scrollBar->Clear();
+ scrollBar->ClearTokens();
+ scrollBar->AddIntToken("menuwidth", menuWidth + 2);
+ int y = (100 - GetHeight())/2;
+ if (header)
+ y += header->GetHeight();
+
+ scrollBar->AddIntToken("posy", y);
+ scrollBar->AddIntToken("totalheight", menuHeight);
+ scrollBar->AddIntToken("height", scrollBarHeight);
+ scrollBar->AddIntToken("offset", offset);
+ scrollBar->Display();
+}
+
+void cRecMenu::DrawHeader(void) {
+ if (!header)
+ return;
+ double width = (double)menuWidth / (double)100;
+ double x = (double)(100 - menuWidth)/(double)200;
+ int totalHeight = GetHeight();
+ double y = (double)((100 - totalHeight) / 2) / (double)100;
+
+ if (header->IsNew()) {
+ recMenuGrid->ClearTokens();
+ header->SetTokens(recMenuGrid);
+ recMenuGrid->SetGrid(header->Id(), x, y, width, (double)header->GetHeight()/(double)100);
+ }
+}
+
+void cRecMenu::DrawFooter(void) {
+ if (!footer)
+ return;
+ double width = (double)menuWidth / (double)100;
+ double x = (double)(100 - menuWidth)/(double)200;
+ int totalHeight = GetHeight();
+ int totalMenuHeight = menuHeight;
+ if (header)
+ totalMenuHeight += header->GetHeight();
+ double y = (double)((100 - totalHeight) / 2 + totalMenuHeight) / (double)100;
+
+ if (footer->IsNew()) {
+ recMenuGrid->ClearTokens();
+ footer->SetTokens(recMenuGrid);
+ recMenuGrid->SetGrid(footer->Id(), x, y, width, (double)footer->GetHeight()/(double)100);
+ } else {
+ recMenuGrid->MoveGrid(footer->Id(), x, y, width, (double)footer->GetHeight()/(double)100);
+ }
+ if (footer->Active()) {
+ active = footer;
+ recMenuGrid->SetCurrent(footer->Id(), true);
+ }
+}
\ No newline at end of file diff --git a/recmenu.h b/recmenu.h new file mode 100644 index 0000000..99111c2 --- /dev/null +++ b/recmenu.h @@ -0,0 +1,59 @@ +#ifndef __TVGUIDE_RECMENU_H
+#define __TVGUIDE_RECMENU_H
+
+#include <list>
+#include "libskindesigner/osdelements.h"
+#include "recmenuitem.h"
+
+// --- cRecMenu -------------------------------------------------------------
+
+class cRecMenu {
+private:
+ int menuWidth;
+ int menuHeight;
+ int maxMenuHeight;
+ cOsdView *osdView;
+ cViewElement *back;
+ cViewElement *scrollBar;
+ cViewGrid *recMenuGrid;
+ cList<cRecMenuItem> menuItems;
+ cRecMenuItem *start;
+ cRecMenuItem *stop;
+ int itemCount;
+ cRecMenuItem *active;
+ cRecMenuItem *header;
+ cRecMenuItem *footer;
+ bool scrolling;
+ bool SeekForward(bool page);
+ bool SeekBack(bool page);
+ void AdjustActive(bool down);
+ void DrawScrollbar(void);
+ void DrawHeader(void);
+ void DrawFooter(void);
+protected:
+ void SetMenuWidth(int width) { menuWidth = width; };
+ void AddMenuItem(cRecMenuItem *item, bool inFront = false);
+ void AddHeader(cRecMenuItem *header);
+ void AddFooter(cRecMenuItem *footer);
+ int GetNumActive(void);
+ cRecMenuItem *GetActive(void) { return active; };
+ bool ScrollUp(bool retry);
+ bool ScrollDown(bool retry);
+ bool PageUp(void);
+ bool PageDown(void);
+ void ClearMenuItems(void);
+ void InitMenuItems(void);
+ int GetWidth(void) { return menuWidth; };
+ int GetHeight(void);
+public:
+ cRecMenu(void);
+ virtual ~cRecMenu(void);
+ void Init(cOsdView *osdView);
+ void DrawBackground(void);
+ void Draw(void);
+ void Hide(void) { osdView->Deactivate(true); };
+ void Show(void) { osdView->Activate(); };
+ void Flush(void) { osdView->Display(); };
+ virtual eRecMenuState ProcessKey(eKeys Key);
+};
+#endif //__TVGUIDE_RECMENU_H
\ No newline at end of file diff --git a/recmenuitem.c b/recmenuitem.c new file mode 100644 index 0000000..566de82 --- /dev/null +++ b/recmenuitem.c @@ -0,0 +1,1573 @@ +#include "recmenuitem.h"
+#include "recmenus.h"
+#include "config.h"
+#include "helpers.h"
+#include <math.h>
+#include <wctype.h>
+#include <vdr/remote.h>
+
+long cRecMenuItem::idCounter;
+
+// --- cRecMenuItem -------------------------------------------------------------
+
+cRecMenuItem::cRecMenuItem(void) {
+ id = idCounter;
+ idCounter++;
+ init = true;
+ active = false;
+ selectable = true;
+ action = rmsNotConsumed;
+ height = 0;
+ text = "";
+}
+
+cRecMenuItem::~cRecMenuItem(void) {
+}
+
+bool cRecMenuItem::IsNew(void) {
+ if (init) {
+ init = false;
+ return true;
+ }
+ return false;
+}
+
+// --- cRecMenuItemInfo -------------------------------------------------------
+
+cRecMenuItemInfo::cRecMenuItemInfo(string line1, int numLines, string line2, string line3, string line4) {
+ selectable = false;
+ this->numLines = numLines;
+ this->line1 = line1;
+ this->line2 = line2;
+ this->line3 = line3;
+ this->line4 = line4;
+ if (numLines == 1)
+ height = 12;
+ else if (numLines == 2)
+ height = 16;
+ else if (numLines == 3)
+ height = 20;
+ else if (numLines == 4)
+ height = 24;
+ this->active = false;
+}
+
+cRecMenuItemInfo::~cRecMenuItemInfo(void) {
+}
+
+void cRecMenuItemInfo::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("info", 1);
+ menu->AddIntToken("lines", numLines);
+ menu->AddStringToken("line1", line1);
+ menu->AddStringToken("line2", line2);
+ menu->AddStringToken("line3", line3);
+ menu->AddStringToken("line4", line4);
+}
+
+// --- cRecMenuItemButton -------------------------------------------------------
+
+cRecMenuItemButton::cRecMenuItemButton(string text, eRecMenuState action, bool active) {
+ selectable = true;
+ height = 8;
+ this->text = text;
+ this->action = action;
+ this->active = active;
+}
+
+cRecMenuItemButton::~cRecMenuItemButton(void) {
+}
+
+void cRecMenuItemButton::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("button", 1);
+ menu->AddStringToken("buttontext", text);
+}
+
+eRecMenuState cRecMenuItemButton::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kOk:
+ return action;
+ break;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemButtonYesNo -------------------------------------------------------
+cRecMenuItemButtonYesNo::cRecMenuItemButtonYesNo(string textYes,
+ string textNo,
+ eRecMenuState actionYes,
+ eRecMenuState actionNo,
+ bool active) {
+ height = 8;
+ selectable = true;
+ this->textYes = textYes;
+ this->textNo = textNo;
+ this->action = actionYes;
+ this->actionNo = actionNo;
+ this->active = active;
+ yesActive = true;
+}
+
+cRecMenuItemButtonYesNo::~cRecMenuItemButtonYesNo(void) {
+}
+
+void cRecMenuItemButtonYesNo::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("buttonyesno", 1);
+ menu->AddIntToken("yes", yesActive);
+ menu->AddStringToken("textyes", textYes);
+ menu->AddStringToken("textno", textNo);
+}
+
+eRecMenuState cRecMenuItemButtonYesNo::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ if (!yesActive) {
+ yesActive = true;
+ return rmsRefresh;
+ } else
+ return rmsNotConsumed;
+ break;
+ case kRight:
+ if (yesActive) {
+ yesActive = false;
+ return rmsRefresh;
+ } else
+ return rmsNotConsumed;
+ break;
+ case kOk:
+ if (yesActive)
+ return action;
+ return actionNo;
+ break;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+
+// --- cRecMenuItemInt -------------------------------------------------------
+cRecMenuItemInt::cRecMenuItemInt(string text,
+ int initialVal,
+ int minVal,
+ int maxVal,
+ bool active,
+ int *callback,
+ eRecMenuState action) {
+
+ height = 8;
+ selectable = true;
+ this->text = text;
+ this->currentVal = initialVal;
+ this->minVal = minVal;
+ this->maxVal = maxVal;
+ this->active = active;
+ this->callback = callback;
+ this->action = action;
+ fresh = true;
+}
+
+cRecMenuItemInt::~cRecMenuItemInt(void) {
+}
+
+void cRecMenuItemInt::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("intselector", 1);
+ menu->AddIntToken("value", currentVal);
+ menu->AddStringToken("text", text);
+}
+
+eRecMenuState cRecMenuItemInt::ProcessKey(eKeys Key) {
+ int oldValue = currentVal;
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ fresh = true;
+ if (currentVal > minVal) {
+ currentVal--;
+ if (callback)
+ *callback = currentVal;
+ }
+ return rmsRefresh;
+ break;
+ case kRight:
+ fresh = true;
+ if (currentVal < maxVal) {
+ currentVal++;
+ if (callback)
+ *callback = currentVal;
+ }
+ return rmsRefresh;
+ break;
+ case k0 ... k9:
+ if (fresh) {
+ currentVal = 0;
+ fresh = false;
+ }
+ currentVal = currentVal * 10 + (Key - k0);
+ if (!((currentVal >= minVal) && (currentVal <= maxVal)))
+ currentVal = oldValue;
+ if (callback)
+ *callback = currentVal;
+ return rmsRefresh;
+ break;
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemBool -------------------------------------------------------
+cRecMenuItemBool::cRecMenuItemBool(string text,
+ bool initialVal,
+ bool active,
+ bool *callback,
+ eRecMenuState action) {
+ height = 8;
+ selectable = true;
+ this->text = text;
+ this->yes = initialVal;
+ this->active = active;
+ this->callback = callback;
+ this->action = action;
+}
+
+cRecMenuItemBool::~cRecMenuItemBool(void) {
+}
+
+void cRecMenuItemBool::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("boolselector", 1);
+ menu->AddIntToken("value", yes);
+ menu->AddStringToken("text", text);
+}
+
+eRecMenuState cRecMenuItemBool::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ case kRight:
+ yes = !yes;
+ if (callback)
+ *callback = yes;
+ return rmsRefresh;
+ break;
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemSelect -------------------------------------------------------
+cRecMenuItemSelect::cRecMenuItemSelect(string text,
+ vector<string> strings,
+ int initialVal,
+ bool active,
+ int *callback,
+ eRecMenuState action) {
+ height = 8;
+ selectable = true;
+ this->text = text;
+ this->strings = strings;
+ numValues = strings.size();
+ if ((initialVal < 0) || (initialVal > numValues-1))
+ this->currentVal = 0;
+ else
+ this->currentVal = initialVal;
+ this->active = active;
+ this->callback = callback;
+ this->action = action;
+}
+
+cRecMenuItemSelect::~cRecMenuItemSelect(void) {
+}
+
+void cRecMenuItemSelect::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("stringselector", 1);
+ menu->AddStringToken("text", text);
+ menu->AddStringToken("value", strings[currentVal]);
+}
+
+eRecMenuState cRecMenuItemSelect::ProcessKey(eKeys Key) {
+ int oldValue = currentVal;
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ currentVal--;
+ if (currentVal<0)
+ currentVal = numValues - 1;
+ if (callback)
+ *callback = currentVal;
+ return rmsRefresh;
+ break;
+ case kRight:
+ currentVal = (currentVal+1)%numValues;
+ if (callback)
+ *callback = currentVal;
+ return rmsRefresh;
+ break;
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemText -------------------------------------------------------
+cRecMenuItemText::cRecMenuItemText(string text,
+ char *initialVal,
+ int length,
+ bool active,
+ char *callback) {
+
+ height = 16;
+ selectable = true;
+ this->text = text;
+ value = initialVal;
+ this->active = active;
+ this->callback = callback;
+ this->length = length;
+ allowed = trVDR(FileNameChars);
+ pos = -1;
+ offset = 0;
+ insert = uppercase = false;
+ newchar = true;
+ lengthUtf8 = 0;
+ valueUtf8 = NULL;
+ allowedUtf8 = NULL;
+ charMapUtf8 = NULL;
+ currentCharUtf8 = NULL;
+ lastKey = kNone;
+ buffer = "";
+}
+
+cRecMenuItemText::~cRecMenuItemText(void) {
+ delete[] valueUtf8;
+ delete[] allowedUtf8;
+ delete[] charMapUtf8;
+}
+
+void cRecMenuItemText::EnterEditMode(void) {
+ if (!valueUtf8) {
+ valueUtf8 = new uint[length];
+ lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
+ int l = strlen(allowed) + 1;
+ allowedUtf8 = new uint[l];
+ Utf8ToArray(allowed, allowedUtf8, l);
+ const char *charMap = trVDR("CharMap$ 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9");
+ l = strlen(charMap) + 1;
+ charMapUtf8 = new uint[l];
+ Utf8ToArray(charMap, charMapUtf8, l);
+ currentCharUtf8 = charMapUtf8;
+ AdvancePos();
+ }
+}
+
+void cRecMenuItemText::LeaveEditMode(bool SaveValue) {
+ if (valueUtf8) {
+ if (SaveValue) {
+ Utf8FromArray(valueUtf8, value, length);
+ stripspace(value);
+ if (callback) {
+ strncpy(callback, value, TEXTINPUTLENGTH);
+ }
+ }
+ lengthUtf8 = 0;
+ delete[] valueUtf8;
+ valueUtf8 = NULL;
+ delete[] allowedUtf8;
+ allowedUtf8 = NULL;
+ delete[] charMapUtf8;
+ charMapUtf8 = NULL;
+ pos = -1;
+ offset = 0;
+ newchar = true;
+ }
+}
+
+void cRecMenuItemText::AdvancePos(void) {
+ if (pos < length - 2 && pos < lengthUtf8) {
+ if (++pos >= lengthUtf8) {
+ if (pos >= 2 && valueUtf8[pos - 1] == ' ' && valueUtf8[pos - 2] == ' ')
+ pos--; // allow only two blanks at the end
+ else {
+ valueUtf8[pos] = ' ';
+ valueUtf8[pos + 1] = 0;
+ lengthUtf8++;
+ }
+ }
+ }
+ newchar = true;
+ if (!insert && Utf8is(alpha, valueUtf8[pos]))
+ uppercase = Utf8is(upper, valueUtf8[pos]);
+}
+
+uint cRecMenuItemText::Inc(uint c, bool Up) {
+ uint *p = IsAllowed(c);
+ if (!p)
+ p = allowedUtf8;
+ if (Up) {
+ if (!*++p)
+ p = allowedUtf8;
+ } else if (--p < allowedUtf8) {
+ p = allowedUtf8;
+ while (*p && *(p + 1))
+ p++;
+ }
+ return *p;
+}
+
+void cRecMenuItemText::Type(uint c) {
+ if (insert && lengthUtf8 < length - 1)
+ Insert();
+ valueUtf8[pos] = c;
+ if (pos < length - 2)
+ pos++;
+ if (pos >= lengthUtf8) {
+ valueUtf8[pos] = ' ';
+ valueUtf8[pos + 1] = 0;
+ lengthUtf8 = pos + 1;
+ }
+}
+
+void cRecMenuItemText::Insert(void) {
+ memmove(valueUtf8 + pos + 1, valueUtf8 + pos, (lengthUtf8 - pos + 1) * sizeof(*valueUtf8));
+ lengthUtf8++;
+ valueUtf8[pos] = ' ';
+}
+
+void cRecMenuItemText::Delete(void) {
+ memmove(valueUtf8 + pos, valueUtf8 + pos + 1, (lengthUtf8 - pos) * sizeof(*valueUtf8));
+ lengthUtf8--;
+}
+
+uint *cRecMenuItemText::IsAllowed(uint c) {
+ if (allowedUtf8) {
+ for (uint *a = allowedUtf8; *a; a++) {
+ if (c == *a)
+ return a;
+ }
+ }
+ return NULL;
+}
+
+void cRecMenuItemText::SetText(void) {
+ if (InEditMode()) {
+ int textAreaWidth = 800;
+ if (pos < offset)
+ offset = pos;
+ int WidthFromOffset = 0;
+ int EndPos = lengthUtf8;
+ for (int i = offset; i < lengthUtf8; i++) {
+ WidthFromOffset += 20;
+ if (WidthFromOffset > textAreaWidth) {
+ if (pos >= i) {
+ do {
+ WidthFromOffset -= 20;
+ offset++;
+ } while (WidthFromOffset > textAreaWidth && offset < pos);
+ EndPos = pos + 1;
+ } else {
+ EndPos = i;
+ break;
+ }
+ }
+ }
+ char buf[1000];
+ char *p = buf;
+ if (offset)
+ *p++ = '<';
+ p += Utf8FromArray(valueUtf8 + offset, p, sizeof(buf) - (p - buf), pos - offset);
+ *p++ = '[';
+ if (insert && newchar)
+ *p++ = ']';
+ p += Utf8FromArray(&valueUtf8[pos], p, sizeof(buf) - (p - buf), 1);
+ if (!(insert && newchar))
+ *p++ = ']';
+ p += Utf8FromArray(&valueUtf8[pos + 1], p, sizeof(buf) - (p - buf), EndPos - pos - 1);
+ if (EndPos != lengthUtf8)
+ *p++ = '>';
+ *p = 0;
+ buffer = buf;
+ } else {
+ buffer = "";
+ }
+}
+
+void cRecMenuItemText::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("textinput", 1);
+ menu->AddIntToken("editmode", InEditMode());
+ menu->AddStringToken("text", text);
+ if (buffer.size() > 0) {
+ menu->AddStringToken("value", buffer);
+ } else {
+ menu->AddStringToken("value", value);
+ }
+}
+
+eRecMenuState cRecMenuItemText::ProcessKey(eKeys Key) {
+ bool consumed = false;
+ bool SameKey = NORMALKEY(Key) == lastKey;
+
+ if (Key != kNone) {
+ lastKey = NORMALKEY(Key);
+ } else if (!newchar && k0 <= lastKey && lastKey <= k9 && autoAdvanceTimeout.TimedOut()) {
+ AdvancePos();
+ newchar = true;
+ currentCharUtf8 = NULL;
+ SetText();
+ return rmsRefresh;
+ }
+
+ switch ((int)Key) {
+ case kRed: // Switch between upper- and lowercase characters
+ if (InEditMode()) {
+ if (!insert || !newchar) {
+ uppercase = !uppercase;
+ valueUtf8[pos] = uppercase ? Utf8to(upper, valueUtf8[pos]) : Utf8to(lower, valueUtf8[pos]);
+ }
+ consumed = true;
+ }
+ break;
+ case kGreen: // Toggle insert/overwrite modes
+ if (InEditMode()) {
+ insert = !insert;
+ newchar = true;
+ consumed = true;
+ }
+ break;
+ case kYellow|k_Repeat:
+ case kYellow: // Remove the character at the current position; in insert mode it is the character to the right of the cursor
+ if (InEditMode()) {
+ if (lengthUtf8 > 1) {
+ if (!insert || pos < lengthUtf8 - 1)
+ Delete();
+ else if (insert && pos == lengthUtf8 - 1)
+ valueUtf8[pos] = ' '; // in insert mode, deleting the last character replaces it with a blank to keep the cursor position
+ // reduce position, if we removed the last character
+ if (pos == lengthUtf8)
+ pos--;
+ } else if (lengthUtf8 == 1)
+ valueUtf8[0] = ' '; // This is the last character in the string, replace it with a blank
+ if (Utf8is(alpha, valueUtf8[pos]))
+ uppercase = Utf8is(upper, valueUtf8[pos]);
+ newchar = true;
+ consumed = true;
+ }
+ break;
+ case kLeft|k_Repeat:
+ case kLeft:
+
+ if (pos > 0) {
+ if (!insert || newchar)
+ pos--;
+ newchar = true;
+ if (!insert && Utf8is(alpha, valueUtf8[pos]))
+ uppercase = Utf8is(upper, valueUtf8[pos]);
+ }
+ consumed = true;
+ break;
+ case kRight|k_Repeat:
+ case kRight:
+ if (InEditMode()) {
+ AdvancePos();
+ } else {
+ EnterEditMode();
+ }
+ consumed = true;
+ break;
+ case kUp|k_Repeat:
+ case kUp:
+ case kDown|k_Repeat:
+ case kDown:
+ if (InEditMode()) {
+ if (insert && newchar) {
+ // create a new character in insert mode
+ if (lengthUtf8 < length - 1)
+ Insert();
+ }
+ if (uppercase)
+ valueUtf8[pos] = Utf8to(upper, Inc(Utf8to(lower, valueUtf8[pos]), NORMALKEY(Key) == kUp));
+ else
+ valueUtf8[pos] = Inc( valueUtf8[pos], NORMALKEY(Key) == kUp);
+ newchar = false;
+ consumed = true;
+ }
+ break;
+ case k0|k_Repeat ... k9|k_Repeat:
+ case k0 ... k9: {
+ if (InEditMode()) {
+ if (Setup.NumberKeysForChars) {
+ if (!SameKey) {
+ if (!newchar)
+ AdvancePos();
+ currentCharUtf8 = NULL;
+ }
+ if (!currentCharUtf8 || !*currentCharUtf8 || *currentCharUtf8 == '\t') {
+ // find the beginning of the character map entry for Key
+ int n = NORMALKEY(Key) - k0;
+ currentCharUtf8 = charMapUtf8;
+ while (n > 0 && *currentCharUtf8) {
+ if (*currentCharUtf8++ == '\t')
+ n--;
+ }
+ // find first allowed character
+ while (*currentCharUtf8 && *currentCharUtf8 != '\t' && !IsAllowed(*currentCharUtf8))
+ currentCharUtf8++;
+ }
+ if (*currentCharUtf8 && *currentCharUtf8 != '\t') {
+ if (insert && newchar) {
+ // create a new character in insert mode
+ if (lengthUtf8 < length - 1)
+ Insert();
+ }
+ valueUtf8[pos] = *currentCharUtf8;
+ if (uppercase)
+ valueUtf8[pos] = Utf8to(upper, valueUtf8[pos]);
+ // find next allowed character
+ do {
+ currentCharUtf8++;
+ } while (*currentCharUtf8 && *currentCharUtf8 != '\t' && !IsAllowed(*currentCharUtf8));
+ newchar = false;
+ autoAdvanceTimeout.Set(AUTO_ADVANCE_TIMEOUT);
+ }
+ } else {
+ Type('0' + NORMALKEY(Key) - k0);
+ }
+ consumed = true;
+ }
+ break; }
+ case kBack:
+ case kOk:
+ if (InEditMode()) {
+ LeaveEditMode(Key == kOk);
+ } else {
+ EnterEditMode();
+ }
+ consumed = true;
+ break;
+ default:
+ if (InEditMode() && BASICKEY(Key) == kKbd) {
+ int c = KEYKBD(Key);
+ if (c <= 0xFF) {
+ if (IsAllowed(Utf8to(lower, c)))
+ Type(c);
+ else {
+ switch (c) {
+ case 0x7F: // backspace
+ if (pos > 0) {
+ pos--;
+ ProcessKey(kYellow);
+ }
+ break;
+ default: ;
+ }
+ }
+ } else {
+ switch (c) {
+ case kfHome: pos = 0; break;
+ case kfEnd: pos = lengthUtf8 - 1; break;
+ case kfIns: ProcessKey(kGreen);
+ case kfDel: ProcessKey(kYellow);
+ default: ;
+ }
+ }
+ consumed = true;
+ }
+ break;
+ }
+ SetText();
+ if (consumed)
+ return rmsRefresh;
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemTime -------------------------------------------------------
+cRecMenuItemTime::cRecMenuItemTime(string text,
+ int initialVal,
+ bool active,
+ int *callback,
+ eRecMenuState action) {
+ height = 8;
+ selectable = true;
+ this->text = text;
+ this->value = initialVal;
+ hh = value / 100;
+ mm = value % 100;
+ pos = 0;
+ fresh = true;
+ this->active = active;
+ this->callback = callback;
+ this->action = action;
+}
+
+cRecMenuItemTime::~cRecMenuItemTime(void) {
+}
+
+void cRecMenuItemTime::SetTokens(cViewGrid *menu) {
+ char buf[10];
+ switch (pos) {
+ case 1: snprintf(buf, sizeof(buf), "%01d-:--", hh / 10); break;
+ case 2: snprintf(buf, sizeof(buf), "%02d:--", hh); break;
+ case 3: snprintf(buf, sizeof(buf), "%02d:%01d-", hh, mm / 10); break;
+ default: snprintf(buf, sizeof(buf), "%02d:%02d", hh, mm);
+ }
+ menu->ClearTokens();
+ menu->AddIntToken("timeselector", 1);
+ menu->AddStringToken("text", text);
+ menu->AddStringToken("value", buf);
+}
+
+eRecMenuState cRecMenuItemTime::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kLeft|k_Repeat:
+ case kLeft: {
+ if (--mm < 0) {
+ mm = 59;
+ if (--hh < 0)
+ hh = 23;
+ }
+ fresh = true;
+ value = hh * 100 + mm;
+ if (callback)
+ *callback = value;
+ return rmsRefresh;
+ break; }
+ case kRight|k_Repeat:
+ case kRight: {
+ if (++mm > 59) {
+ mm = 0;
+ if (++hh > 23)
+ hh = 0;
+ }
+ fresh = true;
+ value = hh * 100 + mm;
+ if (callback)
+ *callback = value;
+ return rmsRefresh;
+ break; }
+ case k0|k_Repeat ... k9|k_Repeat:
+ case k0 ... k9: {
+ if (fresh || pos > 3) {
+ pos = 0;
+ fresh = false;
+ }
+ int n = Key - k0;
+ switch (pos) {
+ case 0:
+ if (n <= 2) {
+ hh = n * 10;
+ mm = 0;
+ pos++;
+ }
+ break;
+ case 1:
+ if (hh + n <= 23) {
+ hh += n;
+ pos++;
+ }
+ break;
+ case 2:
+ if (n <= 5) {
+ mm += n * 10;
+ pos++;
+ }
+ break;
+ case 3:
+ if (mm + n <= 59) {
+ mm += n;
+ pos++;
+ }
+ break;
+ default: ;
+ }
+ value = hh * 100 + mm;
+ if (callback)
+ *callback = value;
+ return rmsRefresh;
+ break; }
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemDay -------------------------------------------------------
+cRecMenuItemDay::cRecMenuItemDay(string text,
+ time_t initialVal,
+ bool active,
+ time_t *callback,
+ eRecMenuState action) {
+
+ height = 8;
+ selectable = true;
+ this->text = text;
+ this->currentVal = cTimer::SetTime(initialVal, 0);
+ this->active = active;
+ this->callback = callback;
+ this->action = action;
+}
+
+cRecMenuItemDay::~cRecMenuItemDay(void) {
+}
+
+void cRecMenuItemDay::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("dayselector", 1);
+ menu->AddStringToken("text", text);
+ menu->AddStringToken("value", *DateString(currentVal));
+}
+
+eRecMenuState cRecMenuItemDay::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ currentVal -= 60*60*24;
+ if (callback)
+ *callback = currentVal;
+ return rmsRefresh;
+ break;
+ case kRight:
+ currentVal += 60*60*24;
+ if (callback)
+ *callback = currentVal;
+ return rmsRefresh;
+ break;
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemChannelChooser -------------------------------------------------------
+cRecMenuItemChannelChooser::cRecMenuItemChannelChooser(string text,
+ cChannel *initialChannel,
+ bool active,
+ int *callback,
+ eRecMenuState action) {
+
+ height = 8;
+ selectable = true;
+ this->text = text;
+ this->channel = initialChannel;
+ if (initialChannel)
+ initialChannelSet = true;
+ else
+ initialChannelSet = false;
+ channelNumber = 0;
+ fresh = true;
+ this->active = active;
+ this->callback = callback;
+ this->action = action;
+}
+
+cRecMenuItemChannelChooser::~cRecMenuItemChannelChooser(void) {
+}
+
+void cRecMenuItemChannelChooser::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("channelselector", 1);
+ menu->AddStringToken("text", text);
+ if (channel) {
+ menu->AddIntToken("channelnumber", channel->Number());
+ menu->AddStringToken("channelname", channel->Name());
+ string channelId = *(channel->GetChannelID().ToString());
+ menu->AddStringToken("channelid", channelId);
+ menu->AddIntToken("channellogoexisis", menu->ChannelLogoExists(channelId));
+ } else {
+ menu->AddIntToken("channelnumber", 0);
+ menu->AddStringToken("channelname", tr("all Channels"));
+ menu->AddStringToken("channelid", "");
+ menu->AddIntToken("channellogoexisis", false);
+ }
+}
+
+eRecMenuState cRecMenuItemChannelChooser::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kLeft: {
+ fresh = true;
+ if (!channel)
+ return rmsConsumed;
+ cChannel *prev = channel;
+ cChannel *firstChannel = Channels.First();
+ if(firstChannel->GroupSep())
+ firstChannel = Channels.Next(firstChannel);
+ if (prev == firstChannel) {
+ if (!initialChannelSet)
+ channel = NULL;
+ } else {
+ while (prev = Channels.Prev(prev)) {
+ if(!prev->GroupSep()) {
+ channel = prev;
+ break;
+ }
+ }
+ }
+ if (callback) {
+ if (channel)
+ *callback = channel->Number();
+ else
+ *callback = 0;
+ }
+ return rmsRefresh;
+ break; }
+ case kRight: {
+ fresh = true;
+ if (!channel) {
+ channel = Channels.First();
+ if(channel->GroupSep())
+ channel = Channels.Next(channel);
+ } else {
+ cChannel *next = channel;
+ while (next = Channels.Next(next)) {
+ if(!next->GroupSep()) {
+ channel = next;
+ break;
+ }
+ }
+ }
+ if (callback) {
+ if (channel)
+ *callback = channel->Number();
+ else
+ *callback = 0;
+ }
+ return rmsRefresh;
+ break; }
+ case k0 ... k9: {
+ if (fresh) {
+ channelNumber = 0;
+ fresh = false;
+ }
+ channelNumber = channelNumber * 10 + (Key - k0);
+ cChannel *chanNew = Channels.GetByNumber(channelNumber);
+ if (chanNew) {
+ channel = chanNew;
+ if (callback)
+ *callback = channel->Number();
+ }
+ return rmsRefresh;
+ break; }
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemDayChooser -------------------------------------------------------
+cRecMenuItemDayChooser::cRecMenuItemDayChooser(string text,
+ int weekdays,
+ bool active,
+ int *callback) {
+ height = 8;
+ selectable = true;
+ this->text = text;
+ if (weekdays < 1)
+ weekdays *= -1;
+ this->weekdays = weekdays;
+ this->active = active;
+ this->callback = callback;
+ selectedDay = 0;
+}
+
+cRecMenuItemDayChooser::~cRecMenuItemDayChooser(void) {
+}
+
+void cRecMenuItemDayChooser::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("weekdayselector", 1);
+ menu->AddStringToken("text", text);
+ menu->AddIntToken("dayselected", selectedDay);
+
+ string days = trVDR("MTWTFSS");
+ for (unsigned day=0; day<days.length(); ++day) {
+ string strDay = *cString::sprintf("%c", days.at(day));
+ stringstream nameWeekday;
+ nameWeekday << "day" << day << "abbr";
+ stringstream nameWeekdaySet;
+ nameWeekdaySet << "day" << day << "set";
+ menu->AddStringToken(nameWeekday.str(), strDay);
+ menu->AddIntToken(nameWeekdaySet.str(), WeekDaySet(day));
+ }
+}
+
+bool cRecMenuItemDayChooser::WeekDaySet(unsigned day) {
+ return weekdays & (1 << day);
+}
+
+void cRecMenuItemDayChooser::ToggleDay(void) {
+ bool dayActive = WeekDaySet(selectedDay);
+ int dayBit = pow(2, selectedDay);
+ if (dayActive) {
+ weekdays -= dayBit;
+ } else {
+ weekdays += dayBit;
+ }
+ if (callback) {
+ *callback = weekdays;
+ }
+}
+
+eRecMenuState cRecMenuItemDayChooser::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kLeft: {
+ selectedDay--;
+ if (selectedDay<0)
+ selectedDay += 7;
+ return rmsRefresh;
+ break; }
+ case kRight: {
+ selectedDay = (selectedDay+1)%7;
+ return rmsRefresh;
+ break; }
+ case kOk:
+ ToggleDay();
+ return rmsRefresh;
+ break;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemSelectDirectory -------------------------------------------------------
+cRecMenuItemSelectDirectory::cRecMenuItemSelectDirectory(string text,
+ string originalFolder,
+ bool active,
+ char *callback,
+ eRecMenuState action,
+ bool isSearchTimer) {
+
+ height = 8;
+ selectable = true;
+ this->text = text;
+ this->originalFolder = originalFolder;
+ this->active = active;
+ this->callback = callback;
+ this->action = action;
+ folders.push_back(tr("root video folder"));
+ if (isSearchTimer && config.instRecFixedFolder.size() > 0)
+ folders.push_back(config.instRecFixedFolder);
+ ReadRecordingDirectories(&folders, NULL, "");
+ numValues = folders.size();
+ this->currentVal = GetInitial();
+}
+
+void cRecMenuItemSelectDirectory::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("directoryselector", 1);
+ menu->AddStringToken("text", text);
+ menu->AddStringToken("folder", folders[currentVal]);
+}
+
+eRecMenuState cRecMenuItemSelectDirectory::ProcessKey(eKeys Key) {
+ int oldValue = currentVal;
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ currentVal--;
+ if (currentVal<0)
+ currentVal = numValues - 1;
+ if (callback) {
+ SetCallback();
+ }
+ return rmsRefresh;
+ break;
+ case kRight: {
+ currentVal = (currentVal+1)%numValues;
+ if (callback) {
+ SetCallback();
+ }
+ return rmsRefresh;
+ break; }
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+void cRecMenuItemSelectDirectory::SetCallback(void) {
+ string newFolder = folders[currentVal];
+ if (!newFolder.compare(tr("root video folder")))
+ newFolder = "";
+ strncpy(callback, newFolder.c_str(), TEXTINPUTLENGTH);
+}
+
+int cRecMenuItemSelectDirectory::GetInitial(void) {
+ if (originalFolder.size() == 0)
+ return 0;
+ for (int i=0; i < numValues; i++) {
+ if (!folders[i].compare(originalFolder)) {
+ return i;
+ }
+ }
+ return 0;
+}
+
+// --- cRecMenuItemTimerConflictHeader -------------------------------------------------------
+cRecMenuItemTimerConflictHeader::cRecMenuItemTimerConflictHeader(time_t conflictStart,
+ time_t conflictStop,
+ time_t overlapStart,
+ time_t overlapStop) {
+ height = 10;
+ selectable = false;
+ active = false;
+ this->conflictStart = conflictStart;
+ this->conflictStop = conflictStop;
+ this->overlapStart = overlapStart;
+ this->overlapStop = overlapStop;
+}
+
+cRecMenuItemTimerConflictHeader::~cRecMenuItemTimerConflictHeader(void) {
+}
+
+void cRecMenuItemTimerConflictHeader::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("timerconflictheader", 1);
+ menu->AddStringToken("text", tr("Timer Conflict"));
+ menu->AddStringToken("conflictstart", *TimeString(conflictStart));
+ menu->AddStringToken("conflictstop", *TimeString(conflictStop));
+ menu->AddStringToken("overlapstart", *TimeString(overlapStart));
+ menu->AddStringToken("overlapstop", *TimeString(overlapStop));
+
+ int olStart = (double)(overlapStart - conflictStart) / (double)(conflictStop - conflictStart) * 100;
+ int olWidth = (double)(overlapStop - overlapStart) / (double)(conflictStop - conflictStart) * 100;
+ menu->AddIntToken("overlapstartpercent", olStart);
+ menu->AddIntToken("overlapwidthpercent", olWidth);
+}
+
+// --- cRecMenuItemTimer -------------------------------------------------------
+cRecMenuItemTimer::cRecMenuItemTimer(const cTimer *timer,
+ eRecMenuState action1,
+ eRecMenuState action2,
+ eRecMenuState action3,
+ eRecMenuState action4,
+ time_t conflictStart,
+ time_t conflictStop,
+ time_t overlapStart,
+ time_t overlapStop,
+ bool active) {
+ height = 12;
+ selectable = true;
+ this->timer = timer;
+ this->action = action1;
+ this->action2 = action2;
+ this->action3 = action3;
+ this->action4 = action4;
+ iconActive = 0;
+ this->conflictStart = conflictStart;
+ this->conflictStop = conflictStop;
+ this->overlapStart = overlapStart;
+ this->overlapStop = overlapStop;
+ this->active = active;
+}
+
+cRecMenuItemTimer::~cRecMenuItemTimer(void) {
+}
+
+void cRecMenuItemTimer::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("timerconflict", 1);
+
+ const cChannel *channel = timer->Channel();
+ int channelTransponder = 0;
+ string channelName = "";
+ string channelId = "";
+ if (channel) {
+ channelTransponder = channel->Transponder();
+ channelName = channel->Name() ? channel->Name() : "";
+ channelId = *(channel->GetChannelID().ToString());
+ }
+ menu->AddIntToken("transponder", channelTransponder);
+ menu->AddStringToken("channelname", channelName);
+ menu->AddStringToken("channelid", channelId);
+
+ const cEvent *event = timer->Event();
+ string timerTitle = "";
+ if (event && event->Title()) {
+ timerTitle = event->Title();
+ }
+ menu->AddStringToken("timertitle", timerTitle);
+ menu->AddStringToken("starttime", *TimeString(timer->StartTime()));
+ menu->AddStringToken("stoptime", *TimeString(timer->StopTime()));
+ menu->AddStringToken("date", *ShortDateString(timer->StartTime()));
+ menu->AddStringToken("weekday", *WeekDayName(timer->StartTime()));
+
+ menu->AddIntToken("infoactive", (iconActive==0)?true:false);
+ menu->AddIntToken("deleteactive", (iconActive==1)?true:false);
+ menu->AddIntToken("editactive", (iconActive==2)?true:false);
+ menu->AddIntToken("searchactive", (iconActive==3)?true:false);
+
+
+ int conflictTime = conflictStop - conflictStart;
+ int timerStart = (double)(timer->StartTime() - conflictStart) / (double)conflictTime * 100;
+ int timerWidth = (double)(timer->StopTime() - timer->StartTime()) / (double)conflictTime * 100;
+ int olStart = (double)(overlapStart - conflictStart) / (double)(conflictStop - conflictStart) * 100;
+ int olWidth = (double)(overlapStop - overlapStart) / (double)(conflictStop - conflictStart) * 100;
+ menu->AddIntToken("timerstartpercent", timerStart);
+ menu->AddIntToken("timerwidthpercent", timerWidth);
+ menu->AddIntToken("overlapstartpercent", olStart);
+ menu->AddIntToken("overlapwidthpercent", olWidth);
+}
+
+eRecMenuState cRecMenuItemTimer::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ if (iconActive > 0) {
+ iconActive--;
+ return rmsRefresh;
+ } else
+ return rmsNotConsumed;
+ break;
+ case kRight:
+ if (iconActive < 3) {
+ iconActive++;
+ return rmsRefresh;
+ } else
+ return rmsNotConsumed;
+ break;
+ case kOk:
+ if (iconActive == 0)
+ return action;
+ else if (iconActive == 1)
+ return action2;
+ else if (iconActive == 2)
+ return action3;
+ else if (iconActive == 3)
+ return action4;
+ break;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemEvent -------------------------------------------------------
+cRecMenuItemEvent::cRecMenuItemEvent(const cEvent *event,
+ eRecMenuState action1,
+ eRecMenuState action2,
+ bool active) {
+ height = 8;
+ selectable = true;
+ this->event = event;
+ this->action = action1;
+ this->action2 = action2;
+ iconActive = 0;
+ this->active = active;
+}
+
+cRecMenuItemEvent::~cRecMenuItemEvent(void) {
+}
+
+void cRecMenuItemEvent::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("event", 1);
+
+ const cChannel *channel = Channels.GetByChannelID(event->ChannelID());
+ string channelName = "";
+ string channelId = "";
+ if (channel && channel->Name()) {
+ channelName = channel->Name();
+ channelId = *(channel->GetChannelID().ToString());
+ menu->AddIntToken("channelnumber", channel->Number());
+ }
+
+ menu->AddStringToken("channelname", channelName);
+ menu->AddStringToken("channelid", channelId);
+ menu->AddIntToken("channellogoexisis", menu->ChannelLogoExists(channelId));
+ menu->AddStringToken("title", event->Title() ? event->Title() : "");
+ menu->AddStringToken("shorttext", event->ShortText() ? event->ShortText() : "");
+ menu->AddStringToken("date", *ShortDateString(event->StartTime()));
+ menu->AddStringToken("weekday", *WeekDayName(event->StartTime()));
+ menu->AddStringToken("starttime", *TimeString(event->StartTime()));
+ menu->AddStringToken("stoptime", *TimeString(event->EndTime()));
+ menu->AddIntToken("hastimer", event->HasTimer());
+}
+
+eRecMenuState cRecMenuItemEvent::ProcessKey(eKeys Key) {
+ bool consumed = false;
+ switch (Key & ~k_Repeat) {
+ case kOk:
+ return action;
+ break;
+ case kRed:
+ if (!event->HasTimer())
+ return action2;
+ break;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemRecording -------------------------------------------------------
+cRecMenuItemRecording::cRecMenuItemRecording(cRecording *recording, bool active) {
+ height = 8;
+ selectable = true;
+ this->recording = recording;
+ this->active = active;
+}
+
+void cRecMenuItemRecording::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("recording", 1);
+ if (!recording)
+ return;
+ const cRecordingInfo *recInfo = recording->Info();
+ cChannel *channel = NULL;
+ if (recInfo)
+ channel = Channels.GetByChannelID(recInfo->ChannelID());
+ string channelName = tr("unknown channel");
+ string channelId = "";
+ if (channel && channel->Name()) {
+ channelName = channel->Name();
+ channelId = *(channel->GetChannelID().ToString());
+ menu->AddIntToken("channelnumber", channel->Number());
+ }
+ menu->AddStringToken("channelname", channelName);
+ menu->AddStringToken("channelid", channelId);
+ menu->AddIntToken("channellogoexisis", menu->ChannelLogoExists(channelId));
+
+ string recName = recording->Name() ? recording->Name() : "";
+ string recDate = *ShortDateString(recording->Start());
+ string recStartTime = *TimeString(recording->Start());
+ int recDuration = recording->LengthInSeconds() / 60;
+
+ menu->AddStringToken("recname", recName);
+ menu->AddStringToken("recdate", recDate);
+ menu->AddStringToken("recstarttime", recStartTime);
+ menu->AddIntToken("recduration", recDuration);
+}
+
+// --- cRecMenuItemSearchTimer -------------------------------------------------------
+cRecMenuItemSearchTimer::cRecMenuItemSearchTimer(cTVGuideSearchTimer timer,
+ eRecMenuState action1,
+ eRecMenuState action2,
+ eRecMenuState action3,
+ bool active) {
+ height = 8;
+ this->timer = timer;
+ this->action = action1;
+ this->action2 = action2;
+ this->action3 = action3;
+ selectable = true;
+ this->active = active;
+ iconActive = 0;
+}
+
+cRecMenuItemSearchTimer::~cRecMenuItemSearchTimer(void) {
+}
+
+void cRecMenuItemSearchTimer::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("searchtimer", 1);
+ menu->AddIntToken("timeractive", timer.Active());
+ menu->AddStringToken("searchstring", timer.SearchString());
+ menu->AddIntToken("activetimers", timer.GetNumTimers());
+ menu->AddIntToken("recordingsdone", timer.GetNumRecordings());
+ menu->AddIntToken("searchactive", (iconActive==0)?true:false);
+ menu->AddIntToken("editactive", (iconActive==1)?true:false);
+ menu->AddIntToken("deleteactive", (iconActive==2)?true:false);
+}
+
+eRecMenuState cRecMenuItemSearchTimer::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ if (iconActive > 0) {
+ iconActive--;
+ return rmsRefresh;
+ }
+ return rmsNotConsumed;
+ break;
+ case kRight: {
+ if (iconActive < 2) {
+ iconActive++;
+ return rmsRefresh;
+ }
+ return rmsNotConsumed;
+ break; }
+ case kOk:
+ if (iconActive == 0)
+ return action;
+ else if (iconActive == 1)
+ return action2;
+ else if (iconActive == 2)
+ return action3;
+ break;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemTimelineHeader -------------------------------------------------------
+cRecMenuItemTimelineHeader::cRecMenuItemTimelineHeader(time_t day) {
+ height = 15;
+ timer = NULL;
+ this->day = day;
+ selectable = false;
+ active = false;
+}
+
+cRecMenuItemTimelineHeader::~cRecMenuItemTimelineHeader(void) {
+}
+
+void cRecMenuItemTimelineHeader::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("timelineheader", 1);
+ string daydate = *DateString(day);
+ menu->AddStringToken("date", daydate);
+ if (!timer) {
+ menu->AddIntToken("timerset", false);
+ return;
+ }
+ menu->AddIntToken("timerset", true);
+
+ const cChannel *channel = timer->Channel();
+ string channelName = "";
+ string channelId = "";
+ int channelNumber = 0;
+ int transponder = 0;
+ if (channel) {
+ channelName = channel->Name() ? channel->Name() : "";
+ channelId = *(channel->GetChannelID().ToString());
+ channelNumber = channel->Number();
+ transponder = channel->Transponder();
+ }
+ menu->AddStringToken("channelname", channelName);
+ menu->AddStringToken("channelid", channelId);
+ menu->AddIntToken("channelnumber", channelNumber);
+ menu->AddIntToken("channeltransponder", transponder);
+ menu->AddIntToken("channellogoexisis", menu->ChannelLogoExists(channelId));
+
+ menu->AddStringToken("timerstart", *TimeString(timer->StartTime()));
+ menu->AddStringToken("timerstop", *TimeString(timer->StopTime()));
+
+ const cEvent *event = timer->Event();
+ string eventTitle = "";
+ string eventShortText = "";
+ string eventStart = "";
+ string eventStop = "";
+ if (event) {
+ eventTitle = event->Title() ? event->Title() : "";
+ eventShortText = event->ShortText() ? event->ShortText() : "";
+ eventStart = *event->GetTimeString();
+ eventStop = *event->GetEndTimeString();
+ }
+ menu->AddStringToken("eventtitle", eventTitle);
+ menu->AddStringToken("eventshorttext", eventShortText);
+ menu->AddStringToken("eventstart", eventStart);
+ menu->AddStringToken("eventstop", eventStop);
+}
+
+// --- cRecMenuItemTimelineTimer -------------------------------------------------------
+cRecMenuItemTimelineTimer::cRecMenuItemTimelineTimer(cTimer *timer, time_t start, time_t stop, bool active) {
+ height = 8;
+ this->timer = timer;
+ this->start = start;
+ this->stop = stop;
+ selectable = true;
+ this->active = active;
+}
+
+cRecMenuItemTimelineTimer::~cRecMenuItemTimelineTimer(void) {
+}
+
+void cRecMenuItemTimelineTimer::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("timelinetimer", 1);
+
+ int secsPerDay = 24*60*60;
+ time_t timerStart = timer->StartTime() - start;
+ time_t timerStop = timer->StopTime() - start;
+ if (timerStart < 0)
+ timerStart = 0;
+ if (timerStop > secsPerDay)
+ timerStop = secsPerDay;
+
+ int percentStart = ((double)timerStart / (double)secsPerDay) * 1000;
+ int percentWidth = ((double)(timerStop - timerStart) / (double)secsPerDay) * 1000;
+ menu->AddIntToken("timerstart", percentStart);
+ menu->AddIntToken("timerwidth", percentWidth);
+}
+
+cTimer *cRecMenuItemTimelineTimer::GetTimer(void) {
+ return timer;
+}
+
+eRecMenuState cRecMenuItemTimelineTimer::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kOk:
+ return rmsTimelineTimerEdit;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemFavorite -------------------------------------------------------
+cRecMenuItemFavorite::cRecMenuItemFavorite(cTVGuideSearchTimer favorite,
+ eRecMenuState action1,
+ bool active) {
+ height = 8;
+ this->favorite = favorite;
+ this->action = action1;
+ selectable = true;
+ this->active = active;
+}
+
+void cRecMenuItemFavorite::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("favorite", 1);
+ menu->AddStringToken("favdesc", favorite.SearchString());
+}
+
+eRecMenuState cRecMenuItemFavorite::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
+// --- cRecMenuItemFavoriteStatic -------------------------------------------------------
+cRecMenuItemFavoriteStatic::cRecMenuItemFavoriteStatic(string text, eRecMenuState action, bool active) {
+ height = 8;
+ this->text = text;
+ this->action = action;
+ selectable = true;
+ this->active = active;
+}
+
+void cRecMenuItemFavoriteStatic::SetTokens(cViewGrid *menu) {
+ menu->ClearTokens();
+ menu->AddIntToken("favorite", 1);
+ menu->AddStringToken("favdesc", text);
+}
+
+eRecMenuState cRecMenuItemFavoriteStatic::ProcessKey(eKeys Key) {
+ switch (Key & ~k_Repeat) {
+ case kOk:
+ return action;
+ default:
+ break;
+ }
+ return rmsNotConsumed;
+}
+
diff --git a/recmenuitem.h b/recmenuitem.h new file mode 100644 index 0000000..9ea6d19 --- /dev/null +++ b/recmenuitem.h @@ -0,0 +1,495 @@ +#ifndef __TVGUIDE_RECMENUITEM_H
+#define __TVGUIDE_RECMENUITEM_H
+
+#define AUTO_ADVANCE_TIMEOUT 1500
+
+using namespace std;
+
+#include <string>
+#include <sstream>
+#include <vdr/plugin.h>
+#include <vdr/tools.h>
+#include "libskindesigner/osdelements.h"
+#include "searchtimer.h"
+
+enum eRecMenuState {
+ rmsConsumed,
+ rmsNotConsumed,
+ rmsRefresh,
+ rmsContinue,
+ rmsClose,
+ rmsDisabled,
+ //INSTANT TIMER
+ rmsInstantRecord,
+ rmsInstantRecordFolder,
+ rmsIgnoreTimerConflict,
+ rmsDeleteTimerConflictMenu,
+ rmsEditTimerConflictMenu,
+ rmsSearchRerunsTimerConflictMenu,
+ rmsSaveTimerConflictMenu,
+ rmsTimerConflictShowInfo,
+ rmsDeleteTimer,
+ rmsDeleteTimerConfirmation,
+ rmsEditTimer,
+ rmsSaveTimer,
+ //SEARCH
+ rmsSearch,
+ rmsSearchWithOptions,
+ rmsSearchPerform,
+ rmsSearchShowInfo,
+ rmsSearchRecord,
+ rmsSearchRecordConfirm,
+ rmsSearchNothingFoundConfirm,
+ //SERIES TIMER
+ rmsSeriesTimer,
+ rmsSeriesTimerFolder,
+ rmsSeriesTimerCreate,
+ //SEARCHTIMER
+ rmsSearchTimer,
+ rmsSearchTimerOptions,
+ rmsSearchTimers,
+ rmsSearchTimerEdit,
+ rmsSearchTimerEditAdvanced,
+ rmsSearchTimerTest,
+ rmsSearchTimerSave,
+ rmsSearchTimerCreateWithTemplate,
+ rmsSearchTimerDeleteConfirm,
+ rmsSearchTimerDelete,
+ rmsSearchTimerDeleteWithTimers,
+ rmsSearchTimerRecord,
+ //SWITCHTIMER
+ rmsSwitchTimer,
+ rmsSwitchTimerCreate,
+ rmsSwitchTimerDelete,
+ //RECORDINGS SEARCH
+ rmsRecordingSearch,
+ rmsRecordingSearchResult,
+ //TIMER CONFLICTS
+ rmsTimerConflict,
+ rmsTimerConflicts,
+ rmsTimerConflictIgnoreReruns,
+ rmsTimerConflictRecordRerun,
+ //TIMELINE
+ rmsTimeline,
+ rmsTimelineTimerEdit,
+ rmsTimelineTimerSave,
+ rmsTimelineTimerDelete,
+ //FAVORITES
+ rmsFavoritesRecord,
+ rmsFavoritesNow,
+ rmsFavoritesNext,
+ rmsFavoritesUser1,
+ rmsFavoritesUser2,
+ rmsFavoritesUser3,
+ rmsFavoritesUser4,
+};
+
+// --- cRecMenuItem -------------------------------------------------------------
+class cRecMenuItem : public cListObject {
+protected:
+ static long idCounter;
+ long id;
+ bool init;
+ bool active;
+ bool selectable;
+ eRecMenuState action;
+ //height of item in percent of screen height
+ int height;
+ string text;
+public:
+ cRecMenuItem(void);
+ virtual ~cRecMenuItem(void);
+ virtual void SetActive(void) { active = true; }
+ virtual void SetInactive(void) { active = false; }
+ long Id(void) { return id; };
+ bool Selectable(void) { return selectable; }
+ bool IsNew(void);
+ bool SetNew(void) { init = true; };
+ bool Active(void) { return active; }
+ int GetHeight(void) { return height; };
+ string GetText(void) { return text; };
+ virtual void SetTokens(cViewGrid *menu) {};
+ virtual eRecMenuState ProcessKey(eKeys Key) { return rmsNotConsumed; };
+};
+
+// --- cRecMenuItemInfo -------------------------------------------------------
+class cRecMenuItemInfo : public cRecMenuItem {
+private:
+ int numLines;
+ string line1;
+ string line2;
+ string line3;
+ string line4;
+public:
+ cRecMenuItemInfo(string line1, int numLines = 1, string line2 = "", string line3 = "", string line4 = "");
+ virtual ~cRecMenuItemInfo(void);
+ string GetText(void) { return text; };
+ void SetTokens(cViewGrid *menu);
+};
+
+// --- cRecMenuItemButton -------------------------------------------------------
+class cRecMenuItemButton : public cRecMenuItem {
+private:
+ string text;
+public:
+ cRecMenuItemButton(string text, eRecMenuState action, bool active);
+ virtual ~cRecMenuItemButton(void);
+ string GetText(void) { return text; };
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemButtonYesNo -------------------------------------------------------
+class cRecMenuItemButtonYesNo : public cRecMenuItem {
+private:
+ string textYes;
+ string textNo;
+ eRecMenuState actionNo;
+ bool yesActive;
+public:
+ cRecMenuItemButtonYesNo(string textYes,
+ string textNo,
+ eRecMenuState actionYes,
+ eRecMenuState actionNo,
+ bool active);
+ virtual ~cRecMenuItemButtonYesNo(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemInt -------------------------------------------------------
+class cRecMenuItemInt : public cRecMenuItem {
+private:
+ int currentVal;
+ int *callback;
+ int minVal;
+ int maxVal;
+ bool fresh;
+public:
+ cRecMenuItemInt(string text,
+ int initialVal,
+ int minVal,
+ int maxVal,
+ bool active = false,
+ int *callback = NULL,
+ eRecMenuState action = rmsNotConsumed);
+ virtual ~cRecMenuItemInt(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemBool -------------------------------------------------------
+class cRecMenuItemBool : public cRecMenuItem {
+private:
+ bool yes;
+ bool *callback;
+public:
+ cRecMenuItemBool(string text,
+ bool initialVal,
+ bool active = false,
+ bool *callback = NULL,
+ eRecMenuState action = rmsNotConsumed);
+ virtual ~cRecMenuItemBool(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemSelect -------------------------------------------------------
+class cRecMenuItemSelect : public cRecMenuItem {
+private:
+ int currentVal;
+ int *callback;
+ vector<string> strings;
+ int numValues;
+public:
+ cRecMenuItemSelect(string text,
+ vector<string> strings,
+ int initialVal,
+ bool active = false,
+ int *callback = NULL,
+ eRecMenuState action = rmsNotConsumed);
+ virtual ~cRecMenuItemSelect(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemText -------------------------------------------------------
+class cRecMenuItemText : public cRecMenuItem {
+private:
+ char *value;
+ string buffer;
+ char *callback;
+ int length;
+ const char *allowed;
+ int pos, offset;
+ bool insert, newchar, uppercase;
+ int lengthUtf8;
+ uint *valueUtf8;
+ uint *allowedUtf8;
+ uint *charMapUtf8;
+ uint *currentCharUtf8;
+ eKeys lastKey;
+ cTimeMs autoAdvanceTimeout;
+ uint *IsAllowed(uint c);
+ void AdvancePos(void);
+ uint Inc(uint c, bool Up);
+ void Type(uint c);
+ void Insert(void);
+ void Delete(void);
+ void EnterEditMode(void);
+ void LeaveEditMode(bool SaveValue = false);
+ bool InEditMode(void) { return valueUtf8 != NULL; };
+ void SetText(void);
+public:
+ cRecMenuItemText(string title,
+ char *initialVal,
+ int length,
+ bool active = false,
+ char *callback = NULL);
+ virtual ~cRecMenuItemText(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemTime -------------------------------------------------------
+class cRecMenuItemTime : public cRecMenuItem {
+private:
+ int value;
+ int *callback;
+ int mm;
+ int hh;
+ int pos;
+ bool fresh;
+public:
+ cRecMenuItemTime(string text,
+ int initialVal,
+ bool active = false,
+ int *callback = NULL,
+ eRecMenuState action = rmsNotConsumed);
+ virtual ~cRecMenuItemTime(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemDay -------------------------------------------------------
+class cRecMenuItemDay : public cRecMenuItem {
+private:
+ time_t currentVal;
+ time_t *callback;
+public:
+ cRecMenuItemDay(string text,
+ time_t initialVal,
+ bool active = false,
+ time_t *callback = NULL,
+ eRecMenuState action = rmsNotConsumed);
+ virtual ~cRecMenuItemDay(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemChannelChooser -------------------------------------------------------
+class cRecMenuItemChannelChooser : public cRecMenuItem {
+private:
+ cChannel *channel;
+ int channelNumber;
+ int *callback;
+ bool initialChannelSet;
+ bool fresh;
+public:
+ cRecMenuItemChannelChooser (string text,
+ cChannel *initialChannel,
+ bool active = false,
+ int *callback = NULL,
+ eRecMenuState action = rmsNotConsumed);
+ virtual ~cRecMenuItemChannelChooser(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemDayChooser -------------------------------------------------------
+class cRecMenuItemDayChooser : public cRecMenuItem {
+private:
+ int weekdays;
+ int *callback;
+ int selectedDay;
+ void ToggleDay(void);
+ bool WeekDaySet(unsigned day);
+public:
+ cRecMenuItemDayChooser (string text,
+ int weekdays,
+ bool active = false,
+ int *callback = NULL);
+ virtual ~cRecMenuItemDayChooser(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemSelectDirectory -------------------------------------------------------
+class cRecMenuItemSelectDirectory : public cRecMenuItem {
+private:
+ string text;
+ string originalFolder;
+ vector<string> folders;
+ int currentVal;
+ char *callback;
+ int numValues;
+ void SetCallback(void);
+ int GetInitial(void);
+public:
+ cRecMenuItemSelectDirectory(string text,
+ string originalFolder,
+ bool active = false,
+ char *callback = NULL,
+ eRecMenuState action = rmsNotConsumed,
+ bool isSearchTimer = false);
+ virtual ~cRecMenuItemSelectDirectory(void) {};
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemTimerConflictHeader -------------------------------------------------------
+class cRecMenuItemTimerConflictHeader: public cRecMenuItem {
+private:
+ time_t conflictStart;
+ time_t conflictStop;
+ time_t overlapStart;
+ time_t overlapStop;
+public:
+ cRecMenuItemTimerConflictHeader(time_t conflictStart,
+ time_t conflictStop,
+ time_t overlapStart,
+ time_t overlapStop);
+ void SetTokens(cViewGrid *menu);
+ virtual ~cRecMenuItemTimerConflictHeader(void);
+};
+
+// --- cRecMenuItemTimer -------------------------------------------------------
+class cRecMenuItemTimer : public cRecMenuItem {
+private:
+ const cTimer *timer;
+ eRecMenuState action2;
+ eRecMenuState action3;
+ eRecMenuState action4;
+ int iconActive;
+ time_t conflictStart;
+ time_t conflictStop;
+ time_t overlapStart;
+ time_t overlapStop;
+public:
+ cRecMenuItemTimer(const cTimer *timer,
+ eRecMenuState action1,
+ eRecMenuState action2,
+ eRecMenuState action3,
+ eRecMenuState action4,
+ time_t conflictStart,
+ time_t conflictStop,
+ time_t overlapStart,
+ time_t overlapStop,
+ bool active);
+ virtual ~cRecMenuItemTimer(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemEvent -------------------------------------------------------
+class cRecMenuItemEvent : public cRecMenuItem {
+private:
+ const cEvent *event;
+ eRecMenuState action2;
+ int iconActive;
+public:
+ cRecMenuItemEvent(const cEvent *event,
+ eRecMenuState action1,
+ eRecMenuState action2,
+ bool active);
+ virtual ~cRecMenuItemEvent(void);
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+ const cEvent *GetEvent(void) { return event; };
+};
+
+// --- cRecMenuItemRecording -------------------------------------------------------
+class cRecMenuItemRecording : public cRecMenuItem {
+private:
+ cRecording *recording;
+public:
+ cRecMenuItemRecording(cRecording *recording, bool active);
+ virtual ~cRecMenuItemRecording(void) {};
+ void SetTokens(cViewGrid *menu);
+};
+
+// --- cRecMenuItemSearchTimer -------------------------------------------------------
+class cRecMenuItemSearchTimer : public cRecMenuItem {
+private:
+ cTVGuideSearchTimer timer;
+ eRecMenuState action1;
+ eRecMenuState action2;
+ eRecMenuState action3;
+ int iconActive;
+public:
+ cRecMenuItemSearchTimer(cTVGuideSearchTimer timer,
+ eRecMenuState action1,
+ eRecMenuState action2,
+ eRecMenuState action3,
+ bool active);
+ virtual ~cRecMenuItemSearchTimer(void);
+ cTVGuideSearchTimer GetTimer(void) { return timer; };
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemTimelineHeader -------------------------------------------------------
+class cRecMenuItemTimelineHeader : public cRecMenuItem {
+private:
+ time_t day;
+ cTimer *timer;
+public:
+ cRecMenuItemTimelineHeader(time_t day);
+ virtual ~cRecMenuItemTimelineHeader(void);
+ void SetTokens(cViewGrid *menu);
+ void SetDay(time_t day) { this->day = day; };
+ void SetCurrentTimer(cTimer *timer) { this->timer = timer; };
+ void UnsetCurrentTimer(void) { timer = NULL; };
+};
+
+// --- cRecMenuItemTimelineTimer -------------------------------------------------------
+class cRecMenuItemTimelineTimer : public cRecMenuItem {
+private:
+ cTimer *timer;
+ time_t start;
+ time_t stop;
+public:
+ cRecMenuItemTimelineTimer(cTimer *timer, time_t start, time_t stop, bool active);
+ virtual ~cRecMenuItemTimelineTimer(void);
+ void SetTokens(cViewGrid *menu);
+ cTimer *GetTimer(void);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemFavorite -------------------------------------------------------
+class cRecMenuItemFavorite : public cRecMenuItem {
+private:
+ cTVGuideSearchTimer favorite;
+ eRecMenuState action1;
+public:
+ cRecMenuItemFavorite(cTVGuideSearchTimer favorite,
+ eRecMenuState action1,
+ bool active);
+ virtual ~cRecMenuItemFavorite(void) {};
+ void SetTokens(cViewGrid *menu);
+ cTVGuideSearchTimer GetFavorite(void) { return favorite; };
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+// --- cRecMenuItemFavoriteStatic -------------------------------------------------------
+class cRecMenuItemFavoriteStatic : public cRecMenuItem {
+private:
+ string text;
+public:
+ cRecMenuItemFavoriteStatic(string text, eRecMenuState action, bool active);
+ virtual ~cRecMenuItemFavoriteStatic(void) {};
+ void SetTokens(cViewGrid *menu);
+ eRecMenuState ProcessKey(eKeys Key);
+};
+
+#endif //__TVGUIDE_RECMENUITEM_H
\ No newline at end of file diff --git a/recmenus.c b/recmenus.c new file mode 100644 index 0000000..d20250a --- /dev/null +++ b/recmenus.c @@ -0,0 +1,1143 @@ +#include <string>
+#include <sstream>
+#include <algorithm>
+#include "config.h"
+#include "services/remotetimers.h"
+#include "helpers.h"
+#include "recmenus.h"
+
+// --- cRecMenuMain ---------------------------------------------------------
+cRecMenuMain::cRecMenuMain(bool epgSearchAvailable, bool timerActive, bool switchTimerActive) {
+ SetMenuWidth(50);
+ eRecMenuState action = rmsInstantRecord;
+ if (!timerActive) {
+ if (config.instRecFolderMode == eFolderSelect)
+ action = rmsInstantRecordFolder;
+ AddMenuItem(new cRecMenuItemButton(tr("Instant Record"), action, true));
+ } else {
+ AddMenuItem(new cRecMenuItemButton(tr("Delete Timer"), rmsDeleteTimer, true));
+ AddMenuItem(new cRecMenuItemButton(tr("Edit Timer"), rmsEditTimer, false));
+ }
+
+ AddMenuItem(new cRecMenuItemButton(tr("Timer Timeline"), rmsTimeline, false));
+
+ if (epgSearchAvailable) {
+ AddMenuItem(new cRecMenuItemButton(tr("Create Search Timer"), rmsSearchTimer, false));
+ AddMenuItem(new cRecMenuItemButton(tr("Search Timers"), rmsSearchTimers, false));
+ }
+
+ if (config.instRecFolderMode == eFolderSelect)
+ action = rmsSeriesTimerFolder;
+ else
+ action = rmsSeriesTimer;
+ AddMenuItem(new cRecMenuItemButton(tr("Create Series Timer"), action, false));
+
+ if (epgSearchAvailable) {
+ if (!switchTimerActive) {
+ AddMenuItem(new cRecMenuItemButton(tr("Create Switch Timer"), rmsSwitchTimer, false));
+ } else {
+ AddMenuItem(new cRecMenuItemButton(tr("Delete Switch Timer"), rmsSwitchTimerDelete, false));
+ }
+ AddMenuItem(new cRecMenuItemButton(tr("Search"), rmsSearch, false));
+ }
+
+ if (epgSearchAvailable) {
+ AddMenuItem(new cRecMenuItemButton(tr("Check for Timer Conflicts"), rmsTimerConflicts, false));
+ }
+
+ AddMenuItem(new cRecMenuItemButton(tr("Search in Recordings"), rmsRecordingSearch, false));
+};
+
+/******************************************************************************************
+* Instant Timer Menus
+******************************************************************************************/
+
+// --- cRecMenuConfirmTimer ---------------------------------------------------------
+cRecMenuConfirmTimer::cRecMenuConfirmTimer(const cEvent *event) {
+ SetMenuWidth(50);
+
+ bool eventHasTimer = false;
+ if (config.useRemoteTimers && pRemoteTimers) {
+ RemoteTimers_GetMatch_v1_0 rtMatch;
+ rtMatch.event = event;
+ pRemoteTimers->Service("RemoteTimers::GetMatch-v1.0", &rtMatch);
+ if (rtMatch.timerMatch == tmFull) {
+ eventHasTimer = true;
+ }
+ } else {
+ eventHasTimer = event->HasTimer();
+ }
+
+ string message;
+ if (eventHasTimer) {
+ message = tr("Timer created");
+ } else {
+ message = tr("Timer NOT created");
+ }
+ cString channelName = Channels.GetByChannelID(event->ChannelID())->Name();
+ string datetime = *cString::sprintf("%s %s - %s", *event->GetDateString(), *event->GetTimeString(), *event->GetEndTimeString());
+ string eventTitle = event->Title() ? event->Title() : "";
+ AddHeader(new cRecMenuItemInfo(message, 4, *channelName, datetime, eventTitle));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+// --- cRecMenuAskFolder ---------------------------------------------------------
+cRecMenuAskFolder::cRecMenuAskFolder(const cEvent *event, eRecMenuState nextAction) {
+ SetMenuWidth(80);
+ this->nextAction = nextAction;
+ string message = tr("Set Folder for");
+ string eventTitle = event->Title() ? event->Title() : "";
+ AddHeader(new cRecMenuItemInfo(message, 2, eventTitle));
+ AddMenuItem(new cRecMenuItemButton(tr("root video folder"), nextAction, true));
+
+ ReadRecordingDirectories(&folders, NULL, "");
+ int numFolders = folders.size();
+ for (int i=0; i < numFolders; i++) {
+ AddMenuItem(new cRecMenuItemButton(folders[i].c_str(), nextAction, false));
+ }
+}
+
+string cRecMenuAskFolder::GetFolder(void) {
+ std::string folder = "";
+ int folderActive = GetNumActive();
+ if (folderActive > 0 && folderActive < folders.size() + 1)
+ folder = folders[folderActive - 1];
+ return folder;
+}
+
+// --- cRecMenuConfirmDeleteTimer ---------------------------------------------------------
+cRecMenuConfirmDeleteTimer::cRecMenuConfirmDeleteTimer(const cEvent *event) {
+ SetMenuWidth(50);
+ string message = tr("Timer deleted");
+ cString channelName = Channels.GetByChannelID(event->ChannelID())->Name();
+ string datetime = *cString::sprintf("%s %s - %s", *event->GetDateString(), *event->GetTimeString(), *event->GetEndTimeString());
+ string eventTitle = event->Title() ? event->Title() : "";
+
+ AddHeader(new cRecMenuItemInfo(message, 4, *channelName, datetime, eventTitle));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+// --- cRecMenuAskDeleteTimer ---------------------------------------------------------
+cRecMenuAskDeleteTimer::cRecMenuAskDeleteTimer(const cEvent *event) {
+ SetMenuWidth(50);
+ string message = tr("Timer");
+ cString channelName = Channels.GetByChannelID(event->ChannelID())->Name();
+ string eventTitle = event->Title() ? event->Title() : "";
+ string message2 = tr("still recording - really delete?");
+
+ AddHeader(new cRecMenuItemInfo(message, 4, *channelName, eventTitle, message2));
+ AddMenuItem(new cRecMenuItemButtonYesNo(tr("Yes"), tr("No"), rmsDeleteTimerConfirmation, rmsClose, true));
+}
+
+// --- cRecMenuTimerConflicts ---------------------------------------------------------
+cRecMenuTimerConflicts::cRecMenuTimerConflicts(cTVGuideTimerConflicts *conflicts) {
+ SetMenuWidth(80);
+ int numConflicts = conflicts->NumConflicts();
+
+ string text;
+ if (numConflicts == 1) {
+ text = *cString::sprintf("%s %s %s", tr("One"), tr("Timer Conflict"), tr("detected"));
+ } else {
+ text = *cString::sprintf("%d %s %s", conflicts->NumConflicts(), tr("Timer Conflicts"), tr("detected"));
+ }
+ AddHeader(new cRecMenuItemInfo(text));
+
+ for (int i=0; i<numConflicts; i++) {
+ cTVGuideTimerConflict *conflict = conflicts->GetConflict(i);
+ if (!conflict)
+ continue;
+ cString dateTime = DayDateTime(conflict->time);
+ int numTimers = conflict->timerIDs.size();
+ cString textConflict = cString::sprintf("%s: %s (%d %s)", tr("Show conflict"), *dateTime, numTimers, tr("timers involved"));
+ bool isActive = (i==0)?true:false;
+ AddMenuItem(new cRecMenuItemButton(*textConflict, rmsTimerConflict, isActive));
+ }
+
+ AddFooter(new cRecMenuItemButton(tr("Ignore Conflicts"), rmsClose, false));
+}
+
+int cRecMenuTimerConflicts::GetTimerConflict(void) {
+ return GetNumActive();
+}
+
+// --- cRecMenuTimerConflict ---------------------------------------------------------
+cRecMenuTimerConflict::cRecMenuTimerConflict(cTVGuideTimerConflict *conflict) {
+ SetMenuWidth(95);
+ this->conflict = conflict;
+ AddHeader(new cRecMenuItemTimerConflictHeader(conflict->timeStart,
+ conflict->timeStop,
+ conflict->overlapStart,
+ conflict->overlapStop));
+
+ int i=0;
+ for(vector<int>::iterator it = conflict->timerIDs.begin(); it != conflict->timerIDs.end(); it++) {
+ const cTimer *timer = Timers.Get(*it);
+ if (timer) {
+ AddMenuItem(new cRecMenuItemTimer(timer,
+ rmsTimerConflictShowInfo,
+ rmsDeleteTimerConflictMenu,
+ rmsEditTimerConflictMenu,
+ rmsSearchRerunsTimerConflictMenu,
+ conflict->timeStart,
+ conflict->timeStop,
+ conflict->overlapStart,
+ conflict->overlapStop,
+ (!i)?true:false));
+ i++;
+ }
+ }
+ AddMenuItem(new cRecMenuItemButton(tr("Ignore Conflict"), rmsIgnoreTimerConflict, false));
+}
+
+int cRecMenuTimerConflict::GetTimerConflictIndex(void) {
+ return GetNumActive();
+}
+
+// --- cRecMenuNoTimerConflict ---------------------------------------------------------
+cRecMenuNoTimerConflict::cRecMenuNoTimerConflict(void) {
+ SetMenuWidth(50);
+ string text = tr("No Timer Conflicts found");
+ AddHeader(new cRecMenuItemInfo(text));
+ AddMenuItem(new cRecMenuItemButton(tr("Close"), rmsClose, true));
+}
+
+// --- cRecMenuRerunResults ---------------------------------------------------------
+cRecMenuRerunResults::cRecMenuRerunResults(const cEvent *original, const cEvent **reruns, int numReruns) {
+ this->reruns = reruns;
+ this->numReruns = numReruns;
+ SetMenuWidth(70);
+
+ string message1 = tr("reruns for");
+ string message2 = tr("rerun for");
+ string message3 = tr("found");
+ string line1 = *cString::sprintf("%d %s:", numReruns, (numReruns>1)?(message1.c_str()):(message2.c_str()));
+ string line2 = *cString::sprintf("\"%s\" %s", original->Title(), message3.c_str());
+
+ AddHeader(new cRecMenuItemInfo(line1, 2, line2));
+
+ cRecMenuItem *button = new cRecMenuItemButton(tr("Ignore reruns"), rmsTimerConflictIgnoreReruns, false);
+ AddFooter(button);
+
+ if (reruns && (numReruns > 0)) {
+ for (int i=0; i<numReruns; i++) {
+ AddMenuItem(new cRecMenuItemEvent(reruns[i], rmsSearchShowInfo, rmsTimerConflictRecordRerun, (i==0)?true:false));
+ }
+ }
+}
+
+int cRecMenuRerunResults::GetTotalNumMenuItems(void) {
+ return numReruns;
+}
+
+const cEvent *cRecMenuRerunResults::GetEvent(void) {
+ cRecMenuItemEvent *activeItem = dynamic_cast<cRecMenuItemEvent*>(GetActive());
+ if (activeItem)
+ return activeItem->GetEvent();
+ return NULL;
+}
+
+// --- cRecMenuNoRerunsFound ---------------------------------------------------------
+cRecMenuNoRerunsFound::cRecMenuNoRerunsFound(string searchString) {
+ SetMenuWidth(50);
+ string message = tr("No reruns found for Event");
+ string eventQuoted = *cString::sprintf("\"%s\"", searchString.c_str());
+
+ AddHeader(new cRecMenuItemInfo(message, 2, eventQuoted));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+// --- cRecMenuConfirmRerunUsed ---------------------------------------------------------
+cRecMenuConfirmRerunUsed::cRecMenuConfirmRerunUsed(const cEvent *original, const cEvent *replace) {
+ SetMenuWidth(70);
+ cString channelOrig = Channels.GetByChannelID(original->ChannelID())->Name();
+ cString channelReplace = Channels.GetByChannelID(replace->ChannelID())->Name();
+ string line1 = *cString::sprintf("%s \"%s\"", tr("Timer for"), original->Title());
+ string line2 = *cString::sprintf("%s %s %s", *original->GetDateString(), *original->GetTimeString(), *channelOrig);
+ string line3 = *cString::sprintf("%s \"%s\"", tr("replaced by rerun"), replace->Title());
+ string line4 = *cString::sprintf("%s %s %s", *replace->GetDateString(), *replace->GetTimeString(), *channelReplace);
+
+ AddHeader(new cRecMenuItemInfo(line1, 4, line2, line3, line4));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsTimerConflicts, true));
+}
+
+// --- cRecMenuEditTimer ---------------------------------------------------------
+cRecMenuEditTimer::cRecMenuEditTimer(cTimer *timer, eRecMenuState nextState) {
+ SetMenuWidth(70);
+ if (!timer)
+ return;
+ originalTimer = timer;
+ string title = "";
+ string channelName = "";
+ if (timer->Event() && timer->Event()->Title())
+ title = timer->Event()->Title();
+ if (timer->Channel() && timer->Channel()->Name())
+ channelName = timer->Channel()->Name();
+ cRecMenuItemInfo *infoItem = new cRecMenuItemInfo(tr("Edit Timer"), 3, title, channelName);
+ AddHeader(infoItem);
+
+ timerActive = false;
+ if (config.useRemoteTimers && pRemoteTimers) {
+ RemoteTimers_GetMatch_v1_0 rtMatch;
+ rtMatch.event = timer->Event();
+ pRemoteTimers->Service("RemoteTimers::GetMatch-v1.0", &rtMatch);
+ if (rtMatch.timer) {
+ if (rtMatch.timerMatch == tmFull)
+ timerActive = true;
+ }
+ } else
+ timerActive = timer->HasFlags(tfActive);
+
+ day = timer->Day();
+ start = timer->Start();
+ stop = timer->Stop();
+ prio = timer->Priority();
+ lifetime = timer->Lifetime();
+ strncpy(folder, GetDirectoryFromTimer(timer->File()).c_str(), TEXTINPUTLENGTH);
+
+ AddMenuItem(new cRecMenuItemBool(tr("Timer Active"), timerActive, true, &timerActive));
+ AddMenuItem(new cRecMenuItemInt(tr("Priority"), prio, 0, MAXPRIORITY, false, &prio));
+ AddMenuItem(new cRecMenuItemInt(tr("Lifetime"), lifetime, 0, MAXLIFETIME, false, &lifetime));
+ AddMenuItem(new cRecMenuItemDay(tr("Day"), day, false, &day));
+ AddMenuItem(new cRecMenuItemTime(tr("Timer start time"), start, false, &start));
+ AddMenuItem(new cRecMenuItemTime(tr("Timer stop time"), stop, false, &stop));
+ AddMenuItem(new cRecMenuItemInfo(tr("Timer File"), 2, timer->File() ? timer->File() : ""));
+ AddMenuItem(new cRecMenuItemSelectDirectory(tr("New Folder"), string(folder), false, folder));
+ if (nextState == rmsTimelineTimerSave) {
+ AddMenuItem(new cRecMenuItemButton(tr("Delete Timer"), rmsTimelineTimerDelete, false));
+ AddFooter(new cRecMenuItemButtonYesNo(tr("Save"), tr("Cancel"), nextState, rmsTimeline, false));
+ } else {
+ AddFooter(new cRecMenuItemButtonYesNo(tr("Save"), tr("Cancel"), nextState, rmsClose, false));
+ }
+}
+
+cTimer *cRecMenuEditTimer::GetOriginalTimer(void) {
+ return originalTimer;
+}
+
+cTimer cRecMenuEditTimer::GetTimer(void) {
+ cTimer t;
+ if (timerActive)
+ t.SetFlags(tfActive);
+ else
+ t.SetFlags(tfNone);
+ t.SetDay(day);
+ t.SetStart(start);
+ t.SetStop(stop);
+ t.SetPriority(prio);
+ t.SetLifetime(lifetime);
+ string newFolder(folder);
+ string newFile = originalTimer->File();
+ size_t found = newFile.find_last_of('~');
+ if (found != string::npos) {
+ string fileName = newFile.substr(found+1);
+ if (newFolder.size() > 0)
+ newFile = *cString::sprintf("%s~%s", newFolder.c_str(), fileName.c_str());
+ else
+ newFile = fileName;
+ } else {
+ if (newFolder.size() > 0)
+ newFile = *cString::sprintf("%s~%s", newFolder.c_str(), newFile.c_str());
+ }
+ std::replace(newFile.begin(), newFile.end(), '/', '~');
+ t.SetFile(newFile.c_str());
+ return t;
+}
+
+/******************************************************************************************
+* Series Timer Menus
+******************************************************************************************/
+
+// --- cRecMenuSeriesTimer ---------------------------------------------------------
+cRecMenuSeriesTimer::cRecMenuSeriesTimer(cChannel *initialChannel, const cEvent *event, string folder) {
+ if (!initialChannel)
+ return;
+ timerActive = true;
+ channel = initialChannel->Number();
+ dayOfWeek = 127;
+ priority = MAXPRIORITY;
+ lifetime = MAXLIFETIME;
+ CalculateTimes(event);
+ this->folder = folder;
+
+ SetMenuWidth(70);
+ string line1 = tr("Create Series Timer based on");
+ string line2 = *cString::sprintf("\"%s\"", event->Title());
+ AddHeader(new cRecMenuItemInfo(line1, 2, line2));
+
+ AddMenuItem(new cRecMenuItemBool(tr("Timer Active"), timerActive, false, &timerActive));
+ AddMenuItem(new cRecMenuItemChannelChooser(tr("Channel"), initialChannel, false, &channel));
+ AddMenuItem(new cRecMenuItemTime(tr("Series Timer start time"), start, false, &start));
+ AddMenuItem(new cRecMenuItemTime(tr("Series Timer stop time"), stop, false, &stop));
+ AddMenuItem(new cRecMenuItemDayChooser(tr("Days to record"), dayOfWeek, false, &dayOfWeek));
+ AddMenuItem(new cRecMenuItemDay(tr("Day to start"), tstart, false, &tstart));
+ AddMenuItem(new cRecMenuItemInt(tr("Priority"), priority, 0, MAXPRIORITY, false, &priority));
+ AddMenuItem(new cRecMenuItemInt(tr("Lifetime"), lifetime, 0, MAXLIFETIME, false, &lifetime));
+
+ AddFooter(new cRecMenuItemButtonYesNo(tr("Create Timer"), tr("Cancel"), rmsSeriesTimerCreate, rmsClose, true));
+}
+
+cTimer *cRecMenuSeriesTimer::GetTimer(void) {
+ cChannel *chan = Channels.GetByNumber(channel);
+ cTimer *seriesTimer = new cTimer(NULL, NULL, chan);
+ cString fileName = "TITLE EPISODE";
+ if (folder.size() > 0) {
+ std::replace(folder.begin(), folder.end(), '/', '~');
+ fileName = cString::sprintf("%s~%s", folder.c_str(), *fileName);
+ }
+ seriesTimer->SetDay(tstart);
+ seriesTimer->SetStart(start);
+ seriesTimer->SetStop(stop);
+ seriesTimer->SetPriority(priority);
+ seriesTimer->SetLifetime(lifetime);
+ seriesTimer->SetWeekDays(dayOfWeek);
+ seriesTimer->SetFile(*fileName);
+ if (timerActive)
+ seriesTimer->SetFlags(tfActive);
+ else
+ seriesTimer->SetFlags(tfNone);
+ return seriesTimer;
+}
+
+void cRecMenuSeriesTimer::CalculateTimes(const cEvent *event) {
+ tstart = event->StartTime();
+ tstart -= Setup.MarginStart * 60;
+ time_t tstop = tstart + event->Duration();
+ tstop += Setup.MarginStop * 60;
+
+ struct tm tm_r;
+ struct tm *time = localtime_r(&tstart, &tm_r);
+ start = time->tm_hour * 100 + time->tm_min;
+ time = localtime_r(&tstop, &tm_r);
+ stop = time->tm_hour * 100 + time->tm_min;
+ if (stop >= 2400)
+ stop -= 2400;
+}
+
+// --- cRecMenuConfirmSeriesTimer ---------------------------------------------------------
+cRecMenuConfirmSeriesTimer::cRecMenuConfirmSeriesTimer(cTimer *seriesTimer) {
+ SetMenuWidth(50);
+ string line1 = tr("Series Timer created");
+ string line2 = "";
+ if (seriesTimer) {
+ cString days = cTimer::PrintDay(seriesTimer->Day(), seriesTimer->WeekDays(), true);
+ line2 = cString::sprintf("%s, %s: %s, %s: %s", *days, tr("Start"), *TimeString(seriesTimer->StartTime()), tr("Stop"), *TimeString(seriesTimer->StopTime()));
+ }
+ AddHeader(new cRecMenuItemInfo(line1, 2, line2));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+/******************************************************************************************
+* SearchTimer Menus
+******************************************************************************************/
+
+// --- cRecMenuSearchTimer ---------------------------------------------------------
+cRecMenuSearchTimer::cRecMenuSearchTimer(const cEvent *event) {
+ SetMenuWidth(70);
+ string message = tr("Configure Search Timer based on");
+ string infoText = *cString::sprintf("\"%s\"", event->Title());
+ AddHeader(new cRecMenuItemInfo(message, 2, infoText));
+ strncpy(searchString, event->Title(), TEXTINPUTLENGTH);
+ AddMenuItem(new cRecMenuItemText(tr("Search Expression:"), searchString, TEXTINPUTLENGTH, false));
+ AddFooter(new cRecMenuItemButtonYesNo(tr("Continue"), tr("Cancel"), rmsSearchTimerOptions, rmsClose, true));
+}
+
+// --- cRecMenuSearchTimerTemplates ---------------------------------------------------------
+cRecMenuSearchTimerTemplates::cRecMenuSearchTimerTemplates(cTVGuideSearchTimer searchTimer, vector<TVGuideEPGSearchTemplate> templates) {
+ SetMenuWidth(70);
+ this->searchTimer = searchTimer;
+ this->templates = templates;
+ string message = tr("Configure Search Timer for Search String");
+ AddHeader(new cRecMenuItemInfo(message, 2, searchTimer.SearchString()));
+ AddMenuItem(new cRecMenuItemButton(tr("Manually configure Options"), rmsSearchTimerEdit, true));
+
+ numTemplates = templates.size();
+ for (int i=0; i<numTemplates; i++) {
+ string buttonText = *cString::sprintf("%s \"%s\"", tr("Use Template"), templates[i].name.c_str());
+ AddMenuItem(new cRecMenuItemButton(buttonText, rmsSearchTimerCreateWithTemplate, false));
+ }
+}
+
+TVGuideEPGSearchTemplate cRecMenuSearchTimerTemplates::GetTemplate(void) {
+ TVGuideEPGSearchTemplate templ;
+ int tmplActive = GetNumActive() - 1;
+ if (tmplActive >= 0 && tmplActive < templates.size())
+ templ = templates[tmplActive];
+ return templ;
+}
+
+// --- cRecMenuSearchTimers ---------------------------------------------------------
+cRecMenuSearchTimers::cRecMenuSearchTimers(std::vector<cTVGuideSearchTimer> searchTimers) {
+ SetMenuWidth(70);
+ this->searchTimers = searchTimers;
+ numSearchTimers = searchTimers.size();
+ string headline;
+ if (numSearchTimers > 0) {
+ headline = tr("EPGSearch Search Timers");
+ } else {
+ headline = tr("No Search Timers Configured");
+ }
+ AddHeader(new cRecMenuItemInfo(headline));
+ cRecMenuItem *button = new cRecMenuItemButton(tr("Close"), rmsClose, (!numSearchTimers)?true:false);
+ if (numSearchTimers > 0) {
+ SetMenuItems();
+ AddFooter(button);
+ } else {
+ AddMenuItem(button);
+ }
+}
+
+void cRecMenuSearchTimers::SetMenuItems(void) {
+ for (int i = 0; i < numSearchTimers; i++) {
+ AddMenuItem(new cRecMenuItemSearchTimer(searchTimers[i], rmsSearchTimerTest, rmsSearchTimerEdit, rmsSearchTimerDeleteConfirm, (i==0)?true:false));
+ }
+}
+
+cTVGuideSearchTimer cRecMenuSearchTimers::GetSearchTimer(void) {
+ cRecMenuItemSearchTimer *activeItem = dynamic_cast<cRecMenuItemSearchTimer*>(GetActive());
+ return activeItem->GetTimer();
+}
+
+// --- cRecMenuSearchTimerEdit ---------------------------------------------------------
+cRecMenuSearchTimerEdit::cRecMenuSearchTimerEdit(cTVGuideSearchTimer searchTimer, bool advancedOptions) {
+ this->advancedOptions = advancedOptions;
+ this->searchTimer = searchTimer;
+ strncpy(searchString, searchTimer.SearchString().c_str(), TEXTINPUTLENGTH);
+ timerActive = searchTimer.Active();
+ mode = searchTimer.SearchMode();
+ useTitle = searchTimer.UseTitle();
+ useSubtitle = searchTimer.UseSubtitle();
+ useDescription = searchTimer.UseDescription();
+ useChannel = searchTimer.UseChannel();
+ startChannel = searchTimer.StartChannel();
+ stopChannel = searchTimer.StopChannel();
+ useTime = searchTimer.UseTime();
+ startTime = searchTimer.StartTime();
+ stopTime = searchTimer.StopTime();
+ useDayOfWeek = searchTimer.UseDayOfWeek();
+ dayOfWeek = searchTimer.DayOfWeek();
+ priority = searchTimer.Priority();
+ lifetime = searchTimer.Lifetime();
+ useEpisode = searchTimer.UseEpisode();
+ std::string dir = searchTimer.Directory();
+ strncpy(directory, dir.c_str(), TEXTINPUTLENGTH);
+ marginStart = searchTimer.MarginStart();
+ marginStop = searchTimer.MarginStop();
+ useVPS = searchTimer.UseVPS();
+ avoidRepeats = searchTimer.AvoidRepeats();
+ allowedRepeats = searchTimer.AllowedRepeats();
+ compareTitle = searchTimer.CompareTitle();
+ compareSubtitle = searchTimer.CompareSubtitle();
+ compareSummary = searchTimer.CompareSummary();
+ useInFavorites = searchTimer.UseInFavorites();
+
+ SetMenuWidth(70);
+ string infoText;
+ if (searchTimer.GetID() > -1) {
+ infoText = tr("Configure Search Timer Options");
+ } else {
+ infoText = tr("Create Search Timer");
+ }
+ AddHeader(new cRecMenuItemInfo(infoText));
+ cRecMenuItemButtonYesNo *footerButton = new cRecMenuItemButtonYesNo(tr("Save Search Timer"), tr("Cancel"), rmsSearchTimerSave, rmsSearchTimers, false);
+ AddFooter(footerButton);
+ CreateMenuItems();
+}
+
+void cRecMenuSearchTimerEdit::CreateMenuItems(void) {
+ AddMenuItem(new cRecMenuItemText(tr("Search String"), searchString, TEXTINPUTLENGTH, true, searchString));
+ AddMenuItem(new cRecMenuItemBool(tr("Active"), timerActive, false, &timerActive, rmsSearchTimerSave));
+ vector<string> searchModes;
+ searchTimer.GetSearchModes(&searchModes);
+ AddMenuItem(new cRecMenuItemSelect(tr("Search Mode"), searchModes, mode, false, &mode, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Use Title"), useTitle, false, &useTitle, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Use Subtitle"), useSubtitle, false, &useSubtitle, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Use Description"), useDescription, false, &useDescription, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Limit Channels"), useChannel, false, &useChannel, rmsSearchTimerSave));
+ if (startChannel == 0)
+ startChannel = 1;
+ if (stopChannel == 0)
+ stopChannel = 1;
+ AddMenuItem(new cRecMenuItemChannelChooser(tr("Start Channel"), Channels.GetByNumber(startChannel), false, &startChannel, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemChannelChooser(tr("Stop Channel"), Channels.GetByNumber(stopChannel), false, &stopChannel, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Use Time"), useTime, false, &useTime, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemTime(tr("Start after"), startTime, false, &startTime, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemTime(tr("Start before"), stopTime, false, &stopTime, rmsSearchTimerSave));
+
+ if (!advancedOptions) {
+ AddMenuItem(new cRecMenuItemButton(tr("Display advanced Options"), rmsSearchTimerEditAdvanced, false));
+ } else {
+ AddMenuItem(new cRecMenuItemBool(tr("Limit Days of the Week"), useDayOfWeek, false, &useDayOfWeek, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemDayChooser(tr("Select Days"), dayOfWeek, false, &dayOfWeek));
+ AddMenuItem(new cRecMenuItemInt(tr("Priority"), priority, 0, 99, false, &priority, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemInt(tr("Lifetime"), lifetime, 0, 99, false, &lifetime, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemInt(tr("Time margin for start in minutes"), marginStart, 0, 30, false, &marginStart, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemInt(tr("Time margin for stop in minutes"), marginStop, 0, 30, false, &marginStop, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Series Recording"), useEpisode, false, &useEpisode, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemSelectDirectory(tr("Folder"), string(directory), false, directory, rmsSearchTimerSave, true));
+ AddMenuItem(new cRecMenuItemBool(tr("Use VPS"), useVPS, false, &useVPS, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Avoid Repeats"), avoidRepeats, false, &avoidRepeats, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemInt(tr("Number of allowed repeats"), allowedRepeats, 0, 30, false, &allowedRepeats, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Compare Title"), compareTitle, false, &compareTitle, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Compare Subtitle"), compareSubtitle, false, &compareSubtitle, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Compare Description"), compareSummary, false, &compareSummary, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemBool(tr("Use in Favorites"), useInFavorites, false, &useInFavorites, rmsSearchTimerSave));
+ AddMenuItem(new cRecMenuItemButton(tr("Hide advanced Options"), rmsSearchTimerEdit, false));
+ }
+
+ AddMenuItem(new cRecMenuItemButton(tr("Display Results for Search Timer"), rmsSearchTimerTest, false));
+}
+
+cTVGuideSearchTimer cRecMenuSearchTimerEdit::GetSearchTimer(void) {
+ searchTimer.SetSearchString(searchString);
+ searchTimer.SetActive(timerActive);
+ searchTimer.SetSearchMode(mode);
+ searchTimer.SetUseTitle(useTitle);
+ searchTimer.SetUseSubtitle(useSubtitle);
+ searchTimer.SetUseDesription(useDescription);
+ searchTimer.SetUseChannel(useChannel);
+ if (useChannel) {
+ searchTimer.SetStartChannel(startChannel);
+ searchTimer.SetStopChannel(stopChannel);
+ }
+ searchTimer.SetUseTime(useTime);
+ if (useTime) {
+ searchTimer.SetStartTime(startTime);
+ searchTimer.SetStopTime(stopTime);
+ }
+ searchTimer.SetUseDayOfWeek(useDayOfWeek);
+ if (useDayOfWeek) {
+ searchTimer.SetDayOfWeek(dayOfWeek);
+ }
+ searchTimer.SetPriority(priority);
+ searchTimer.SetLifetime(lifetime);
+ searchTimer.SetUseEpisode(useEpisode);
+ string dir(directory);
+ std::replace(dir.begin(), dir.end(), '/', '~');
+ searchTimer.SetDirectory(dir);
+ searchTimer.SetMarginStart(marginStart);
+ searchTimer.SetMarginStop(marginStop);
+ searchTimer.SetUseVPS(useVPS);
+ searchTimer.SetAvoidRepeats(avoidRepeats);
+ if (avoidRepeats) {
+ searchTimer.SetAllowedRepeats(allowedRepeats);
+ searchTimer.SetCompareTitle(compareTitle);
+ searchTimer.SetCompareSubtitle(compareSubtitle);
+ searchTimer.SetCompareSummary(compareSummary);
+ }
+ searchTimer.SetUseInFavorites(useInFavorites);
+ return searchTimer;
+}
+
+// --- cRecMenuSearchTimerDeleteConfirm ---------------------------------------------
+cRecMenuSearchTimerDeleteConfirm::cRecMenuSearchTimerDeleteConfirm(cTVGuideSearchTimer searchTimer) {
+ SetMenuWidth(70);
+ this->searchTimer = searchTimer;
+ string line1 = tr("Really delete Search Timer");
+ string line2 = *cString::sprintf("\"%s\"?", searchTimer.SearchString().c_str());
+ AddHeader(new cRecMenuItemInfo(line1, 2, line2));
+ AddMenuItem(new cRecMenuItemButton(tr("Delete only Search Timer"), rmsSearchTimerDelete, true));
+ AddMenuItem(new cRecMenuItemButton(tr("Delete Search Timer and created Timers"), rmsSearchTimerDeleteWithTimers, false));
+ AddFooter(new cRecMenuItemButton(tr("Cancel"), rmsClose, false));
+}
+
+cTVGuideSearchTimer cRecMenuSearchTimerDeleteConfirm::GetSearchTimer(void) {
+ return searchTimer;
+}
+
+// --- cRecMenuSearchTimerCreateConfirm ---------------------------------------------------------
+cRecMenuSearchTimerCreateConfirm::cRecMenuSearchTimerCreateConfirm(bool success) {
+ SetMenuWidth(50);
+ cRecMenuItemInfo *infoItem = NULL;
+ if (success) {
+ string line1 = tr("Search Timer sucessfully created");
+ string line2 = tr("Search Timer update initialised");
+ infoItem = new cRecMenuItemInfo(line1, 2, line2);
+ } else {
+ string line1 = tr("Search Timer NOT sucessfully created");
+ infoItem = new cRecMenuItemInfo(line1);
+ }
+ AddHeader(infoItem);
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+// --- cRecMenuSearchTimerTemplatesCreate ---------------------------------------------------------
+cRecMenuSearchTimerTemplatesCreate::cRecMenuSearchTimerTemplatesCreate(TVGuideEPGSearchTemplate templ, cTVGuideSearchTimer searchTimer) {
+ SetMenuWidth(70);
+ this->templ = templ;
+ this->searchTimer = searchTimer;
+
+ string line1 = tr("Creating Search Timer");
+ string line2 = *cString::sprintf("%s: \"%s\"", tr("Search Term"), searchTimer.SearchString().c_str());
+ string line3 = *cString::sprintf("%s \"%s\"", tr("Using Template"), templ.name.c_str());
+
+ AddHeader(new cRecMenuItemInfo(line1, 3, line2, line3));
+ AddMenuItem(new cRecMenuItemButton(tr("Display Results for Search Timer"), rmsSearchTimerTest, true));
+ AddMenuItem(new cRecMenuItemButtonYesNo(tr("Create Search Timer"), tr("Use other Template"), rmsSearchTimerSave, rmsSearchTimerOptions, false));
+}
+
+
+// --- cRecMenuSearchTimerResults ---------------------------------------------------------
+cRecMenuSearchTimerResults::cRecMenuSearchTimerResults(string searchString, const cEvent **searchResults, int numResults, string templateName, eRecMenuState action2) {
+ SetMenuWidth(70);
+ this->searchResults = searchResults;
+ this->action2 = action2;
+ this->numResults = numResults;
+ string message1 = "", message2 = "", message3 = "";
+ if (action2 == rmsFavoritesRecord) {
+ message1 = tr("search results for Favorite");
+ message2 = tr("search result for Favorite");
+ } else {
+ message1 = tr("search results for Search Timer");
+ message2 = tr("search result for Search Timer");
+ message3 = tr("Using Template");
+ }
+ cRecMenuItem *infoItem = NULL;
+ if (templateName.size() > 0) {
+ string line1 = *cString::sprintf("%d %s:", numResults, (numResults>1)?(message1.c_str()):(message2.c_str()));
+ string line2 = *cString::sprintf("\"%s\"", searchString.c_str());
+ string line3 = *cString::sprintf("%s \"%s\"", message3.c_str(), templateName.c_str());
+ infoItem = new cRecMenuItemInfo(line1, 3, line2, line3);
+ } else {
+ string line1 = *cString::sprintf("%d %s:", numResults, (numResults>1)?(message1.c_str()):(message2.c_str()), searchString.c_str());
+ string line2 = *cString::sprintf("\"%s\"", searchString.c_str());
+ infoItem = new cRecMenuItemInfo(line1, 2, line2);
+ }
+ AddHeader(infoItem);
+
+ cRecMenuItem *button = new cRecMenuItemButton(tr("Close"), rmsClose, false);
+ if (searchResults && (numResults > 0)) {
+ for (int i=0; i<numResults; i++) {
+ AddMenuItem(new cRecMenuItemEvent(searchResults[i], rmsSearchShowInfo, action2, (i==0)?true:false));
+ }
+ AddFooter(button);
+ } else {
+ AddMenuItem(button);
+ }
+}
+
+const cEvent *cRecMenuSearchTimerResults::GetEvent(void) {
+ const cEvent *ev = NULL;
+ if (cRecMenuItemEvent *activeItem = dynamic_cast<cRecMenuItemEvent*>(GetActive()))
+ ev = activeItem->GetEvent();
+ return ev;
+}
+
+// --- cRecMenuSearchTimerNothingFound ---------------------------------------------------------
+cRecMenuSearchTimerNothingFound::cRecMenuSearchTimerNothingFound(string searchString) {
+ SetMenuWidth(50);
+ string line1 = tr("Nothing found for Search String");
+ string line2 = *cString::sprintf("\"%s\"", searchString.c_str());
+ AddHeader(new cRecMenuItemInfo(line1, 2, line2));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+/******************************************************************************************
+* SwitchTimer Menus
+******************************************************************************************/
+
+// --- cRecMenuSwitchTimer ---------------------------------------------------------
+cRecMenuSwitchTimer::cRecMenuSwitchTimer(void) {
+ SetMenuWidth(60);
+ switchMinsBefore = 2;
+ announceOnly = 0;
+
+ AddHeader(new cRecMenuItemInfo(tr("Configure Options for Switchtimer")));
+ AddMenuItem(new cRecMenuItemInt(tr("Minutes before switching"), switchMinsBefore, 0, 10, false, &switchMinsBefore));
+ vector<string> switchModes;
+ switchModes.push_back(tr("switch"));
+ switchModes.push_back(tr("announce only"));
+ switchModes.push_back(tr("ask for switch"));
+ AddMenuItem(new cRecMenuItemSelect(tr("Switch Mode"), switchModes, announceOnly, false, &announceOnly));
+
+ AddFooter(new cRecMenuItemButtonYesNo(tr("Create"), tr("Cancel"), rmsSwitchTimerCreate, rmsClose, true));
+}
+
+cSwitchTimer cRecMenuSwitchTimer::GetSwitchTimer(void) {
+ cSwitchTimer st;
+ st.switchMinsBefore = switchMinsBefore;
+ st.announceOnly = announceOnly;
+ return st;
+}
+
+// --- cRecMenuSwitchTimerConfirm ---------------------------------------------------------
+cRecMenuSwitchTimerConfirm::cRecMenuSwitchTimerConfirm(bool success) {
+ SetMenuWidth(50);
+ string message1 = tr("Switch Timer sucessfully created");
+ string message2 = tr("Switch Timer NOT sucessfully created");
+ string infoText = success?message1:message2;
+ AddHeader(new cRecMenuItemInfo(infoText));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+// --- cRecMenuSwitchTimerDelete ---------------------------------------------------------
+cRecMenuSwitchTimerDelete::cRecMenuSwitchTimerDelete(void) {
+ SetMenuWidth(50);
+ AddHeader(new cRecMenuItemInfo(tr("Switch Timer deleted")));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+/******************************************************************************************
+* Search Menus
+******************************************************************************************/
+
+// --- cRecMenuSearch ---------------------------------------------------------
+cRecMenuSearch::cRecMenuSearch(string searchString, bool withOptions) {
+ SetMenuWidth(60);
+ strncpy(this->searchString, searchString.c_str(), TEXTINPUTLENGTH);
+ mode = 0;
+ channelNr = 0;
+ useTitle = true;
+ useSubTitle = true;
+ useDescription = false;
+
+ AddHeader(new cRecMenuItemInfo(tr("Search")));
+ AddMenuItem(new cRecMenuItemText(tr("Search Expression:"), this->searchString, TEXTINPUTLENGTH, false, this->searchString));
+
+ if (withOptions) {
+ vector<string> searchModes;
+ cTVGuideSearchTimer searchTimer;
+ searchTimer.GetSearchModes(&searchModes);
+ AddMenuItem(new cRecMenuItemSelect(tr("Search Mode"), searchModes, 0, false, &mode));
+ AddMenuItem(new cRecMenuItemChannelChooser(tr("Channel to Search"), NULL, false, &channelNr));
+ AddMenuItem(new cRecMenuItemBool(tr("Search in title"), true, false, &useTitle));
+ AddMenuItem(new cRecMenuItemBool(tr("Search in Subtitle"), true, false, &useSubTitle));
+ AddMenuItem(new cRecMenuItemBool(tr("Search in Description"), false, false, &useDescription));
+ } else {
+ AddMenuItem(new cRecMenuItemButton(tr("Show Search Options"), rmsSearchWithOptions, false));
+ }
+
+ cRecMenuItemButtonYesNo *button = new cRecMenuItemButtonYesNo(tr("Perform Search"), tr("Cancel"), rmsSearchPerform, rmsClose, true);
+ AddFooter(button);
+}
+
+Epgsearch_searchresults_v1_0 cRecMenuSearch::GetEPGSearchStruct(void) {
+ Epgsearch_searchresults_v1_0 data;
+ data.query = searchString;
+ data.mode = mode;
+ data.channelNr = channelNr;
+ data.useTitle = useTitle;
+ data.useSubTitle = useSubTitle;
+ data.useDescription = useDescription;
+ return data;
+}
+
+// --- cRecMenuSearchResults ---------------------------------------------------------
+cRecMenuSearchResults::cRecMenuSearchResults(string searchString, const cEvent **searchResults, int numResults) {
+ this->searchResults = searchResults;
+ SetMenuWidth(70);
+ this->searchString = searchString;
+ this->numResults = numResults;
+
+ string message = "";
+ if (numResults > 1) {
+ message = *cString::sprintf("%d %s", numResults, tr("search results for"));
+ } else {
+ message = *cString::sprintf("%d %s", numResults, tr("search result for"));
+ }
+ string searchStringQuoted = *cString::sprintf("\"%s\"", searchString.c_str());
+ AddHeader(new cRecMenuItemInfo(message, 2, searchStringQuoted));
+
+ cRecMenuItem *buttons = new cRecMenuItemButtonYesNo(tr("Adapt Search"), tr("Close"), rmsSearch, rmsClose, false);
+ AddFooter(buttons);
+ if (searchResults && (numResults > 0)) {
+ for (int i=0; i<numResults; i++) {
+ AddMenuItem(new cRecMenuItemEvent(searchResults[i], rmsSearchShowInfo, rmsSearchRecord, (i==0)?true:false));
+ }
+ }
+}
+
+const cEvent *cRecMenuSearchResults::GetEvent(void) {
+ cRecMenuItemEvent *activeItem = dynamic_cast<cRecMenuItemEvent*>(GetActive());
+ if (activeItem)
+ return activeItem->GetEvent();
+ return NULL;
+}
+
+// --- cRecMenuSearchNothingFound ---------------------------------------------------------
+cRecMenuSearchNothingFound::cRecMenuSearchNothingFound(string searchString, bool tooShort) {
+ SetMenuWidth(50);
+ string text;
+ if (!tooShort) {
+ cString message = tr("Nothing found for Search String");
+ text = *cString::sprintf("%s\n\"%s\"",
+ *message,
+ searchString.c_str());
+ } else {
+ cString message = tr("Search String has to have at least three letters");
+ text = *cString::sprintf("%s\n\"%s\"",
+ *message,
+ searchString.c_str());
+
+ }
+ AddHeader(new cRecMenuItemInfo(text));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+// --- cRecMenuSearchConfirmTimer ---------------------------------------------------------
+cRecMenuSearchConfirmTimer::cRecMenuSearchConfirmTimer(const cEvent *event) {
+ SetMenuWidth(50);
+
+ string message = tr("Timer created");
+ cString channelName = Channels.GetByChannelID(event->ChannelID())->Name();
+ string line3 = *cString::sprintf("%s %s - %s", *event->GetDateString(), *event->GetTimeString(), *event->GetEndTimeString());
+ string line4 = event->Title() ? event->Title() : "";
+
+ AddHeader(new cRecMenuItemInfo(message, 4, *channelName, line3, line4));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+/******************************************************************************************
+* Timeline
+******************************************************************************************/
+
+// --- cRecMenuTimeline ---------------------------------------------------------
+cRecMenuTimeline::cRecMenuTimeline(void) {
+ SetMenuWidth(95);
+ SetStartStop();
+ GetTimersForDay();
+
+ timelineHeader = new cRecMenuItemTimelineHeader(timeStart);
+ AddHeader(timelineHeader);
+
+ timelineFooter = new cRecMenuItemButton(tr("Close"), rmsClose, false);
+ AddFooter(timelineFooter);
+ SetTimers();
+}
+
+void cRecMenuTimeline::SetHeaderTimer(void) {
+ cTimer *currentTimer = NULL;
+ cRecMenuItem *currentItem = GetActive();
+ if (!currentItem) {
+ timelineHeader->UnsetCurrentTimer();
+ return;
+ }
+ if (cRecMenuItemTimelineTimer *myActiveItem = dynamic_cast<cRecMenuItemTimelineTimer*>(currentItem)) {
+ currentTimer = myActiveItem->GetTimer();
+ }
+ timelineHeader->SetCurrentTimer(currentTimer);
+ timelineHeader->SetNew();
+}
+
+void cRecMenuTimeline::SetStartStop(void) {
+ time_t now = time(0);
+ tm *timeStruct = localtime(&now);
+ timeStart = now - timeStruct->tm_hour * 3600 - timeStruct->tm_min * 60 - timeStruct->tm_sec;
+ today = timeStart;
+ timeStop = timeStart + 24*3600 - 1;
+}
+
+void cRecMenuTimeline::GetTimersForDay(void) {
+ timersToday.clear();
+ for (cTimer *t = Timers.First(); t; t = Timers.Next(t)) {
+ if (((t->StartTime() > timeStart) && (t->StartTime() <= timeStop)) || ((t->StopTime() > timeStart) && (t->StopTime() <= timeStop))) {
+ timersToday.push_back(t);
+ }
+ }
+ numTimersToday = timersToday.size();
+}
+
+void cRecMenuTimeline::SetTimers(void) {
+ if (numTimersToday == 0) {
+ AddMenuItem(new cRecMenuItemInfo(tr("No active Timers")));
+ timelineFooter->SetActive();
+ } else {
+ for (int i=0; i<numTimersToday; i++) {
+ AddMenuItem(new cRecMenuItemTimelineTimer(timersToday[i], timeStart, timeStop, (i==0)?true:false));
+ }
+ timelineFooter->SetInactive();
+ }
+}
+
+void cRecMenuTimeline::PrevDay(void) {
+ if ((timeStart - 3600*24) < today)
+ return;
+ timeStart -= 3600*24;
+ timeStop -= 3600*24;
+ ClearMenuItems();
+ GetTimersForDay();
+ SetTimers();
+ InitMenuItems();
+ SetHeaderTimer();
+ timelineHeader->SetDay(timeStart);
+ timelineHeader->SetNew();
+}
+
+void cRecMenuTimeline::NextDay(void) {
+ timeStart += 3600*24;
+ timeStop += 3600*24;
+ ClearMenuItems();
+ GetTimersForDay();
+ SetTimers();
+ InitMenuItems();
+ SetHeaderTimer();
+ timelineHeader->SetDay(timeStart);
+ timelineHeader->SetNew();
+}
+
+cTimer *cRecMenuTimeline::GetTimer(void) {
+ if (cRecMenuItemTimelineTimer *activeItem = dynamic_cast<cRecMenuItemTimelineTimer*>(GetActive()))
+ return activeItem->GetTimer();
+ return NULL;
+}
+
+
+eRecMenuState cRecMenuTimeline::ProcessKey(eKeys Key) {
+ eRecMenuState state = rmsContinue;
+ switch (Key & ~k_Repeat) {
+ case kLeft:
+ PrevDay();
+ Draw();
+ break;
+ state = rmsConsumed;
+ case kRight:
+ NextDay();
+ Draw();
+ state = rmsConsumed;
+ break;
+ case kUp: {
+ if (ScrollUp(false)) {
+ SetHeaderTimer();
+ Draw();
+ }
+ state = rmsConsumed;
+ break; }
+ case kDown: {
+ if (ScrollDown(false)) {
+ SetHeaderTimer();
+ Draw();
+ }
+ state = rmsConsumed;
+ break; }
+ default:
+ break;
+ }
+ if (state != rmsConsumed) {
+ state = cRecMenu::ProcessKey(Key);
+ }
+ return state;
+}
+
+/******************************************************************************************
+* Recording Search Menus
+******************************************************************************************/
+
+// --- cRecMenuRecordingSearch ---------------------------------------------------------
+cRecMenuRecordingSearch::cRecMenuRecordingSearch(string search) {
+ SetMenuWidth(60);
+ strncpy(searchString, search.c_str(), TEXTINPUTLENGTH);
+ AddHeader(new cRecMenuItemInfo(tr("Search in Recordings")));
+ AddMenuItem(new cRecMenuItemText(tr("Search Expression:"), searchString, TEXTINPUTLENGTH, false, searchString));
+ AddMenuItem(new cRecMenuItemButtonYesNo(tr("Perform Search"), tr("Cancel"), rmsRecordingSearchResult, rmsClose, true));
+}
+
+// --- cRecMenuRecordingSearchResults ---------------------------------------------------------
+cRecMenuRecordingSearchResults::cRecMenuRecordingSearchResults(string searchString, cRecording **searchResults, int numResults) {
+ SetMenuWidth(80);
+ this->searchString = searchString;
+ this->searchResults = searchResults;
+ this->numResults = numResults;
+ string line1 = *cString::sprintf("%s %d %s %s:", tr("Found"), numResults, (numResults>1)?tr("recordings"):tr("recording"), tr("for"));
+ string line2 = *cString::sprintf("\"%s\"", searchString.c_str());
+ AddHeader(new cRecMenuItemInfo(line1, 2, line2));
+
+ if (searchResults && (numResults > 0)) {
+ for (int i=0; i<numResults; i++) {
+ AddMenuItem(new cRecMenuItemRecording(searchResults[i], (i==0)?true:false));
+ }
+ cRecMenuItem *buttons = new cRecMenuItemButtonYesNo(tr("Adapt Search"), tr("Close"), rmsRecordingSearch, rmsClose, false);
+ AddFooter(buttons);
+ } else {
+ cRecMenuItem *buttons = new cRecMenuItemButtonYesNo(tr("Adapt Search"), tr("Close"), rmsRecordingSearch, rmsClose, true);
+ AddMenuItem(buttons);
+ }
+}
+
+// --- cRecMenuRecordingSearchNotFound ---------------------------------------------------------
+cRecMenuRecordingSearchNotFound::cRecMenuRecordingSearchNotFound(string searchString) {
+ SetMenuWidth(50);
+ string line2 = *cString::sprintf("\"%s\"", searchString.c_str());
+ AddHeader(new cRecMenuItemInfo(tr("No recordings found for"), 2, line2));
+ AddMenuItem(new cRecMenuItemButton(tr("OK"), rmsClose, true));
+}
+
+/******************************************************************************************
+* Favorites
+******************************************************************************************/
+
+// --- cRecMenuFavorites ---------------------------------------------------------
+
+cRecMenuFavorites::cRecMenuFavorites(vector<cTVGuideSearchTimer> favorites) {
+ SetMenuWidth(70);
+ this->favorites = favorites;
+
+ CreateFavoritesMenuItems();
+
+ string header;
+ if (numFavorites > 0) {
+ header = tr("Favorites");
+ } else {
+ header = tr("No Favorites available");
+ }
+ AddHeader(new cRecMenuItemInfo(header));
+ cRecMenuItem *button = new cRecMenuItemButton(tr("Close"), rmsClose, (numFavorites==0)?true:false);
+ if (numFavorites != 0)
+ AddFooter(button);
+ else
+ AddMenuItem(button);
+}
+
+void cRecMenuFavorites::CreateFavoritesMenuItems(void) {
+ bool active = true;
+ if (config.favWhatsOnNow) {
+ AddMenuItem(new cRecMenuItemFavoriteStatic(tr("What's on now"), rmsFavoritesNow, active));
+ active = false;
+ numFavorites++;
+ }
+ if (config.favWhatsOnNext) {
+ AddMenuItem(new cRecMenuItemFavoriteStatic(tr("What's on next"), rmsFavoritesNext, active));
+ active = false;
+ numFavorites++;
+ }
+ if (config.favUseTime1) {
+ string desc = *cString::sprintf("%s (%s)", config.descUser1.c_str(), NiceTime(config.favTime1).c_str());
+ AddMenuItem(new cRecMenuItemFavoriteStatic(desc, rmsFavoritesUser1, active));
+ active = false;
+ numFavorites++;
+ }
+ if (config.favUseTime2) {
+ string desc = *cString::sprintf("%s (%s)", config.descUser2.c_str(), NiceTime(config.favTime2).c_str());
+ AddMenuItem(new cRecMenuItemFavoriteStatic(desc, rmsFavoritesUser2, active));
+ active = false;
+ numFavorites++;
+ }
+ if (config.favUseTime3) {
+ string desc = *cString::sprintf("%s (%s)", config.descUser3.c_str(), NiceTime(config.favTime3).c_str());
+ AddMenuItem(new cRecMenuItemFavoriteStatic(desc, rmsFavoritesUser3, active));
+ active = false;
+ numFavorites++;
+ }
+ if (config.favUseTime4) {
+ string desc = *cString::sprintf("%s (%s)", config.descUser4.c_str(), NiceTime(config.favTime4).c_str());
+ AddMenuItem(new cRecMenuItemFavoriteStatic(desc, rmsFavoritesUser4, active));
+ active = false;
+ numFavorites++;
+ }
+
+ int numAdditionalFavs = favorites.size();
+ for (int i = 0; i < numAdditionalFavs; i++) {
+ AddMenuItem(new cRecMenuItemFavorite(favorites[i], rmsSearchTimerTest, active));
+ active = false;
+ numFavorites++;
+ }
+}
+
+string cRecMenuFavorites::NiceTime(int favTime) {
+ int hours = favTime/100;
+ int mins = favTime - hours * 100;
+ return *cString::sprintf("%02d:%02d", hours, mins);
+}
+
+cTVGuideSearchTimer cRecMenuFavorites::GetFavorite(void) {
+ cRecMenuItemFavorite *activeItem = dynamic_cast<cRecMenuItemFavorite*>(GetActive());
+ return activeItem->GetFavorite();
+}
diff --git a/recmenus.h b/recmenus.h new file mode 100644 index 0000000..ad2bf8d --- /dev/null +++ b/recmenus.h @@ -0,0 +1,451 @@ +#ifndef __TVGUIDE_RECMENUS_H
+#define __TVGUIDE_RECMENUS_H
+
+#define TEXTINPUTLENGTH 256
+
+#include "services/epgsearch.h"
+#include "recmenu.h"
+#include "recmanager.h"
+#include "timerconflict.h"
+#include "switchtimer.h"
+
+// --- cRecMenuMain ---------------------------------------------------------
+class cRecMenuMain : public cRecMenu {
+private:
+ int intcallback;
+ bool boolcallback;
+ int stringselectcallback;
+ char searchString[TEXTINPUTLENGTH];
+ int timecallback;
+ time_t daycallback;
+public:
+ cRecMenuMain(bool epgSearchAvailable, bool timerActive, bool switchTimerActive);
+ virtual ~cRecMenuMain(void) {};
+};
+
+/******************************************************************************************
+* Instant Timer Menus
+******************************************************************************************/
+
+// --- cRecMenuConfirmTimer ---------------------------------------------------------
+class cRecMenuConfirmTimer: public cRecMenu {
+public:
+ cRecMenuConfirmTimer(const cEvent *event);
+ virtual ~cRecMenuConfirmTimer(void) {};
+};
+
+// --- cRecMenuAskFolder ---------------------------------------------------------
+class cRecMenuAskFolder: public cRecMenu {
+private:
+ vector<string> folders;
+ eRecMenuState nextAction;
+public:
+ cRecMenuAskFolder(const cEvent *event, eRecMenuState nextAction);
+ virtual ~cRecMenuAskFolder(void) {};
+ string GetFolder(void);
+};
+
+// --- cRecMenuConfirmDeleteTimer ---------------------------------------------------------
+class cRecMenuConfirmDeleteTimer: public cRecMenu {
+public:
+ cRecMenuConfirmDeleteTimer(const cEvent *event);
+ virtual ~cRecMenuConfirmDeleteTimer(void) {};
+};
+
+// --- cRecMenuAskDeleteTimer ---------------------------------------------------------
+class cRecMenuAskDeleteTimer: public cRecMenu {
+public:
+ cRecMenuAskDeleteTimer(const cEvent *event);
+ virtual ~cRecMenuAskDeleteTimer(void) {};
+};
+
+// --- cRecMenuTimerConflicts ---------------------------------------------------------
+class cRecMenuTimerConflicts: public cRecMenu {
+public:
+ cRecMenuTimerConflicts(cTVGuideTimerConflicts *conflicts);
+ virtual ~cRecMenuTimerConflicts(void) {};
+ int GetTimerConflict(void);
+};
+
+// --- cRecMenuTimerConflict ---------------------------------------------------------
+class cRecMenuTimerConflict: public cRecMenu {
+private:
+ cTVGuideTimerConflict *conflict;
+public:
+ cRecMenuTimerConflict(cTVGuideTimerConflict *conflict);
+ virtual ~cRecMenuTimerConflict(void) {};
+ int GetTimerConflictIndex(void);
+};
+
+// --- cRecMenuNoTimerConflict ---------------------------------------------------------
+class cRecMenuNoTimerConflict: public cRecMenu {
+public:
+ cRecMenuNoTimerConflict(void);
+ virtual ~cRecMenuNoTimerConflict(void) {};
+};
+
+// --- cRecMenuRerunResults ---------------------------------------------------------
+class cRecMenuRerunResults: public cRecMenu {
+private:
+ const cEvent **reruns;
+ int numReruns;
+public:
+ cRecMenuRerunResults(const cEvent *original, const cEvent **reruns, int numReruns);
+ int GetTotalNumMenuItems(void);
+ virtual ~cRecMenuRerunResults(void) {
+ delete[] reruns;
+ };
+ const cEvent *GetEvent(void);
+};
+
+// --- cRecMenuNoRerunsFound ---------------------------------------------------------
+class cRecMenuNoRerunsFound: public cRecMenu {
+public:
+ cRecMenuNoRerunsFound(string searchString);
+ virtual ~cRecMenuNoRerunsFound(void) {};
+};
+
+// --- cRecMenuConfirmRerunUsed ---------------------------------------------------------
+class cRecMenuConfirmRerunUsed: public cRecMenu {
+public:
+ cRecMenuConfirmRerunUsed(const cEvent *original, const cEvent *replace);
+ virtual ~cRecMenuConfirmRerunUsed(void) {};
+};
+
+// --- cRecMenuEditTimer ---------------------------------------------------------
+class cRecMenuEditTimer: public cRecMenu {
+private:
+ cTimer *originalTimer;
+ bool timerActive;
+ time_t day;
+ int start;
+ int stop;
+ int prio;
+ int lifetime;
+ char folder[TEXTINPUTLENGTH];
+public:
+ cRecMenuEditTimer(cTimer *timer, eRecMenuState nextState);
+ virtual ~cRecMenuEditTimer(void) {};
+ cTimer GetTimer(void);
+ cTimer *GetOriginalTimer(void);
+};
+
+/******************************************************************************************
+* Series Timer Menus
+******************************************************************************************/
+
+// --- cRecMenuSeriesTimer ---------------------------------------------------------
+class cRecMenuSeriesTimer: public cRecMenu {
+ string folder;
+ bool timerActive;
+ int channel;
+ time_t tstart;
+ int start;
+ int stop;
+ int dayOfWeek;
+ int priority;
+ int lifetime;
+ void CalculateTimes(const cEvent *event);
+public:
+ cRecMenuSeriesTimer(cChannel *initialChannel, const cEvent *event, string folder);
+ virtual ~cRecMenuSeriesTimer(void) {};
+ cTimer *GetTimer(void);
+};
+
+// --- cRecMenuConfirmSeriesTimer ---------------------------------------------------------
+class cRecMenuConfirmSeriesTimer: public cRecMenu {
+public:
+ cRecMenuConfirmSeriesTimer(cTimer *seriesTimer);
+ virtual ~cRecMenuConfirmSeriesTimer(void) {};
+};
+
+/******************************************************************************************
+* SearchTimer Menus
+******************************************************************************************/
+
+// --- cRecMenuSearchTimer ---------------------------------------------------------
+class cRecMenuSearchTimer: public cRecMenu {
+private:
+ char searchString[TEXTINPUTLENGTH];
+public:
+ cRecMenuSearchTimer(const cEvent *event);
+ virtual ~cRecMenuSearchTimer(void) {};
+ string GetSearchString(void) { return searchString; };
+};
+
+// --- cRecMenuSearchTimerTemplates ---------------------------------------------------------
+class cRecMenuSearchTimerTemplates: public cRecMenu {
+private:
+ int numTemplates;
+ cTVGuideSearchTimer searchTimer;
+ vector<TVGuideEPGSearchTemplate> templates;
+public:
+ cRecMenuSearchTimerTemplates(cTVGuideSearchTimer searchTimer, vector<TVGuideEPGSearchTemplate> templates);
+ virtual ~cRecMenuSearchTimerTemplates(void) {};
+ cTVGuideSearchTimer GetSearchTimer(void) { return searchTimer; };
+ TVGuideEPGSearchTemplate GetTemplate(void);
+};
+
+// --- cRecMenuSearchTimers ---------------------------------------------------------
+class cRecMenuSearchTimers: public cRecMenu {
+private:
+ int numSearchTimers;
+ vector<cTVGuideSearchTimer> searchTimers;
+ void SetMenuItems(void);
+public:
+ cRecMenuSearchTimers(vector<cTVGuideSearchTimer> searchTimers);
+ virtual ~cRecMenuSearchTimers(void) {};
+ cTVGuideSearchTimer GetSearchTimer(void);
+};
+
+// --- cRecMenuSearchTimerEdit ---------------------------------------------------------
+class cRecMenuSearchTimerEdit: public cRecMenu {
+private:
+ bool advancedOptions;
+ cTVGuideSearchTimer searchTimer;
+ char searchString[TEXTINPUTLENGTH];
+ bool timerActive;
+ int mode;
+ bool useTitle;
+ bool useSubtitle;
+ bool useDescription;
+ bool useChannel;
+ int startChannel;
+ int stopChannel;
+ bool useTime;
+ int startTime;
+ int stopTime;
+ bool useDayOfWeek;
+ int dayOfWeek;
+ int priority;
+ int lifetime;
+ bool useEpisode;
+ char directory[TEXTINPUTLENGTH];
+ int marginStart;
+ int marginStop;
+ bool useVPS;
+ bool avoidRepeats;
+ int allowedRepeats;
+ bool compareTitle;
+ bool compareSubtitle;
+ bool compareSummary;
+ bool useInFavorites;
+ void CreateMenuItems(void);
+public:
+ cRecMenuSearchTimerEdit(cTVGuideSearchTimer searchTimer, bool advancedOptions);
+ virtual ~cRecMenuSearchTimerEdit(void) {};
+ cTVGuideSearchTimer GetSearchTimer(void);
+};
+
+// --- cRecMenuSearchTimerDeleteConfirm ---------------------------------------------
+class cRecMenuSearchTimerDeleteConfirm: public cRecMenu {
+private:
+ cTVGuideSearchTimer searchTimer;
+public:
+ cRecMenuSearchTimerDeleteConfirm(cTVGuideSearchTimer searchTimer);
+ virtual ~cRecMenuSearchTimerDeleteConfirm(void) {};
+ cTVGuideSearchTimer GetSearchTimer(void);
+};
+
+// --- cRecMenuSearchTimerCreateConfirm ---------------------------------------------------------
+class cRecMenuSearchTimerCreateConfirm: public cRecMenu {
+private:
+public:
+ cRecMenuSearchTimerCreateConfirm(bool success);
+ virtual ~cRecMenuSearchTimerCreateConfirm(void) {};
+};
+
+// --- cRecMenuSearchTimerTemplatesCreate ---------------------------------------------------------
+class cRecMenuSearchTimerTemplatesCreate: public cRecMenu {
+private:
+ TVGuideEPGSearchTemplate templ;
+ cTVGuideSearchTimer searchTimer;
+public:
+ cRecMenuSearchTimerTemplatesCreate(TVGuideEPGSearchTemplate templ, cTVGuideSearchTimer searchTimer);
+ virtual ~cRecMenuSearchTimerTemplatesCreate(void) {};
+ cTVGuideSearchTimer GetSearchTimer(void) { return searchTimer; };
+ TVGuideEPGSearchTemplate GetTemplate(void) { return templ; };
+};
+
+// --- cRecMenuSearchTimerResults ---------------------------------------------------------
+class cRecMenuSearchTimerResults: public cRecMenu {
+private:
+ const cEvent **searchResults;
+ int numResults;
+ eRecMenuState action2;
+public:
+ cRecMenuSearchTimerResults(string searchString, const cEvent **searchResults, int numResults, string templateName = "", eRecMenuState action2 = rmsDisabled);
+ virtual ~cRecMenuSearchTimerResults(void) {
+ delete[] searchResults;
+ };
+ const cEvent *GetEvent(void);
+};
+
+// --- cRecMenuSearchTimerNothingFound ---------------------------------------------------------
+class cRecMenuSearchTimerNothingFound: public cRecMenu {
+public:
+ cRecMenuSearchTimerNothingFound(string searchString);
+ virtual ~cRecMenuSearchTimerNothingFound(void) {};
+};
+
+/******************************************************************************************
+* SwitchTimer Menus
+******************************************************************************************/
+
+// --- cRecMenuSwitchTimer ---------------------------------------------------------
+class cRecMenuSwitchTimer: public cRecMenu {
+private:
+ int switchMinsBefore;
+ int announceOnly;
+public:
+ cRecMenuSwitchTimer(void);
+ virtual ~cRecMenuSwitchTimer(void) {};
+ cSwitchTimer GetSwitchTimer(void);
+};
+
+// --- cRecMenuSwitchTimerConfirm ---------------------------------------------------------
+class cRecMenuSwitchTimerConfirm: public cRecMenu {
+private:
+public:
+ cRecMenuSwitchTimerConfirm(bool success);
+ virtual ~cRecMenuSwitchTimerConfirm(void) {};
+};
+
+// --- cRecMenuSwitchTimerDelete ---------------------------------------------------------
+class cRecMenuSwitchTimerDelete: public cRecMenu {
+private:
+public:
+ cRecMenuSwitchTimerDelete(void);
+ virtual ~cRecMenuSwitchTimerDelete(void) {};
+};
+
+/******************************************************************************************
+* Search Menus
+******************************************************************************************/
+
+// --- cRecMenuSearch ---------------------------------------------------------
+class cRecMenuSearch: public cRecMenu {
+private:
+ char searchString[TEXTINPUTLENGTH];
+ int mode;
+ int channelNr;
+ bool useTitle;
+ bool useSubTitle;
+ bool useDescription;
+public:
+ cRecMenuSearch(string searchString, bool withOptions);
+ virtual ~cRecMenuSearch(void) {};
+ string GetSearchString(void) { return searchString; };
+ Epgsearch_searchresults_v1_0 GetEPGSearchStruct(void);
+};
+
+// --- cRecMenuSearchResults ---------------------------------------------------------
+class cRecMenuSearchResults: public cRecMenu {
+private:
+ string searchString;
+ const cEvent **searchResults;
+ int numResults;
+public:
+ cRecMenuSearchResults(string searchString, const cEvent **searchResults, int numResults);
+ virtual ~cRecMenuSearchResults(void) {
+ delete[] searchResults;
+ };
+ string GetSearchString(void) { return searchString; };
+ const cEvent *GetEvent(void);
+};
+
+// --- cRecMenuSearchNothingFound ---------------------------------------------------------
+class cRecMenuSearchNothingFound: public cRecMenu {
+public:
+ cRecMenuSearchNothingFound(string searchString, bool tooShort = false);
+ virtual ~cRecMenuSearchNothingFound(void) {};
+};
+
+// --- cRecMenuSearchConfirmTimer ---------------------------------------------------------
+class cRecMenuSearchConfirmTimer: public cRecMenu {
+public:
+ cRecMenuSearchConfirmTimer(const cEvent *event);
+ virtual ~cRecMenuSearchConfirmTimer(void) {};
+};
+
+/******************************************************************************************
+* Timeline
+******************************************************************************************/
+
+// --- cRecMenuTimeline ---------------------------------------------------------
+class cRecMenuTimeline: public cRecMenu {
+private:
+ vector<cTimer*> timersToday;
+ int numTimersToday;
+ time_t today;
+ time_t timeStart;
+ time_t timeStop;
+ cRecMenuItemTimelineHeader *timelineHeader;
+ cRecMenuItem *timelineFooter;
+ void SetStartStop(void);
+ void GetTimersForDay(void);
+ void SetTimers(void);
+ void PrevDay(void);
+ void NextDay(void);
+ void ClearMenu(void);
+public:
+ cRecMenuTimeline(void);
+ virtual ~cRecMenuTimeline(void) {};
+ eRecMenuState ProcessKey(eKeys Key);
+ void SetHeaderTimer(void);
+ cTimer *GetTimer(void);
+};
+
+/******************************************************************************************
+* Recording Search Menus
+******************************************************************************************/
+
+// --- cRecMenuRecordingSearch ---------------------------------------------------------
+class cRecMenuRecordingSearch: public cRecMenu {
+private:
+ char searchString[TEXTINPUTLENGTH];
+public:
+ cRecMenuRecordingSearch(string search);
+ virtual ~cRecMenuRecordingSearch(void) {};
+ string GetSearchString(void) { return searchString; };
+};
+
+// --- cRecMenuRecordingSearchResults ---------------------------------------------------------
+class cRecMenuRecordingSearchResults: public cRecMenu {
+private:
+ string searchString;
+ cRecording **searchResults;
+ int numResults;
+public:
+ cRecMenuRecordingSearchResults(string searchString, cRecording **searchResults, int numResults);
+ virtual ~cRecMenuRecordingSearchResults(void) {
+ delete[] searchResults;
+ };
+ string GetSearchString(void) { return searchString; };
+};
+
+// --- cRecMenuRecordingSearchNotFound ---------------------------------------------------------
+class cRecMenuRecordingSearchNotFound: public cRecMenu {
+public:
+ cRecMenuRecordingSearchNotFound(string searchString);
+ virtual ~cRecMenuRecordingSearchNotFound(void) {};
+};
+
+/******************************************************************************************
+* Favorites
+******************************************************************************************/
+
+// --- cRecMenuFavorites ---------------------------------------------------------
+class cRecMenuFavorites: public cRecMenu {
+private:
+ vector<cTVGuideSearchTimer> favorites;
+ int numFavorites;
+ void CreateFavoritesMenuItems(void);
+ string NiceTime(int favTime);
+public:
+ cRecMenuFavorites(vector<cTVGuideSearchTimer> favorites);
+ virtual ~cRecMenuFavorites(void) {};
+ cTVGuideSearchTimer GetFavorite(void);
+};
+
+#endif //__TVGUIDE_RECMENUS_H
\ No newline at end of file diff --git a/recmenuview.c b/recmenuview.c new file mode 100644 index 0000000..5f20197 --- /dev/null +++ b/recmenuview.c @@ -0,0 +1,846 @@ +#include "tvguidengosd.h"
+#include "recmenuview.h"
+
+cRecMenuView::cRecMenuView(void) {
+ active = false;
+ recMenuView = NULL;
+ recMenuViewBuffer = NULL;
+ recMenuViewBuffer2 = NULL;
+ event = NULL;
+ displayEvent = NULL;
+ activeMenu = NULL;
+ activeMenuBuffer = NULL;
+ activeMenuBuffer2 = NULL;
+ recManager = new cRecManager();
+ recManager->SetEPGSearchPlugin();
+ timerConflicts = NULL;
+}
+
+cRecMenuView::~cRecMenuView() {
+ if (activeMenu)
+ delete activeMenu;
+ if (activeMenuBuffer)
+ delete activeMenuBuffer;
+ if (activeMenuBuffer2)
+ delete activeMenuBuffer2;
+ if (recMenuView)
+ delete recMenuView;
+ if (recMenuViewBuffer)
+ delete recMenuViewBuffer;
+ if (recMenuViewBuffer2)
+ delete recMenuViewBuffer2;
+}
+
+void cRecMenuView::Init(cOsdView *recMenuView, cOsdView *recMenuViewBuffer, cOsdView *recMenuViewBuffer2) {
+ active = true;
+ this->recMenuView = recMenuView;
+ this->recMenuViewBuffer = recMenuViewBuffer;
+ this->recMenuViewBuffer2 = recMenuViewBuffer2;
+}
+
+void cRecMenuView::DisplayRecMenu(const cEvent *event) {
+ if (!event) {
+ return;
+ }
+ this->event = event;
+ activeMenu = new cRecMenuMain(recManager->EpgSearchAvailable(), recManager->CheckEventForTimer(event), SwitchTimers.EventInSwitchList(event));
+ DisplayMenu();
+}
+
+void cRecMenuView::DisplayFavorites(void) {
+ vector<cTVGuideSearchTimer> favorites;
+ recManager->GetFavorites(&favorites);
+ activeMenu = new cRecMenuFavorites(favorites);
+ DisplayMenu();
+}
+
+void cRecMenuView::Close(void) {
+ if (activeMenu) {
+ delete activeMenu;
+ activeMenu = NULL;
+ }
+ if (activeMenuBuffer) {
+ delete activeMenuBuffer;
+ activeMenuBuffer = NULL;
+ }
+ if (activeMenuBuffer2) {
+ delete activeMenuBuffer2;
+ activeMenuBuffer2 = NULL;
+ }
+ delete recMenuView;
+ recMenuView = NULL;
+ delete recMenuViewBuffer;
+ recMenuViewBuffer = NULL;
+ delete recMenuViewBuffer2;
+ recMenuViewBuffer2 = NULL;
+ active = false;
+}
+
+void cRecMenuView::Hide(void) {
+ if (recMenuViewBuffer)
+ recMenuViewBuffer->Deactivate(true);
+ else
+ recMenuView->Deactivate(true);
+}
+
+void cRecMenuView::Activate(void) {
+ if (recMenuViewBuffer)
+ recMenuViewBuffer->Activate();
+ else
+ recMenuView->Activate();
+}
+
+eOSState cRecMenuView::ProcessKey(eKeys Key) {
+ eOSState state = osContinue;
+ eRecMenuState nextState = rmsContinue;
+ if (!activeMenu)
+ return state;
+ nextState = activeMenu->ProcessKey(Key);
+ if ((nextState == rmsClose) || ((nextState == rmsNotConsumed)&&(Key == kBack))) {
+ if (activeMenuBuffer2) {
+ delete activeMenu;
+ activeMenu = activeMenuBuffer2;
+ activeMenuBuffer2 = NULL;
+ activeMenu->Show();
+ return osContinue;
+ } else if (activeMenuBuffer) {
+ delete activeMenu;
+ activeMenu = activeMenuBuffer;
+ activeMenuBuffer = NULL;
+ activeMenu->Show();
+ return osContinue;
+ } else {
+ Close();
+ return osEnd;
+ }
+ }
+ state = StateMachine(nextState);
+ if (activeMenu)
+ activeMenu->Flush();
+ return state;
+}
+
+void cRecMenuView::DisplayMenu(bool buffer, bool buffer2) {
+ if (!activeMenu)
+ return;
+ if (buffer) {
+ activeMenu->Init(recMenuViewBuffer);
+ } else if (buffer2) {
+ activeMenu->Init(recMenuViewBuffer2);
+ } else {
+ activeMenu->Init(recMenuView);
+ }
+ if (cRecMenuTimeline *timeline = dynamic_cast<cRecMenuTimeline*>(activeMenu)) {
+ timeline->SetHeaderTimer();
+ }
+ activeMenu->Draw();
+}
+
+void cRecMenuView::DisplaySearchTimerList(void) {
+ delete activeMenu;
+ vector<cTVGuideSearchTimer> searchTimers;
+ recManager->GetSearchTimers(&searchTimers);
+ activeMenu = new cRecMenuSearchTimers(searchTimers);
+ DisplayMenu();
+}
+
+bool cRecMenuView::DisplayTimerConflict(cTimer *timer) {
+ int timerID = 0;
+ for (cTimer *t = Timers.First(); t; t = Timers.Next(t)) {
+ if (t == timer)
+ return DisplayTimerConflict(timerID);
+ timerID++;
+ }
+ return false;
+}
+
+bool cRecMenuView::DisplayTimerConflict(int timerID) {
+ if (timerConflicts)
+ delete timerConflicts;
+ timerConflicts = recManager->CheckTimerConflict();
+ if (!timerConflicts)
+ return false;
+ int showTimerConflict = timerConflicts->GetCorrespondingConflict(timerID);
+ if (showTimerConflict > -1) {
+ timerConflicts->SetCurrentConflict(showTimerConflict);
+ cTVGuideTimerConflict *conflict = timerConflicts->GetCurrentConflict();
+ if (!conflict)
+ return false;
+ activeMenu = new cRecMenuTimerConflict(conflict);
+ DisplayMenu();
+ return true;
+ }
+ return false;
+}
+
+void cRecMenuView::DisplayFavoriteResults(string header, const cEvent **result, int numResults) {
+ if (numResults) {
+ activeMenuBuffer = activeMenu;
+ activeMenuBuffer->Hide();
+ activeMenu = new cRecMenuSearchTimerResults(header, result, numResults, "", rmsFavoritesRecord);
+ } else {
+ activeMenuBuffer = activeMenu;
+ activeMenuBuffer->Hide();
+ activeMenu = new cRecMenuSearchTimerNothingFound(header);
+ }
+ DisplayMenu(true);
+}
+
+eOSState cRecMenuView::StateMachine(eRecMenuState nextState) {
+ eOSState state = osContinue;
+ switch (nextState) {
+/***************************************************************************************
+* INSTANT RECORDING
+****************************************************************************************/
+ case rmsInstantRecord: {
+ //caller: main menu or folder chooser
+ //Creating timer for active Event, if no conflict, confirm and exit
+ string recFolder = "";
+ if (cRecMenuAskFolder *menu = dynamic_cast<cRecMenuAskFolder*>(activeMenu)) {
+ recFolder = menu->GetFolder();
+ }
+ delete activeMenu;
+ cTimer *timer = recManager->createTimer(event, recFolder);
+ if (!DisplayTimerConflict(timer)) {
+ activeMenu = new cRecMenuConfirmTimer(event);
+ DisplayMenu();
+ }
+ break; }
+ case rmsInstantRecordFolder: {
+ //caller: main menu
+ //Asking for Folder
+ delete activeMenu;
+ activeMenu = new cRecMenuAskFolder(event, rmsInstantRecord);
+ DisplayMenu();
+ break; }
+ case rmsDeleteTimer: {
+ //caller: main menu
+ //delete timer for active event
+ delete activeMenu;
+ if (recManager->IsRecorded(event)) {
+ activeMenu = new cRecMenuAskDeleteTimer(event);
+ } else {
+ recManager->DeleteTimer(event);
+ activeMenu = new cRecMenuConfirmDeleteTimer(event);
+ }
+ DisplayMenu();
+ break; }
+ case rmsDeleteTimerConfirmation: {
+ //delete running timer for active event
+ recManager->DeleteTimer(event);
+ delete activeMenu;
+ activeMenu = new cRecMenuConfirmDeleteTimer(event);
+ DisplayMenu();
+ break; }
+ case rmsEditTimer: {
+ //edit timer for active event
+ cTimer *timer = recManager->GetTimerForEvent(event);
+ if (timer) {
+ delete activeMenu;
+ activeMenu = new cRecMenuEditTimer(timer, rmsSaveTimer);
+ DisplayMenu();
+ }
+ break; }
+ case rmsSaveTimer: {
+ //caller: cRecMenuEditTimer
+ //save timer for active event
+ cTimer timerModified;
+ cTimer *originalTimer;
+ if (cRecMenuEditTimer *menu = dynamic_cast<cRecMenuEditTimer*>(activeMenu)) {
+ timerModified = menu->GetTimer();
+ originalTimer = menu->GetOriginalTimer();
+ } else break;
+ recManager->SaveTimer(originalTimer, timerModified);
+ Close();
+ state = osEnd;
+ break; }
+ case rmsIgnoreTimerConflict:
+ //caller: cRecMenuTimerConflict
+ //Confirming created Timer
+ delete activeMenu;
+ activeMenu = new cRecMenuConfirmTimer(event);
+ DisplayMenu();
+ break;
+ case rmsTimerConflictShowInfo: {
+ //caller: cRecMenuTimerConflict
+ int timerIndex;
+ if (cRecMenuTimerConflict *menu = dynamic_cast<cRecMenuTimerConflict*>(activeMenu)) {
+ timerIndex = menu->GetTimerConflictIndex();
+ } else break;
+ int timerID = timerConflicts->GetCurrentConflictTimerID(timerIndex);
+ cTimer *t = Timers.Get(timerID);
+ if (t) {
+ displayEvent = t->Event();
+ if (displayEvent) {
+ state = osUser1;
+ }
+ }
+ break; }
+ case rmsDeleteTimerConflictMenu: {
+ //caller: cRecMenuTimerConflict
+ //delete timer out of current timer conflict
+ int timerIndex;
+ if (cRecMenuTimerConflict *menu = dynamic_cast<cRecMenuTimerConflict*>(activeMenu)) {
+ timerIndex = menu->GetTimerConflictIndex();
+ } else break;
+ int timerID = timerConflicts->GetCurrentConflictTimerID(timerIndex);
+ recManager->DeleteTimer(timerID);
+ delete activeMenu;
+ if (!DisplayTimerConflict(timerID)) {
+ activeMenu = new cRecMenuConfirmTimer(event);
+ DisplayMenu();
+ }
+ break; }
+ case rmsEditTimerConflictMenu: {
+ //caller: cRecMenuTimerConflict
+ //edit timer out of current timer conflict
+ int timerIndex;
+ if (cRecMenuTimerConflict *menu = dynamic_cast<cRecMenuTimerConflict*>(activeMenu)) {
+ timerIndex = menu->GetTimerConflictIndex();
+ } else break;
+ int timerID = timerConflicts->GetCurrentConflictTimerID(timerIndex);
+ cTimer *timer = Timers.Get(timerID);
+ if (timer) {
+ delete activeMenu;
+ activeMenu = new cRecMenuEditTimer(timer, rmsSaveTimerConflictMenu);
+ DisplayMenu();
+ }
+ break; }
+ case rmsSaveTimerConflictMenu: {
+ //caller: cRecMenuEditTimer
+ //save timer from current timer conflict
+ cTimer timerModified;
+ cTimer *originalTimer;
+ if (cRecMenuEditTimer *menu = dynamic_cast<cRecMenuEditTimer*>(activeMenu)) {
+ timerModified = menu->GetTimer();
+ originalTimer = menu->GetOriginalTimer();
+ } else break;
+ recManager->SaveTimer(originalTimer, timerModified);
+ delete activeMenu;
+ if (!DisplayTimerConflict(originalTimer)) {
+ activeMenu = new cRecMenuConfirmTimer(event);
+ DisplayMenu();
+ }
+ break; }
+/***************************************************************************************
+* SERIES TIMER
+****************************************************************************************/
+ case rmsSeriesTimer: {
+ //caller: main menu oder folder chooser
+ string recFolder = "";
+ if (cRecMenuAskFolder *menu = dynamic_cast<cRecMenuAskFolder*>(activeMenu)) {
+ recFolder = menu->GetFolder();
+ }
+ delete activeMenu;
+ cChannel *channel = Channels.GetByChannelID(event->ChannelID());
+ activeMenu = new cRecMenuSeriesTimer(channel, event, recFolder);
+ DisplayMenu();
+ break; }
+ case rmsSeriesTimerFolder:
+ //caller: main menu
+ //Asking for Folder
+ delete activeMenu;
+ activeMenu = new cRecMenuAskFolder(event, rmsSeriesTimer);
+ DisplayMenu();
+ break;
+ case rmsSeriesTimerCreate: {
+ //caller: cRecMenuSeriesTimer
+ cTimer *seriesTimer;
+ if (cRecMenuSeriesTimer *menu = dynamic_cast<cRecMenuSeriesTimer*>(activeMenu)) {
+ seriesTimer = menu->GetTimer();
+ } else break;
+ recManager->CreateSeriesTimer(seriesTimer);
+ delete activeMenu;
+ activeMenu = new cRecMenuConfirmSeriesTimer(seriesTimer);
+ DisplayMenu();
+ break; }
+/**********************************************************************************************
+ * SWITCH TIMER
+ ***********************************************************************************************/
+ case rmsSwitchTimer:
+ delete activeMenu;
+ activeMenu = new cRecMenuSwitchTimer();
+ DisplayMenu();
+ break;
+ case rmsSwitchTimerCreate: {
+ cSwitchTimer switchTimer;
+ if (cRecMenuSwitchTimer *menu = dynamic_cast<cRecMenuSwitchTimer*>(activeMenu)) {
+ switchTimer = menu->GetSwitchTimer();
+ } else break;
+ bool success = recManager->CreateSwitchTimer(event, switchTimer);
+ delete activeMenu;
+ activeMenu = new cRecMenuSwitchTimerConfirm(success);
+ DisplayMenu();
+ break; }
+ case rmsSwitchTimerDelete:
+ recManager->DeleteSwitchTimer(event);
+ delete activeMenu;
+ activeMenu = new cRecMenuSwitchTimerDelete();
+ DisplayMenu();
+ break;
+/**********************************************************************************************
+ * SEARCH TIMER
+ ***********************************************************************************************/
+ case rmsSearchTimer:
+ //Caller: main menu
+ //set search String for search timer
+ delete activeMenu;
+ activeMenu = new cRecMenuSearchTimer(event);
+ DisplayMenu();
+ break;
+ case rmsSearchTimerOptions: {
+ //Caller: cRecMenuSearchTimer, cRecMenuSearchTimerTemplates
+ //Choose to set options manually or by template
+ string searchString;
+ cTVGuideSearchTimer searchTimer;
+ bool reload = false;
+ if (cRecMenuSearchTimer *menu = dynamic_cast<cRecMenuSearchTimer*>(activeMenu)) {
+ searchString = menu->GetSearchString();
+ } else if (cRecMenuSearchTimerTemplatesCreate *menu = dynamic_cast<cRecMenuSearchTimerTemplatesCreate*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ reload = true;
+ } else break;
+ delete activeMenu;
+ if (searchString.size() < 4) {
+ activeMenu = new cRecMenuSearchTimer(event);
+ } else {
+ if (!reload) {
+ searchTimer.SetSearchString(searchString);
+ }
+ vector<TVGuideEPGSearchTemplate> epgSearchTemplates;
+ recManager->ReadEPGSearchTemplates(&epgSearchTemplates);
+ if (epgSearchTemplates.size() > 0) {
+ activeMenu = new cRecMenuSearchTimerTemplates(searchTimer, epgSearchTemplates);
+ } else {
+ activeMenu = new cRecMenuSearchTimerEdit(searchTimer, false);
+ }
+ }
+ DisplayMenu();
+ break; }
+ case rmsSearchTimers: {
+ //caller: main menu
+ DisplaySearchTimerList();
+ break; }
+ case rmsSearchTimerEdit:
+ case rmsSearchTimerEditAdvanced: {
+ //caller: cRecMenuSearchTimers, cRecMenuSearchTimerEdit, cRecMenuSearchTimerTemplates
+ cTVGuideSearchTimer searchTimer;
+ bool advancedOptions = false;
+ if (cRecMenuSearchTimers *menu = dynamic_cast<cRecMenuSearchTimers*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ } else if (cRecMenuSearchTimerEdit *menu = dynamic_cast<cRecMenuSearchTimerEdit*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ advancedOptions = (nextState == rmsSearchTimerEditAdvanced)?true:false;
+ } else if (cRecMenuSearchTimerTemplates *menu = dynamic_cast<cRecMenuSearchTimerTemplates*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ } else break;
+ delete activeMenu;
+ activeMenu = new cRecMenuSearchTimerEdit(searchTimer, advancedOptions);
+ DisplayMenu();
+ break; }
+ case rmsSearchTimerTest: {
+ //caller: cRecMenuSearchTimerEdit, cRecMenuSearchTimerTemplatesCreate, cRecMenuSearchTimers, cRecMenuFavorites
+ //show results of currently choosen search timer
+ cTVGuideSearchTimer searchTimer;
+ eRecMenuState recState = rmsDisabled;
+ if (cRecMenuSearchTimerEdit *menu = dynamic_cast<cRecMenuSearchTimerEdit*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ recState = rmsSearchTimerRecord;
+ } else if (cRecMenuSearchTimers *menu = dynamic_cast<cRecMenuSearchTimers*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ recState = rmsSearchTimerRecord;
+ } else if (cRecMenuSearchTimerTemplatesCreate *menu = dynamic_cast<cRecMenuSearchTimerTemplatesCreate*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ TVGuideEPGSearchTemplate tmpl = menu->GetTemplate();
+ searchTimer.SetTemplate(tmpl.templValue);
+ searchTimer.Parse(true);
+ } else if (cRecMenuFavorites *menu = dynamic_cast<cRecMenuFavorites*>(activeMenu)) {
+ searchTimer = menu->GetFavorite();
+ recState = rmsFavoritesRecord;
+ }
+ else break;
+ int numSearchResults = 0;
+ string searchString = searchTimer.BuildSearchString();
+ const cEvent **searchResult = recManager->PerformSearchTimerSearch(searchString, numSearchResults);
+ if (numSearchResults) {
+ activeMenuBuffer = activeMenu;
+ activeMenuBuffer->Hide();
+ activeMenu = new cRecMenuSearchTimerResults(searchTimer.SearchString(), searchResult, numSearchResults, "", recState);
+ } else {
+ activeMenuBuffer = activeMenu;
+ activeMenuBuffer->Hide();
+ activeMenu = new cRecMenuSearchTimerNothingFound(searchTimer.SearchString());
+ }
+ DisplayMenu(true);
+ break; }
+ case rmsSearchTimerSave: {
+ //caller: cRecMenuSearchTimerEdit, cRecMenuSearchTimerTemplatesCreate
+ //create new or modify existing search timer
+ cTVGuideSearchTimer searchTimer;
+ if (cRecMenuSearchTimerEdit *menu = dynamic_cast<cRecMenuSearchTimerEdit*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ } else if (cRecMenuSearchTimerTemplatesCreate *menu = dynamic_cast<cRecMenuSearchTimerTemplatesCreate*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ TVGuideEPGSearchTemplate tmpl = menu->GetTemplate();
+ searchTimer.SetTemplate(tmpl.templValue);
+ searchTimer.Parse(true);
+ } else break;
+ bool success = recManager->SaveSearchTimer(&searchTimer);
+ recManager->UpdateSearchTimers();
+ if (searchTimer.GetID() >= 0) {
+ //Timer modified, show list
+ DisplaySearchTimerList();
+ } else {
+ //new timer, confirm
+ delete activeMenu;
+ activeMenu = new cRecMenuSearchTimerCreateConfirm(success);
+ DisplayMenu();
+ }
+ break; }
+ case rmsSearchTimerCreateWithTemplate: {
+ //caller: cRecMenuSearchTimerTemplates
+ //create new search timer from template
+ TVGuideEPGSearchTemplate templ;
+ cTVGuideSearchTimer searchTimer;
+ if (cRecMenuSearchTimerTemplates *menu = dynamic_cast<cRecMenuSearchTimerTemplates*>(activeMenu)) {
+ templ = menu->GetTemplate();
+ searchTimer = menu->GetSearchTimer();
+ } else break;
+ delete activeMenu;
+ activeMenu = new cRecMenuSearchTimerTemplatesCreate(templ, searchTimer);
+ DisplayMenu();
+ break; }
+ case rmsSearchTimerDeleteConfirm: {
+ //caller: cRecMenuSearchTimers
+ //Ask for confirmation and if timers created by this search timer should alo be deleted
+ cTVGuideSearchTimer searchTimer;
+ if (cRecMenuSearchTimers *menu = dynamic_cast<cRecMenuSearchTimers*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ } else break;
+ activeMenuBuffer = activeMenu;
+ activeMenuBuffer->Hide();
+ activeMenu = new cRecMenuSearchTimerDeleteConfirm(searchTimer);
+ DisplayMenu(true);
+ break; }
+ case rmsSearchTimerDelete:
+ case rmsSearchTimerDeleteWithTimers: {
+ //caller: cRecMenuSearchTimerDeleteConfirm
+ //actually delete searchtimer
+ cTVGuideSearchTimer searchTimer;
+ if (cRecMenuSearchTimerDeleteConfirm *menu = dynamic_cast<cRecMenuSearchTimerDeleteConfirm*>(activeMenu)) {
+ searchTimer = menu->GetSearchTimer();
+ } else break;
+ bool delTimers = (nextState==rmsSearchTimerDeleteWithTimers)?true:false;
+ recManager->DeleteSearchTimer(&searchTimer, delTimers);
+ delete activeMenuBuffer;
+ activeMenuBuffer = NULL;
+ DisplaySearchTimerList();
+ break; }
+ case rmsSearchTimerRecord: {
+ //caller: cRecMenuSearchTimerResults
+ const cEvent *ev = NULL;
+ if (cRecMenuSearchTimerResults *menu = dynamic_cast<cRecMenuSearchTimerResults*>(activeMenu)) {
+ ev = menu->GetEvent();
+ } else break;
+ if (!ev)
+ break;
+ recManager->createTimer(ev, "");
+ activeMenuBuffer2 = activeMenu;
+ activeMenuBuffer2->Hide();
+ activeMenu = new cRecMenuSearchConfirmTimer(ev);
+ DisplayMenu(false, true);
+ break; }
+/**********************************************************************************************
+* SEARCH
+***********************************************************************************************/
+ case rmsSearch:
+ case rmsSearchWithOptions: {
+ //caller: main menu, cRecMenuSearch, cRecMenuSearchResults
+ bool withOptions = false;
+ string searchString = event->Title();
+ if (cRecMenuSearch *menu = dynamic_cast<cRecMenuSearch*>(activeMenu)) {
+ withOptions = true;
+ searchString = menu->GetSearchString();
+ } else if (cRecMenuSearchResults *menu = dynamic_cast<cRecMenuSearchResults*>(activeMenu)) {
+ searchString = menu->GetSearchString();
+ }
+ delete activeMenu;
+ activeMenu = new cRecMenuSearch(searchString, withOptions);
+ DisplayMenu();
+ break; }
+ case rmsSearchPerform: {
+ //caller: cRecMenuSearch
+ bool useBuffer = false;
+ Epgsearch_searchresults_v1_0 epgSearchData;
+ if (cRecMenuSearch *menu = dynamic_cast<cRecMenuSearch*>(activeMenu)) {
+ epgSearchData = menu->GetEPGSearchStruct();
+ } else break;
+ string searchString = epgSearchData.query;
+ if (searchString.size() < 3) {
+ activeMenu->Hide();
+ activeMenuBuffer = activeMenu;
+ useBuffer = true;
+ activeMenu = new cRecMenuSearchNothingFound(searchString, true);
+ } else {
+ int numSearchResults = 0;
+ const cEvent **searchResult = recManager->PerformSearch(epgSearchData, numSearchResults);
+ if (searchResult) {
+ delete activeMenu;
+ activeMenu = new cRecMenuSearchResults(searchString, searchResult, numSearchResults);
+ } else {
+ activeMenu->Hide();
+ activeMenuBuffer = activeMenu;
+ useBuffer = true;
+ activeMenu = new cRecMenuSearchNothingFound(searchString);
+ }
+ }
+ DisplayMenu(useBuffer);
+ break; }
+ case rmsSearchShowInfo: {
+ //caller: cRecMenuSearchResults, cRecMenuSearchTimerResults, cRecMenuRerunResults
+ if (cRecMenuSearchResults *menu = dynamic_cast<cRecMenuSearchResults*>(activeMenu)) {
+ displayEvent = menu->GetEvent();
+ } else if (cRecMenuSearchTimerResults *menu = dynamic_cast<cRecMenuSearchTimerResults*>(activeMenu)) {
+ displayEvent = menu->GetEvent();
+ } else if (cRecMenuRerunResults *menu = dynamic_cast<cRecMenuRerunResults*>(activeMenu)) {
+ displayEvent = menu->GetEvent();
+ } else break;
+ if (displayEvent) {
+ state = osUser1;
+ }
+ break;}
+ case rmsSearchRecord: {
+ //caller: cRecMenuSearchResults
+ const cEvent *ev = NULL;
+ if (cRecMenuSearchResults *menu = dynamic_cast<cRecMenuSearchResults*>(activeMenu)) {
+ ev = menu->GetEvent();
+ } else break;
+ if (!ev)
+ break;
+ recManager->createTimer(ev, "");
+ activeMenuBuffer = activeMenu;
+ activeMenuBuffer->Hide();
+ activeMenu = new cRecMenuSearchConfirmTimer(ev);
+ DisplayMenu(true);
+ break;}
+/**********************************************************************************************
+ * CHECK FOR TIMER CONFLICTS
+ ***********************************************************************************************/
+ case rmsTimerConflicts: {
+ //caller: main menu
+ //Show timer conflict
+ if (timerConflicts) {
+ delete timerConflicts;
+ }
+ timerConflicts = recManager->CheckTimerConflict();
+ delete activeMenu;
+ int numConflicts = timerConflicts->NumConflicts();
+ if (numConflicts > 0) {
+ activeMenu = new cRecMenuTimerConflicts(timerConflicts);
+ } else {
+ activeMenu = new cRecMenuNoTimerConflict();
+ }
+ DisplayMenu();
+ break; }
+ case rmsTimerConflict: {
+ //caller: cRecMenuTimerConflicts
+ //Show timer conflict
+ if (!timerConflicts)
+ break;
+ int timerConflict;
+ if (cRecMenuTimerConflicts *menu = dynamic_cast<cRecMenuTimerConflicts*>(activeMenu)) {
+ timerConflict = menu->GetTimerConflict();
+ } else break;
+ timerConflicts->SetCurrentConflict(timerConflict);
+ delete activeMenu;
+ activeMenu = new cRecMenuTimerConflict(timerConflicts->GetCurrentConflict());
+ DisplayMenu();
+ break; }
+ case rmsSearchRerunsTimerConflictMenu: {
+ //caller: cRecMenuTimerConflict
+ //Show reruns for timer from timer conflict
+ if (!timerConflicts)
+ break;
+ int timerConflict;
+ if (cRecMenuTimerConflict *menu = dynamic_cast<cRecMenuTimerConflict*>(activeMenu)) {
+ timerConflict = menu->GetTimerConflictIndex();
+ } else break;
+ int timerID = timerConflicts->GetCurrentConflictTimerID(timerConflict);
+ cTimer *timer = Timers.Get(timerID);
+ if (timer) {
+ const cEvent *event = timer->Event();
+ if (event) {
+ int numReruns = 0;
+ const cEvent **reruns = recManager->LoadReruns(event, numReruns);
+ if (reruns && (numReruns > 0)) {
+ activeMenuBuffer = activeMenu;
+ activeMenuBuffer->Hide();
+ activeMenu = new cRecMenuRerunResults(event, reruns, numReruns);
+ } else {
+ activeMenuBuffer = activeMenu;
+ activeMenuBuffer->Hide();
+ activeMenu = new cRecMenuNoRerunsFound((event->Title())?event->Title():"");
+ }
+ DisplayMenu(true);
+ }
+ }
+ break; }
+ case rmsTimerConflictRecordRerun: {
+ //caller: cRecMenuRerunResults
+ //buffer: cRecMenuTimerConflict
+ if (!activeMenuBuffer)
+ break;
+ if (!timerConflicts)
+ break;
+ const cEvent *replace;
+ int originalConflictIndex;
+ if (cRecMenuRerunResults *menu = dynamic_cast<cRecMenuRerunResults*>(activeMenu)) {
+ replace = menu->GetEvent();
+ } else break;
+ if (cRecMenuTimerConflict *menu = dynamic_cast<cRecMenuTimerConflict*>(activeMenuBuffer)) {
+ originalConflictIndex = menu->GetTimerConflictIndex();
+ } else break;
+ int originalTimerID = timerConflicts->GetCurrentConflictTimerID(originalConflictIndex);
+ cTimer *timerOriginal = Timers.Get(originalTimerID);
+ if (replace && timerOriginal) {
+ recManager->DeleteTimer(timerOriginal->Event());
+ recManager->createTimer(replace);
+ activeMenuBuffer->Show();
+ delete activeMenuBuffer;
+ activeMenuBuffer = NULL;
+ delete activeMenu;
+ activeMenu = new cRecMenuConfirmRerunUsed(timerOriginal->Event(), replace);
+ DisplayMenu();
+ }
+ break; }
+ /**********************************************************************************************
+ * TIMELINE
+ ***********************************************************************************************/
+ case rmsTimeline: {
+ delete activeMenu;
+ activeMenu = new cRecMenuTimeline();
+ DisplayMenu();
+ break; }
+ case rmsTimelineTimerEdit: {
+ cTimer *timer;
+ if (cRecMenuTimeline *menu = dynamic_cast<cRecMenuTimeline*>(activeMenu)) {
+ timer = menu->GetTimer();
+ } else break;
+ if (timer) {
+ delete activeMenu;
+ activeMenu = new cRecMenuEditTimer(timer, rmsTimelineTimerSave);
+ DisplayMenu();
+ }
+ break;}
+ case rmsTimelineTimerSave: {
+ cTimer timerModified;
+ cTimer *originalTimer;
+ if (cRecMenuEditTimer *menu = dynamic_cast<cRecMenuEditTimer*>(activeMenu)) {
+ timerModified = menu->GetTimer();
+ originalTimer = menu->GetOriginalTimer();
+ } else break;
+ recManager->SaveTimer(originalTimer, timerModified);
+ delete activeMenu;
+ activeMenu = new cRecMenuTimeline();
+ DisplayMenu();
+ break; }
+ case rmsTimelineTimerDelete: {
+ cTimer *timer;
+ if (cRecMenuEditTimer *menu = dynamic_cast<cRecMenuEditTimer*>(activeMenu)) {
+ timer = menu->GetOriginalTimer();
+ } else break;
+ recManager->DeleteTimer(timer);
+ delete activeMenu;
+ activeMenu = new cRecMenuTimeline();
+ DisplayMenu();
+ break; }
+ default:
+ break;
+/**********************************************************************************************
+ * RECORDINGS SEARCH
+ ***********************************************************************************************/
+ case rmsRecordingSearch: {
+ //caller: main menu or rmsRecordingSearchResult
+ string searchString = event->Title();
+ if (cRecMenuRecordingSearchResults *menu = dynamic_cast<cRecMenuRecordingSearchResults*>(activeMenu)) {
+ searchString = menu->GetSearchString();
+ };
+ delete activeMenu;
+ activeMenu = new cRecMenuRecordingSearch(searchString);
+ DisplayMenu();
+ break; }
+ case rmsRecordingSearchResult: {
+ //caller: cRecMenuRecordingSearch
+ string searchString;
+ if (cRecMenuRecordingSearch *menu = dynamic_cast<cRecMenuRecordingSearch*>(activeMenu)) {
+ searchString = menu->GetSearchString();
+ } else break;
+ delete activeMenu;
+ if (searchString.size() < 4) {
+ activeMenu = new cRecMenuRecordingSearch(searchString);
+ } else {
+ int numSearchResults = 0;
+ cRecording **searchResult = recManager->SearchForRecordings(searchString, numSearchResults);
+ if (numSearchResults == 0) {
+ activeMenu = new cRecMenuRecordingSearchNotFound(searchString);
+ } else {
+ activeMenu = new cRecMenuRecordingSearchResults(searchString, searchResult, numSearchResults);
+ }
+ }
+ DisplayMenu();
+ break; }
+ /**********************************************************************************************
+ * FAVORITES
+ *********************************************************************************************/
+ case rmsFavoritesRecord: {
+ //caller: cRecMenuSearchTimerResults
+ const cEvent *ev = NULL;
+ if (cRecMenuSearchTimerResults *menu = dynamic_cast<cRecMenuSearchTimerResults*>(activeMenu)) {
+ ev = menu->GetEvent();
+ } else break;
+ if (!ev)
+ break;
+ recManager->createTimer(ev, "");
+ activeMenuBuffer2 = activeMenu;
+ activeMenuBuffer2->Hide();
+ activeMenu = new cRecMenuSearchConfirmTimer(ev);
+ DisplayMenu(false, true);
+ break;}
+ case rmsFavoritesNow:
+ case rmsFavoritesNext: {
+ int numResults = 0;
+ bool nowOrNext;
+ string header;
+ if (nextState == rmsFavoritesNow) {
+ nowOrNext = true;
+ header = tr("What's on now");
+ } else {
+ nowOrNext = false;
+ header = tr("What's on next");
+ }
+ const cEvent **result = recManager->WhatsOnNow(nowOrNext, numResults);
+ DisplayFavoriteResults(header, result, numResults);
+ break; }
+ case rmsFavoritesUser1: {
+ int numResults = 0;
+ const cEvent **result = recManager->UserDefinedTime(1, numResults);
+ DisplayFavoriteResults(config.descUser1, result, numResults);
+ break; }
+ case rmsFavoritesUser2: {
+ int numResults = 0;
+ const cEvent **result = recManager->UserDefinedTime(2, numResults);
+ DisplayFavoriteResults(config.descUser2, result, numResults);
+ break; }
+ case rmsFavoritesUser3: {
+ int numResults = 0;
+ const cEvent **result = recManager->UserDefinedTime(3, numResults);
+ DisplayFavoriteResults(config.descUser3, result, numResults);
+ break; }
+ case rmsFavoritesUser4: {
+ int numResults = 0;
+ const cEvent **result = recManager->UserDefinedTime(4, numResults);
+ DisplayFavoriteResults(config.descUser4, result, numResults);
+ break; }
+ }
+ return state;
+}
diff --git a/recmenuview.h b/recmenuview.h new file mode 100644 index 0000000..dfc526c --- /dev/null +++ b/recmenuview.h @@ -0,0 +1,44 @@ +#ifndef __TVGUIDE_RECMENUVIEW_H
+#define __TVGUIDE_RECMENUVIEW_H
+
+#include "config.h"
+#include "libskindesigner/osdelements.h"
+#include "recmanager.h"
+#include "recmenus.h"
+
+class cRecMenuView {
+private:
+ bool active;
+ cOsdView *recMenuView;
+ cOsdView *recMenuViewBuffer;
+ cOsdView *recMenuViewBuffer2;
+ const cEvent *event;
+ const cEvent *displayEvent;
+ cRecManager *recManager;
+ cRecMenu *activeMenu;
+ cRecMenu *activeMenuBuffer;
+ cRecMenu *activeMenuBuffer2;
+ cTVGuideTimerConflicts *timerConflicts;
+ void DrawBackground(int menuWidth, int menuHeight, bool scrolling);
+ void DisplayMenu(bool buffer = false, bool buffer2 = false);
+ void DisplaySearchTimerList(void);
+ bool DisplayTimerConflict(cTimer *timer);
+ bool DisplayTimerConflict(int timerID);
+ void DisplayFavoriteResults(string header, const cEvent **result, int numResults);
+ eOSState StateMachine(eRecMenuState nextState);
+public:
+ cRecMenuView(void);
+ virtual ~cRecMenuView(void);
+ void Init(cOsdView *recMenuView, cOsdView *recMenuViewBuffer, cOsdView *recMenuViewBuffer2);
+ void DisplayRecMenu(const cEvent *event);
+ void DisplayFavorites(void);
+ void Close(void);
+ void Hide(void);
+ void Activate(void);
+ eOSState ProcessKey(eKeys Key);
+ void Flush(void) { activeMenu->Flush(); };
+ const cEvent *GetEvent(void) { return displayEvent; };
+ bool Active(void) { return active; };
+};
+
+#endif //__TVGUIDE_RECMENUVIEW_H
diff --git a/searchtimer.c b/searchtimer.c new file mode 100644 index 0000000..6570c55 --- /dev/null +++ b/searchtimer.c @@ -0,0 +1,570 @@ +#include <string>
+#include <vector>
+#include <sstream>
+#include <algorithm>
+#include <vdr/channels.h>
+#include <vdr/device.h>
+#include "helpers.h"
+#include "searchtimer.h"
+
+// -- cTVGuideSearchTimer -----------------------------------------------------------------
+cTVGuideSearchTimer::cTVGuideSearchTimer(void) {
+ strTimer = "";
+ ID = -1;
+ searchString = "";
+ useTime = false;
+ startTime = 0000;
+ stopTime = 2359;
+ useChannel = false;
+ channelMin = Channels.GetByNumber(cDevice::CurrentChannel());
+ channelMax = Channels.GetByNumber(cDevice::CurrentChannel());
+ channelGroup = "";
+ useCase = false;
+ mode = 0;
+ useTitle = true;
+ useSubtitle = true;
+ useDescription = true;
+ useDuration = false;
+ minDuration = 0;
+ maxDuration = 2359;
+ useAsSearchTimer = true;
+ useDayOfWeek = false;
+ dayOfWeek = 0;
+ directory = "";
+ useEpisode = 0;
+ priority = 99;
+ lifetime = 99;
+ marginStart = 5;
+ marginStop = 5;
+ useVPS = false;
+ action = 0;
+ useExtEPGInfo = 0;
+ extEPGInfoValues = "";
+ avoidRepeats = 1;
+ allowedRepeats = 1;
+ compareTitle = 1;
+ compareSubtitle = 2;
+ compareSummary = 1;
+ catvaluesAvoidRepeat = 0;
+ repeatsWithinDays = 0;
+ delAfterDays = 0;
+ recordingsKeep = 0;
+ switchMinsBefore = 0;
+ pauseOnNrRecordings = 0;
+ blacklistMode = 0;
+ blacklists = "";
+ fuzzyTolerance = 0;
+ useInFavorites = 0;
+ menuTemplate = 0;
+ delMode = 0;
+ delAfterCountRecs = 0;
+ delAfterDaysOfFirstRec = 0;
+ useAsSearchTimerFrom = 0;
+ useAsSearchTimerTil = 0;
+ ignoreMissingEPGCats = 0;
+ unmuteSoundOnSwitch = 0;
+ compareSummaryMatchInPercent = 0;
+ contentsFilter = "";
+ compareDate = 0;
+}
+
+cTVGuideSearchTimer::~cTVGuideSearchTimer(void) {
+}
+
+bool cTVGuideSearchTimer::operator < (const cTVGuideSearchTimer& other) const {
+ std::string searchStringOther = other.SearchString();
+ searchStringOther = StrToLowerCase(searchStringOther);
+ std::string thisSearchString = StrToLowerCase(searchString);
+ int comp = thisSearchString.compare(searchStringOther);
+ if (comp < 0)
+ return true;
+ return false;
+}
+
+
+void cTVGuideSearchTimer::SetTemplate(std::string tmpl) {
+ std::stringstream searchTimerString;
+ searchTimerString << "0:";
+ searchTimerString << tmpl;
+ strTimer = searchTimerString.str();
+}
+
+int cTVGuideSearchTimer::DayOfWeek(void) {
+ int vdrDayOfWeek = 0;
+ if (dayOfWeek >= 0) {
+ vdrDayOfWeek = pow(2, (dayOfWeek+6)%7);
+ } else if (dayOfWeek < 0) {
+ int absDayOfWeek = abs(dayOfWeek);
+ for (int i=0; i < 7; i++) {
+ if (absDayOfWeek & (1 << i)) {
+ vdrDayOfWeek += pow(2, (i+6)%7);
+ }
+ }
+ }
+ return vdrDayOfWeek;
+}
+
+void cTVGuideSearchTimer::SetDayOfWeek(int VDRDayOfWeek) {
+ int epgSearchDayOfWeek = 0;
+ for (int i=0; i < 7; i++) {
+ if (VDRDayOfWeek & (1 << i)) {
+ epgSearchDayOfWeek += pow(2, (i+1)%7);
+ }
+ }
+ this->dayOfWeek = epgSearchDayOfWeek * (-1);
+}
+
+/*
+ 0 - unique search timer id
+ 1 - the search term
+ 2 - use time? 0/1
+ 3 - start time in HHMM
+ 4 - stop time in HHMM
+ 5 - use channel? 0 = no, 1 = Interval, 2 = Channel group, 3 = FTA only
+ 6 - if 'use channel' = 1 then channel id[|channel id] in VDR format,
+ one entry or min/max entry separated with |, if 'use channel' = 2
+ then the channel group name
+ 7 - match case? 0/1
+ 8 - search mode:
+ 0 - the whole term must appear as substring
+ 1 - all single terms (delimiters are blank,',', ';', '|' or '~')
+ must exist as substrings.
+ 2 - at least one term (delimiters are blank, ',', ';', '|' or '~')
+ must exist as substring.
+ 3 - matches exactly
+ 4 - regular expression
+ 9 - use title? 0/1
+ 10 - use subtitle? 0/1
+ 11 - use description? 0/1
+ 12 - use duration? 0/1
+ 13 - min duration in hhmm
+ 14 - max duration in hhmm
+ 15 - use as search timer? 0/1
+ 16 - use day of week? 0/1
+ 17 - day of week (0 = Sunday, 1 = Monday...;
+ -1 Sunday, -2 Monday, -4 Tuesday, ...; -7 Sun, Mon, Tue)
+ 18 - use series recording? 0/1
+ 19 - directory for recording
+ 20 - priority of recording
+ 21 - lifetime of recording
+ 22 - time margin for start in minutes
+ 23 - time margin for stop in minutes
+ 24 - use VPS? 0/1
+ 25 - action:
+ 0 = create a timer
+ 1 = announce only via OSD (no timer)
+ 2 = switch only (no timer)
+ 3 = announce via OSD and switch (no timer)
+ 4 = announce via mail
+ 26 - use extended EPG info? 0/1
+ 27 - extended EPG info values. This entry has the following format
+ (delimiter is '|' for each category, '#' separates id and value):
+ 1 - the id of the extended EPG info category as specified in
+ epgsearchcats.conf
+ 2 - the value of the extended EPG info category
+ (a ':' will be translated to "!^colon^!", e.g. in "16:9")
+ 28 - avoid repeats? 0/1
+ 29 - allowed repeats
+ 30 - compare title when testing for a repeat? 0/1
+ 31 - compare subtitle when testing for a repeat? 0/1/2
+ 0 - no
+ 1 - yes
+ 2 - yes, if present
+ 32 - compare description when testing for a repeat? 0/1
+ 33 - compare extended EPG info when testing for a repeat?
+ This entry is a bit field of the category IDs.
+ 34 - accepts repeats only within x days
+ 35 - delete a recording automatically after x days
+ 36 - but keep this number of recordings anyway
+ 37 - minutes before switch (if action = 2)
+ 38 - pause if x recordings already exist
+ 39 - blacklist usage mode (0 none, 1 selection, 2 all)
+ 40 - selected blacklist IDs separated with '|'
+ 41 - fuzzy tolerance value for fuzzy searching
+ 42 - use this search in favorites menu (0 no, 1 yes)
+ 43 - id of a menu search template
+ 44 - auto deletion mode (0 don't delete search timer, 1 delete after given
+ count of recordings, 2 delete after given days after first recording)
+ 45 - count of recordings after which to delete the search timer
+ 46 - count of days after the first recording after which to delete the search
+ timer
+ 47 - first day where the search timer is active (see parameter 16)
+ 48 - last day where the search timer is active (see parameter 16)
+ 49 - ignore missing EPG categories? 0/1
+ 50 - unmute sound if off when used as switch timer
+ 51 - percentage of match when comparing the summary of two events (with 'avoid repeats')
+ 52 - HEX representation of the content descriptors, each descriptor ID is represented with 2 chars
+ 53 - compare date when testing for a repeat? (0=no, 1=same day, 2=same week, 3=same month)
+*/
+bool cTVGuideSearchTimer::Parse(bool readTemplate) {
+ splitstring s(strTimer.c_str());
+ std::vector<std::string> values = s.split(':', 1);
+ int numValues = values.size();
+ if (numValues < 12)
+ return false;
+ for (int value = 0; value < numValues; value++) {
+ switch (value) {
+ case 0:
+ if (!readTemplate)
+ ID = atoi(values[value].c_str());
+ break;
+ case 1:
+ if (!readTemplate) {
+ std::string searchStringMasked = values[value];
+ std::replace(searchStringMasked.begin(), searchStringMasked.end(), '|', ':');
+ searchString = searchStringMasked;
+ }
+ break;
+ case 2:
+ useTime = atoi(values[value].c_str());
+ break;
+ case 3:
+ if (useTime) {
+ startTime = atoi(values[value].c_str());
+ }
+ break;
+ case 4:
+ if (useTime) {
+ stopTime = atoi(values[value].c_str());
+ }
+ break;
+ case 5:
+ useChannel = atoi(values[value].c_str());
+ break;
+ case 6:
+ if (useChannel == 0) {
+ channelMin = NULL;
+ channelMax = NULL;
+ } else if (useChannel == 1) {
+ char *channelMinbuffer = NULL;
+ char *channelMaxbuffer = NULL;
+ int channels = sscanf(values[value].c_str(), "%a[^|]|%a[^|]", &channelMinbuffer, &channelMaxbuffer);
+ channelMin = Channels.GetByChannelID(tChannelID::FromString(channelMinbuffer), true, true);
+ if (!channelMin) {
+ channelMin = channelMax = NULL;
+ useChannel = 0;
+ }
+ if (channels == 1)
+ channelMax = channelMin;
+ else {
+ channelMax = Channels.GetByChannelID(tChannelID::FromString(channelMaxbuffer), true, true);
+ if (!channelMax) {
+ channelMin = channelMax = NULL;
+ useChannel = 0;
+ }
+ }
+ free(channelMinbuffer);
+ free(channelMaxbuffer);
+ } else if (useChannel == 2) {
+ channelGroup = values[value];
+ }
+ break;
+ case 7:
+ useCase = atoi(values[value].c_str());
+ break;
+ case 8:
+ mode = atoi(values[value].c_str());
+ break;
+ case 9:
+ useTitle = atoi(values[value].c_str());
+ break;
+ case 10:
+ useSubtitle = atoi(values[value].c_str());
+ break;
+ case 11:
+ useDescription = atoi(values[value].c_str());
+ break;
+ case 12:
+ useDuration = atoi(values[value].c_str());
+ break;
+ case 13:
+ minDuration = atoi(values[value].c_str());
+ break;
+ case 14:
+ maxDuration = atoi(values[value].c_str());
+ break;
+ case 15:
+ useAsSearchTimer = atoi(values[value].c_str());
+ break;
+ case 16:
+ useDayOfWeek = atoi(values[value].c_str());
+ break;
+ case 17:
+ dayOfWeek = atoi(values[value].c_str());
+ break;
+ case 18:
+ useEpisode = atoi(values[value].c_str());
+ break;
+ case 19:
+ directory = values[value];
+ break;
+ case 20:
+ priority = atoi(values[value].c_str());
+ break;
+ case 21:
+ lifetime = atoi(values[value].c_str());
+ break;
+ case 22:
+ marginStart = atoi(values[value].c_str());
+ break;
+ case 23:
+ marginStop = atoi(values[value].c_str());
+ break;
+ case 24:
+ useVPS = atoi(values[value].c_str());
+ break;
+ case 25:
+ action = atoi(values[value].c_str());
+ break;
+ case 26:
+ useExtEPGInfo = atoi(values[value].c_str());
+ break;
+ case 27:
+ extEPGInfoValues = values[value];
+ break;
+ case 28:
+ avoidRepeats = atoi(values[value].c_str());
+ break;
+ case 29:
+ allowedRepeats = atoi(values[value].c_str());
+ break;
+ case 30:
+ compareTitle = atoi(values[value].c_str());
+ break;
+ case 31:
+ compareSubtitle = atoi(values[value].c_str());
+ break;
+ case 32:
+ compareSummary = atoi(values[value].c_str());
+ break;
+ case 33:
+ catvaluesAvoidRepeat = atol(values[value].c_str());
+ break;
+ case 34:
+ repeatsWithinDays = atoi(values[value].c_str());
+ break;
+ case 35:
+ delAfterDays = atoi(values[value].c_str());
+ break;
+ case 36:
+ recordingsKeep = atoi(values[value].c_str());
+ break;
+ case 37:
+ switchMinsBefore = atoi(values[value].c_str());
+ break;
+ case 38:
+ pauseOnNrRecordings = atoi(values[value].c_str());
+ break;
+ case 39:
+ blacklistMode = atoi(values[value].c_str());
+ break;
+ case 40:
+ blacklists = values[value];
+ break;
+ case 41:
+ fuzzyTolerance = atoi(values[value].c_str());
+ break;
+ case 42:
+ useInFavorites = atoi(values[value].c_str());
+ break;
+ case 43:
+ menuTemplate = atoi(values[value].c_str());
+ break;
+ case 44:
+ delMode = atoi(values[value].c_str());
+ break;
+ case 45:
+ delAfterCountRecs = atoi(values[value].c_str());
+ break;
+ case 46:
+ delAfterDaysOfFirstRec = atoi(values[value].c_str());
+ break;
+ case 47:
+ useAsSearchTimerFrom = atol(values[value].c_str());
+ break;
+ case 48:
+ useAsSearchTimerTil = atol(values[value].c_str());
+ break;
+ case 49:
+ ignoreMissingEPGCats = atoi(values[value].c_str());
+ break;
+ case 50:
+ unmuteSoundOnSwitch = atoi(values[value].c_str());
+ break;
+ case 51:
+ compareSummaryMatchInPercent = atoi(values[value].c_str());
+ break;
+ case 52:
+ contentsFilter = values[value];
+ break;
+ case 53:
+ compareDate = atoi(values[value].c_str());
+ break;
+ default:
+ break;
+ }
+ }
+ return true;
+}
+
+std::string cTVGuideSearchTimer::BuildSearchString(void) {
+ std::stringstream search;
+ // 0 - 2
+ if (ID > -1)
+ search << ID << ":";
+ else
+ search << ":";
+ std::string searchStringMasked = searchString;
+ std::replace(searchStringMasked.begin(), searchStringMasked.end(), ':', '|');
+ search << searchStringMasked << ":";
+ search << useTime << ":";
+
+ // 3 - 6
+ if (useTime) {
+ search << *cString::sprintf("%04d", startTime) << ":";
+ search << *cString::sprintf("%04d", stopTime) << ":";
+ } else {
+ search << "::";
+ }
+
+ search << useChannel << ":";
+ if (useChannel == 1) {
+ if (channelMin && channelMax) {
+ if (channelMin->Number() < channelMax->Number())
+ search << std::string(channelMin->GetChannelID().ToString()) << "|" << std::string(channelMax->GetChannelID().ToString()) << ":";
+ else
+ search << std::string(channelMin->GetChannelID().ToString()) << ":";
+ } else {
+ search << "0:";
+ }
+ } else if (useChannel == 2) {
+ search << channelGroup << ":";
+ } else {
+ search << "0:";
+ }
+ // 7 - 14
+ search << useCase << ":";
+ search << mode << ":";
+ search << useTitle << ":";
+ search << useSubtitle << ":";
+ search << useDescription << ":";
+ search << useDuration << ":";
+ if (useDuration) {
+ search << *cString::sprintf("%04d", minDuration) << ":";
+ search << *cString::sprintf("%04d", maxDuration) << ":";
+ } else {
+ search << "::";
+ }
+ //15 - 53
+ search << useAsSearchTimer << ":";
+ search << useDayOfWeek << ":";
+ search << dayOfWeek << ":";
+ search << useEpisode << ":";
+ search << directory << ":";
+ search << priority << ":";
+ search << lifetime << ":";
+ search << marginStart << ":";
+ search << marginStop << ":";
+ search << useVPS << ":";
+ search << action << ":";
+ search << useExtEPGInfo << ":";
+ search << extEPGInfoValues << ":";
+ search << avoidRepeats << ":";
+ search << allowedRepeats << ":";
+ search << compareTitle << ":";
+ search << compareSubtitle << ":";
+ search << compareSummary << ":";
+ search << catvaluesAvoidRepeat << ":";
+ search << repeatsWithinDays << ":";
+ search << delAfterDays << ":";
+ search << recordingsKeep << ":";
+ search << switchMinsBefore << ":";
+ search << pauseOnNrRecordings << ":";
+ search << blacklistMode << ":";
+ search << blacklists << ":";
+ search << fuzzyTolerance << ":";
+ search << useInFavorites << ":";
+ search << menuTemplate << ":";
+ search << delMode << ":";
+ search << delAfterCountRecs << ":";
+ search << delAfterDaysOfFirstRec << ":";
+ search << useAsSearchTimerFrom << ":";
+ search << useAsSearchTimerTil << ":";
+ search << ignoreMissingEPGCats << ":";
+ search << unmuteSoundOnSwitch << ":";
+ search << compareSummaryMatchInPercent << ":";
+ search << contentsFilter << ":";
+ search << compareDate;
+
+ strTimer = search.str();
+ return strTimer;
+}
+
+bool cTVGuideSearchTimer::Active(void) {
+ if (useAsSearchTimer)
+ return true;
+ return false;
+}
+
+
+int cTVGuideSearchTimer::GetNumTimers(void) {
+ int numTimers = 0;
+ if (ID < 0)
+ return numTimers;
+ for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer)) {
+ char* searchID = GetAuxValue(timer, "s-id");
+ if (!searchID) continue;
+ if (ID == atoi(searchID))
+ numTimers++;
+ free(searchID);
+ }
+ return numTimers;
+}
+
+int cTVGuideSearchTimer::GetNumRecordings(void) {
+ int numRecordings = 0;
+ if (ID < 0)
+ return numRecordings;
+ for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
+ if (recording->IsEdited())
+ continue;
+ if (!recording->Info())
+ continue;
+ char* searchID = GetAuxValue(recording, "s-id");
+ if (!searchID) continue;
+ if (ID == atoi(searchID))
+ numRecordings++;
+ free(searchID);
+ }
+ return numRecordings;
+}
+
+void cTVGuideSearchTimer::GetSearchModes(std::vector<std::string> *searchModes) {
+ searchModes->push_back(tr("whole term must appear"));
+ searchModes->push_back(tr("all terms must exist"));
+ searchModes->push_back(tr("one term must exist"));
+ searchModes->push_back(tr("exact match"));
+ searchModes->push_back(tr("regular expression"));
+}
+
+
+void cTVGuideSearchTimer::Dump(void) {
+ esyslog("tvguide searchtimer: strTimer: %s", strTimer.c_str());
+ esyslog("tvguide searchtimer: ID: %d", ID);
+ esyslog("tvguide searchtimer: searchString: %s", searchString.c_str());
+ esyslog("tvguide searchtimer: useTime: %d", useTime);
+ esyslog("tvguide searchtimer: startTime: %d", startTime);
+ esyslog("tvguide searchtimer: stopTime: %d", stopTime);
+ esyslog("tvguide searchtimer: useChannel: %d", useChannel);
+ if (channelMin)
+ esyslog("tvguide searchtimer: channelMin: %s", channelMin->Name());
+ if (channelMax)
+ esyslog("tvguide searchtimer: channelMax: %s", channelMax->Name());
+ esyslog("tvguide searchtimer: channelGroup: %s", channelGroup.c_str());
+ esyslog("tvguide searchtimer: useCase: %d", useCase);
+ esyslog("tvguide searchtimer: mode: %d", mode);
+ esyslog("tvguide searchtimer: useTitle: %d", useTitle);
+ esyslog("tvguide searchtimer: useSubtitle: %d", useSubtitle);
+ esyslog("tvguide searchtimer: useDescription: %d", useDescription);
+}
+
diff --git a/searchtimer.h b/searchtimer.h new file mode 100644 index 0000000..f797ebd --- /dev/null +++ b/searchtimer.h @@ -0,0 +1,136 @@ +#ifndef __TVGUIDE_SEARCHTIMER_H
+#define __TVGUIDE_SEARCHTIMER_H
+
+
+class cTVGuideSearchTimer {
+private:
+ std::string strTimer;
+ int ID;
+ std::string searchString;
+ int useTime;
+ int startTime;
+ int stopTime;
+ int useChannel;
+ cChannel *channelMin;
+ cChannel *channelMax;
+ std::string channelGroup;
+ int useCase;
+ int mode;
+ int useTitle;
+ int useSubtitle;
+ int useDescription;
+ int useDuration;
+ int minDuration;
+ int maxDuration;
+ int useAsSearchTimer;
+ int useDayOfWeek;
+ int dayOfWeek;
+ int useEpisode;
+ std::string directory;
+ int priority;
+ int lifetime;
+ int marginStart;
+ int marginStop;
+ int useVPS;
+ int action;
+ int useExtEPGInfo;
+ std::string extEPGInfoValues;
+ int avoidRepeats;
+ int allowedRepeats;
+ int compareTitle;
+ int compareSubtitle;
+ int compareSummary;
+ unsigned long catvaluesAvoidRepeat;
+ int repeatsWithinDays;
+ int delAfterDays;
+ int recordingsKeep;
+ int switchMinsBefore;
+ int pauseOnNrRecordings;
+ int blacklistMode;
+ std::string blacklists;
+ int fuzzyTolerance;
+ int useInFavorites;
+ int menuTemplate;
+ int delMode;
+ int delAfterCountRecs;
+ int delAfterDaysOfFirstRec;
+ int useAsSearchTimerFrom;
+ int useAsSearchTimerTil;
+ int ignoreMissingEPGCats;
+ int unmuteSoundOnSwitch;
+ int compareSummaryMatchInPercent;
+ std::string contentsFilter;
+ int compareDate;
+public:
+ cTVGuideSearchTimer(void);
+ virtual ~cTVGuideSearchTimer(void);
+ bool operator < (const cTVGuideSearchTimer& other) const;
+ void SetEPGSearchString(std::string strTimer) { this->strTimer = strTimer; };
+ void SetTemplate(std::string tmpl);
+ bool Parse(bool readTemplate = false);
+ std::string BuildSearchString(void);
+ int GetID(void) { return ID; };
+ //GETTER
+ std::string SearchString(void) const { return searchString; };
+ bool Active(void);
+ bool UseTitle(void) { return useTitle; };
+ bool UseSubtitle(void) { return useSubtitle; };
+ bool UseDescription(void) { return useDescription; };
+ int SearchMode(void) { return mode; };
+ bool UseChannel(void) { return useChannel; };
+ int StartChannel(void) { return (channelMin)?channelMin->Number():0; };
+ int StopChannel(void) { return (channelMax)?channelMax->Number():0; };
+ bool UseTime(void) { return useTime; };
+ int StartTime(void) { return startTime; };
+ int StopTime(void) { return stopTime; };
+ bool UseDayOfWeek(void) { return useDayOfWeek; };
+ int DayOfWeek(void);
+ int UseEpisode(void) { return useEpisode; };
+ std::string Directory(void) { return directory; };
+ int Priority(void) { return priority; };
+ int Lifetime(void) { return lifetime; };
+ int MarginStart(void) { return marginStart; };
+ int MarginStop(void) { return marginStop; };
+ bool UseVPS(void) { return useVPS; };
+ bool AvoidRepeats(void) { return avoidRepeats; };
+ int AllowedRepeats(void) { return allowedRepeats; };
+ bool CompareTitle(void) { return compareTitle; };
+ bool CompareSubtitle(void) { return compareSubtitle; };
+ bool CompareSummary(void) { return compareSummary; };
+ bool UseInFavorites(void) { return useInFavorites; };
+ //SETTER
+ void SetSearchString(std::string searchString) { this->searchString = searchString; };
+ void SetActive(bool active) { useAsSearchTimer = active; };
+ void SetSearchMode(int searchMode) { mode = searchMode; };
+ void SetUseTitle(bool useTitle) { this->useTitle = useTitle; };
+ void SetUseSubtitle(bool useSubtitle) { this->useSubtitle = useSubtitle; };
+ void SetUseDesription(bool useDescription) { this->useDescription = useDescription; };
+ void SetUseChannel(bool useChannel) { this->useChannel = useChannel; };
+ void SetStartChannel(int startChannel) { channelMin = Channels.GetByNumber(startChannel); };
+ void SetStopChannel(int stopChannel) { channelMax = Channels.GetByNumber(stopChannel); };
+ void SetUseTime(bool useTime) { this->useTime = useTime; };
+ void SetStartTime(int startTime) { this->startTime = startTime; };
+ void SetStopTime(int stopTime) { this->stopTime = stopTime; };
+ void SetUseDayOfWeek(bool useDayOfWeek) { this->useDayOfWeek = useDayOfWeek; };
+ void SetDayOfWeek(int VDRDayOfWeek);
+ void SetUseEpisode(int useEpisode) { this->useEpisode = useEpisode; };
+ void SetDirectory(std::string directory) { this-> directory = directory; };
+ void SetPriority(int priority) { this->priority = priority; };
+ void SetLifetime(int lifetime) { this->lifetime = lifetime; };
+ void SetMarginStart(int marginStart) { this->marginStart = marginStart; };
+ void SetMarginStop(int marginStop) { this->marginStop = marginStop; };
+ void SetUseVPS(bool useVPS) { this->useVPS = useVPS; };
+ void SetAvoidRepeats(bool avoidRepeats) { this->avoidRepeats = avoidRepeats; };
+ void SetAllowedRepeats(int allowedRepeats) { this->allowedRepeats = allowedRepeats; };
+ void SetCompareTitle(bool compareTitle) { this->compareTitle = compareTitle; };
+ void SetCompareSubtitle(bool compareSubtitle) { this->compareSubtitle = compareSubtitle; };
+ void SetCompareSummary(bool compareSummary) { this->compareSummary = compareSummary; };
+ void SetUseInFavorites(bool useInFavorites) { this->useInFavorites = useInFavorites; };
+ //COMMON
+ int GetNumTimers(void);
+ int GetNumRecordings(void);
+ void GetSearchModes(std::vector<std::string> *searchModes);
+ void Dump(void);
+};
+
+#endif //__TVGUIDE_SEARCHTIMER_H
diff --git a/services/epgsearch.h b/services/epgsearch.h new file mode 100644 index 0000000..2669da4 --- /dev/null +++ b/services/epgsearch.h @@ -0,0 +1,202 @@ +/* -*- c++ -*- +Copyright (C) 2004-2013 Christian Wieninger + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + +The author can be reached at cwieninger@gmx.de + +The project's page is at http://winni.vdr-developer.org/epgsearch +*/ + +#ifndef EPGSEARCHSERVICES_INC +#define EPGSEARCHSERVICES_INC + +#include <string> +#include <list> +#include <memory> +#include <set> +#include <vdr/osdbase.h> + +// Data structure for service "Epgsearch-search-v1.0" +struct Epgsearch_search_v1_0 +{ +// in + char* query; // search term + int mode; // search mode (0=phrase, 1=and, 2=or, 3=regular expression) + int channelNr; // channel number to search in (0=any) + bool useTitle; // search in title + bool useSubTitle; // search in subtitle + bool useDescription; // search in description +// out + cOsdMenu* pResultMenu; // pointer to the menu of results +}; + +// Data structure for service "Epgsearch-exttimeredit-v1.0" +struct Epgsearch_exttimeredit_v1_0 +{ +// in + cTimer* timer; // pointer to the timer to edit + bool bNew; // flag that indicates, if this is a new timer or an existing one + const cEvent* event; // pointer to the event corresponding to this timer (may be NULL) +// out + cOsdMenu* pTimerMenu; // pointer to the menu of results +}; + +// Data structure for service "Epgsearch-enablesearchtimers-v1.0" +struct Epgsearch_enablesearchtimers_v1_0 +{ +// in + bool enable; // enable search timer thread? +}; + +// Data structure for service "Epgsearch-updatesearchtimers-v1.0" +struct Epgsearch_updatesearchtimers_v1_0 +{ +// in + bool showMessage; // inform via osd when finished? +}; + +// Data structure for service "Epgsearch-osdmessage-v1.0" +struct Epgsearch_osdmessage_v1_0 +{ +// in + char* message; // the message to display + eMessageType type; +}; + +// Data structure for service "EpgsearchMenu-v1.0" +struct EpgSearchMenu_v1_0 +{ +// in +// out + cOsdMenu* Menu; // pointer to the menu +}; + +// Data structure for service "Epgsearch-lastconflictinfo-v1.0" +struct Epgsearch_lastconflictinfo_v1_0 +{ +// in +// out + time_t nextConflict; // next conflict date, 0 if none + int relevantConflicts; // number of relevant conflicts + int totalConflicts; // total number of conflicts +}; + +// Data structure for service "Epgsearch-searchresults-v1.0" +struct Epgsearch_searchresults_v1_0 +{ +// in + char* query; // search term + int mode; // search mode (0=phrase, 1=and, 2=or, 3=regular expression) + int channelNr; // channel number to search in (0=any) + bool useTitle; // search in title + bool useSubTitle; // search in subtitle + bool useDescription; // search in description +// out + + class cServiceSearchResult : public cListObject + { + public: + const cEvent* event; + cServiceSearchResult(const cEvent* Event) : event(Event) {} + }; + + cList<cServiceSearchResult>* pResultList; // pointer to the results +}; + +// Data structure for service "Epgsearch-switchtimer-v1.0" +struct Epgsearch_switchtimer_v1_0 +{ +// in + const cEvent* event; + int mode; // mode (0=query existence, 1=add/modify, 2=delete) +// in/out + int switchMinsBefore; + int announceOnly; +// out + bool success; // result +}; + +// Data structures for service "Epgsearch-services-v1.0" +class cServiceHandler +{ + public: + virtual std::list<std::string> SearchTimerList() = 0; + // returns a list of search timer entries in the same format as used in epgsearch.conf + virtual int AddSearchTimer(const std::string&) = 0; + // adds a new search timer and returns its ID (-1 on error) + virtual bool ModSearchTimer(const std::string&) = 0; + // edits an existing search timer and returns success + virtual bool DelSearchTimer(int) = 0; + // deletes search timer with given ID and returns success + virtual std::list<std::string> QuerySearchTimer(int) = 0; + // returns the search result of the searchtimer with given ID in the same format as used in SVDRP command 'QRYS' (->MANUAL) + virtual std::list<std::string> QuerySearch(std::string) = 0; + // returns the search result of the searchtimer with given settings in the same format as used in SVDRP command 'QRYS' (->MANUAL) + virtual std::list<std::string> ExtEPGInfoList() = 0; + // returns a list of extended EPG categories in the same format as used in epgsearchcats.conf + virtual std::list<std::string> ChanGrpList() = 0; + // returns a list of channel groups maintained by epgsearch + virtual std::list<std::string> BlackList() = 0; + // returns a list of blacklists in the same format as used in epgsearchblacklists.conf + virtual std::set<std::string> DirectoryList() = 0; + // List of all recording directories used in recordings, timers, search timers or in epgsearchdirs.conf + virtual ~cServiceHandler() {} + // Read a setup value + virtual std::string ReadSetupValue(const std::string& entry) = 0; + // Write a setup value + virtual bool WriteSetupValue(const std::string& entry, const std::string& value) = 0; +}; + +struct Epgsearch_services_v1_0 +{ +// in/out + std::auto_ptr<cServiceHandler> handler; +}; + +// Data structures for service "Epgsearch-services-v1.1" +class cServiceHandler_v1_1 : public cServiceHandler +{ + public: + // Get timer conflicts + virtual std::list<std::string> TimerConflictList(bool relOnly=false) = 0; + // Check if a conflict check is advised + virtual bool IsConflictCheckAdvised() = 0; +}; + +struct Epgsearch_services_v1_1 +{ +// in/out + std::auto_ptr<cServiceHandler_v1_1> handler; +}; + +// Data structures for service "Epgsearch-services-v1.2" +class cServiceHandler_v1_2 : public cServiceHandler_v1_1 +{ + public: + // List of all recording directories used in recordings, timers (and optionally search timers or in epgsearchdirs.conf) + virtual std::set<std::string> ShortDirectoryList() = 0; + // Evaluate an expression against an event + virtual std::string Evaluate(const std::string& expr, const cEvent* event) = 0; +}; + +struct Epgsearch_services_v1_2 +{ +// in/out + std::auto_ptr<cServiceHandler_v1_2> handler; +}; + +#endif diff --git a/services/remotetimers.h b/services/remotetimers.h new file mode 100644 index 0000000..cd86b7a --- /dev/null +++ b/services/remotetimers.h @@ -0,0 +1,33 @@ +#ifndef REMOTETIMERSERVICES_INC +#define REMOTETIMERSERVICES_INC + +#include <vdr/epg.h> +#include <vdr/timers.h> + +// RemoteTimers services +struct RemoteTimers_Event_v1_0 { + //in + const cEvent *event; + //out + cTimer *timer; + cString errorMsg; +}; + +struct RemoteTimers_GetMatch_v1_0 { + //in + const cEvent *event; + //out + cTimer *timer; + int timerMatch; + int timerType; + bool isRemote; +}; + +struct RemoteTimers_Timer_v1_0 { + //in+out + cTimer *timer; + //out + cString errorMsg; +}; + +#endif //REMOTETIMERSERVICES_INC
\ No newline at end of file diff --git a/services/scraper2vdr.h b/services/scraper2vdr.h new file mode 100644 index 0000000..703d077 --- /dev/null +++ b/services/scraper2vdr.h @@ -0,0 +1,194 @@ +#ifndef __SCRAPER2VDRSERVICES_H
+#define __SCRAPER2VDRSERVICES_H
+
+#include <vdr/epg.h>
+#include <vdr/recording.h>
+
+enum tvType {
+ tSeries,
+ tMovie,
+ tNone,
+};
+
+/*********************************************************************
+* Helper Structures
+*********************************************************************/
+class cTvMedia {
+public:
+ cTvMedia(void) {
+ path = "";
+ width = height = 0;
+ };
+ std::string path;
+ int width;
+ int height;
+};
+
+class cEpisode {
+public:
+ cEpisode(void) {
+ number = 0;
+ season = 0;
+ name = "";
+ firstAired = "";
+ guestStars = "";
+ overview = "";
+ rating = 0.0;
+ };
+ int number;
+ int season;
+ std::string name;
+ std::string firstAired;
+ std::string guestStars;
+ std::string overview;
+ float rating;
+ cTvMedia episodeImage;
+};
+
+class cActor {
+public:
+ cActor(void) {
+ name = "";
+ role = "";
+ };
+ std::string name;
+ std::string role;
+ cTvMedia actorThumb;
+};
+
+/*********************************************************************
+* Data Structures for Service Calls
+*********************************************************************/
+
+// Data structure for service "GetEventType"
+class ScraperGetEventType {
+public:
+ ScraperGetEventType(void) {
+ event = NULL;
+ recording = NULL;
+ type = tNone;
+ movieId = 0;
+ seriesId = 0;
+ episodeId = 0;
+ };
+// in
+ const cEvent *event; // check type for this event
+ const cRecording *recording; // or for this recording
+//out
+ tvType type; //typeSeries or typeMovie
+ int movieId;
+ int seriesId;
+ int episodeId;
+};
+
+//Data structure for full series and episode information
+class cMovie {
+public:
+ cMovie(void) {
+ title = "";
+ originalTitle = "";
+ tagline = "";
+ overview = "";
+ adult = false;
+ collectionName = "";
+ budget = 0;
+ revenue = 0;
+ genres = "";
+ homepage = "";
+ releaseDate = "";
+ runtime = 0;
+ popularity = 0.0;
+ voteAverage = 0.0;
+ };
+//IN
+ int movieId; // movieId fetched from ScraperGetEventType
+//OUT
+ std::string title;
+ std::string originalTitle;
+ std::string tagline;
+ std::string overview;
+ bool adult;
+ std::string collectionName;
+ int budget;
+ int revenue;
+ std::string genres;
+ std::string homepage;
+ std::string releaseDate;
+ int runtime;
+ float popularity;
+ float voteAverage;
+ cTvMedia poster;
+ cTvMedia fanart;
+ cTvMedia collectionPoster;
+ cTvMedia collectionFanart;
+ std::vector<cActor> actors;
+};
+
+//Data structure for full series and episode information
+class cSeries {
+public:
+ cSeries(void) {
+ seriesId = 0;
+ episodeId = 0;
+ name = "";
+ overview = "";
+ firstAired = "";
+ network = "";
+ genre = "";
+ rating = 0.0;
+ status = "";
+ };
+//IN
+ int seriesId; // seriesId fetched from ScraperGetEventType
+ int episodeId; // episodeId fetched from ScraperGetEventType
+//OUT
+ std::string name;
+ std::string overview;
+ std::string firstAired;
+ std::string network;
+ std::string genre;
+ float rating;
+ std::string status;
+ cEpisode episode;
+ std::vector<cActor> actors;
+ std::vector<cTvMedia> posters;
+ std::vector<cTvMedia> banners;
+ std::vector<cTvMedia> fanarts;
+ cTvMedia seasonPoster;
+};
+
+// Data structure for service "GetPosterBanner"
+class ScraperGetPosterBanner {
+public:
+ ScraperGetPosterBanner(void) {
+ type = tNone;
+ };
+// in
+ const cEvent *event; // check type for this event
+//out
+ tvType type; //typeSeries or typeMovie
+ cTvMedia poster;
+ cTvMedia banner;
+};
+
+// Data structure for service "GetPoster"
+class ScraperGetPoster {
+public:
+// in
+ const cEvent *event; // check type for this event
+ const cRecording *recording; // or for this recording
+//out
+ cTvMedia poster;
+};
+
+// Data structure for service "GetPosterThumb"
+class ScraperGetPosterThumb {
+public:
+// in
+ const cEvent *event; // check type for this event
+ const cRecording *recording; // or for this recording
+//out
+ cTvMedia poster;
+};
+
+#endif //__SCRAPER2VDRSERVICES_H
\ No newline at end of file @@ -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; +} @@ -0,0 +1,30 @@ +#ifndef __TVGUIDE_SETUP_H +#define __TVGUIDE_SETUP_H + +#include <vdr/plugin.h> +#include "config.h" + +extern cTVGuideConfig config; + +class cTvGuideSetup : public cMenuSetupPage { + public: + cTvGuideSetup(void); + virtual ~cTvGuideSetup(); + private: + cTVGuideConfig tmpConfig; + const char * displayModeItems[2]; + const char * jumpMode[2]; + const char * numMode[2]; + const char * blueMode[3]; + char description1[256]; + char description2[256]; + char description3[256]; + char description4[256]; + const char * recFolderMode[3]; + char fixedFolder[256]; + const char *useSubtitleRerunTexts[3]; + void Setup(void); + virtual eOSState ProcessKey(eKeys Key); + virtual void Store(void); +}; +#endif //__TVGUIDE_SETUP_H diff --git a/switchtimer.c b/switchtimer.c new file mode 100644 index 0000000..f56c469 --- /dev/null +++ b/switchtimer.c @@ -0,0 +1,113 @@ +#include "switchtimer.h" + +cSwitchTimers SwitchTimers; + +// -- cSwitchTimer ----------------------------------------------------------------- +cSwitchTimer::cSwitchTimer(void) { + eventID = 0; + startTime = 0; + switchMinsBefore = 2; + announceOnly = 0; +} + +cSwitchTimer::cSwitchTimer(const cEvent* Event) { + eventID = 0; + startTime = 0; + if (Event) { + eventID = Event->EventID(); + channelID = Event->ChannelID(); + startTime = Event->StartTime(); + } +} + +bool cSwitchTimer::Parse(const char *s) { + char *line; + char *pos; + char *pos_next; + int parameter = 1; + int valuelen; +#define MAXVALUELEN (10 * MaxFileName) + + char value[MAXVALUELEN]; + startTime=0; + + pos = line = strdup(s); + pos_next = pos + strlen(pos); + if (*pos_next == '\n') *pos_next = 0; + while (*pos) { + while (*pos == ' ') + pos++; + if (*pos) { + if (*pos != ':') { + pos_next = strchr(pos, ':'); + if (!pos_next) + pos_next = pos + strlen(pos); + valuelen = pos_next - pos + 1; + if (valuelen > MAXVALUELEN) + valuelen = MAXVALUELEN; + strn0cpy(value, pos, valuelen); + pos = pos_next; + switch (parameter) { + case 1: + channelID = tChannelID::FromString(value); + break; + case 2: + eventID = atoi(value); + break; + case 3: + startTime = atol(value); + break; + default: + break; + } + } + parameter++; + } + if (*pos) + pos++; + } + free(line); + return (parameter >= 3) ? true : false; +} + +bool cSwitchTimers::EventInSwitchList(const cEvent* event) { + if (!event) return false; + cMutexLock SwitchTimersLock(this); + cSwitchTimer* switchTimer = SwitchTimers.First(); + while (switchTimer) { + if (switchTimer->eventID == event->EventID()) + return true; + switchTimer = SwitchTimers.Next(switchTimer); + } + return false; +} + +bool cSwitchTimers::ChannelInSwitchList(const cChannel* channel) { + if (!channel) return false; + cMutexLock SwitchTimersLock(this); + cSwitchTimer* switchTimer = SwitchTimers.First(); + while (switchTimer) { + if (switchTimer->channelID == channel->GetChannelID()) + return true; + switchTimer = SwitchTimers.Next(switchTimer); + } + return false; +} + +void cSwitchTimers::DeleteSwitchTimer(const cEvent *event) { + if (!event) return; + cMutexLock SwitchTimersLock(this); + cSwitchTimer* switchTimer = SwitchTimers.First(); + cSwitchTimer* delTimer = NULL; + + while (switchTimer) { + if (switchTimer->eventID == event->EventID()) { + delTimer = switchTimer; + break; + } + switchTimer = SwitchTimers.Next(switchTimer); + } + if (delTimer) { + SwitchTimers.Del(delTimer, true); + } +} diff --git a/switchtimer.h b/switchtimer.h new file mode 100644 index 0000000..09ebe88 --- /dev/null +++ b/switchtimer.h @@ -0,0 +1,31 @@ +#ifndef __TVGUIDE_SWITCHTIMER_H +#define __TVGUIDE_SWITCHTIMER_H + +#include <vdr/plugin.h> + +class cSwitchTimer : public cListObject { +public: + tEventID eventID; + time_t startTime; + tChannelID channelID; + int switchMinsBefore; + int announceOnly; + cSwitchTimer(void); + cSwitchTimer(const cEvent* Event); + bool Parse(const char *s); + void SetEventID(tEventID eventID) { this->eventID = eventID; }; + void SetStartTime(time_t startTime) { this->startTime = startTime; }; +}; + +class cSwitchTimers : public cConfig<cSwitchTimer>, public cMutex { +public: + cSwitchTimers(void) {} + ~cSwitchTimers(void) {} + bool EventInSwitchList(const cEvent* event); + bool ChannelInSwitchList(const cChannel* channel); + void DeleteSwitchTimer(const cEvent *event); +}; + +extern cSwitchTimers SwitchTimers; + +#endif //__TVGUIDE_SWITCHTIMER_H diff --git a/templates/plug-tvguideng-detail.xml b/templates/plug-tvguideng-detail.xml new file mode 100644 index 0000000..37d4af1 --- /dev/null +++ b/templates/plug-tvguideng-detail.xml @@ -0,0 +1,237 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE displayplugin SYSTEM "../../../dtd/displayplugin.dtd"> + +<displayplugin x="0" y="0" width="100%" height="100%" fadetime="0" scaletvx="0" scaletvy="0" scaletvwidth="100%" scaletvheight="100%"> + + <viewelement name="background"> + </viewelement> + + <!-- Available Variables scrollbar: + {height} height of scrollbar in tenth of a percent of total height + {offset} offset in tenth of a percent of total height + --> + <scrollbar> + </scrollbar> + + <!-- Available Variables Footer: + {red1} true if red button is button 1 + {red2} true if red button is button 2 + {red3} true if red button is button 3 + {red4} true if red button is button 4 + {green1} true if green button is button 1 + {green2} true if green button is button 2 + {green3} true if green button is button 3 + {green4} true if green button is button 4 + {yellow1} true if yellow button is button 1 + {yellow2} true if yellow button is button 2 + {yellow3} true if yellow button is button 3 + {yellow4} true if yellow button is button 4 + {blue1} true if blue button is button 1 + {blue2} true if blue button is button 2 + {blue3} true if blue button is button 3 + {blue4} true if blue button is button 4 + {red} label of red button + {green} label of green button + {yellow} label of yellow button + {blue} label of blue button + --> + <viewelement name="footer"> + </viewelement> + + <!-- Available Variables time: + {time} timestring in hh:mm + {sec} current seconds + {min} current minutes + {hour} current hours + {hmins} current "hourminutes" to display an hour hand + --> + <viewelement name="time"> + </viewelement> + + + <!-- Available Variables in detailheader elements: + {title} title of event + {shorttext} shorttext of event + {start} event start time in hh::mm + {stop} event stop time + {day} Day of event as three letter abrivation + {date} date of current event in dd.mm.yy + {daynumeric} day as number + {month} month as number + {year} year as number + {running} true if event is currently running + {elapsed} elapsed time of event, if not running 0 + {duration} duration of event + {durationhours} duration, full hours + {durationminutes} duration, rest of minutes + {vps} vps description string + {channelname} Channelname of event + {channelnumber} Channelnumber of event + {channellogoexists} true if a channel logo exists + {channelid} ChannelID as path to display channel logo + {ismovie} true if event is scraped as a movie + {isseries} true if event is scraped as a series + {posteravailable} true if a poster is available + {posterwidth} width of scraped poster + {posterheight} height of scraped poster + {posterpath} absolute path of scraped poster + {banneravailable} true if a banner is available + {bannerwidth} width of banner + {bannerheight} height of banner + {bannerpath} path of banner + {epgpicavailable} true if a epg picture is available + {epgpicpath} path of epg picture + --> + <viewelement name="header"> + </viewelement> + + <!-- Available Variables in tab elements: + {title} title of event + {shorttext} shorttext of event + {description} description of event + {start} event start time in hh::mm + {stop} event stop time + {day} Day of event as three letter abrivation + {date} date of current event in dd.mm.yy + {daynumeric} day as number + {month} month as number + {year} year as number + {running} true if event is currently running + {elapsed} elapsed time of event, if not running 0 + {duration} duration of event + {durationhours} duration, full hours + {durationminutes} duration, rest of minutes + {vps} vps description string + {channellogoexists} true if a channel logo exists + {channelid} ChannelID as path to display channel logo + {hasreruns} true if reruns of this event are found + {reruns[]} array with reruns + {reruns[title]} title of rerun + {reruns[shorttext]} shorttext of rerun + {reruns[date]} date of rerun in dd:mm + {reruns[day]} short dayname of rerun + {reruns[start]} start time of rerun in hh:mm + {reruns[stop]} stop time of rerun in hh:mm + {reruns[channelname]} name of channel on which rerun occurs + {reruns[channelnumber]} number of channel on which rerun occurs + {reruns[channelid]} id of channel on which rerun occurs to display channel logo + {reruns[channellogoexists]} true if channel logo exists + {epgpic1avaialble} true if first epg picture is available + {epgpic2avaialble} true if first epg picture is available + {epgpic3avaialble} true if first epg picture is available + {epgpic1path} path of first epg picture + {epgpic2path} path of second epg picture + {epgpic3path} path of third epg picture + + {ismovie} true if event is scraped as a movie + Available variables for movies: + {movietitle} movie title from themoviedb + {movieoriginalTitle} movie original title from themoviedb + {movietagline} movie tagline from themoviedb + {movieoverview} movie overview from themoviedb + {movieadult} true if movie is rated as adult + {moviebudget} movie budget from themoviedb in $ + {movierevenue} movie revenue from themoviedb in $ + {moviegenres} movie genres from themoviedb + {moviehomepage} movie homepage from themoviedb + {moviereleasedate} movie release date from themoviedb + {movieruntime} movie runtime from themoviedb + {moviepopularity} movie popularity from themoviedb + {movievoteaverage} movie vote average from themoviedb + {posterwidth} width of scraped poster + {posterheight} height of scraped poster + {posterpath} absolute path of scraped poster + {fanartwidth} width of scraped fanart + {fanartheight} height of scraped fanart + {fanartpath} absolute path of scraped fanart + {movieiscollection} true if movie is part of a collection + {moviecollectionName} name of movie collection + {collectionposterwidth} width of scraped collection poster + {collectionposterheight} height of scraped collection poster + {collectionposterpath} absolute path of scraped collection poster + {collectionfanartwidth} width of scraped collection fanart + {collectionfanartheight} height of scraped collection fanart + {collectionfanartpath} absolute path of scraped collection fanart + {actors[]} array with movie actors + {actors[name]} real name of actor + {actors[role]} actor role + {actors[thumb]} absolute path of scraped actor thumb + {actors[thumbwidth]} width of scraped actor thumb + {actors[thumbheight]} height of scraped actor thumb + + {isseries} true if event is scraped as a series + Available variables for series: + {seriesname} name of series + {seriesoverview} series overview + {seriesfirstaired} first aired date + {seriesnetwork} network which produces series + {seriesgenre} series genre + {seriesrating} series thetvdb rating + {seriesstatus} status of series (running / finished) + {episodetitle} title of episode + {episodenumber} number of episode + {episodeseason} season of episode + {episodefirstaired} first aired date of episode + {episodegueststars} guest stars of episode + {episodeoverview} episode overview + {episoderating} user rating for episode + {episodeimagewidth} episode image width + {episodeimageheight} episode image height + {episodeimagepath} episode image path + {seasonposterwidth} episode season poster width + {seasonposterheight} episode season poster height + {seasonposterpath} episode season poster path + {seriesposter1width} width of 1st poster + {seriesposter1height} height of 1st poster + {seriesposter1path} path of 1st poster + {seriesposter2width} width of 2nd poster + {seriesposter2height} height of 2nd poster + {seriesposter2path} path of 2nd poster + {seriesposter3width} width of 3rd poster + {seriesposter3height} height of 3rd poster + {seriesposter3path} path of 3rd poster + {seriesfanart1width} width of 1st fanart + {seriesfanart1height} height of 1st fanart + {seriesfanart1path} path of 1st fanart + {seriesfanart2width} width of 2nd fanart + {seriesfanart2height} height of 2nd fanart + {seriesfanart2path} path of 2nd fanart + {seriesfanart3width} width of 3rd fanart + {seriesfanart3height} height of 3rd fanart + {seriesfanart3path} path of 3rd fanart + {seriesbanner1width} width of 1st banner + {seriesbanner1height} height of 1st banner + {seriesbanner1path} path of 1st banner + {seriesbanner2width} width of 2nd banner + {seriesbanner2height} height of 2nd banner + {seriesbanner2path} path of 2nd banner + {seriesbanner3width} width of 3rd banner + {seriesbanner3height} height of 3rd banner + {seriesbanner3path} path of 3rd fanart + {actors[]} array with movie actors + {actors[name]} real name of actor + {actors[role]} actor role + {actors[thumb]} absolute path of scraped actor thumb + {actors[thumbwidth]} width of scraped actor thumb + {actors[thumbheight]} height of scraped actor thumb + --> + + <!-- a tab is one scrolling area, just position and draw as inside a normal area --> + <!-- just define as many tabs as needed --> + + <!-- TAB EPGINFO --> + <tab name="EPG Info" x="0" y="0" width="100%" height="100%" layer="1" scrollheight="{areaheight}/4"> + </tab> + + <!-- Available Variables tablabels: + {currenttab} name of currently active tab + {prevtab} name of prev tab + {nexttab} name of next tab + {tabs[]} array with available tab labels + {tabs[title]} title of tab + {tabs[current]} true if tab is displayed currently + --> + <tablabels> + </tablabels> + +</displayplugin> diff --git a/templates/plug-tvguideng-recmenu.xml b/templates/plug-tvguideng-recmenu.xml new file mode 100644 index 0000000..46222ec --- /dev/null +++ b/templates/plug-tvguideng-recmenu.xml @@ -0,0 +1,186 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE displayplugin SYSTEM "../../../dtd/displayplugin.dtd"> + +<displayplugin x="0" y="0" width="100%" height="100%" fadetime="0" scaletvx="0" scaletvy="0" scaletvwidth="100%" scaletvheight="100%"> + <!-- Available Variables Background: + {menuwidth} menuwidth in percent of screenwidth + {menuheight} menuheight in percent of screenheight + {hasscrollbar} true if menu needs a scrollbar + --> + <viewelement name="background"> + </viewelement> + + <!-- Available Variables Scrollbar: + {menuwidth} menuwidth in percent of screenwidth + {posy} y position of scrollbar start in percent of screenheight + {totalheight} height of complete scrollbar in percent of screenheight + {height} height in tenth of a percent of total height + {offset} offset in tenth of a percent + --> + <viewelement name="scrollbar"> + </viewelement> + + <grid name="recmenu" x="0" y="0" width="100%" height="100%"> + <!-- Background + {current} true if item is currently selected + --> + <!-- info item + {info} true + {lines} number of lines to display (max. 4) + {line1} text of line 1 + {line2} text of line 1 + {line3} text of line 1 + {line4} text of line 1 + --> + <!-- button + {button} true + {buttontext} text to display on button + --> + <!-- yes / no button + {buttonyesno} true + {yes} true if button is set to yes + {textyes} text to display on yes button + {textno} text to display on no button + --> + <!-- Int Selector + {intselector} true + {text} title of selector + {value} current value of selector, integer + --> + <!-- Bool Selector + {boolselector} true + {text} title of selector + {value} current value of selector, true or false + --> + <!-- String Selector + {stringselector} true + {text} title of selector + {value} current value of selector, string + --> + <!-- Text Input + {textinput} true + {editmode} true if currently in edit mode + {text} title of selector + {value} current value of selector, string + --> + <!-- Time Selector + {timeselector} true + {text} title of selector + {value} current value of selector, hh:mm + --> + <!-- Day Selector + {dayselector} true + {text} title of selector + {value} current value of selector, dd.mm + --> + <!-- Channel Selector + {channelselector} true + {text} title of selector + {channelnumber} number of currently selected channel, 0 for "all channels" + {channelname} name of channel or "all channels" + {channelid} id of channel + {channellogoexisis} true if channel logo exists + --> + <!-- Weekday Selector + {weekdayselector} true + {text} title of selector + {dayselected} number of currently selected day (0 - 6) + {day0abbr} ... {day6abbr} localized one character abbrevation for weekdays from Monday to Sunday + {day0set} ... {day6set} true if according weekday from Monday to Sunday is set + --> + <!-- Directory Selector + {directoryselector} true + {text} title of selector + {folder} current folder of selector, string + --> + <!-- Timerconflict Header + {timerconflictheader} true + {text} title of Timerconflict Header + {conflictstart} start of conflict in hh:mm + {conflictstop} end of conflict in hh:mm + {overlapstart} start of overlap in hh:mm + {overlapstop} end of overlap in hh:mm + {overlapstartpercent} start of overlap in percent of total conflict time width + {overlapwidthpercent} width of overlap in percent of total conflict time width + --> + <!-- Timerconflict + {timerconflict} true + {timertitle} title of timer + {channelname} name of channel + {channelid} channel ID + {transponder} transponder of channel + {starttime} start of timer in hh:mm + {stoptime} end of timer in hh:mm + {date} date of timer in dd.mm.yy + {weekday} weekday of timer, 3 letter abrivation + {infoactive} true if info icon is active + {deleteactive} true if delete icon is active + {editactive} true if edit icon is active + {searchactive} true if search icon is active + {timerstartpercent} start of timer in percent of total conflict time width + {timerwidthpercent} end of timer in percent of total conflict time width + {overlapstartpercent} start of overlap in percent of total conflict time width + {overlapwidthpercent} width of overlap in percent of total conflict time width + --> + <!-- Event + {event} true + {title} title of event + {shorttext} shorttext of event + {starttime} start of event in hh:mm + {stoptime} end of event in hh:mm + {date} date of event in dd.mm.yy + {weekday} weekday of event, 3 letter abrivation + {channelnumber} number of channel + {channelname} name of channel + {channelid} id of channel + {channellogoexisis} true if channel logo exists + {hastimer} true if event has a timer + --> + <!-- Recording + {recording} true + {recname} title of recording + {recstarttime} start of recording in hh:mm + {recdate} date of recording in dd.mm.yy + {recduration} duration of recording in min + {channelnumber} number of channel + {channelname} name of channel + {channelid} id of channel + {channellogoexisis} true if channel logo exists + --> + <!-- Searchtimer + {searchtimer} true + {timeractive} true if searchtimer is active + {searchstring} searchtimer search string + {activetimers} number of active timers caused by this searchtimer + {recordingsdone} number of recordings done by this searchtimer + {searchactive} true if search icon is active + {editactive} true if edit icon is active + {deleteactive} true if delete icon is active + --> + <!-- Timeline Header + {timelineheader} true + {date} date of current day in weekdayname dd.mm.yyyy + {timerset} true if timer info is set + {channelname} name of channel of timer + {channelid} channel ID of channel of timer + {channellogoexisis} true if channel logo exists + {channelnumber} number of channel of timer + {channeltransponder} transponder of channel of timer + {timerstart} start of timer in hh:mm + {timerstop} end of timer in hh:mm + {eventtitle} title of according event + {eventshorttext} short text of according event + {eventstart} start time of according event + {eventstop} end time of according event + --> + <!-- Timeline Timer + {timelinetimer} true + {timerstart} start of timer in tenth percent of complete 24h width + {timerwidth} width of timer in tenth percent of complete 24h width + --> + <!-- Favorites + {favorite} true + {favdesc} description of favorite + --> + </grid> +</displayplugin> diff --git a/templates/plug-tvguideng-root.xml b/templates/plug-tvguideng-root.xml new file mode 100644 index 0000000..9e2c329 --- /dev/null +++ b/templates/plug-tvguideng-root.xml @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE displayplugin SYSTEM "../../../dtd/displayplugin.dtd"> + +<displayplugin x="0" y="0" width="100%" height="100%" fadetime="0" scaletvx="0" scaletvy="0" scaletvwidth="100%" scaletvheight="100%"> + + <viewelement name="background_hor"> + </viewelement> + + <viewelement name="background_ver"> + </viewelement> + + <!-- Tokens available in Header + {isdummy} true if active element is a dummy element + {title} title of event of active grid + {shorttext} shorttext of event of active grid + {description} detailed description of event of active grid + {start} event start time in hh::mm + {stop} event stop time + {day} day of event of active grid + {date} date of event of active grid in dd.mm.yy + {daynumeric} day as number + {month} month as number + {year} year as number + {running} true if event is currently running + {elapsed} elapsed time of event, if not running 0 + {duration} duration of event + {durationhours} duration, full hours + {durationminutes} duration, rest of minutes + {channelname} Channel Name + {channelnumber} Channel Number + {channelid} ChannelID as path to display channel logo + {channellogoexists} true if channel logo exists + {hasposter} true if a scraped poster is available for this element + {posterwidth} width of scraped poster + {posterheight} height of scraped poster + {posterpath} absolute path of scraped poster + --> + <viewelement name="header"> + </viewelement> + + <!-- Available Variables Footer: + {red1} true if red button is button 1 + {red2} true if red button is button 2 + {red3} true if red button is button 3 + {red4} true if red button is button 4 + {green1} true if green button is button 1 + {green2} true if green button is button 2 + {green3} true if green button is button 3 + {green4} true if green button is button 4 + {yellow1} true if yellow button is button 1 + {yellow2} true if yellow button is button 2 + {yellow3} true if yellow button is button 3 + {yellow4} true if yellow button is button 4 + {blue1} true if blue button is button 1 + {blue2} true if blue button is button 2 + {blue3} true if blue button is button 3 + {blue4} true if blue button is button 4 + {red} label of red button + {green} label of green button + {yellow} label of yellow button + {blue} label of blue button + --> + <viewelement name="footer"> + </viewelement> + + <!-- Available Variables time: + {time} timestring in hh:mm + {sec} current seconds + {min} current minutes + {hour} current hours + {hmins} current "hourminutes" to display an hour hand + {day} day in digits + {dayleadingzero} day in digits with leading 0 + {dayname} Full name of the day + {daynameshort} Short 3 char name of the day + {month} month in digits with leading 0 + {monthname} Full name of the month + {monthnameshort} 3 letter abbrivation of month name + {year} year in yyyy + --> + <viewelement name="time"> + </viewelement> + + <!-- Tokens available in datetimeline + {weekday} weekday of current display + {date} date of current display + --> + <viewelement name="datetimeline_hor"> + </viewelement> + + <viewelement name="datetimeline_ver"> + </viewelement> + + <!-- Tokens available in timeindicator + {percenttotal} position of current time indicator in tenth of a percent of complete time shown + --> + <viewelement name="timeindicator_hor"> + </viewelement> + + <viewelement name="timeindicator_ver"> + </viewelement> + + <!-- Tokens available in timeline + {timestring} time of grid in hh:mm + --> + <grid name="timeline_hor" x="0" y="0" width="100%" height="100%"> + </grid> + + <grid name="timeline_ver" x="0" y="0" width="100%" height="100%"> + </grid> + + <!-- Tokens available in channels + {name} name of channel + {number} number of channel + {channelid} id of channel to display channel logo + {channellogoexists} true if channel logo exists + --> + <grid name="channels_hor" x="0" y="0" width="100%" height="100%"> + </grid> + + <grid name="channels_ver" x="0" y="0" width="100%" height="100%"> + </grid> + + <!-- Tokens available in channelgroups + {color} alternates grid by grid from true to false + {group} name of channel group + --> + <grid name="channelgroups_hor" x="0" y="0" width="100%" height="100%"> + </grid> + + <grid name="channelgroups_ver" x="0" y="0" width="100%" height="100%"> + </grid> + + <!-- Tokens available in schedules + {color} alternates grid by grid from true to false + {dummy} true if grid is a dummy grid + {timer} true if a timer is set for the according event + {switchtimer} true if a switchtimer is set for the according event + {title} title of grid + {shorttext} shorttext of grid + {start} start time in hh:mm + {stop} stop time in hh:dd + --> + <grid name="schedules_hor" x="0" y="0" width="100%" height="100%"> + </grid> + + <grid name="schedules_ver" x="0" y="0" width="100%" height="100%"> + </grid> + + <!-- Tokens available in channeljump + {channel} current user input for channel jump + --> + <viewelement name="channeljump"> + </viewelement> + +</displayplugin> diff --git a/timeline.c b/timeline.c new file mode 100644 index 0000000..143a6a6 --- /dev/null +++ b/timeline.c @@ -0,0 +1,153 @@ +#include "timeline.h" + +// --- cTimelineElement ------------------------------------------------------------- + +cTimelineElement::cTimelineElement(time_t elementTime) { + init = true; + this->elementTime = elementTime; +} + +cTimelineElement::~cTimelineElement() { + +} + +bool cTimelineElement::IsNew(void) { + if (init) { + init = false; + return true; + } + return init; +} + +bool cTimelineElement::IsFullHour(void) { + if (elementTime%3600 == 0) + return true; + return false; +} + +// --- cTimeline ------------------------------------------------------------- + +cTimeline::cTimeline(cViewGrid *timelineGrid, cViewElement *timelineDate, cViewElement *timeIndicator, cTimeManager *timeManager) { + this->timelineGrid = timelineGrid; + this->timelineDate = timelineDate; + this->timeIndicator = timeIndicator; + this->timeManager = timeManager; + steps = config.displayHours * 2; + stepDuration = 30 * 60; + weekday = ""; + timeIndicatorShown = true; +} + +cTimeline::~cTimeline(void) { + Clear(); + delete timelineDate; + delete timeIndicator; + delete timelineGrid; +} + +void cTimeline::Init(void) { + time_t startTime = timeManager->GetStart(); + time_t elementTime = startTime; + for (int i=0; i < steps; i++) { + cTimelineElement *e = new cTimelineElement(elementTime); + grids.Add(e); + elementTime += stepDuration; + } +} + +void cTimeline::Clear(void) { + timelineGrid->Clear(); + grids.Clear(); +} + +void cTimeline::ScrollForward(int stepMinutes) { + int numSteps = stepMinutes / 30; + for (int i=0; i<numSteps; i++) { + cTimelineElement *e = grids.First(); + timelineGrid->Delete(e->Id()); + grids.Del(e); + } + time_t newStartTime = timeManager->GetEnd() - stepMinutes * 60; + for (int i=0; i<numSteps; i++) { + cTimelineElement *e = new cTimelineElement(newStartTime); + grids.Add(e); + newStartTime += stepDuration; + } +} + +void cTimeline::ScrollBack(int stepMinutes) { + time_t startTime = timeManager->GetStart(); + time_t endTime = timeManager->GetEnd(); + int numSteps = stepMinutes / 30; + for (int i=0; i<numSteps; i++) { + cTimelineElement *e = grids.Last(); + timelineGrid->Delete(e->Id()); + grids.Del(e); + } + time_t newStartTime = timeManager->GetStart() + (numSteps-1) * 30 * 60; + for (int i=0; i<numSteps; i++) { + cTimelineElement *e = new cTimelineElement(newStartTime); + grids.Ins(e); + newStartTime -= stepDuration; + } +} + +void cTimeline::Draw(void) { + DrawDate(); + DrawTimeIndicator(); + double x, y, width, height; + if (config.displayMode == eHorizontal) { + x = 0.0; + y = 0.0; + width = (double)1 / (double)steps; + height = 1.0; + } else { + x = 0.0; + y = 0.0; + width = 1.0; + height = (double)1 / (double)steps; + } + for (cTimelineElement *e = grids.First(); e; e = grids.Next(e)) { + if (e->IsNew()) { + timelineGrid->ClearTokens(); + timelineGrid->AddIntToken("fullhour", e->IsFullHour()); + timelineGrid->AddStringToken("timestring", e->ToString()); + timelineGrid->SetGrid(e->Id(), x, y, width, height); + } else { + timelineGrid->MoveGrid(e->Id(), x, y, width, height); + } + if (config.displayMode == eHorizontal) + x += width; + else + y += height; + } + timelineGrid->Display(); +} + +void cTimeline::DrawDate(void) { + string weekdayNew = *(timeManager->GetWeekday()); + if (!weekdayNew.compare(weekday)) + return; + weekday = weekdayNew; + timelineDate->Clear(); + timelineDate->ClearTokens(); + string date = *(timeManager->GetDate()); + timelineDate->AddStringToken("weekday", weekday); + timelineDate->AddStringToken("date", date); + timelineDate->Display(); +} + +void cTimeline::DrawTimeIndicator(void) { + if (timeManager->NowVisible()) { + timeIndicatorShown = true; + int distance = (timeManager->GetNow() - timeManager->GetStart()) / 60; + int percentTotal = distance*1000/(config.displayHours*60); + timeIndicator->Clear(); + timeIndicator->ClearTokens(); + timeIndicator->AddIntToken("percenttotal", percentTotal); + timeIndicator->Display(); + } else if (timeIndicatorShown) { + timeIndicatorShown = false; + timeIndicator->Clear(); + } +} diff --git a/timeline.h b/timeline.h new file mode 100644 index 0000000..c6914d2 --- /dev/null +++ b/timeline.h @@ -0,0 +1,50 @@ +#ifndef __TVGUIDE_TIMELINE_H +#define __TVGUIDE_TIMELINE_H + +#include <set> +#include <vdr/tools.h> +#include "libskindesigner/osdelements.h" +#include "config.h" +#include "timemanager.h" + +// --- cTimelineElement ------------------------------------------------------------- + +class cTimelineElement : public cListObject { +private: + bool init; + time_t elementTime; +public: + cTimelineElement(time_t elementTime); + virtual ~cTimelineElement(void); + long Id(void) { return elementTime; }; + bool IsNew(void); + bool IsFullHour(void); + string ToString(void) { return *TimeString(elementTime); }; +}; + +// --- cTimeline ------------------------------------------------------------- + +class cTimeline { +private: + cViewGrid *timelineGrid; + cViewElement *timelineDate; + cViewElement *timeIndicator; + cTimeManager *timeManager; + cList<cTimelineElement> grids; + int steps; + int stepDuration; + string weekday; + bool timeIndicatorShown; + void DrawDate(void); + void DrawTimeIndicator(void); +public: + cTimeline(cViewGrid *timelineGrid, cViewElement *timelineDate, cViewElement *timeIndicator, cTimeManager *timeManager); + virtual ~cTimeline(void); + void Init(void); + void Clear(void); + void ScrollForward(int stepMinutes); + void ScrollBack(int stepMinutes); + void Draw(void); +}; + +#endif //__TVGUIDE_TIMELINE_H diff --git a/timemanager.c b/timemanager.c new file mode 100644 index 0000000..51c02be --- /dev/null +++ b/timemanager.c @@ -0,0 +1,172 @@ +#include <time.h>
+#include <vdr/tools.h>
+#include "timemanager.h"
+
+cTimeManager::cTimeManager(void) {
+ displayHours = 4;
+ displaySeconds = displayHours * 3600;
+ timeFormat = e24Hours;
+}
+
+cTimeManager::~cTimeManager(void) {
+}
+
+cString cTimeManager::PrintTime(time_t displayTime) {
+ struct tm *ts;
+ ts = localtime(&displayTime);
+ cString strTime = cString::sprintf("%d.%d-%d:%d.%d", ts->tm_mday, ts->tm_mon+1, ts->tm_hour, ts->tm_min, ts->tm_sec);
+ return strTime;
+}
+
+
+void cTimeManager::Now() {
+ t = time(0);
+ tStart = t;
+ tStart = GetRounded();
+ tEnd = tStart + displaySeconds;
+}
+
+void cTimeManager::AddMinutes(int step) {
+ tStart += step*60;
+ tEnd += step*60;
+}
+
+bool cTimeManager::DelMinutes(int step) {
+ if ((tStart - step*60)+30*60 < t) {
+ return true;
+ }
+ tStart -= step*60;
+ tEnd -= step*60;
+ return false;
+}
+
+void cTimeManager::SetTime(time_t newTime) {
+ tStart = newTime;
+ tEnd = tStart + displaySeconds;
+}
+
+time_t cTimeManager::GetPrevPrimetime(time_t current) {
+ tm *st = localtime(¤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 <vdr/tools.h>
+
+enum eTimeFormat {
+ e12Hours,
+ e24Hours
+};
+
+// --- cTimeManager -------------------------------------------------------------
+
+class cTimeManager {
+ private:
+ time_t t;
+ time_t tStart;
+ time_t tEnd;
+ int displayHours;
+ int displaySeconds;
+ eTimeFormat timeFormat;
+ public:
+ cTimeManager(void);
+ virtual ~cTimeManager(void);
+ static cString PrintTime(time_t displayTime);
+ void Now();
+ time_t GetNow() { return t; };
+ void AddMinutes(int step);
+ bool DelMinutes(int step);
+ void SetTime(time_t newTime);
+ time_t Get() {return t;};
+ time_t GetStart() {return tStart;};
+ time_t GetEnd() {return tEnd;};
+ cString GetCurrentTime();
+ cString GetDate();
+ cString GetWeekday();
+ time_t GetPrevPrimetime(time_t current);
+ time_t GetNextPrimetime(time_t current);
+ bool TooFarInPast(time_t current);
+ bool IsStart(int activeStart);
+ time_t GetRounded();
+ bool NowVisible(void);
+ int GetDisplaySeconds(void) {return displaySeconds; };
+ void Debug();
+};
+
+// --- cTimeInterval -------------------------------------------------------------
+
+class cTimeInterval {
+ private:
+ time_t start;
+ time_t stop;
+ public:
+ cTimeInterval(time_t start, time_t stop);
+ virtual ~cTimeInterval(void);
+ time_t Start(void) { return start; };
+ time_t Stop(void) { return stop; };
+ cTimeInterval *Intersect(cTimeInterval *interval);
+ cTimeInterval *Union(cTimeInterval *interval);
+};
+
+#endif //__TVGUIDE_TIMEMANAGER_H
\ No newline at end of file diff --git a/timerconflict.c b/timerconflict.c new file mode 100644 index 0000000..9f01427 --- /dev/null +++ b/timerconflict.c @@ -0,0 +1,177 @@ +#include <string>
+#include <vector>
+#include <vdr/timers.h>
+#include "helpers.h"
+#include "timemanager.h"
+#include "timerconflict.h"
+
+cTVGuideTimerConflict::cTVGuideTimerConflict(void) {
+ time = 0;
+ timeStart = 0;
+ timeStop = 0;
+ overlapStart = 0;
+ overlapStop = 0;
+ percentPossible = 0;
+ timerID = 0;
+}
+
+cTVGuideTimerConflict::~cTVGuideTimerConflict(void) {
+
+}
+
+bool cTVGuideTimerConflict::timerInvolved(int involvedID) {
+ int numConflicts = timerIDs.size();
+ for (int i=0; i<numConflicts; i++) {
+ if (timerIDs[i] == involvedID)
+ return true;
+ }
+ return false;
+}
+
+// --- cTVGuideTimerConflicts------------------------------------
+
+cTVGuideTimerConflicts::cTVGuideTimerConflicts(void) {
+ numConflicts = 0;
+ currentConflict = -1;
+}
+
+cTVGuideTimerConflicts::~cTVGuideTimerConflicts(void) {
+ for(std::vector<cTVGuideTimerConflict*>::const_iterator it = conflicts.begin(); it != conflicts.end(); it++) {
+ cTVGuideTimerConflict *conf = *it;
+ delete conf;
+ }
+ conflicts.clear();
+}
+
+void cTVGuideTimerConflicts::AddConflict(std::string epgSearchConflictLine) {
+ /* TIMERCONFLICT FORMAT:
+ The result list looks like this for example when we have 2 timer conflicts at one time:
+ 1190232780:152|30|50#152#45:45|10|50#152#45
+ '1190232780' is the time of the conflict in seconds since 1970-01-01.
+ It's followed by list of timers that have a conflict at this time:
+ '152|30|50#1 int editTimer(cTimer *timer, bool active, int prio, int start, int stop);
+ 52#45' is the description of the first conflicting timer. Here:
+ '152' is VDR's timer id of this timer as returned from VDR's LSTT command
+ '30' is the percentage of recording that would be done (0...100)
+ '50#152#45' is the list of concurrent timers at this conflict
+ '45|10|50#152#45' describes the next conflict
+ */
+ cTVGuideTimerConflict *conflict = new cTVGuideTimerConflict();
+ splitstring s(epgSearchConflictLine.c_str());
+ std::vector<std::string> flds = s.split(':');
+ if (flds.size() < 2)
+ return;
+ conflict->time = atoi(flds[0].c_str());
+ splitstring s2(flds[1].c_str());
+ std::vector<std::string> flds2 = s2.split('|');
+ if (flds2.size() < 3)
+ return;
+ conflict->timerID = atoi(flds2[0].c_str());
+ conflict->percentPossible = atoi(flds2[1].c_str());
+ splitstring s3(flds2[2].c_str());
+ std::vector<std::string> flds3 = s3.split('#');
+ std::vector<int> timerIDs;
+ for (int k = 0; k < flds3.size(); k++) {
+ timerIDs.push_back(atoi(flds3[k].c_str()) - 1);
+ }
+ conflict->timerIDs = timerIDs;
+ conflicts.push_back(conflict);
+}
+
+void cTVGuideTimerConflicts::CalculateConflicts(void) {
+ numConflicts = conflicts.size();
+ time_t startTime = 0;
+ time_t endTime = 0;
+ for (int i=0; i < numConflicts; i++) {
+ cTimeInterval *unionSet = NULL;
+ int numTimers = conflicts[i]->timerIDs.size();
+ for (int j=0; j < numTimers; j++) {
+ const cTimer *timer = Timers.Get(conflicts[i]->timerIDs[j]);
+ if (timer) {
+ if (!unionSet) {
+ unionSet = new cTimeInterval(timer->StartTime(), timer->StopTime());
+ } else {
+ cTimeInterval *timerInterval = new cTimeInterval(timer->StartTime(), timer->StopTime());
+ cTimeInterval *newUnion = unionSet->Union(timerInterval);
+ delete unionSet;
+ delete timerInterval;
+ unionSet = newUnion;
+ }
+ }
+ }
+ conflicts[i]->timeStart = unionSet->Start();
+ conflicts[i]->timeStop = unionSet->Stop();
+ delete unionSet;
+
+ cTimeInterval *intersect = NULL;
+ for (int j=0; j < numTimers; j++) {
+ const cTimer *timer = Timers.Get(conflicts[i]->timerIDs[j]);
+ if (timer) {
+ if (!intersect) {
+ intersect = new cTimeInterval(timer->StartTime(), timer->StopTime());
+ } else {
+ cTimeInterval *timerInterval = new cTimeInterval(timer->StartTime(), timer->StopTime());
+ cTimeInterval *newIntersect = intersect->Intersect(timerInterval);
+ if (newIntersect) {
+ delete intersect;
+ intersect = newIntersect;
+ }
+ delete timerInterval;
+ }
+ }
+ }
+ conflicts[i]->overlapStart = intersect->Start();
+ conflicts[i]->overlapStop = intersect->Stop();
+ delete intersect;
+ }
+}
+
+cTVGuideTimerConflict *cTVGuideTimerConflicts::GetCurrentConflict(void) {
+ if (currentConflict < 0)
+ return NULL;
+ if (currentConflict > (numConflicts-1))
+ return NULL;
+ return conflicts[currentConflict];
+}
+
+int cTVGuideTimerConflicts::GetCurrentConflictTimerID(int timerIndex) {
+ if (currentConflict < 0)
+ return -1;
+ if (currentConflict > (numConflicts-1))
+ return -1;
+ int numTimersInConflict = conflicts[currentConflict]->timerIDs.size();
+ if (timerIndex > (numTimersInConflict - 1))
+ return -1;
+ return conflicts[currentConflict]->timerIDs[timerIndex];
+}
+
+int cTVGuideTimerConflicts::GetCorrespondingConflict(int timerID) {
+ int conflictIndex = -1;
+ if (numConflicts > 0) {
+ for (int i=0; i<numConflicts; i++) {
+ if (conflicts[i]->timerInvolved(timerID)) {
+ conflictIndex = i;
+ break;
+ }
+ }
+ }
+ return conflictIndex;
+}
+
+cTVGuideTimerConflict *cTVGuideTimerConflicts::GetConflict(int conflictIndex) {
+ if (conflictIndex < 0)
+ return NULL;
+ if (conflictIndex > (numConflicts-1))
+ return NULL;
+ return conflicts[conflictIndex];
+}
+
+std::vector<cTVGuideTimerConflict*> cTVGuideTimerConflicts::GetConflictsBetween(time_t start, time_t stop) {
+ std::vector<cTVGuideTimerConflict*> conflictsFound;
+ for (int i=0; i < numConflicts; i++) {
+ if ((conflicts[i]->timeStart > start) && (conflicts[i]->timeStart < stop)||
+ (conflicts[i]->timeStop > start) && (conflicts[i]->timeStop < stop))
+ conflictsFound.push_back(conflicts[i]);
+ }
+ return conflictsFound;
+}
\ No newline at end of file diff --git a/timerconflict.h b/timerconflict.h new file mode 100644 index 0000000..d3def89 --- /dev/null +++ b/timerconflict.h @@ -0,0 +1,38 @@ +#ifndef __TVGUIDE_TIMERCONFLICT_H
+#define __TVGUIDE_TIMERCONFLICT_H
+
+class cTVGuideTimerConflict {
+public:
+ cTVGuideTimerConflict(void);
+ virtual ~cTVGuideTimerConflict(void);
+ time_t time;
+ time_t timeStart;
+ time_t timeStop;
+ time_t overlapStart;
+ time_t overlapStop;
+ int percentPossible;
+ int timerID;
+ std::vector<int> timerIDs;
+ bool timerInvolved(int involvedID);
+};
+
+class cTVGuideTimerConflicts {
+private:
+ std::vector<cTVGuideTimerConflict*> conflicts;
+ int numConflicts;
+ int currentConflict;
+public:
+ cTVGuideTimerConflicts(void);
+ virtual ~cTVGuideTimerConflicts(void);
+ void AddConflict(std::string epgSearchConflictLine);
+ void CalculateConflicts(void);
+ int NumConflicts(void) {return numConflicts; };
+ void SetCurrentConflict(int current) { currentConflict = current; };
+ cTVGuideTimerConflict *GetCurrentConflict(void);
+ int GetCurrentConflictTimerID(int timerIndex);
+ int GetCorrespondingConflict(int timerID);
+ cTVGuideTimerConflict *GetConflict(int conflictIndex);
+ std::vector<cTVGuideTimerConflict*> GetConflictsBetween(time_t start, time_t stop);
+};
+
+#endif //__TVGUIDE_RECMMANAGER_H
\ No newline at end of file diff --git a/tvguideng.c b/tvguideng.c new file mode 100644 index 0000000..8f9b5ba --- /dev/null +++ b/tvguideng.c @@ -0,0 +1,156 @@ +/* + * tvguideng.c: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + * $Id$ + */ + +#include <vdr/plugin.h> +#include "libskindesigner/services.h" +#define DEFINE_CONFIG 1 +#include "config.h" +#include "setup.h" +#include "tvguidengosd.h" + +static const char *VERSION = "0.0.1"; +static const char *DESCRIPTION = "TV Guide for Skindesigner Skins"; +static const char *MAINMENUENTRY = "TV Guide NG"; + +class cPluginTvguideng : public cPlugin { +private: + // Add any member variables or functions you may need here. +public: + cPluginTvguideng(void); + virtual ~cPluginTvguideng(); + virtual const char *Version(void) { return VERSION; } + virtual const char *Description(void) { return DESCRIPTION; } + virtual const char *CommandLineHelp(void); + virtual bool ProcessArgs(int argc, char *argv[]); + virtual bool Initialize(void); + virtual bool Start(void); + virtual void Stop(void); + virtual void Housekeeping(void); + virtual void MainThreadHook(void); + virtual cString Active(void); + virtual time_t WakeupTime(void); + virtual const char *MainMenuEntry(void) { return (config.showMainMenuEntry)?MAINMENUENTRY:NULL; } + virtual cOsdObject *MainMenuAction(void); + virtual cMenuSetupPage *SetupMenu(void); + virtual bool SetupParse(const char *Name, const char *Value); + virtual bool Service(const char *Id, void *Data = NULL); + virtual const char **SVDRPHelpPages(void); + virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode); + }; + +cPluginTvguideng::cPluginTvguideng(void) { +} + +cPluginTvguideng::~cPluginTvguideng() { +} + +const char *cPluginTvguideng::CommandLineHelp(void) { + return NULL; +} + +bool cPluginTvguideng::ProcessArgs(int argc, char *argv[]) { + return true; +} + +bool cPluginTvguideng::Initialize(void) { + return true; +} + +bool cPluginTvguideng::Start(void) { + RegisterPlugin reg; + reg.name = "tvguideng"; + reg.SetView(viRootView, "root.xml"); + reg.SetViewElement(viRootView, verBackgroundHor, "background_hor"); + reg.SetViewElement(viRootView, verBackgroundVer, "background_ver"); + reg.SetViewElement(viRootView, verHeader, "header"); + reg.SetViewElement(viRootView, verTime, "time"); + reg.SetViewElement(viRootView, verFooter, "footer"); + reg.SetViewElement(viRootView, verDateTimelineHor, "datetimeline_hor"); + reg.SetViewElement(viRootView, verDateTimelineVer, "datetimeline_ver"); + reg.SetViewElement(viRootView, verTimeIndicatorHor, "timeindicator_hor"); + reg.SetViewElement(viRootView, verTimeIndicatorVer, "timeindicator_ver"); + reg.SetViewElement(viRootView, verChannelJump, "channeljump"); + reg.SetViewGrid(viRootView, vgChannelsHor, "channels_hor"); + reg.SetViewGrid(viRootView, vgChannelsVer, "channels_ver"); + reg.SetViewGrid(viRootView, vgSchedulesHor, "schedules_hor"); + reg.SetViewGrid(viRootView, vgSchedulesVer, "schedules_ver"); + reg.SetViewGrid(viRootView, vgTimelineHor, "timeline_hor"); + reg.SetViewGrid(viRootView, vgTimelineVer, "timeline_ver"); + reg.SetViewGrid(viRootView, vgChannelGroupsHor, "channelgroups_hor"); + reg.SetViewGrid(viRootView, vgChannelGroupsVer, "channelgroups_ver"); + //Detail View + reg.SetSubView(viRootView, viDetailView, "detail.xml"); + reg.SetViewElement(viDetailView, vedBackground, "background"); + reg.SetViewElement(viDetailView, vedHeader, "header"); + reg.SetViewElement(viDetailView, vedFooter, "footer"); + reg.SetViewElement(viDetailView, vedTime, "time"); + //Search & Recording Menus + reg.SetSubView(viRootView, viRecMenu, "recmenu.xml"); + reg.SetViewElement(viRecMenu, vemBackground, "background"); + reg.SetViewElement(viRecMenu, vemScrollbar, "scrollbar"); + reg.SetViewGrid(viRecMenu, vgRecordingMenu, "recmenu"); + static cPlugin *pSkinDesigner = cPluginManager::GetPlugin("skindesigner"); + if (pSkinDesigner) { + pSkinDesigner->Service("RegisterPlugin", ®); + } else { + esyslog("tvguideng: skindesigner not available"); + } + + return true; +} + +void cPluginTvguideng::Stop(void) { +} + +void cPluginTvguideng::Housekeeping(void) { +} + +void cPluginTvguideng::MainThreadHook(void) { +} + +cString cPluginTvguideng::Active(void) { + return NULL; +} + +time_t cPluginTvguideng::WakeupTime(void) { + return 0; +} + +cOsdObject *cPluginTvguideng::MainMenuAction(void) { + return new cTVGuideOSD(); +} + +cMenuSetupPage *cPluginTvguideng::SetupMenu(void) { + return new cTvGuideSetup(); +} + +bool cPluginTvguideng::SetupParse(const char *Name, const char *Value) { + return config.SetupParse(Name, Value); +} + +bool cPluginTvguideng::Service(const char *Id, void *Data) { + if (strcmp(Id, "MainMenuHooksPatch-v1.0::osSchedule") == 0 && config.replaceOriginalSchedule != 0) { + if (Data == NULL) + return true; + cOsdObject **guide = (cOsdObject**) Data; + if (guide) + *guide = MainMenuAction(); + return true; + } + return false; +} + +const char **cPluginTvguideng::SVDRPHelpPages(void) { + return NULL; +} + +cString cPluginTvguideng::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode) { + return NULL; +} + +VDRPLUGINCREATOR(cPluginTvguideng); // Don't touch this! diff --git a/tvguidengosd.c b/tvguidengosd.c new file mode 100644 index 0000000..c49f816 --- /dev/null +++ b/tvguidengosd.c @@ -0,0 +1,475 @@ +#include "tvguidengosd.h" + +cTVGuideOSD::cTVGuideOSD(void) { + timeManager = NULL; + epgGrid = NULL; + channelJumper = NULL; + detailView = NULL; + recMenuView = NULL; +} + +cTVGuideOSD::~cTVGuideOSD(void) { + if (recMenuView && recMenuView->Active()) { + recMenuView->Close(); + } + if (timeManager) + delete timeManager; + if (epgGrid) + delete epgGrid; + if (channelJumper) + delete channelJumper; + if (detailView) + delete detailView; + if (recMenuView) + delete recMenuView; +} + +void cTVGuideOSD::Show(void) { + bool skinDesignerAvailable = InitSkindesignerInterface("tvguideng"); + if (!skinDesignerAvailable) { + return; + esyslog("tvguideng: skindesigner not available"); + } + + cOsdView *rootView = GetOsdView(viRootView); + if (!rootView) { + esyslog("tvguideng: used skindesigner skin does not support tvguideng"); + return; + } + SwitchTimers.Load(AddDirectory(cPlugin::ConfigDirectory("epgsearch"), "epgsearchswitchtimers.conf")); + recMenuView = new cRecMenuView(); + pRemoteTimers = cPluginManager::CallFirstService("RemoteTimers::RefreshTimers-v1.0", NULL); + if (pRemoteTimers) { + isyslog("tvguideng: remotetimers-plugin is available"); + } + if (config.useRemoteTimers && pRemoteTimers) { + cString errorMsg; + if (!pRemoteTimers->Service("RemoteTimers::RefreshTimers-v1.0", &errorMsg)) { + esyslog("tvguideng: %s", *errorMsg); + } + } + + timeManager = new cTimeManager(); + timeManager->Now(); + + epgGrid = new cEpgGrid(rootView, timeManager); + + cChannel *startChannel = Channels.GetByNumber(cDevice::CurrentChannel()); + + epgGrid->Init(startChannel); + epgGrid->DrawHeader(); + epgGrid->DrawTime(); + epgGrid->DrawFooter(); + epgGrid->DrawChannelHeaders(); + epgGrid->DrawTimeline(); + epgGrid->DrawChannelgroups(); + epgGrid->DrawGrid(); + epgGrid->Flush(); +} + +eOSState cTVGuideOSD::ProcessKey(eKeys Key) { + if (!epgGrid) { + return osEnd; + } + + eOSState state = osContinue; + + if (recMenuView->Active() && !detailView) { + state = recMenuView->ProcessKey(Key); + if (state == osEnd) { + epgGrid->SetTimers(); + epgGrid->Activate(); + epgGrid->DrawGrid(); + epgGrid->Flush(); + } else if (state == osUser1) { + DetailView(recMenuView->GetEvent()); + } + state = osContinue; + if (epgGrid->DrawTime()) + epgGrid->Flush(); + } else if (detailView) { + //Detail View Key Handling + switch (Key & ~k_Repeat) { + case kBack: + CloseDetailedView(); + break; + case kUp: + detailView->Up(); + detailView->Flush(); + break; + case kDown: + detailView->Down(); + detailView->Flush(); + break; + case kLeft: + detailView->Left(); + detailView->Flush(); + break; + case kRight: + detailView->Right(); + detailView->Flush(); + break; + case kOk: + CloseDetailedView(); + break; + case kBlue: + state = ChannelSwitch(); + break; + default: + break; + } + if (detailView && state != osEnd && detailView->DrawTime()) { + detailView->Flush(); + } + } else { + + //EPG Grid is active + switch (Key & ~k_Repeat) { + case kBack: + state=osEnd; + break; + case kUp: + KeyUp(); + break; + case kDown: + KeyDown(); + break; + case kLeft: + KeyLeft(); + break; + case kRight: + KeyRight(); + break; + case kOk: + state = KeyOk(epgGrid->GetCurrentEvent()); + break; + case k0 ... k9: + NumericKey(Key - k0); + break; + case kRed: + KeyRed(); + break; + case kGreen: + KeyGreen(); + break; + case kYellow: + KeyYellow(); + break; + case kBlue: + state = KeyBlue(epgGrid->GetCurrentEvent()); + break; + case kNone: + if (channelJumper) CheckTimeout(); + break; + default: + break; + } + if (state != osEnd && epgGrid->DrawTime()) + epgGrid->Flush(); + } + return state; +} + +void cTVGuideOSD::KeyLeft(void) { + if (config.displayMode == eHorizontal) { + TimeBack(); + } else if (config.displayMode == eVertical) { + ChannelsBack(); + } +} + +void cTVGuideOSD::KeyRight(void) { + if (config.displayMode == eHorizontal) { + TimeForward(); + } else if (config.displayMode == eVertical) { + ChannelsForward(); + } +} + +void cTVGuideOSD::KeyUp(void) { + if (config.displayMode == eHorizontal) { + ChannelsBack(); + } else if (config.displayMode == eVertical) { + TimeBack(); + } +} + +void cTVGuideOSD::KeyDown(void) { + if (config.displayMode == eHorizontal) { + ChannelsForward(); + } else if (config.displayMode == eVertical) { + TimeForward(); + } +} + +void cTVGuideOSD::TimeForward(void) { + bool scrolled = epgGrid->TimeForward(); + if (!scrolled) { + epgGrid->UpdateActive(); + } else { + epgGrid->DrawGrid(); + } + epgGrid->DrawHeader(); + epgGrid->Flush(); +} + +void cTVGuideOSD::TimeBack(void) { + bool scrolled = epgGrid->TimeBack(); + if (!scrolled) { + epgGrid->UpdateActive(); + } else { + epgGrid->DrawGrid(); + } + epgGrid->DrawHeader(); + epgGrid->Flush(); +} + +void cTVGuideOSD::ChannelsForward(void) { + bool scrolled = epgGrid->ChannelForward(); + if (!scrolled) { + epgGrid->UpdateActive(); + } else { + epgGrid->DrawChannelHeaders(); + epgGrid->DrawChannelgroups(); + epgGrid->DrawGrid(); + } + if (config.channelJumpMode == eGroupJump) + epgGrid->DrawFooter(); + epgGrid->DrawHeader(); + epgGrid->Flush(); +} + +void cTVGuideOSD::ChannelsBack(void) { + bool scrolled = epgGrid->ChannelBack(); + if (!scrolled) { + epgGrid->UpdateActive(); + } else { + epgGrid->DrawChannelHeaders(); + epgGrid->DrawChannelgroups(); + epgGrid->DrawGrid(); + } + if (config.channelJumpMode == eGroupJump) + epgGrid->DrawFooter(); + epgGrid->DrawHeader(); + epgGrid->Flush(); +} + +void cTVGuideOSD::NumericKey(int key) { + if (config.numKeyMode == eTimeJump) { + NumericKeyTimeJump(key); + } else if (config.numKeyMode == eChannelJump) { + NumericKeyChannelJump(key); + } +} + +void cTVGuideOSD::NumericKeyTimeJump(int key) { + switch (key) { + case 1: { + bool tooFarInPast = timeManager->DelMinutes(config.bigStepHours*60); + if (tooFarInPast) + return; + } + break; + case 3: { + timeManager->AddMinutes(config.bigStepHours*60); + } + break; + case 4: { + bool tooFarInPast = timeManager->DelMinutes(config.hugeStepHours*60); + if (tooFarInPast) + return; + } + break; + case 6: { + timeManager->AddMinutes(config.hugeStepHours*60); + } + break; + case 7: { + cTimeManager primeChecker; + primeChecker.Now(); + time_t prevPrime = primeChecker.GetPrevPrimetime(timeManager->GetStart()); + if (primeChecker.TooFarInPast(prevPrime)) + return; + timeManager->SetTime(prevPrime); + } + break; + case 9: { + cTimeManager primeChecker; + time_t nextPrime = primeChecker.GetNextPrimetime(timeManager->GetStart()); + timeManager->SetTime(nextPrime); + } + break; + default: + return; + } + epgGrid->RebuildEpgGrid(); + epgGrid->DrawGrid(); + epgGrid->DrawHeader(); + epgGrid->DrawTimeline(); + epgGrid->Flush(); +} + +void cTVGuideOSD::NumericKeyChannelJump(int key) { + if (!channelJumper) { + channelJumper = epgGrid->GetChannelJumper(); + } + channelJumper->Set(key); + channelJumper->Draw(); + epgGrid->Flush(); +} + +void cTVGuideOSD::CheckTimeout(void) { + if (!channelJumper) + return; + if (!channelJumper->TimeOut()) + return; + int newChannelNum = channelJumper->GetChannel(); + delete channelJumper; + channelJumper = NULL; + const cChannel *newChannel = Channels.GetByNumber(newChannelNum); + if (!newChannel) { + return; + } + epgGrid->Clear(); + epgGrid->Init(newChannel); + epgGrid->DrawHeader(); + epgGrid->DrawTime(); + epgGrid->DrawFooter(); + epgGrid->DrawChannelHeaders(); + epgGrid->DrawTimeline(); + epgGrid->DrawChannelgroups(); + epgGrid->DrawGrid(); + epgGrid->Flush(); +} + +void cTVGuideOSD::KeyGreen(void) { + const cChannel *prevStart = NULL; + if (config.channelJumpMode == eNumJump) { + prevStart = epgGrid->GetPrevChannelNumJump(); + } else if (config.channelJumpMode == eGroupJump) { + if (epgGrid->IsFirstGroup()) { + return; + } + prevStart = epgGrid->GetPrevChannelGroupJump(); + } + epgGrid->Clear(); + epgGrid->Init(prevStart); + epgGrid->DrawHeader(); + epgGrid->DrawTime(); + epgGrid->DrawFooter(); + epgGrid->DrawChannelHeaders(); + epgGrid->DrawChannelgroups(); + if (config.channelJumpMode == eGroupJump) + epgGrid->DrawFooter(); + epgGrid->DrawTimeline(); + epgGrid->DrawGrid(); + epgGrid->Flush(); +} + +void cTVGuideOSD::KeyYellow(void) { + const cChannel *nextStart = NULL; + if (config.channelJumpMode == eNumJump) { + nextStart = epgGrid->GetNextChannelNumJump(); + } else if (config.channelJumpMode == eGroupJump) { + if (epgGrid->IsLastGroup()) { + return; + } + if (config.hideLastChannelGroup && epgGrid->IsSecondLastGroup()) { + return; + } + nextStart = epgGrid->GetNextChannelGroupJump(); + } + epgGrid->Clear(); + epgGrid->Init(nextStart); + epgGrid->DrawHeader(); + epgGrid->DrawTime(); + epgGrid->DrawFooter(); + epgGrid->DrawChannelHeaders(); + epgGrid->DrawChannelgroups(); + if (config.channelJumpMode == eGroupJump) + epgGrid->DrawFooter(); + epgGrid->DrawTimeline(); + epgGrid->DrawGrid(); + epgGrid->Flush(); +} + +eOSState cTVGuideOSD::KeyBlue(const cEvent *e) { + if (config.blueKeyMode == eBlueKeySwitch) { + return ChannelSwitch(); + } else if (config.blueKeyMode == eBlueKeyEPG) { + DetailView(e); + } else if (config.blueKeyMode == eBlueKeyFavorites) { + Favorites(); + } + return osContinue; +} + +eOSState cTVGuideOSD::KeyOk(const cEvent *e) { + if (config.blueKeyMode == eBlueKeySwitch) { + DetailView(e); + } else if (config.blueKeyMode == eBlueKeyEPG) { + return ChannelSwitch(); + } else if (config.blueKeyMode == eBlueKeyFavorites) { + DetailView(e); + } + return osContinue; +} + +eOSState cTVGuideOSD::ChannelSwitch(void) { + const cChannel *currentChannel = epgGrid->GetCurrentChannel(); + if (!currentChannel) { + return osContinue; + } + cDevice::PrimaryDevice()->SwitchChannel(currentChannel, true); + if (config.closeOnSwitch) { + return osEnd; + } + return osContinue; +} + +void cTVGuideOSD::DetailView(const cEvent *e) { + if (!e) + return; + epgGrid->Deactivate(true); + if (recMenuView->Active()) + recMenuView->Hide(); + cOsdView *dV = GetOsdView(viRootView, viDetailView); + detailView = new cDetailView(dV, e); + detailView->Draw(); + detailView->DrawTime(); + detailView->Flush(); +} + +void cTVGuideOSD::CloseDetailedView(void) { + delete detailView; + detailView = NULL; + epgGrid->Activate(); + epgGrid->Flush(); + if (recMenuView->Active()) { + recMenuView->Activate(); + recMenuView->Flush(); + } +} + +void cTVGuideOSD::KeyRed(void) { + const cEvent *e = epgGrid->GetCurrentEvent(); + if (!e) + return; + epgGrid->Deactivate(false); + cOsdView *recView = GetOsdView(viRootView, viRecMenu); + cOsdView *recViewBuffer = GetOsdView(viRootView, viRecMenu); + cOsdView *recViewBuffer2 = GetOsdView(viRootView, viRecMenu); + recMenuView->Init(recView, recViewBuffer, recViewBuffer2); + recMenuView->DisplayRecMenu(e); + recMenuView->Flush(); +} + +void cTVGuideOSD::Favorites(void) { + epgGrid->Deactivate(false); + cOsdView *recView = GetOsdView(viRootView, viRecMenu); + cOsdView *recViewBuffer = GetOsdView(viRootView, viRecMenu); + cOsdView *recViewBuffer2 = GetOsdView(viRootView, viRecMenu); + recMenuView->Init(recView, recViewBuffer, recViewBuffer2); + recMenuView->DisplayFavorites(); + recMenuView->Flush(); +} diff --git a/tvguidengosd.h b/tvguidengosd.h new file mode 100644 index 0000000..9fc8a00 --- /dev/null +++ b/tvguidengosd.h @@ -0,0 +1,93 @@ +#ifndef __TVGUIDENGOSD_H +#define __TVGUIDENGOSD_H + +#include <vdr/osdbase.h> +#include <vdr/plugin.h> +#include "libskindesigner/skindesignerosdbase.h" +#include "libskindesigner/osdelements.h" +#include "config.h" +#include "timemanager.h" +#include "epggrid.h" +#include "channeljump.h" +#include "detailview.h" +#include "recmenuview.h" + +enum eViews { + viRootView, + viDetailView, + viRecMenu +}; + +enum eViewElementsRoot { + verBackgroundHor, + verBackgroundVer, + verHeader, + verFooter, + verTime, + verDateTimelineHor, + verDateTimelineVer, + verTimeIndicatorHor, + verTimeIndicatorVer, + verChannelJump +}; + +enum eViewGrids { + vgChannelsHor, + vgChannelsVer, + vgSchedulesHor, + vgSchedulesVer, + vgChannelGroupsHor, + vgChannelGroupsVer, + vgTimelineHor, + vgTimelineVer, + vgRecordingMenu +}; + +enum eViewElementsDetail { + vedBackground, + vedHeader, + vedFooter, + vedTime +}; + +enum eViewElementsRecMenu { + vemBackground, + vemScrollbar +}; + +class cTVGuideOSD : public cSkindesignerOsdObject { +private: + cTimeManager *timeManager; + cEpgGrid *epgGrid; + cChannelJump *channelJumper; + cDetailView *detailView; + cRecMenuView *recMenuView; + void KeyLeft(void); + void KeyRight(void); + void KeyUp(void); + void KeyDown(void); + void TimeForward(void); + void TimeBack(void); + void ChannelsForward(void); + void ChannelsBack(void); + void NumericKey(int key); + void NumericKeyTimeJump(int key); + void NumericKeyChannelJump(int key); + void CheckTimeout(void); + void KeyGreen(void); + void KeyYellow(void); + eOSState KeyBlue(const cEvent *e); + eOSState KeyOk(const cEvent *e); + void KeyRed(void); + void DetailView(const cEvent *e); + void CloseDetailedView(void); + eOSState ChannelSwitch(void); + void Favorites(void); +public: + cTVGuideOSD(void); + virtual ~cTVGuideOSD(void); + virtual void Show(void); + virtual eOSState ProcessKey(eKeys Key); +}; + +#endif //__TVGUIDENGOSD_H |