diff options
author | Lars Op den Kamp <lars@opdenkamp.eu> | 2012-09-01 17:12:07 +0200 |
---|---|---|
committer | Lars Op den Kamp <lars@opdenkamp.eu> | 2012-09-05 00:17:22 +0200 |
commit | 4e9cb43928abbddd5d683cb5d9d66c72b3bbf88e (patch) | |
tree | 58c59c5d5621c553118edb569e1e815c45567f83 | |
parent | c79dd456775c37963a27d9f563c405d83ee19c5c (diff) |
[pvr] added PVR support to XBMC (taken from https://github.com/opdenkamp/xbmc/commit/c576c080532a0e4c4ffc7babd57782f80a6951ba)
add-ons are not included, but can be found here: https://github.com/opdenkamp/xbmc-pvr-addons
334 files changed, 51454 insertions, 426 deletions
diff --git a/.gitignore b/.gitignore index c80d0e2bf8..a3d6d24800 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ config.log *.vcxproj.*.user *.vcxproj.user *.obj +*.idb *ReSharper* *.idb @@ -65,6 +66,11 @@ config.log .libs/ .deps/ +# Eclipse project files. Not needed as they are generated in two clicks if needed. +.settings +.project +.cproject + # / /.dummy /.dummy.in @@ -75,6 +81,7 @@ config.log /build-aux/config.guess /build-aux/config.sub /build-aux/install-sh +/build-aux/ltmain.sh /build-aux/missing /build-aux/ltmain.sh /autotools @@ -153,6 +160,17 @@ config.log /lib/asap/xbmc/xbmc_asap.res +# /lib/addons/ +/lib/addons/library.xbmc.addon/Makefile +/lib/addons/library.xbmc.gui/Makefile +/lib/addons/library.xbmc.pvr/Makefile +/lib/addons/library.xbmc.addon/project/VS2010Express/Release +/lib/addons/library.xbmc.addon/project/VS2010Express/Debug +/lib/addons/library.xbmc.gui/project/VS2010Express/Release +/lib/addons/library.xbmc.gui/project/VS2010Express/Debug +/lib/addons/library.xbmc.pvr/project/VS2010Express/Release +/lib/addons/library.xbmc.pvr/project/VS2010Express/Debug + # /lib/cmyth/ lib/cmyth/Makefile @@ -220,6 +238,11 @@ lib/cmyth/Makefile /lib/libass/libass/Makefile.in /lib/libass/libtool /lib/libass/ltmain.sh +/lib/libass/m4/libtool.m4 +/lib/libass/m4/ltoptions.m4 +/lib/libass/m4/ltsugar.m4 +/lib/libass/m4/ltversion.m4 +/lib/libass/m4/lt~obsolete.m4 /lib/libass/missing /lib/libass/shave/libtool.m4 /lib/libass/shave/ltoptions.m4 @@ -303,6 +326,7 @@ lib/cmyth/Makefile /lib/libapetag/config.h /lib/libapetag/install-sh /lib/libapetag/libtool +/lib/libapetag/m4/lt~obsolete.m4 /lib/libapetag/stamp-h1 # /project @@ -1138,6 +1162,12 @@ lib/cmyth/Makefile /lib/libXDAAP/libXDAAP_win32/Debug /lib/libXDAAP/libXDAAP_win32/Release +# /xbmc/osx/ +/xbmc/osx/Makefile + +# /portable_data +/portable_data + /xbmc/screensavers/Makefile /xbmc/screensavers/rsxs-0.9/Makefile @@ -1237,3 +1267,10 @@ xbmc/visualizations/EGLHelpers/Makefile /xbmc/visualizations/XBMCProjectM/libprojectM/config.inp /xbmc/win32/git_rev.h + +/addons/library.xbmc.addon/libXBMC_addon.dll +/addons/library.xbmc.addon/libXBMC_addon.lib +/addons/library.xbmc.gui/libXBMC_gui.dll +/addons/library.xbmc.gui/libXBMC_gui.lib +/addons/library.xbmc.pvr/libXBMC_pvr.dll +/addons/library.xbmc.pvr/libXBMC_pvr.lib diff --git a/Makefile.in b/Makefile.in index 3af486ca9d..1cea93f3c4 100644 --- a/Makefile.in +++ b/Makefile.in @@ -41,6 +41,7 @@ DIRECTORY_ARCHIVES=$(DVDPLAYER_ARCHIVES) \ xbmc/cores/playercorefactory/playercorefactory.a \ xbmc/dbwrappers/dbwrappers.a \ xbmc/dialogs/dialogs.a \ + xbmc/epg/epg.a \ xbmc/filesystem/MusicDatabaseDirectory/musicdatabasedirectory.a \ xbmc/filesystem/VideoDatabaseDirectory/videodatabasedirectory.a \ xbmc/filesystem/filesystem.a \ @@ -70,6 +71,13 @@ DIRECTORY_ARCHIVES=$(DVDPLAYER_ARCHIVES) \ xbmc/playlists/playlists.a \ xbmc/powermanagement/powermanagement.a \ xbmc/programs/programs.a \ + xbmc/pvr/addons/pvraddons.a \ + xbmc/pvr/channels/pvrchannels.a \ + xbmc/pvr/dialogs/pvrdialogs.a \ + xbmc/pvr/pvr.a \ + xbmc/pvr/recordings/pvrrecordings.a \ + xbmc/pvr/timers/pvrtimers.a \ + xbmc/pvr/windows/pvrwindows.a \ xbmc/rendering/rendering.a \ xbmc/settings/settings.a \ xbmc/storage/storage.a \ @@ -191,6 +199,11 @@ ifneq (@DISABLE_GOOM@,1) VIS_DIRS+=xbmc/visualizations/Goom endif +LIBADDON_DIRS=\ + lib/addons/library.xbmc.addon \ + lib/addons/library.xbmc.pvr \ + lib/addons/library.xbmc.gui \ + CONFLUENCE_MEDIA=addons/skin.confluence/media SKIN_DIRS=$(CONFLUENCE_MEDIA) @@ -200,7 +213,7 @@ SKIN_DIRS+=$(TOUCHED_MEDIA) endif DIRS= $(BIN_DIRS) $(EC_DIRS) $(XBMCTEX_DIRS) $(DVDPCODECS_DIRS) $(PAPCODECS_DIRS) \ - $(LIB_DIRS) $(SS_DIRS) $(VIS_DIRS) $(SKIN_DIRS) + $(LIB_DIRS) $(SS_DIRS) $(VIS_DIRS) $(LIBADDON_DIRS) $(SKIN_DIRS) LIBS=@LIBS@ CFLAGS=@CFLAGS@ @@ -235,7 +248,7 @@ all : $(FINAL_TARGETS) include Makefile.include .PHONY : dllloader exports visualizations screensavers eventclients papcodecs \ - dvdpcodecs imagelib codecs externals force skins + dvdpcodecs imagelib codecs externals force skins libaddon # hack targets to keep build system up to date Makefile : config.status $(addsuffix .in, $(AUTOGENERATED_MAKEFILES)) @@ -308,6 +321,10 @@ visualizations: $(VIS_DIRS) screensavers: $(SS_DIRS) +libaddon: exports + $(MAKE) -C lib/addons/library.xbmc.addon + $(MAKE) -C lib/addons/library.xbmc.gui + $(MAKE) -C lib/addons/library.xbmc.pvr libpython: dllloader $(MAKE) -C xbmc/interfaces/python $(MAKE) -C xbmc/interfaces/python/xbmcmodule @@ -355,10 +372,10 @@ codecs: papcodecs dvdpcodecs libs: libhdhomerun libid3tag imagelib libexif system/libcpluff-@ARCH@.so $(CMYTH) -externals: codecs libs visualizations screensavers +externals: codecs libs visualizations screensavers libaddon xcode_depends: \ - codecs libs visualizations screensavers eventclients skins \ + codecs libs visualizations screensavers eventclients skins libaddon \ lib/libsquish/libsquish.a \ lib/libapetag/.libs/libapetag.a \ lib/libRTV/librtv.a \ @@ -377,6 +394,8 @@ DYNOBJSXBMC= \ xbmc/cores/DllLoader/exports/exports.a \ xbmc/settings/settings.a \ xbmc/video/video.a \ + xbmc/pvr/addons/pvraddons.a \ + xbmc/pvr/windows/pvrwindows.a \ xbmc/guilib/guilib.a # must be dynamic to avoid linker errors ifeq ($(findstring freebsd,@ARCH@),freebsd) @@ -521,6 +540,8 @@ uninstall: @rm -rf $(DESTDIR)$(datarootdir)/xbmc $(DESTDIR)$(bindir)/xbmc @rm -rf $(DESTDIR)$(bindir)/xbmc-standalone @rm -rf $(DESTDIR)$(datarootdir)/xsessions/XBMC.desktop + @rm -rf $(libdir)/libXBMC_* + @rm -rf $(prefix)/include/xbmc @echo "Done!" clean-xbmc.bin: @@ -540,11 +561,13 @@ clean-screensavers: for d in $(SS_DIRS); do if test -f $$d/Makefile; then $(MAKE) -C $$d clean; fi; done clean-visualisations: for d in $(VIS_DIRS); do if test -f $$d/Makefile; then $(MAKE) -C $$d clean; fi; done +clean-libaddons: + for d in $(LIBADDON_DIRS); do if test -f $$d/Makefile; then $(MAKE) -C $$d clean; fi; done clean-codecs: clean-dvdpcodecs clean-papcodecs clean-externals: clean-codecs clean-eventclients clean-xbmctex clean-libs \ - clean-screensavers clean-visualisations + clean-screensavers clean-visualisations clean-libaddons check: for d in $(CHECK_DIRS); do if test -f $$d/Makefile; then $(MAKE) -C $$d $@; fi; done diff --git a/XBMC-ATV2.xcodeproj/project.pbxproj b/XBMC-ATV2.xcodeproj/project.pbxproj index 92959215be..90f3814be3 100644 --- a/XBMC-ATV2.xcodeproj/project.pbxproj +++ b/XBMC-ATV2.xcodeproj/project.pbxproj @@ -45,6 +45,55 @@ C807119F135DB842002F601B /* InputOperations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C807119D135DB842002F601B /* InputOperations.cpp */; }; C8936060152C86EC00812418 /* PythonMonitor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C893605E152C86EC00812418 /* PythonMonitor.cpp */; }; C8936063152C86F500812418 /* monitor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8936061152C86F500812418 /* monitor.cpp */; }; + C8B92A8215735D0300284190 /* Epg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A7515735D0300284190 /* Epg.cpp */; }; + C8B92A8315735D0300284190 /* EpgContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A7715735D0300284190 /* EpgContainer.cpp */; }; + C8B92A8415735D0300284190 /* EpgDatabase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A7915735D0300284190 /* EpgDatabase.cpp */; }; + C8B92A8515735D0300284190 /* EpgInfoTag.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A7B15735D0300284190 /* EpgInfoTag.cpp */; }; + C8B92A8615735D0300284190 /* EpgSearchFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A7D15735D0300284190 /* EpgSearchFilter.cpp */; }; + C8B92A8715735D0300284190 /* GUIEPGGridContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A7F15735D0300284190 /* GUIEPGGridContainer.cpp */; }; + C8B92AD915735D2700284190 /* PVRClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A8D15735D2700284190 /* PVRClient.cpp */; }; + C8B92ADA15735D2700284190 /* PVRClients.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A8F15735D2700284190 /* PVRClients.cpp */; }; + C8B92ADC15735D2700284190 /* PVRChannel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A9315735D2700284190 /* PVRChannel.cpp */; }; + C8B92ADD15735D2700284190 /* PVRChannelGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A9515735D2700284190 /* PVRChannelGroup.cpp */; }; + C8B92ADE15735D2700284190 /* PVRChannelGroupInternal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A9715735D2700284190 /* PVRChannelGroupInternal.cpp */; }; + C8B92ADF15735D2700284190 /* PVRChannelGroups.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A9915735D2700284190 /* PVRChannelGroups.cpp */; }; + C8B92AE015735D2700284190 /* PVRChannelGroupsContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A9B15735D2700284190 /* PVRChannelGroupsContainer.cpp */; }; + C8B92AE115735D2700284190 /* GUIDialogPVRChannelManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A9E15735D2700284190 /* GUIDialogPVRChannelManager.cpp */; }; + C8B92AE215735D2700284190 /* GUIDialogPVRChannelsOSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AA015735D2700284190 /* GUIDialogPVRChannelsOSD.cpp */; }; + C8B92AE315735D2700284190 /* GUIDialogPVRCutterOSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AA215735D2700284190 /* GUIDialogPVRCutterOSD.cpp */; }; + C8B92AE415735D2700284190 /* GUIDialogPVRDirectorOSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AA415735D2700284190 /* GUIDialogPVRDirectorOSD.cpp */; }; + C8B92AE515735D2700284190 /* GUIDialogPVRGroupManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AA615735D2700284190 /* GUIDialogPVRGroupManager.cpp */; }; + C8B92AE615735D2700284190 /* GUIDialogPVRGuideInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AA815735D2700284190 /* GUIDialogPVRGuideInfo.cpp */; }; + C8B92AE715735D2700284190 /* GUIDialogPVRGuideOSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AAA15735D2700284190 /* GUIDialogPVRGuideOSD.cpp */; }; + C8B92AE815735D2700284190 /* GUIDialogPVRGuideSearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AAC15735D2700284190 /* GUIDialogPVRGuideSearch.cpp */; }; + C8B92AE915735D2700284190 /* GUIDialogPVRRecordingInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AAE15735D2700284190 /* GUIDialogPVRRecordingInfo.cpp */; }; + C8B92AEA15735D2700284190 /* GUIDialogPVRTimerSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AB015735D2700284190 /* GUIDialogPVRTimerSettings.cpp */; }; + C8B92AED15735D2700284190 /* PVRDatabase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AB415735D2700284190 /* PVRDatabase.cpp */; }; + C8B92AEE15735D2700284190 /* PVRGUIInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AB615735D2700284190 /* PVRGUIInfo.cpp */; }; + C8B92AEF15735D2700284190 /* PVRManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AB815735D2700284190 /* PVRManager.cpp */; }; + C8B92AF115735D2700284190 /* PVRRecording.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92ABC15735D2700284190 /* PVRRecording.cpp */; }; + C8B92AF215735D2700284190 /* PVRRecordings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92ABE15735D2700284190 /* PVRRecordings.cpp */; }; + C8B92AF415735D2700284190 /* PVRTimerInfoTag.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AC215735D2700284190 /* PVRTimerInfoTag.cpp */; }; + C8B92AF515735D2700284190 /* PVRTimers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AC415735D2700284190 /* PVRTimers.cpp */; }; + C8B92AF615735D2700284190 /* GUIViewStatePVR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AC715735D2700284190 /* GUIViewStatePVR.cpp */; }; + C8B92AF715735D2700284190 /* GUIWindowPVR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AC915735D2700284190 /* GUIWindowPVR.cpp */; }; + C8B92AF815735D2700284190 /* GUIWindowPVRChannels.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92ACB15735D2700284190 /* GUIWindowPVRChannels.cpp */; }; + C8B92AF915735D2700284190 /* GUIWindowPVRCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92ACD15735D2700284190 /* GUIWindowPVRCommon.cpp */; }; + C8B92AFA15735D2700284190 /* GUIWindowPVRGuide.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92ACF15735D2700284190 /* GUIWindowPVRGuide.cpp */; }; + C8B92AFB15735D2700284190 /* GUIWindowPVRRecordings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AD115735D2700284190 /* GUIWindowPVRRecordings.cpp */; }; + C8B92AFC15735D2700284190 /* GUIWindowPVRSearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AD315735D2700284190 /* GUIWindowPVRSearch.cpp */; }; + C8B92AFD15735D2700284190 /* GUIWindowPVRTimers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AD515735D2700284190 /* GUIWindowPVRTimers.cpp */; }; + C8B92B0715735D5D00284190 /* AddonCallbacks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92AFF15735D5D00284190 /* AddonCallbacks.cpp */; }; + C8B92B0815735D5D00284190 /* AddonCallbacksAddon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92B0115735D5D00284190 /* AddonCallbacksAddon.cpp */; }; + C8B92B0915735D5D00284190 /* AddonCallbacksGUI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92B0315735D5D00284190 /* AddonCallbacksGUI.cpp */; }; + C8B92B0A15735D5D00284190 /* AddonCallbacksPVR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92B0515735D5D00284190 /* AddonCallbacksPVR.cpp */; }; + C8B92B0D15735DBC00284190 /* DVDDemuxPVRClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92B0B15735DBC00284190 /* DVDDemuxPVRClient.cpp */; }; + C8B92B1015735DD900284190 /* DVDInputStreamPVRManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92B0E15735DD900284190 /* DVDInputStreamPVRManager.cpp */; }; + C8B92B1515735DFB00284190 /* PVRDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92B1115735DFB00284190 /* PVRDirectory.cpp */; }; + C8B92B1615735DFB00284190 /* PVRFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92B1315735DFB00284190 /* PVRFile.cpp */; }; + C8B92B1915735E1E00284190 /* GUIDialogExtendedProgressBar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92B1715735E1E00284190 /* GUIDialogExtendedProgressBar.cpp */; }; + C8B92B2115735EBF00284190 /* Observer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92B1D15735EBF00284190 /* Observer.cpp */; }; + C8B92B2215735EBF00284190 /* TextSearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92B1F15735EBF00284190 /* TextSearch.cpp */; }; C8EC5D51136954E400CCC10D /* XBMC_keytable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8EC5D4F136954E400CCC10D /* XBMC_keytable.cpp */; }; DF08E84515829BA600058C77 /* Exception.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF08E84315829BA600058C77 /* Exception.cpp */; }; DF0DF16D13A3AF82008ED511 /* NFSDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF16A13A3AF82008ED511 /* NFSDirectory.cpp */; }; @@ -1066,6 +1115,104 @@ C893605F152C86EC00812418 /* PythonMonitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PythonMonitor.h; sourceTree = "<group>"; }; C8936061152C86F500812418 /* monitor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = monitor.cpp; sourceTree = "<group>"; }; C8936062152C86F500812418 /* monitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = monitor.h; sourceTree = "<group>"; }; + C8B92A7515735D0300284190 /* Epg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Epg.cpp; sourceTree = "<group>"; }; + C8B92A7615735D0300284190 /* Epg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Epg.h; sourceTree = "<group>"; }; + C8B92A7715735D0300284190 /* EpgContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EpgContainer.cpp; sourceTree = "<group>"; }; + C8B92A7815735D0300284190 /* EpgContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EpgContainer.h; sourceTree = "<group>"; }; + C8B92A7915735D0300284190 /* EpgDatabase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EpgDatabase.cpp; sourceTree = "<group>"; }; + C8B92A7A15735D0300284190 /* EpgDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EpgDatabase.h; sourceTree = "<group>"; }; + C8B92A7B15735D0300284190 /* EpgInfoTag.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EpgInfoTag.cpp; sourceTree = "<group>"; }; + C8B92A7C15735D0300284190 /* EpgInfoTag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EpgInfoTag.h; sourceTree = "<group>"; }; + C8B92A7D15735D0300284190 /* EpgSearchFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EpgSearchFilter.cpp; sourceTree = "<group>"; }; + C8B92A7E15735D0300284190 /* EpgSearchFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EpgSearchFilter.h; sourceTree = "<group>"; }; + C8B92A7F15735D0300284190 /* GUIEPGGridContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIEPGGridContainer.cpp; sourceTree = "<group>"; }; + C8B92A8015735D0300284190 /* GUIEPGGridContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIEPGGridContainer.h; sourceTree = "<group>"; }; + C8B92A8D15735D2700284190 /* PVRClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRClient.cpp; sourceTree = "<group>"; }; + C8B92A8E15735D2700284190 /* PVRClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRClient.h; sourceTree = "<group>"; }; + C8B92A8F15735D2700284190 /* PVRClients.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRClients.cpp; sourceTree = "<group>"; }; + C8B92A9015735D2700284190 /* PVRClients.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRClients.h; sourceTree = "<group>"; }; + C8B92A9315735D2700284190 /* PVRChannel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRChannel.cpp; sourceTree = "<group>"; }; + C8B92A9415735D2700284190 /* PVRChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRChannel.h; sourceTree = "<group>"; }; + C8B92A9515735D2700284190 /* PVRChannelGroup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRChannelGroup.cpp; sourceTree = "<group>"; }; + C8B92A9615735D2700284190 /* PVRChannelGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRChannelGroup.h; sourceTree = "<group>"; }; + C8B92A9715735D2700284190 /* PVRChannelGroupInternal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRChannelGroupInternal.cpp; sourceTree = "<group>"; }; + C8B92A9815735D2700284190 /* PVRChannelGroupInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRChannelGroupInternal.h; sourceTree = "<group>"; }; + C8B92A9915735D2700284190 /* PVRChannelGroups.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRChannelGroups.cpp; sourceTree = "<group>"; }; + C8B92A9A15735D2700284190 /* PVRChannelGroups.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRChannelGroups.h; sourceTree = "<group>"; }; + C8B92A9B15735D2700284190 /* PVRChannelGroupsContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRChannelGroupsContainer.cpp; sourceTree = "<group>"; }; + C8B92A9C15735D2700284190 /* PVRChannelGroupsContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRChannelGroupsContainer.h; sourceTree = "<group>"; }; + C8B92A9E15735D2700284190 /* GUIDialogPVRChannelManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRChannelManager.cpp; sourceTree = "<group>"; }; + C8B92A9F15735D2700284190 /* GUIDialogPVRChannelManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRChannelManager.h; sourceTree = "<group>"; }; + C8B92AA015735D2700284190 /* GUIDialogPVRChannelsOSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRChannelsOSD.cpp; sourceTree = "<group>"; }; + C8B92AA115735D2700284190 /* GUIDialogPVRChannelsOSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRChannelsOSD.h; sourceTree = "<group>"; }; + C8B92AA215735D2700284190 /* GUIDialogPVRCutterOSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRCutterOSD.cpp; sourceTree = "<group>"; }; + C8B92AA315735D2700284190 /* GUIDialogPVRCutterOSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRCutterOSD.h; sourceTree = "<group>"; }; + C8B92AA415735D2700284190 /* GUIDialogPVRDirectorOSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRDirectorOSD.cpp; sourceTree = "<group>"; }; + C8B92AA515735D2700284190 /* GUIDialogPVRDirectorOSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRDirectorOSD.h; sourceTree = "<group>"; }; + C8B92AA615735D2700284190 /* GUIDialogPVRGroupManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRGroupManager.cpp; sourceTree = "<group>"; }; + C8B92AA715735D2700284190 /* GUIDialogPVRGroupManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRGroupManager.h; sourceTree = "<group>"; }; + C8B92AA815735D2700284190 /* GUIDialogPVRGuideInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRGuideInfo.cpp; sourceTree = "<group>"; }; + C8B92AA915735D2700284190 /* GUIDialogPVRGuideInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRGuideInfo.h; sourceTree = "<group>"; }; + C8B92AAA15735D2700284190 /* GUIDialogPVRGuideOSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRGuideOSD.cpp; sourceTree = "<group>"; }; + C8B92AAB15735D2700284190 /* GUIDialogPVRGuideOSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRGuideOSD.h; sourceTree = "<group>"; }; + C8B92AAC15735D2700284190 /* GUIDialogPVRGuideSearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRGuideSearch.cpp; sourceTree = "<group>"; }; + C8B92AAD15735D2700284190 /* GUIDialogPVRGuideSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRGuideSearch.h; sourceTree = "<group>"; }; + C8B92AAE15735D2700284190 /* GUIDialogPVRRecordingInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRRecordingInfo.cpp; sourceTree = "<group>"; }; + C8B92AAF15735D2700284190 /* GUIDialogPVRRecordingInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRRecordingInfo.h; sourceTree = "<group>"; }; + C8B92AB015735D2700284190 /* GUIDialogPVRTimerSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRTimerSettings.cpp; sourceTree = "<group>"; }; + C8B92AB115735D2700284190 /* GUIDialogPVRTimerSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRTimerSettings.h; sourceTree = "<group>"; }; + C8B92AB415735D2700284190 /* PVRDatabase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRDatabase.cpp; sourceTree = "<group>"; }; + C8B92AB515735D2700284190 /* PVRDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRDatabase.h; sourceTree = "<group>"; }; + C8B92AB615735D2700284190 /* PVRGUIInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRGUIInfo.cpp; sourceTree = "<group>"; }; + C8B92AB715735D2700284190 /* PVRGUIInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRGUIInfo.h; sourceTree = "<group>"; }; + C8B92AB815735D2700284190 /* PVRManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRManager.cpp; sourceTree = "<group>"; }; + C8B92AB915735D2700284190 /* PVRManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRManager.h; sourceTree = "<group>"; }; + C8B92ABC15735D2700284190 /* PVRRecording.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRRecording.cpp; sourceTree = "<group>"; }; + C8B92ABD15735D2700284190 /* PVRRecording.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRRecording.h; sourceTree = "<group>"; }; + C8B92ABE15735D2700284190 /* PVRRecordings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRRecordings.cpp; sourceTree = "<group>"; }; + C8B92ABF15735D2700284190 /* PVRRecordings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRRecordings.h; sourceTree = "<group>"; }; + C8B92AC215735D2700284190 /* PVRTimerInfoTag.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRTimerInfoTag.cpp; sourceTree = "<group>"; }; + C8B92AC315735D2700284190 /* PVRTimerInfoTag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRTimerInfoTag.h; sourceTree = "<group>"; }; + C8B92AC415735D2700284190 /* PVRTimers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRTimers.cpp; sourceTree = "<group>"; }; + C8B92AC515735D2700284190 /* PVRTimers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRTimers.h; sourceTree = "<group>"; }; + C8B92AC715735D2700284190 /* GUIViewStatePVR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIViewStatePVR.cpp; sourceTree = "<group>"; }; + C8B92AC815735D2700284190 /* GUIViewStatePVR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIViewStatePVR.h; sourceTree = "<group>"; }; + C8B92AC915735D2700284190 /* GUIWindowPVR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVR.cpp; sourceTree = "<group>"; }; + C8B92ACA15735D2700284190 /* GUIWindowPVR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVR.h; sourceTree = "<group>"; }; + C8B92ACB15735D2700284190 /* GUIWindowPVRChannels.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRChannels.cpp; sourceTree = "<group>"; }; + C8B92ACC15735D2700284190 /* GUIWindowPVRChannels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRChannels.h; sourceTree = "<group>"; }; + C8B92ACD15735D2700284190 /* GUIWindowPVRCommon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRCommon.cpp; sourceTree = "<group>"; }; + C8B92ACE15735D2700284190 /* GUIWindowPVRCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRCommon.h; sourceTree = "<group>"; }; + C8B92ACF15735D2700284190 /* GUIWindowPVRGuide.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRGuide.cpp; sourceTree = "<group>"; }; + C8B92AD015735D2700284190 /* GUIWindowPVRGuide.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRGuide.h; sourceTree = "<group>"; }; + C8B92AD115735D2700284190 /* GUIWindowPVRRecordings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRRecordings.cpp; sourceTree = "<group>"; }; + C8B92AD215735D2700284190 /* GUIWindowPVRRecordings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRRecordings.h; sourceTree = "<group>"; }; + C8B92AD315735D2700284190 /* GUIWindowPVRSearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRSearch.cpp; sourceTree = "<group>"; }; + C8B92AD415735D2700284190 /* GUIWindowPVRSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRSearch.h; sourceTree = "<group>"; }; + C8B92AD515735D2700284190 /* GUIWindowPVRTimers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRTimers.cpp; sourceTree = "<group>"; }; + C8B92AD615735D2700284190 /* GUIWindowPVRTimers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRTimers.h; sourceTree = "<group>"; }; + C8B92AFF15735D5D00284190 /* AddonCallbacks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AddonCallbacks.cpp; sourceTree = "<group>"; }; + C8B92B0015735D5D00284190 /* AddonCallbacks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddonCallbacks.h; sourceTree = "<group>"; }; + C8B92B0115735D5D00284190 /* AddonCallbacksAddon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AddonCallbacksAddon.cpp; sourceTree = "<group>"; }; + C8B92B0215735D5D00284190 /* AddonCallbacksAddon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddonCallbacksAddon.h; sourceTree = "<group>"; }; + C8B92B0315735D5D00284190 /* AddonCallbacksGUI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AddonCallbacksGUI.cpp; sourceTree = "<group>"; }; + C8B92B0415735D5D00284190 /* AddonCallbacksGUI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddonCallbacksGUI.h; sourceTree = "<group>"; }; + C8B92B0515735D5D00284190 /* AddonCallbacksPVR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AddonCallbacksPVR.cpp; sourceTree = "<group>"; }; + C8B92B0615735D5D00284190 /* AddonCallbacksPVR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddonCallbacksPVR.h; sourceTree = "<group>"; }; + C8B92B0B15735DBC00284190 /* DVDDemuxPVRClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDDemuxPVRClient.cpp; sourceTree = "<group>"; }; + C8B92B0C15735DBC00284190 /* DVDDemuxPVRClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDDemuxPVRClient.h; sourceTree = "<group>"; }; + C8B92B0E15735DD900284190 /* DVDInputStreamPVRManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDInputStreamPVRManager.cpp; sourceTree = "<group>"; }; + C8B92B0F15735DD900284190 /* DVDInputStreamPVRManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDInputStreamPVRManager.h; sourceTree = "<group>"; }; + C8B92B1115735DFB00284190 /* PVRDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRDirectory.cpp; sourceTree = "<group>"; }; + C8B92B1215735DFB00284190 /* PVRDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRDirectory.h; sourceTree = "<group>"; }; + C8B92B1315735DFB00284190 /* PVRFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRFile.cpp; sourceTree = "<group>"; }; + C8B92B1415735DFB00284190 /* PVRFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRFile.h; sourceTree = "<group>"; }; + C8B92B1715735E1E00284190 /* GUIDialogExtendedProgressBar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogExtendedProgressBar.cpp; sourceTree = "<group>"; }; + C8B92B1815735E1E00284190 /* GUIDialogExtendedProgressBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogExtendedProgressBar.h; sourceTree = "<group>"; }; + C8B92B1D15735EBF00284190 /* Observer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Observer.cpp; sourceTree = "<group>"; }; + C8B92B1E15735EBF00284190 /* Observer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Observer.h; sourceTree = "<group>"; }; + C8B92B1F15735EBF00284190 /* TextSearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextSearch.cpp; sourceTree = "<group>"; }; + C8B92B2015735EBF00284190 /* TextSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextSearch.h; sourceTree = "<group>"; }; C8EC5D4F136954E400CCC10D /* XBMC_keytable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XBMC_keytable.cpp; sourceTree = "<group>"; }; C8EC5D50136954E400CCC10D /* XBMC_keytable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMC_keytable.h; sourceTree = "<group>"; }; DF08E84315829BA600058C77 /* Exception.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Exception.cpp; sourceTree = "<group>"; }; @@ -3146,6 +3293,144 @@ name = Documentation; sourceTree = "<group>"; }; + C8B92A7415735D0300284190 /* epg */ = { + isa = PBXGroup; + children = ( + C8B92A7515735D0300284190 /* Epg.cpp */, + C8B92A7615735D0300284190 /* Epg.h */, + C8B92A7715735D0300284190 /* EpgContainer.cpp */, + C8B92A7815735D0300284190 /* EpgContainer.h */, + C8B92A7915735D0300284190 /* EpgDatabase.cpp */, + C8B92A7A15735D0300284190 /* EpgDatabase.h */, + C8B92A7B15735D0300284190 /* EpgInfoTag.cpp */, + C8B92A7C15735D0300284190 /* EpgInfoTag.h */, + C8B92A7D15735D0300284190 /* EpgSearchFilter.cpp */, + C8B92A7E15735D0300284190 /* EpgSearchFilter.h */, + C8B92A7F15735D0300284190 /* GUIEPGGridContainer.cpp */, + C8B92A8015735D0300284190 /* GUIEPGGridContainer.h */, + ); + path = epg; + sourceTree = "<group>"; + }; + C8B92A8A15735D2700284190 /* pvr */ = { + isa = PBXGroup; + children = ( + C8B92A8B15735D2700284190 /* addons */, + C8B92A9115735D2700284190 /* channels */, + C8B92A9D15735D2700284190 /* dialogs */, + C8B92ABA15735D2700284190 /* recordings */, + C8B92AC015735D2700284190 /* timers */, + C8B92AC615735D2700284190 /* windows */, + C8B92AB415735D2700284190 /* PVRDatabase.cpp */, + C8B92AB515735D2700284190 /* PVRDatabase.h */, + C8B92AB615735D2700284190 /* PVRGUIInfo.cpp */, + C8B92AB715735D2700284190 /* PVRGUIInfo.h */, + C8B92AB815735D2700284190 /* PVRManager.cpp */, + C8B92AB915735D2700284190 /* PVRManager.h */, + ); + path = pvr; + sourceTree = "<group>"; + }; + C8B92A8B15735D2700284190 /* addons */ = { + isa = PBXGroup; + children = ( + C8B92A8D15735D2700284190 /* PVRClient.cpp */, + C8B92A8E15735D2700284190 /* PVRClient.h */, + C8B92A8F15735D2700284190 /* PVRClients.cpp */, + C8B92A9015735D2700284190 /* PVRClients.h */, + ); + path = addons; + sourceTree = "<group>"; + }; + C8B92A9115735D2700284190 /* channels */ = { + isa = PBXGroup; + children = ( + C8B92A9315735D2700284190 /* PVRChannel.cpp */, + C8B92A9415735D2700284190 /* PVRChannel.h */, + C8B92A9515735D2700284190 /* PVRChannelGroup.cpp */, + C8B92A9615735D2700284190 /* PVRChannelGroup.h */, + C8B92A9715735D2700284190 /* PVRChannelGroupInternal.cpp */, + C8B92A9815735D2700284190 /* PVRChannelGroupInternal.h */, + C8B92A9915735D2700284190 /* PVRChannelGroups.cpp */, + C8B92A9A15735D2700284190 /* PVRChannelGroups.h */, + C8B92A9B15735D2700284190 /* PVRChannelGroupsContainer.cpp */, + C8B92A9C15735D2700284190 /* PVRChannelGroupsContainer.h */, + ); + path = channels; + sourceTree = "<group>"; + }; + C8B92A9D15735D2700284190 /* dialogs */ = { + isa = PBXGroup; + children = ( + C8B92A9E15735D2700284190 /* GUIDialogPVRChannelManager.cpp */, + C8B92A9F15735D2700284190 /* GUIDialogPVRChannelManager.h */, + C8B92AA015735D2700284190 /* GUIDialogPVRChannelsOSD.cpp */, + C8B92AA115735D2700284190 /* GUIDialogPVRChannelsOSD.h */, + C8B92AA215735D2700284190 /* GUIDialogPVRCutterOSD.cpp */, + C8B92AA315735D2700284190 /* GUIDialogPVRCutterOSD.h */, + C8B92AA415735D2700284190 /* GUIDialogPVRDirectorOSD.cpp */, + C8B92AA515735D2700284190 /* GUIDialogPVRDirectorOSD.h */, + C8B92AA615735D2700284190 /* GUIDialogPVRGroupManager.cpp */, + C8B92AA715735D2700284190 /* GUIDialogPVRGroupManager.h */, + C8B92AA815735D2700284190 /* GUIDialogPVRGuideInfo.cpp */, + C8B92AA915735D2700284190 /* GUIDialogPVRGuideInfo.h */, + C8B92AAA15735D2700284190 /* GUIDialogPVRGuideOSD.cpp */, + C8B92AAB15735D2700284190 /* GUIDialogPVRGuideOSD.h */, + C8B92AAC15735D2700284190 /* GUIDialogPVRGuideSearch.cpp */, + C8B92AAD15735D2700284190 /* GUIDialogPVRGuideSearch.h */, + C8B92AAE15735D2700284190 /* GUIDialogPVRRecordingInfo.cpp */, + C8B92AAF15735D2700284190 /* GUIDialogPVRRecordingInfo.h */, + C8B92AB015735D2700284190 /* GUIDialogPVRTimerSettings.cpp */, + C8B92AB115735D2700284190 /* GUIDialogPVRTimerSettings.h */, + ); + path = dialogs; + sourceTree = "<group>"; + }; + C8B92ABA15735D2700284190 /* recordings */ = { + isa = PBXGroup; + children = ( + C8B92ABC15735D2700284190 /* PVRRecording.cpp */, + C8B92ABD15735D2700284190 /* PVRRecording.h */, + C8B92ABE15735D2700284190 /* PVRRecordings.cpp */, + C8B92ABF15735D2700284190 /* PVRRecordings.h */, + ); + path = recordings; + sourceTree = "<group>"; + }; + C8B92AC015735D2700284190 /* timers */ = { + isa = PBXGroup; + children = ( + C8B92AC215735D2700284190 /* PVRTimerInfoTag.cpp */, + C8B92AC315735D2700284190 /* PVRTimerInfoTag.h */, + C8B92AC415735D2700284190 /* PVRTimers.cpp */, + C8B92AC515735D2700284190 /* PVRTimers.h */, + ); + path = timers; + sourceTree = "<group>"; + }; + C8B92AC615735D2700284190 /* windows */ = { + isa = PBXGroup; + children = ( + C8B92AC715735D2700284190 /* GUIViewStatePVR.cpp */, + C8B92AC815735D2700284190 /* GUIViewStatePVR.h */, + C8B92AC915735D2700284190 /* GUIWindowPVR.cpp */, + C8B92ACA15735D2700284190 /* GUIWindowPVR.h */, + C8B92ACB15735D2700284190 /* GUIWindowPVRChannels.cpp */, + C8B92ACC15735D2700284190 /* GUIWindowPVRChannels.h */, + C8B92ACD15735D2700284190 /* GUIWindowPVRCommon.cpp */, + C8B92ACE15735D2700284190 /* GUIWindowPVRCommon.h */, + C8B92ACF15735D2700284190 /* GUIWindowPVRGuide.cpp */, + C8B92AD015735D2700284190 /* GUIWindowPVRGuide.h */, + C8B92AD115735D2700284190 /* GUIWindowPVRRecordings.cpp */, + C8B92AD215735D2700284190 /* GUIWindowPVRRecordings.h */, + C8B92AD315735D2700284190 /* GUIWindowPVRSearch.cpp */, + C8B92AD415735D2700284190 /* GUIWindowPVRSearch.h */, + C8B92AD515735D2700284190 /* GUIWindowPVRTimers.cpp */, + C8B92AD615735D2700284190 /* GUIWindowPVRTimers.h */, + ); + path = windows; + sourceTree = "<group>"; + }; DF33C2AA15509C5A0046CDCB /* commons */ = { isa = PBXGroup; children = ( @@ -3549,6 +3834,7 @@ F56C721B131EC151000AD0F6 /* cores */, F56C7357131EC151000AD0F6 /* dbwrappers */, F56C7362131EC151000AD0F6 /* dialogs */, + C8B92A7415735D0300284190 /* epg */, F56C7395131EC151000AD0F6 /* filesystem */, F56C749F131EC152000AD0F6 /* guilib */, F56C7553131EC152000AD0F6 /* input */, @@ -3562,6 +3848,7 @@ F56C7698131EC153000AD0F6 /* playlists */, F56C76AB131EC153000AD0F6 /* powermanagement */, F56C76B4131EC153000AD0F6 /* programs */, + C8B92A8A15735D2700284190 /* pvr */, F56C76BD131EC153000AD0F6 /* rendering */, F56C76C5131EC153000AD0F6 /* screensavers */, F56C76C6131EC153000AD0F6 /* settings */, @@ -3656,6 +3943,14 @@ children = ( F56C71E9131EC151000AD0F6 /* Addon.cpp */, F56C71EA131EC151000AD0F6 /* Addon.h */, + C8B92AFF15735D5D00284190 /* AddonCallbacks.cpp */, + C8B92B0015735D5D00284190 /* AddonCallbacks.h */, + C8B92B0115735D5D00284190 /* AddonCallbacksAddon.cpp */, + C8B92B0215735D5D00284190 /* AddonCallbacksAddon.h */, + C8B92B0315735D5D00284190 /* AddonCallbacksGUI.cpp */, + C8B92B0415735D5D00284190 /* AddonCallbacksGUI.h */, + C8B92B0515735D5D00284190 /* AddonCallbacksPVR.cpp */, + C8B92B0615735D5D00284190 /* AddonCallbacksPVR.h */, F56C71E3131EC151000AD0F6 /* AddonDatabase.cpp */, F56C71E4131EC151000AD0F6 /* AddonDatabase.h */, F56C71EB131EC151000AD0F6 /* AddonDll.h */, @@ -3980,6 +4275,8 @@ F56C7295131EC151000AD0F6 /* DVDDemuxFFmpeg.h */, F56C7296131EC151000AD0F6 /* DVDDemuxHTSP.cpp */, F56C7297131EC151000AD0F6 /* DVDDemuxHTSP.h */, + C8B92B0B15735DBC00284190 /* DVDDemuxPVRClient.cpp */, + C8B92B0C15735DBC00284190 /* DVDDemuxPVRClient.h */, F56C729A131EC151000AD0F6 /* DVDDemuxShoutcast.cpp */, F56C729B131EC151000AD0F6 /* DVDDemuxShoutcast.h */, F56C729C131EC151000AD0F6 /* DVDDemuxUtils.cpp */, @@ -4013,6 +4310,8 @@ F56C72B7131EC151000AD0F6 /* DVDInputStreamMemory.h */, F56C72B8131EC151000AD0F6 /* DVDInputStreamNavigator.cpp */, F56C72B9131EC151000AD0F6 /* DVDInputStreamNavigator.h */, + C8B92B0E15735DD900284190 /* DVDInputStreamPVRManager.cpp */, + C8B92B0F15735DD900284190 /* DVDInputStreamPVRManager.h */, F56C72A3131EC151000AD0F6 /* DVDInputStreamRTMP.cpp */, F56C72A4131EC151000AD0F6 /* DVDInputStreamRTMP.h */, F56C72A5131EC151000AD0F6 /* DVDInputStreamTV.cpp */, @@ -4206,6 +4505,8 @@ F56C736C131EC151000AD0F6 /* GUIDialogContextMenu.h */, F56C736D131EC151000AD0F6 /* GUIDialogFavourites.cpp */, F56C736E131EC151000AD0F6 /* GUIDialogFavourites.h */, + C8B92B1715735E1E00284190 /* GUIDialogExtendedProgressBar.cpp */, + C8B92B1815735E1E00284190 /* GUIDialogExtendedProgressBar.h */, F56C736F131EC151000AD0F6 /* GUIDialogFileBrowser.cpp */, F56C7370131EC151000AD0F6 /* GUIDialogFileBrowser.h */, F56C7371131EC151000AD0F6 /* GUIDialogGamepad.cpp */, @@ -4376,6 +4677,10 @@ F56C743F131EC152000AD0F6 /* PlaylistFileDirectory.h */, F56C7440131EC152000AD0F6 /* PluginDirectory.cpp */, F56C7441131EC152000AD0F6 /* PluginDirectory.h */, + C8B92B1115735DFB00284190 /* PVRDirectory.cpp */, + C8B92B1215735DFB00284190 /* PVRDirectory.h */, + C8B92B1315735DFB00284190 /* PVRFile.cpp */, + C8B92B1415735DFB00284190 /* PVRFile.h */, F56C7442131EC152000AD0F6 /* RarDirectory.cpp */, F56C7443131EC152000AD0F6 /* RarDirectory.h */, DF93D7561444B09C007C6459 /* RarFile.cpp */, @@ -5446,6 +5751,8 @@ F56C774D131EC154000AD0F6 /* md5.h */, 188F76271522186C009870CE /* Mime.cpp */, 188F76281522186C009870CE /* Mime.h */, + C8B92B1D15735EBF00284190 /* Observer.cpp */, + C8B92B1E15735EBF00284190 /* Observer.h */, F56C7752131EC154000AD0F6 /* PerformanceSample.cpp */, F56C7753131EC154000AD0F6 /* PerformanceSample.h */, F56C7754131EC154000AD0F6 /* PerformanceStats.cpp */, @@ -5481,6 +5788,8 @@ F56C7713131EC153000AD0F6 /* StringUtils.h */, F56C7768131EC154000AD0F6 /* SystemInfo.cpp */, F56C7769131EC154000AD0F6 /* SystemInfo.h */, + C8B92B1F15735EBF00284190 /* TextSearch.cpp */, + C8B92B2015735EBF00284190 /* TextSearch.h */, 7CEE2E6B13D6B7A8000ABF2A /* TimeSmoother.cpp */, 7CEE2E6C13D6B7A8000ABF2A /* TimeSmoother.h */, F56C776A131EC154000AD0F6 /* TimeUtils.cpp */, @@ -7238,6 +7547,55 @@ 7C0B990A154B80200065A238 /* AEDeviceInfo.cpp in Sources */, 7C6EB586155E3EC80080368A /* ImageFile.cpp in Sources */, 7C6EB708155F3B160080368A /* HTTPImageHandler.cpp in Sources */, + C8B92A8215735D0300284190 /* Epg.cpp in Sources */, + C8B92A8315735D0300284190 /* EpgContainer.cpp in Sources */, + C8B92A8415735D0300284190 /* EpgDatabase.cpp in Sources */, + C8B92A8515735D0300284190 /* EpgInfoTag.cpp in Sources */, + C8B92A8615735D0300284190 /* EpgSearchFilter.cpp in Sources */, + C8B92A8715735D0300284190 /* GUIEPGGridContainer.cpp in Sources */, + C8B92AD915735D2700284190 /* PVRClient.cpp in Sources */, + C8B92ADA15735D2700284190 /* PVRClients.cpp in Sources */, + C8B92ADC15735D2700284190 /* PVRChannel.cpp in Sources */, + C8B92ADD15735D2700284190 /* PVRChannelGroup.cpp in Sources */, + C8B92ADE15735D2700284190 /* PVRChannelGroupInternal.cpp in Sources */, + C8B92ADF15735D2700284190 /* PVRChannelGroups.cpp in Sources */, + C8B92AE015735D2700284190 /* PVRChannelGroupsContainer.cpp in Sources */, + C8B92AE115735D2700284190 /* GUIDialogPVRChannelManager.cpp in Sources */, + C8B92AE215735D2700284190 /* GUIDialogPVRChannelsOSD.cpp in Sources */, + C8B92AE315735D2700284190 /* GUIDialogPVRCutterOSD.cpp in Sources */, + C8B92AE415735D2700284190 /* GUIDialogPVRDirectorOSD.cpp in Sources */, + C8B92AE515735D2700284190 /* GUIDialogPVRGroupManager.cpp in Sources */, + C8B92AE615735D2700284190 /* GUIDialogPVRGuideInfo.cpp in Sources */, + C8B92AE715735D2700284190 /* GUIDialogPVRGuideOSD.cpp in Sources */, + C8B92AE815735D2700284190 /* GUIDialogPVRGuideSearch.cpp in Sources */, + C8B92AE915735D2700284190 /* GUIDialogPVRRecordingInfo.cpp in Sources */, + C8B92AEA15735D2700284190 /* GUIDialogPVRTimerSettings.cpp in Sources */, + C8B92AED15735D2700284190 /* PVRDatabase.cpp in Sources */, + C8B92AEE15735D2700284190 /* PVRGUIInfo.cpp in Sources */, + C8B92AEF15735D2700284190 /* PVRManager.cpp in Sources */, + C8B92AF115735D2700284190 /* PVRRecording.cpp in Sources */, + C8B92AF215735D2700284190 /* PVRRecordings.cpp in Sources */, + C8B92AF415735D2700284190 /* PVRTimerInfoTag.cpp in Sources */, + C8B92AF515735D2700284190 /* PVRTimers.cpp in Sources */, + C8B92AF615735D2700284190 /* GUIViewStatePVR.cpp in Sources */, + C8B92AF715735D2700284190 /* GUIWindowPVR.cpp in Sources */, + C8B92AF815735D2700284190 /* GUIWindowPVRChannels.cpp in Sources */, + C8B92AF915735D2700284190 /* GUIWindowPVRCommon.cpp in Sources */, + C8B92AFA15735D2700284190 /* GUIWindowPVRGuide.cpp in Sources */, + C8B92AFB15735D2700284190 /* GUIWindowPVRRecordings.cpp in Sources */, + C8B92AFC15735D2700284190 /* GUIWindowPVRSearch.cpp in Sources */, + C8B92AFD15735D2700284190 /* GUIWindowPVRTimers.cpp in Sources */, + C8B92B0715735D5D00284190 /* AddonCallbacks.cpp in Sources */, + C8B92B0815735D5D00284190 /* AddonCallbacksAddon.cpp in Sources */, + C8B92B0915735D5D00284190 /* AddonCallbacksGUI.cpp in Sources */, + C8B92B0A15735D5D00284190 /* AddonCallbacksPVR.cpp in Sources */, + C8B92B0D15735DBC00284190 /* DVDDemuxPVRClient.cpp in Sources */, + C8B92B1015735DD900284190 /* DVDInputStreamPVRManager.cpp in Sources */, + C8B92B1515735DFB00284190 /* PVRDirectory.cpp in Sources */, + C8B92B1615735DFB00284190 /* PVRFile.cpp in Sources */, + C8B92B1915735E1E00284190 /* GUIDialogExtendedProgressBar.cpp in Sources */, + C8B92B2115735EBF00284190 /* Observer.cpp in Sources */, + C8B92B2215735EBF00284190 /* TextSearch.cpp in Sources */, 18E7CAD11578C671001D4554 /* CDDARipJob.cpp in Sources */, 36A9445915821F8300727135 /* DatabaseUtils.cpp in Sources */, 36A9445D15821FAC00727135 /* SortUtils.cpp in Sources */, diff --git a/XBMC-IOS.xcodeproj/project.pbxproj b/XBMC-IOS.xcodeproj/project.pbxproj index cfb63ee130..99eecf2eab 100644 --- a/XBMC-IOS.xcodeproj/project.pbxproj +++ b/XBMC-IOS.xcodeproj/project.pbxproj @@ -46,6 +46,55 @@ C80711AD135DB85F002F601B /* InputOperations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C80711AB135DB85F002F601B /* InputOperations.cpp */; }; C893606F152C870600812418 /* monitor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C893606D152C870600812418 /* monitor.cpp */; }; C8936072152C871400812418 /* PythonMonitor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8936070152C871400812418 /* PythonMonitor.cpp */; }; + C8B929D01573557B00284190 /* Epg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929C31573557B00284190 /* Epg.cpp */; }; + C8B929D11573557B00284190 /* EpgContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929C51573557B00284190 /* EpgContainer.cpp */; }; + C8B929D21573557B00284190 /* EpgDatabase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929C71573557B00284190 /* EpgDatabase.cpp */; }; + C8B929D31573557B00284190 /* EpgInfoTag.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929C91573557B00284190 /* EpgInfoTag.cpp */; }; + C8B929D41573557B00284190 /* EpgSearchFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929CB1573557B00284190 /* EpgSearchFilter.cpp */; }; + C8B929D51573557B00284190 /* GUIEPGGridContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929CD1573557B00284190 /* GUIEPGGridContainer.cpp */; }; + C8B92A27157355F100284190 /* PVRClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929DB157355F000284190 /* PVRClient.cpp */; }; + C8B92A28157355F100284190 /* PVRClients.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929DD157355F000284190 /* PVRClients.cpp */; }; + C8B92A2A157355F100284190 /* PVRChannel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929E1157355F000284190 /* PVRChannel.cpp */; }; + C8B92A2B157355F100284190 /* PVRChannelGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929E3157355F000284190 /* PVRChannelGroup.cpp */; }; + C8B92A2C157355F100284190 /* PVRChannelGroupInternal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929E5157355F000284190 /* PVRChannelGroupInternal.cpp */; }; + C8B92A2D157355F100284190 /* PVRChannelGroups.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929E7157355F000284190 /* PVRChannelGroups.cpp */; }; + C8B92A2E157355F100284190 /* PVRChannelGroupsContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929E9157355F000284190 /* PVRChannelGroupsContainer.cpp */; }; + C8B92A2F157355F100284190 /* GUIDialogPVRChannelManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929EC157355F000284190 /* GUIDialogPVRChannelManager.cpp */; }; + C8B92A30157355F100284190 /* GUIDialogPVRChannelsOSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929EE157355F000284190 /* GUIDialogPVRChannelsOSD.cpp */; }; + C8B92A31157355F100284190 /* GUIDialogPVRCutterOSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929F0157355F000284190 /* GUIDialogPVRCutterOSD.cpp */; }; + C8B92A32157355F100284190 /* GUIDialogPVRDirectorOSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929F2157355F000284190 /* GUIDialogPVRDirectorOSD.cpp */; }; + C8B92A33157355F100284190 /* GUIDialogPVRGroupManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929F4157355F000284190 /* GUIDialogPVRGroupManager.cpp */; }; + C8B92A34157355F100284190 /* GUIDialogPVRGuideInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929F6157355F000284190 /* GUIDialogPVRGuideInfo.cpp */; }; + C8B92A35157355F100284190 /* GUIDialogPVRGuideOSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929F8157355F000284190 /* GUIDialogPVRGuideOSD.cpp */; }; + C8B92A36157355F100284190 /* GUIDialogPVRGuideSearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929FA157355F000284190 /* GUIDialogPVRGuideSearch.cpp */; }; + C8B92A37157355F100284190 /* GUIDialogPVRRecordingInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929FC157355F000284190 /* GUIDialogPVRRecordingInfo.cpp */; }; + C8B92A38157355F100284190 /* GUIDialogPVRTimerSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B929FE157355F000284190 /* GUIDialogPVRTimerSettings.cpp */; }; + C8B92A3B157355F100284190 /* PVRDatabase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A02157355F000284190 /* PVRDatabase.cpp */; }; + C8B92A3C157355F100284190 /* PVRGUIInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A04157355F000284190 /* PVRGUIInfo.cpp */; }; + C8B92A3D157355F100284190 /* PVRManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A06157355F000284190 /* PVRManager.cpp */; }; + C8B92A3F157355F100284190 /* PVRRecording.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A0A157355F000284190 /* PVRRecording.cpp */; }; + C8B92A40157355F100284190 /* PVRRecordings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A0C157355F000284190 /* PVRRecordings.cpp */; }; + C8B92A42157355F100284190 /* PVRTimerInfoTag.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A10157355F000284190 /* PVRTimerInfoTag.cpp */; }; + C8B92A43157355F100284190 /* PVRTimers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A12157355F000284190 /* PVRTimers.cpp */; }; + C8B92A44157355F100284190 /* GUIViewStatePVR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A15157355F000284190 /* GUIViewStatePVR.cpp */; }; + C8B92A45157355F100284190 /* GUIWindowPVR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A17157355F000284190 /* GUIWindowPVR.cpp */; }; + C8B92A46157355F100284190 /* GUIWindowPVRChannels.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A19157355F000284190 /* GUIWindowPVRChannels.cpp */; }; + C8B92A47157355F100284190 /* GUIWindowPVRCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A1B157355F000284190 /* GUIWindowPVRCommon.cpp */; }; + C8B92A48157355F100284190 /* GUIWindowPVRGuide.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A1D157355F000284190 /* GUIWindowPVRGuide.cpp */; }; + C8B92A49157355F100284190 /* GUIWindowPVRRecordings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A1F157355F000284190 /* GUIWindowPVRRecordings.cpp */; }; + C8B92A4A157355F100284190 /* GUIWindowPVRSearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A21157355F000284190 /* GUIWindowPVRSearch.cpp */; }; + C8B92A4B157355F100284190 /* GUIWindowPVRTimers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A23157355F000284190 /* GUIWindowPVRTimers.cpp */; }; + C8B92A4F1573566900284190 /* Observer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A4D1573566900284190 /* Observer.cpp */; }; + C8B92A58157356BE00284190 /* AddonCallbacks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A50157356BE00284190 /* AddonCallbacks.cpp */; }; + C8B92A59157356BE00284190 /* AddonCallbacksAddon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A52157356BE00284190 /* AddonCallbacksAddon.cpp */; }; + C8B92A5A157356BE00284190 /* AddonCallbacksGUI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A54157356BE00284190 /* AddonCallbacksGUI.cpp */; }; + C8B92A5B157356BE00284190 /* AddonCallbacksPVR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A56157356BE00284190 /* AddonCallbacksPVR.cpp */; }; + C8B92A5E1573571200284190 /* DVDDemuxPVRClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A5C1573571200284190 /* DVDDemuxPVRClient.cpp */; }; + C8B92A611573574900284190 /* DVDInputStreamPVRManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A5F1573574900284190 /* DVDInputStreamPVRManager.cpp */; }; + C8B92A661573578A00284190 /* PVRDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A621573578A00284190 /* PVRDirectory.cpp */; }; + C8B92A671573578A00284190 /* PVRFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A641573578A00284190 /* PVRFile.cpp */; }; + C8B92A6A157357C600284190 /* GUIDialogExtendedProgressBar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A68157357C600284190 /* GUIDialogExtendedProgressBar.cpp */; }; + C8B92A701573586300284190 /* TextSearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8B92A6E1573586300284190 /* TextSearch.cpp */; }; C8EC5D26136953E100CCC10D /* XBMC_keytable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8EC5D24136953E100CCC10D /* XBMC_keytable.cpp */; }; DF02A888153382A60084754E /* IOSKeyboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = DF02A887153382A60084754E /* IOSKeyboard.mm */; }; DF0DF18013A3AF9F008ED511 /* NFSDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF0DF17D13A3AF9F008ED511 /* NFSDirectory.cpp */; }; @@ -1068,6 +1117,104 @@ C893606E152C870600812418 /* monitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = monitor.h; sourceTree = "<group>"; }; C8936070152C871400812418 /* PythonMonitor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PythonMonitor.cpp; sourceTree = "<group>"; }; C8936071152C871400812418 /* PythonMonitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PythonMonitor.h; sourceTree = "<group>"; }; + C8B929C31573557B00284190 /* Epg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Epg.cpp; sourceTree = "<group>"; }; + C8B929C41573557B00284190 /* Epg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Epg.h; sourceTree = "<group>"; }; + C8B929C51573557B00284190 /* EpgContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EpgContainer.cpp; sourceTree = "<group>"; }; + C8B929C61573557B00284190 /* EpgContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EpgContainer.h; sourceTree = "<group>"; }; + C8B929C71573557B00284190 /* EpgDatabase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EpgDatabase.cpp; sourceTree = "<group>"; }; + C8B929C81573557B00284190 /* EpgDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EpgDatabase.h; sourceTree = "<group>"; }; + C8B929C91573557B00284190 /* EpgInfoTag.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EpgInfoTag.cpp; sourceTree = "<group>"; }; + C8B929CA1573557B00284190 /* EpgInfoTag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EpgInfoTag.h; sourceTree = "<group>"; }; + C8B929CB1573557B00284190 /* EpgSearchFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EpgSearchFilter.cpp; sourceTree = "<group>"; }; + C8B929CC1573557B00284190 /* EpgSearchFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EpgSearchFilter.h; sourceTree = "<group>"; }; + C8B929CD1573557B00284190 /* GUIEPGGridContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIEPGGridContainer.cpp; sourceTree = "<group>"; }; + C8B929CE1573557B00284190 /* GUIEPGGridContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIEPGGridContainer.h; sourceTree = "<group>"; }; + C8B929DB157355F000284190 /* PVRClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRClient.cpp; sourceTree = "<group>"; }; + C8B929DC157355F000284190 /* PVRClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRClient.h; sourceTree = "<group>"; }; + C8B929DD157355F000284190 /* PVRClients.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRClients.cpp; sourceTree = "<group>"; }; + C8B929DE157355F000284190 /* PVRClients.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRClients.h; sourceTree = "<group>"; }; + C8B929E1157355F000284190 /* PVRChannel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRChannel.cpp; sourceTree = "<group>"; }; + C8B929E2157355F000284190 /* PVRChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRChannel.h; sourceTree = "<group>"; }; + C8B929E3157355F000284190 /* PVRChannelGroup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRChannelGroup.cpp; sourceTree = "<group>"; }; + C8B929E4157355F000284190 /* PVRChannelGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRChannelGroup.h; sourceTree = "<group>"; }; + C8B929E5157355F000284190 /* PVRChannelGroupInternal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRChannelGroupInternal.cpp; sourceTree = "<group>"; }; + C8B929E6157355F000284190 /* PVRChannelGroupInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRChannelGroupInternal.h; sourceTree = "<group>"; }; + C8B929E7157355F000284190 /* PVRChannelGroups.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRChannelGroups.cpp; sourceTree = "<group>"; }; + C8B929E8157355F000284190 /* PVRChannelGroups.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRChannelGroups.h; sourceTree = "<group>"; }; + C8B929E9157355F000284190 /* PVRChannelGroupsContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRChannelGroupsContainer.cpp; sourceTree = "<group>"; }; + C8B929EA157355F000284190 /* PVRChannelGroupsContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRChannelGroupsContainer.h; sourceTree = "<group>"; }; + C8B929EC157355F000284190 /* GUIDialogPVRChannelManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRChannelManager.cpp; sourceTree = "<group>"; }; + C8B929ED157355F000284190 /* GUIDialogPVRChannelManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRChannelManager.h; sourceTree = "<group>"; }; + C8B929EE157355F000284190 /* GUIDialogPVRChannelsOSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRChannelsOSD.cpp; sourceTree = "<group>"; }; + C8B929EF157355F000284190 /* GUIDialogPVRChannelsOSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRChannelsOSD.h; sourceTree = "<group>"; }; + C8B929F0157355F000284190 /* GUIDialogPVRCutterOSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRCutterOSD.cpp; sourceTree = "<group>"; }; + C8B929F1157355F000284190 /* GUIDialogPVRCutterOSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRCutterOSD.h; sourceTree = "<group>"; }; + C8B929F2157355F000284190 /* GUIDialogPVRDirectorOSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRDirectorOSD.cpp; sourceTree = "<group>"; }; + C8B929F3157355F000284190 /* GUIDialogPVRDirectorOSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRDirectorOSD.h; sourceTree = "<group>"; }; + C8B929F4157355F000284190 /* GUIDialogPVRGroupManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRGroupManager.cpp; sourceTree = "<group>"; }; + C8B929F5157355F000284190 /* GUIDialogPVRGroupManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRGroupManager.h; sourceTree = "<group>"; }; + C8B929F6157355F000284190 /* GUIDialogPVRGuideInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRGuideInfo.cpp; sourceTree = "<group>"; }; + C8B929F7157355F000284190 /* GUIDialogPVRGuideInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRGuideInfo.h; sourceTree = "<group>"; }; + C8B929F8157355F000284190 /* GUIDialogPVRGuideOSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRGuideOSD.cpp; sourceTree = "<group>"; }; + C8B929F9157355F000284190 /* GUIDialogPVRGuideOSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRGuideOSD.h; sourceTree = "<group>"; }; + C8B929FA157355F000284190 /* GUIDialogPVRGuideSearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRGuideSearch.cpp; sourceTree = "<group>"; }; + C8B929FB157355F000284190 /* GUIDialogPVRGuideSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRGuideSearch.h; sourceTree = "<group>"; }; + C8B929FC157355F000284190 /* GUIDialogPVRRecordingInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRRecordingInfo.cpp; sourceTree = "<group>"; }; + C8B929FD157355F000284190 /* GUIDialogPVRRecordingInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRRecordingInfo.h; sourceTree = "<group>"; }; + C8B929FE157355F000284190 /* GUIDialogPVRTimerSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRTimerSettings.cpp; sourceTree = "<group>"; }; + C8B929FF157355F000284190 /* GUIDialogPVRTimerSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRTimerSettings.h; sourceTree = "<group>"; }; + C8B92A02157355F000284190 /* PVRDatabase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRDatabase.cpp; sourceTree = "<group>"; }; + C8B92A03157355F000284190 /* PVRDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRDatabase.h; sourceTree = "<group>"; }; + C8B92A04157355F000284190 /* PVRGUIInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRGUIInfo.cpp; sourceTree = "<group>"; }; + C8B92A05157355F000284190 /* PVRGUIInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRGUIInfo.h; sourceTree = "<group>"; }; + C8B92A06157355F000284190 /* PVRManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRManager.cpp; sourceTree = "<group>"; }; + C8B92A07157355F000284190 /* PVRManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRManager.h; sourceTree = "<group>"; }; + C8B92A0A157355F000284190 /* PVRRecording.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRRecording.cpp; sourceTree = "<group>"; }; + C8B92A0B157355F000284190 /* PVRRecording.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRRecording.h; sourceTree = "<group>"; }; + C8B92A0C157355F000284190 /* PVRRecordings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRRecordings.cpp; sourceTree = "<group>"; }; + C8B92A0D157355F000284190 /* PVRRecordings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRRecordings.h; sourceTree = "<group>"; }; + C8B92A10157355F000284190 /* PVRTimerInfoTag.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRTimerInfoTag.cpp; sourceTree = "<group>"; }; + C8B92A11157355F000284190 /* PVRTimerInfoTag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRTimerInfoTag.h; sourceTree = "<group>"; }; + C8B92A12157355F000284190 /* PVRTimers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRTimers.cpp; sourceTree = "<group>"; }; + C8B92A13157355F000284190 /* PVRTimers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRTimers.h; sourceTree = "<group>"; }; + C8B92A15157355F000284190 /* GUIViewStatePVR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIViewStatePVR.cpp; sourceTree = "<group>"; }; + C8B92A16157355F000284190 /* GUIViewStatePVR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIViewStatePVR.h; sourceTree = "<group>"; }; + C8B92A17157355F000284190 /* GUIWindowPVR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVR.cpp; sourceTree = "<group>"; }; + C8B92A18157355F000284190 /* GUIWindowPVR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVR.h; sourceTree = "<group>"; }; + C8B92A19157355F000284190 /* GUIWindowPVRChannels.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRChannels.cpp; sourceTree = "<group>"; }; + C8B92A1A157355F000284190 /* GUIWindowPVRChannels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRChannels.h; sourceTree = "<group>"; }; + C8B92A1B157355F000284190 /* GUIWindowPVRCommon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRCommon.cpp; sourceTree = "<group>"; }; + C8B92A1C157355F000284190 /* GUIWindowPVRCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRCommon.h; sourceTree = "<group>"; }; + C8B92A1D157355F000284190 /* GUIWindowPVRGuide.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRGuide.cpp; sourceTree = "<group>"; }; + C8B92A1E157355F000284190 /* GUIWindowPVRGuide.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRGuide.h; sourceTree = "<group>"; }; + C8B92A1F157355F000284190 /* GUIWindowPVRRecordings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRRecordings.cpp; sourceTree = "<group>"; }; + C8B92A20157355F000284190 /* GUIWindowPVRRecordings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRRecordings.h; sourceTree = "<group>"; }; + C8B92A21157355F000284190 /* GUIWindowPVRSearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRSearch.cpp; sourceTree = "<group>"; }; + C8B92A22157355F000284190 /* GUIWindowPVRSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRSearch.h; sourceTree = "<group>"; }; + C8B92A23157355F000284190 /* GUIWindowPVRTimers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRTimers.cpp; sourceTree = "<group>"; }; + C8B92A24157355F000284190 /* GUIWindowPVRTimers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRTimers.h; sourceTree = "<group>"; }; + C8B92A4D1573566900284190 /* Observer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Observer.cpp; sourceTree = "<group>"; }; + C8B92A4E1573566900284190 /* Observer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Observer.h; sourceTree = "<group>"; }; + C8B92A50157356BE00284190 /* AddonCallbacks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AddonCallbacks.cpp; sourceTree = "<group>"; }; + C8B92A51157356BE00284190 /* AddonCallbacks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddonCallbacks.h; sourceTree = "<group>"; }; + C8B92A52157356BE00284190 /* AddonCallbacksAddon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AddonCallbacksAddon.cpp; sourceTree = "<group>"; }; + C8B92A53157356BE00284190 /* AddonCallbacksAddon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddonCallbacksAddon.h; sourceTree = "<group>"; }; + C8B92A54157356BE00284190 /* AddonCallbacksGUI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AddonCallbacksGUI.cpp; sourceTree = "<group>"; }; + C8B92A55157356BE00284190 /* AddonCallbacksGUI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddonCallbacksGUI.h; sourceTree = "<group>"; }; + C8B92A56157356BE00284190 /* AddonCallbacksPVR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AddonCallbacksPVR.cpp; sourceTree = "<group>"; }; + C8B92A57157356BE00284190 /* AddonCallbacksPVR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddonCallbacksPVR.h; sourceTree = "<group>"; }; + C8B92A5C1573571200284190 /* DVDDemuxPVRClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDDemuxPVRClient.cpp; sourceTree = "<group>"; }; + C8B92A5D1573571200284190 /* DVDDemuxPVRClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDDemuxPVRClient.h; sourceTree = "<group>"; }; + C8B92A5F1573574900284190 /* DVDInputStreamPVRManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDInputStreamPVRManager.cpp; sourceTree = "<group>"; }; + C8B92A601573574900284190 /* DVDInputStreamPVRManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDInputStreamPVRManager.h; sourceTree = "<group>"; }; + C8B92A621573578A00284190 /* PVRDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRDirectory.cpp; sourceTree = "<group>"; }; + C8B92A631573578A00284190 /* PVRDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRDirectory.h; sourceTree = "<group>"; }; + C8B92A641573578A00284190 /* PVRFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRFile.cpp; sourceTree = "<group>"; }; + C8B92A651573578A00284190 /* PVRFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRFile.h; sourceTree = "<group>"; }; + C8B92A68157357C600284190 /* GUIDialogExtendedProgressBar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogExtendedProgressBar.cpp; sourceTree = "<group>"; }; + C8B92A69157357C600284190 /* GUIDialogExtendedProgressBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogExtendedProgressBar.h; sourceTree = "<group>"; }; + C8B92A6E1573586300284190 /* TextSearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextSearch.cpp; sourceTree = "<group>"; }; + C8B92A6F1573586300284190 /* TextSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextSearch.h; sourceTree = "<group>"; }; C8EC5D24136953E100CCC10D /* XBMC_keytable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = XBMC_keytable.cpp; sourceTree = "<group>"; }; C8EC5D25136953E100CCC10D /* XBMC_keytable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMC_keytable.h; sourceTree = "<group>"; }; DF02A831153373EC0084754E /* GUIKeyboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIKeyboard.h; sourceTree = "<group>"; }; @@ -3150,6 +3297,144 @@ name = Documentation; sourceTree = "<group>"; }; + C8B929C21573557B00284190 /* epg */ = { + isa = PBXGroup; + children = ( + C8B929C31573557B00284190 /* Epg.cpp */, + C8B929C41573557B00284190 /* Epg.h */, + C8B929C51573557B00284190 /* EpgContainer.cpp */, + C8B929C61573557B00284190 /* EpgContainer.h */, + C8B929C71573557B00284190 /* EpgDatabase.cpp */, + C8B929C81573557B00284190 /* EpgDatabase.h */, + C8B929C91573557B00284190 /* EpgInfoTag.cpp */, + C8B929CA1573557B00284190 /* EpgInfoTag.h */, + C8B929CB1573557B00284190 /* EpgSearchFilter.cpp */, + C8B929CC1573557B00284190 /* EpgSearchFilter.h */, + C8B929CD1573557B00284190 /* GUIEPGGridContainer.cpp */, + C8B929CE1573557B00284190 /* GUIEPGGridContainer.h */, + ); + path = epg; + sourceTree = "<group>"; + }; + C8B929D8157355F000284190 /* pvr */ = { + isa = PBXGroup; + children = ( + C8B929D9157355F000284190 /* addons */, + C8B929DF157355F000284190 /* channels */, + C8B929EB157355F000284190 /* dialogs */, + C8B92A08157355F000284190 /* recordings */, + C8B92A0E157355F000284190 /* timers */, + C8B92A14157355F000284190 /* windows */, + C8B92A02157355F000284190 /* PVRDatabase.cpp */, + C8B92A03157355F000284190 /* PVRDatabase.h */, + C8B92A04157355F000284190 /* PVRGUIInfo.cpp */, + C8B92A05157355F000284190 /* PVRGUIInfo.h */, + C8B92A06157355F000284190 /* PVRManager.cpp */, + C8B92A07157355F000284190 /* PVRManager.h */, + ); + path = pvr; + sourceTree = "<group>"; + }; + C8B929D9157355F000284190 /* addons */ = { + isa = PBXGroup; + children = ( + C8B929DB157355F000284190 /* PVRClient.cpp */, + C8B929DC157355F000284190 /* PVRClient.h */, + C8B929DD157355F000284190 /* PVRClients.cpp */, + C8B929DE157355F000284190 /* PVRClients.h */, + ); + path = addons; + sourceTree = "<group>"; + }; + C8B929DF157355F000284190 /* channels */ = { + isa = PBXGroup; + children = ( + C8B929E1157355F000284190 /* PVRChannel.cpp */, + C8B929E2157355F000284190 /* PVRChannel.h */, + C8B929E3157355F000284190 /* PVRChannelGroup.cpp */, + C8B929E4157355F000284190 /* PVRChannelGroup.h */, + C8B929E5157355F000284190 /* PVRChannelGroupInternal.cpp */, + C8B929E6157355F000284190 /* PVRChannelGroupInternal.h */, + C8B929E7157355F000284190 /* PVRChannelGroups.cpp */, + C8B929E8157355F000284190 /* PVRChannelGroups.h */, + C8B929E9157355F000284190 /* PVRChannelGroupsContainer.cpp */, + C8B929EA157355F000284190 /* PVRChannelGroupsContainer.h */, + ); + path = channels; + sourceTree = "<group>"; + }; + C8B929EB157355F000284190 /* dialogs */ = { + isa = PBXGroup; + children = ( + C8B929EC157355F000284190 /* GUIDialogPVRChannelManager.cpp */, + C8B929ED157355F000284190 /* GUIDialogPVRChannelManager.h */, + C8B929EE157355F000284190 /* GUIDialogPVRChannelsOSD.cpp */, + C8B929EF157355F000284190 /* GUIDialogPVRChannelsOSD.h */, + C8B929F0157355F000284190 /* GUIDialogPVRCutterOSD.cpp */, + C8B929F1157355F000284190 /* GUIDialogPVRCutterOSD.h */, + C8B929F2157355F000284190 /* GUIDialogPVRDirectorOSD.cpp */, + C8B929F3157355F000284190 /* GUIDialogPVRDirectorOSD.h */, + C8B929F4157355F000284190 /* GUIDialogPVRGroupManager.cpp */, + C8B929F5157355F000284190 /* GUIDialogPVRGroupManager.h */, + C8B929F6157355F000284190 /* GUIDialogPVRGuideInfo.cpp */, + C8B929F7157355F000284190 /* GUIDialogPVRGuideInfo.h */, + C8B929F8157355F000284190 /* GUIDialogPVRGuideOSD.cpp */, + C8B929F9157355F000284190 /* GUIDialogPVRGuideOSD.h */, + C8B929FA157355F000284190 /* GUIDialogPVRGuideSearch.cpp */, + C8B929FB157355F000284190 /* GUIDialogPVRGuideSearch.h */, + C8B929FC157355F000284190 /* GUIDialogPVRRecordingInfo.cpp */, + C8B929FD157355F000284190 /* GUIDialogPVRRecordingInfo.h */, + C8B929FE157355F000284190 /* GUIDialogPVRTimerSettings.cpp */, + C8B929FF157355F000284190 /* GUIDialogPVRTimerSettings.h */, + ); + path = dialogs; + sourceTree = "<group>"; + }; + C8B92A08157355F000284190 /* recordings */ = { + isa = PBXGroup; + children = ( + C8B92A0A157355F000284190 /* PVRRecording.cpp */, + C8B92A0B157355F000284190 /* PVRRecording.h */, + C8B92A0C157355F000284190 /* PVRRecordings.cpp */, + C8B92A0D157355F000284190 /* PVRRecordings.h */, + ); + path = recordings; + sourceTree = "<group>"; + }; + C8B92A0E157355F000284190 /* timers */ = { + isa = PBXGroup; + children = ( + C8B92A10157355F000284190 /* PVRTimerInfoTag.cpp */, + C8B92A11157355F000284190 /* PVRTimerInfoTag.h */, + C8B92A12157355F000284190 /* PVRTimers.cpp */, + C8B92A13157355F000284190 /* PVRTimers.h */, + ); + path = timers; + sourceTree = "<group>"; + }; + C8B92A14157355F000284190 /* windows */ = { + isa = PBXGroup; + children = ( + C8B92A15157355F000284190 /* GUIViewStatePVR.cpp */, + C8B92A16157355F000284190 /* GUIViewStatePVR.h */, + C8B92A17157355F000284190 /* GUIWindowPVR.cpp */, + C8B92A18157355F000284190 /* GUIWindowPVR.h */, + C8B92A19157355F000284190 /* GUIWindowPVRChannels.cpp */, + C8B92A1A157355F000284190 /* GUIWindowPVRChannels.h */, + C8B92A1B157355F000284190 /* GUIWindowPVRCommon.cpp */, + C8B92A1C157355F000284190 /* GUIWindowPVRCommon.h */, + C8B92A1D157355F000284190 /* GUIWindowPVRGuide.cpp */, + C8B92A1E157355F000284190 /* GUIWindowPVRGuide.h */, + C8B92A1F157355F000284190 /* GUIWindowPVRRecordings.cpp */, + C8B92A20157355F000284190 /* GUIWindowPVRRecordings.h */, + C8B92A21157355F000284190 /* GUIWindowPVRSearch.cpp */, + C8B92A22157355F000284190 /* GUIWindowPVRSearch.h */, + C8B92A23157355F000284190 /* GUIWindowPVRTimers.cpp */, + C8B92A24157355F000284190 /* GUIWindowPVRTimers.h */, + ); + path = windows; + sourceTree = "<group>"; + }; DF33C29015509BF50046CDCB /* commons */ = { isa = PBXGroup; children = ( @@ -3912,6 +4197,7 @@ F56C81FD131F42E6000AD0F6 /* cores */, F56C833A131F42E7000AD0F6 /* dbwrappers */, F56C8345131F42E7000AD0F6 /* dialogs */, + C8B929C21573557B00284190 /* epg */, F56C8378131F42E8000AD0F6 /* filesystem */, F56C8482131F42E9000AD0F6 /* guilib */, F56C8536131F42E9000AD0F6 /* input */, @@ -3925,6 +4211,7 @@ F56C8685131F42EB000AD0F6 /* playlists */, F56C8698131F42EB000AD0F6 /* powermanagement */, F56C86A1131F42EB000AD0F6 /* programs */, + C8B929D8157355F000284190 /* pvr */, F56C86AA131F42EB000AD0F6 /* rendering */, F56C86B0131F42EB000AD0F6 /* screensavers */, F56C86B1131F42EB000AD0F6 /* settings */, @@ -4019,6 +4306,14 @@ children = ( F56C81CB131F42E6000AD0F6 /* Addon.cpp */, F56C81CC131F42E6000AD0F6 /* Addon.h */, + C8B92A50157356BE00284190 /* AddonCallbacks.cpp */, + C8B92A51157356BE00284190 /* AddonCallbacks.h */, + C8B92A52157356BE00284190 /* AddonCallbacksAddon.cpp */, + C8B92A53157356BE00284190 /* AddonCallbacksAddon.h */, + C8B92A54157356BE00284190 /* AddonCallbacksGUI.cpp */, + C8B92A55157356BE00284190 /* AddonCallbacksGUI.h */, + C8B92A56157356BE00284190 /* AddonCallbacksPVR.cpp */, + C8B92A57157356BE00284190 /* AddonCallbacksPVR.h */, F56C81C5131F42E6000AD0F6 /* AddonDatabase.cpp */, F56C81C6131F42E6000AD0F6 /* AddonDatabase.h */, F56C81CD131F42E6000AD0F6 /* AddonDll.h */, @@ -4331,6 +4626,8 @@ F56C827B131F42E7000AD0F6 /* DVDDemuxFFmpeg.h */, F56C827C131F42E7000AD0F6 /* DVDDemuxHTSP.cpp */, F56C827D131F42E7000AD0F6 /* DVDDemuxHTSP.h */, + C8B92A5C1573571200284190 /* DVDDemuxPVRClient.cpp */, + C8B92A5D1573571200284190 /* DVDDemuxPVRClient.h */, F56C8280131F42E7000AD0F6 /* DVDDemuxShoutcast.cpp */, F56C8281131F42E7000AD0F6 /* DVDDemuxShoutcast.h */, F56C8282131F42E7000AD0F6 /* DVDDemuxUtils.cpp */, @@ -4364,6 +4661,8 @@ F56C829D131F42E7000AD0F6 /* DVDInputStreamMemory.h */, F56C829E131F42E7000AD0F6 /* DVDInputStreamNavigator.cpp */, F56C829F131F42E7000AD0F6 /* DVDInputStreamNavigator.h */, + C8B92A5F1573574900284190 /* DVDInputStreamPVRManager.cpp */, + C8B92A601573574900284190 /* DVDInputStreamPVRManager.h */, F56C8289131F42E7000AD0F6 /* DVDInputStreamRTMP.cpp */, F56C828A131F42E7000AD0F6 /* DVDInputStreamRTMP.h */, F56C828B131F42E7000AD0F6 /* DVDInputStreamTV.cpp */, @@ -4568,6 +4867,8 @@ F56C834E131F42E8000AD0F6 /* GUIDialogContextMenu.cpp */, F56C834F131F42E8000AD0F6 /* GUIDialogContextMenu.h */, F56C8350131F42E8000AD0F6 /* GUIDialogFavourites.cpp */, + C8B92A68157357C600284190 /* GUIDialogExtendedProgressBar.cpp */, + C8B92A69157357C600284190 /* GUIDialogExtendedProgressBar.h */, F56C8351131F42E8000AD0F6 /* GUIDialogFavourites.h */, F56C8352131F42E8000AD0F6 /* GUIDialogFileBrowser.cpp */, F56C8353131F42E8000AD0F6 /* GUIDialogFileBrowser.h */, @@ -4739,6 +5040,10 @@ F56C8422131F42E8000AD0F6 /* PlaylistFileDirectory.h */, F56C8423131F42E8000AD0F6 /* PluginDirectory.cpp */, F56C8424131F42E8000AD0F6 /* PluginDirectory.h */, + C8B92A621573578A00284190 /* PVRDirectory.cpp */, + C8B92A631573578A00284190 /* PVRDirectory.h */, + C8B92A641573578A00284190 /* PVRFile.cpp */, + C8B92A651573578A00284190 /* PVRFile.h */, F56C8425131F42E8000AD0F6 /* RarDirectory.cpp */, F56C8426131F42E8000AD0F6 /* RarDirectory.h */, DF93D7B51444B105007C6459 /* RarFile.cpp */, @@ -5819,6 +6124,8 @@ F56C873C131F42EC000AD0F6 /* md5.h */, 188F761F1522184E009870CE /* Mime.cpp */, 188F76201522184E009870CE /* Mime.h */, + C8B92A4D1573566900284190 /* Observer.cpp */, + C8B92A4E1573566900284190 /* Observer.h */, F56C8741131F42EC000AD0F6 /* PerformanceSample.cpp */, F56C8742131F42EC000AD0F6 /* PerformanceSample.h */, F56C8743131F42EC000AD0F6 /* PerformanceStats.cpp */, @@ -5854,6 +6161,8 @@ F56C8702131F42EB000AD0F6 /* StringUtils.h */, F56C8757131F42EC000AD0F6 /* SystemInfo.cpp */, F56C8758131F42EC000AD0F6 /* SystemInfo.h */, + C8B92A6E1573586300284190 /* TextSearch.cpp */, + C8B92A6F1573586300284190 /* TextSearch.h */, 7CEE2E7D13D6B7D4000ABF2A /* TimeSmoother.cpp */, 7CEE2E7E13D6B7D4000ABF2A /* TimeSmoother.h */, F56C8759131F42EC000AD0F6 /* TimeUtils.cpp */, @@ -7261,6 +7570,55 @@ 7C0B98F9154B7FF30065A238 /* AEDeviceInfo.cpp in Sources */, 7C6EB570155E3E680080368A /* ImageFile.cpp in Sources */, 7C6EB71A155F3B330080368A /* HTTPImageHandler.cpp in Sources */, + C8B929D01573557B00284190 /* Epg.cpp in Sources */, + C8B929D11573557B00284190 /* EpgContainer.cpp in Sources */, + C8B929D21573557B00284190 /* EpgDatabase.cpp in Sources */, + C8B929D31573557B00284190 /* EpgInfoTag.cpp in Sources */, + C8B929D41573557B00284190 /* EpgSearchFilter.cpp in Sources */, + C8B929D51573557B00284190 /* GUIEPGGridContainer.cpp in Sources */, + C8B92A27157355F100284190 /* PVRClient.cpp in Sources */, + C8B92A28157355F100284190 /* PVRClients.cpp in Sources */, + C8B92A2A157355F100284190 /* PVRChannel.cpp in Sources */, + C8B92A2B157355F100284190 /* PVRChannelGroup.cpp in Sources */, + C8B92A2C157355F100284190 /* PVRChannelGroupInternal.cpp in Sources */, + C8B92A2D157355F100284190 /* PVRChannelGroups.cpp in Sources */, + C8B92A2E157355F100284190 /* PVRChannelGroupsContainer.cpp in Sources */, + C8B92A2F157355F100284190 /* GUIDialogPVRChannelManager.cpp in Sources */, + C8B92A30157355F100284190 /* GUIDialogPVRChannelsOSD.cpp in Sources */, + C8B92A31157355F100284190 /* GUIDialogPVRCutterOSD.cpp in Sources */, + C8B92A32157355F100284190 /* GUIDialogPVRDirectorOSD.cpp in Sources */, + C8B92A33157355F100284190 /* GUIDialogPVRGroupManager.cpp in Sources */, + C8B92A34157355F100284190 /* GUIDialogPVRGuideInfo.cpp in Sources */, + C8B92A35157355F100284190 /* GUIDialogPVRGuideOSD.cpp in Sources */, + C8B92A36157355F100284190 /* GUIDialogPVRGuideSearch.cpp in Sources */, + C8B92A37157355F100284190 /* GUIDialogPVRRecordingInfo.cpp in Sources */, + C8B92A38157355F100284190 /* GUIDialogPVRTimerSettings.cpp in Sources */, + C8B92A3B157355F100284190 /* PVRDatabase.cpp in Sources */, + C8B92A3C157355F100284190 /* PVRGUIInfo.cpp in Sources */, + C8B92A3D157355F100284190 /* PVRManager.cpp in Sources */, + C8B92A3F157355F100284190 /* PVRRecording.cpp in Sources */, + C8B92A40157355F100284190 /* PVRRecordings.cpp in Sources */, + C8B92A42157355F100284190 /* PVRTimerInfoTag.cpp in Sources */, + C8B92A43157355F100284190 /* PVRTimers.cpp in Sources */, + C8B92A44157355F100284190 /* GUIViewStatePVR.cpp in Sources */, + C8B92A45157355F100284190 /* GUIWindowPVR.cpp in Sources */, + C8B92A46157355F100284190 /* GUIWindowPVRChannels.cpp in Sources */, + C8B92A47157355F100284190 /* GUIWindowPVRCommon.cpp in Sources */, + C8B92A48157355F100284190 /* GUIWindowPVRGuide.cpp in Sources */, + C8B92A49157355F100284190 /* GUIWindowPVRRecordings.cpp in Sources */, + C8B92A4A157355F100284190 /* GUIWindowPVRSearch.cpp in Sources */, + C8B92A4B157355F100284190 /* GUIWindowPVRTimers.cpp in Sources */, + C8B92A4F1573566900284190 /* Observer.cpp in Sources */, + C8B92A58157356BE00284190 /* AddonCallbacks.cpp in Sources */, + C8B92A59157356BE00284190 /* AddonCallbacksAddon.cpp in Sources */, + C8B92A5A157356BE00284190 /* AddonCallbacksGUI.cpp in Sources */, + C8B92A5B157356BE00284190 /* AddonCallbacksPVR.cpp in Sources */, + C8B92A5E1573571200284190 /* DVDDemuxPVRClient.cpp in Sources */, + C8B92A611573574900284190 /* DVDInputStreamPVRManager.cpp in Sources */, + C8B92A661573578A00284190 /* PVRDirectory.cpp in Sources */, + C8B92A671573578A00284190 /* PVRFile.cpp in Sources */, + C8B92A6A157357C600284190 /* GUIDialogExtendedProgressBar.cpp in Sources */, + C8B92A701573586300284190 /* TextSearch.cpp in Sources */, 18E7CAD71578C691001D4554 /* CDDARipJob.cpp in Sources */, 36A9444E15821F2C00727135 /* DatabaseUtils.cpp in Sources */, 36A9445215821F5300727135 /* SortUtils.cpp in Sources */, @@ -7297,7 +7655,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CODE_SIGN_IDENTITY = "Don't Code Sign"; + CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = NO; EXECUTABLE_EXTENSION = ""; @@ -7415,7 +7773,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CODE_SIGN_IDENTITY = "Don't Code Sign"; + CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; @@ -7535,7 +7893,7 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_UNIVERSAL_IPHONE_OS)"; - CODE_SIGN_IDENTITY = "Don't Code Sign"; + CODE_SIGN_IDENTITY = ""; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_MODEL_TUNING = ""; @@ -7559,7 +7917,7 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_UNIVERSAL_IPHONE_OS)"; - CODE_SIGN_IDENTITY = "Don't Code Sign"; + CODE_SIGN_IDENTITY = ""; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_MODEL_TUNING = ""; diff --git a/XBMC.xcodeproj/project.pbxproj b/XBMC.xcodeproj/project.pbxproj index 611aaa00a2..01697f6a34 100644 --- a/XBMC.xcodeproj/project.pbxproj +++ b/XBMC.xcodeproj/project.pbxproj @@ -325,6 +325,55 @@ 88ACB01F0DCF409E0083CFDF /* ASAPCodec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 88ACB01C0DCF409E0083CFDF /* ASAPCodec.cpp */; }; C80425711158A0DE00D158A6 /* controlslider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C80425701158A0DE00D158A6 /* controlslider.cpp */; settings = {COMPILER_FLAGS = "-I$XBMC_DEPENDS/include/python2.6"; }; }; C807114D135DB5CC002F601B /* InputOperations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C807114B135DB5CC002F601B /* InputOperations.cpp */; }; + C84828C0156CFCD8005A996F /* PVRClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482874156CFCD8005A996F /* PVRClient.cpp */; }; + C84828C1156CFCD8005A996F /* PVRClients.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482876156CFCD8005A996F /* PVRClients.cpp */; }; + C84828C3156CFCD8005A996F /* PVRChannel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C848287A156CFCD8005A996F /* PVRChannel.cpp */; }; + C84828C4156CFCD8005A996F /* PVRChannelGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C848287C156CFCD8005A996F /* PVRChannelGroup.cpp */; }; + C84828C5156CFCD8005A996F /* PVRChannelGroupInternal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C848287E156CFCD8005A996F /* PVRChannelGroupInternal.cpp */; }; + C84828C6156CFCD8005A996F /* PVRChannelGroups.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482880156CFCD8005A996F /* PVRChannelGroups.cpp */; }; + C84828C7156CFCD8005A996F /* PVRChannelGroupsContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482882156CFCD8005A996F /* PVRChannelGroupsContainer.cpp */; }; + C84828C8156CFCD8005A996F /* GUIDialogPVRChannelManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482885156CFCD8005A996F /* GUIDialogPVRChannelManager.cpp */; }; + C84828C9156CFCD8005A996F /* GUIDialogPVRChannelsOSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482887156CFCD8005A996F /* GUIDialogPVRChannelsOSD.cpp */; }; + C84828CA156CFCD8005A996F /* GUIDialogPVRCutterOSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482889156CFCD8005A996F /* GUIDialogPVRCutterOSD.cpp */; }; + C84828CB156CFCD8005A996F /* GUIDialogPVRDirectorOSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C848288B156CFCD8005A996F /* GUIDialogPVRDirectorOSD.cpp */; }; + C84828CC156CFCD8005A996F /* GUIDialogPVRGroupManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C848288D156CFCD8005A996F /* GUIDialogPVRGroupManager.cpp */; }; + C84828CD156CFCD8005A996F /* GUIDialogPVRGuideInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C848288F156CFCD8005A996F /* GUIDialogPVRGuideInfo.cpp */; }; + C84828CE156CFCD8005A996F /* GUIDialogPVRGuideOSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482891156CFCD8005A996F /* GUIDialogPVRGuideOSD.cpp */; }; + C84828CF156CFCD8005A996F /* GUIDialogPVRGuideSearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482893156CFCD8005A996F /* GUIDialogPVRGuideSearch.cpp */; }; + C84828D0156CFCD8005A996F /* GUIDialogPVRRecordingInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482895156CFCD8005A996F /* GUIDialogPVRRecordingInfo.cpp */; }; + C84828D1156CFCD8005A996F /* GUIDialogPVRTimerSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482897156CFCD8005A996F /* GUIDialogPVRTimerSettings.cpp */; }; + C84828D4156CFCD8005A996F /* PVRDatabase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C848289B156CFCD8005A996F /* PVRDatabase.cpp */; }; + C84828D5156CFCD8005A996F /* PVRGUIInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C848289D156CFCD8005A996F /* PVRGUIInfo.cpp */; }; + C84828D6156CFCD8005A996F /* PVRManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C848289F156CFCD8005A996F /* PVRManager.cpp */; }; + C84828D8156CFCD8005A996F /* PVRRecording.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828A3156CFCD8005A996F /* PVRRecording.cpp */; }; + C84828D9156CFCD8005A996F /* PVRRecordings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828A5156CFCD8005A996F /* PVRRecordings.cpp */; }; + C84828DB156CFCD8005A996F /* PVRTimerInfoTag.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828A9156CFCD8005A996F /* PVRTimerInfoTag.cpp */; }; + C84828DC156CFCD8005A996F /* PVRTimers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828AB156CFCD8005A996F /* PVRTimers.cpp */; }; + C84828DD156CFCD8005A996F /* GUIViewStatePVR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828AE156CFCD8005A996F /* GUIViewStatePVR.cpp */; }; + C84828DE156CFCD8005A996F /* GUIWindowPVR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828B0156CFCD8005A996F /* GUIWindowPVR.cpp */; }; + C84828DF156CFCD8005A996F /* GUIWindowPVRChannels.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828B2156CFCD8005A996F /* GUIWindowPVRChannels.cpp */; }; + C84828E0156CFCD8005A996F /* GUIWindowPVRCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828B4156CFCD8005A996F /* GUIWindowPVRCommon.cpp */; }; + C84828E1156CFCD8005A996F /* GUIWindowPVRGuide.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828B6156CFCD8005A996F /* GUIWindowPVRGuide.cpp */; }; + C84828E2156CFCD8005A996F /* GUIWindowPVRRecordings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828B8156CFCD8005A996F /* GUIWindowPVRRecordings.cpp */; }; + C84828E3156CFCD8005A996F /* GUIWindowPVRSearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828BA156CFCD8005A996F /* GUIWindowPVRSearch.cpp */; }; + C84828E4156CFCD8005A996F /* GUIWindowPVRTimers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828BC156CFCD8005A996F /* GUIWindowPVRTimers.cpp */; }; + C84828F5156CFD5E005A996F /* Epg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828E8156CFD5E005A996F /* Epg.cpp */; }; + C84828F6156CFD5E005A996F /* EpgContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828EA156CFD5E005A996F /* EpgContainer.cpp */; }; + C84828F7156CFD5E005A996F /* EpgDatabase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828EC156CFD5E005A996F /* EpgDatabase.cpp */; }; + C84828F8156CFD5E005A996F /* EpgInfoTag.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828EE156CFD5E005A996F /* EpgInfoTag.cpp */; }; + C84828F9156CFD5E005A996F /* EpgSearchFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828F0156CFD5E005A996F /* EpgSearchFilter.cpp */; }; + C84828FA156CFD5E005A996F /* GUIEPGGridContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828F2156CFD5E005A996F /* GUIEPGGridContainer.cpp */; }; + C84828FE156CFDC3005A996F /* GUIDialogExtendedProgressBar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828FC156CFDC3005A996F /* GUIDialogExtendedProgressBar.cpp */; }; + C8482901156CFE4B005A996F /* Observer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84828FF156CFE4B005A996F /* Observer.cpp */; }; + C8482904156CFED9005A996F /* DVDDemuxPVRClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482902156CFED9005A996F /* DVDDemuxPVRClient.cpp */; }; + C8482909156CFF24005A996F /* PVRDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482905156CFF24005A996F /* PVRDirectory.cpp */; }; + C848290A156CFF24005A996F /* PVRFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482907156CFF24005A996F /* PVRFile.cpp */; }; + C8482910156CFFA0005A996F /* DVDInputStreamPVRManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C848290E156CFFA0005A996F /* DVDInputStreamPVRManager.cpp */; }; + C8482919156CFFE7005A996F /* AddonCallbacks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482911156CFFE7005A996F /* AddonCallbacks.cpp */; }; + C848291A156CFFE7005A996F /* AddonCallbacksAddon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482913156CFFE7005A996F /* AddonCallbacksAddon.cpp */; }; + C848291B156CFFE7005A996F /* AddonCallbacksGUI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482915156CFFE7005A996F /* AddonCallbacksGUI.cpp */; }; + C848291C156CFFE7005A996F /* AddonCallbacksPVR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8482917156CFFE7005A996F /* AddonCallbacksPVR.cpp */; }; + C848291F156D003E005A996F /* TextSearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C848291D156D003E005A996F /* TextSearch.cpp */; }; C84BF7341349BB74006D6FC9 /* JSONServiceDescription.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C84BF7321349BB74006D6FC9 /* JSONServiceDescription.cpp */; }; C85EB75C1174614E0008E5A5 /* Repository.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C85EB75A1174614E0008E5A5 /* Repository.cpp */; }; C8936052152C86CF00812418 /* monitor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C8936050152C86CF00812418 /* monitor.cpp */; }; @@ -1682,6 +1731,104 @@ C80425701158A0DE00D158A6 /* controlslider.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = controlslider.cpp; sourceTree = "<group>"; }; C807114B135DB5CC002F601B /* InputOperations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InputOperations.cpp; sourceTree = "<group>"; }; C807114C135DB5CC002F601B /* InputOperations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InputOperations.h; sourceTree = "<group>"; }; + C8482874156CFCD8005A996F /* PVRClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRClient.cpp; sourceTree = "<group>"; }; + C8482875156CFCD8005A996F /* PVRClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRClient.h; sourceTree = "<group>"; }; + C8482876156CFCD8005A996F /* PVRClients.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRClients.cpp; sourceTree = "<group>"; }; + C8482877156CFCD8005A996F /* PVRClients.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRClients.h; sourceTree = "<group>"; }; + C848287A156CFCD8005A996F /* PVRChannel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRChannel.cpp; sourceTree = "<group>"; }; + C848287B156CFCD8005A996F /* PVRChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRChannel.h; sourceTree = "<group>"; }; + C848287C156CFCD8005A996F /* PVRChannelGroup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRChannelGroup.cpp; sourceTree = "<group>"; }; + C848287D156CFCD8005A996F /* PVRChannelGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRChannelGroup.h; sourceTree = "<group>"; }; + C848287E156CFCD8005A996F /* PVRChannelGroupInternal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRChannelGroupInternal.cpp; sourceTree = "<group>"; }; + C848287F156CFCD8005A996F /* PVRChannelGroupInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRChannelGroupInternal.h; sourceTree = "<group>"; }; + C8482880156CFCD8005A996F /* PVRChannelGroups.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRChannelGroups.cpp; sourceTree = "<group>"; }; + C8482881156CFCD8005A996F /* PVRChannelGroups.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRChannelGroups.h; sourceTree = "<group>"; }; + C8482882156CFCD8005A996F /* PVRChannelGroupsContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRChannelGroupsContainer.cpp; sourceTree = "<group>"; }; + C8482883156CFCD8005A996F /* PVRChannelGroupsContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRChannelGroupsContainer.h; sourceTree = "<group>"; }; + C8482885156CFCD8005A996F /* GUIDialogPVRChannelManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRChannelManager.cpp; sourceTree = "<group>"; }; + C8482886156CFCD8005A996F /* GUIDialogPVRChannelManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRChannelManager.h; sourceTree = "<group>"; }; + C8482887156CFCD8005A996F /* GUIDialogPVRChannelsOSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRChannelsOSD.cpp; sourceTree = "<group>"; }; + C8482888156CFCD8005A996F /* GUIDialogPVRChannelsOSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRChannelsOSD.h; sourceTree = "<group>"; }; + C8482889156CFCD8005A996F /* GUIDialogPVRCutterOSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRCutterOSD.cpp; sourceTree = "<group>"; }; + C848288A156CFCD8005A996F /* GUIDialogPVRCutterOSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRCutterOSD.h; sourceTree = "<group>"; }; + C848288B156CFCD8005A996F /* GUIDialogPVRDirectorOSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRDirectorOSD.cpp; sourceTree = "<group>"; }; + C848288C156CFCD8005A996F /* GUIDialogPVRDirectorOSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRDirectorOSD.h; sourceTree = "<group>"; }; + C848288D156CFCD8005A996F /* GUIDialogPVRGroupManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRGroupManager.cpp; sourceTree = "<group>"; }; + C848288E156CFCD8005A996F /* GUIDialogPVRGroupManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRGroupManager.h; sourceTree = "<group>"; }; + C848288F156CFCD8005A996F /* GUIDialogPVRGuideInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRGuideInfo.cpp; sourceTree = "<group>"; }; + C8482890156CFCD8005A996F /* GUIDialogPVRGuideInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRGuideInfo.h; sourceTree = "<group>"; }; + C8482891156CFCD8005A996F /* GUIDialogPVRGuideOSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRGuideOSD.cpp; sourceTree = "<group>"; }; + C8482892156CFCD8005A996F /* GUIDialogPVRGuideOSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRGuideOSD.h; sourceTree = "<group>"; }; + C8482893156CFCD8005A996F /* GUIDialogPVRGuideSearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRGuideSearch.cpp; sourceTree = "<group>"; }; + C8482894156CFCD8005A996F /* GUIDialogPVRGuideSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRGuideSearch.h; sourceTree = "<group>"; }; + C8482895156CFCD8005A996F /* GUIDialogPVRRecordingInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRRecordingInfo.cpp; sourceTree = "<group>"; }; + C8482896156CFCD8005A996F /* GUIDialogPVRRecordingInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRRecordingInfo.h; sourceTree = "<group>"; }; + C8482897156CFCD8005A996F /* GUIDialogPVRTimerSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRTimerSettings.cpp; sourceTree = "<group>"; }; + C8482898156CFCD8005A996F /* GUIDialogPVRTimerSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRTimerSettings.h; sourceTree = "<group>"; }; + C848289B156CFCD8005A996F /* PVRDatabase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRDatabase.cpp; sourceTree = "<group>"; }; + C848289C156CFCD8005A996F /* PVRDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRDatabase.h; sourceTree = "<group>"; }; + C848289D156CFCD8005A996F /* PVRGUIInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRGUIInfo.cpp; sourceTree = "<group>"; }; + C848289E156CFCD8005A996F /* PVRGUIInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRGUIInfo.h; sourceTree = "<group>"; }; + C848289F156CFCD8005A996F /* PVRManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRManager.cpp; sourceTree = "<group>"; }; + C84828A0156CFCD8005A996F /* PVRManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRManager.h; sourceTree = "<group>"; }; + C84828A3156CFCD8005A996F /* PVRRecording.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRRecording.cpp; sourceTree = "<group>"; }; + C84828A4156CFCD8005A996F /* PVRRecording.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRRecording.h; sourceTree = "<group>"; }; + C84828A5156CFCD8005A996F /* PVRRecordings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRRecordings.cpp; sourceTree = "<group>"; }; + C84828A6156CFCD8005A996F /* PVRRecordings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRRecordings.h; sourceTree = "<group>"; }; + C84828A9156CFCD8005A996F /* PVRTimerInfoTag.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRTimerInfoTag.cpp; sourceTree = "<group>"; }; + C84828AA156CFCD8005A996F /* PVRTimerInfoTag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRTimerInfoTag.h; sourceTree = "<group>"; }; + C84828AB156CFCD8005A996F /* PVRTimers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRTimers.cpp; sourceTree = "<group>"; }; + C84828AC156CFCD8005A996F /* PVRTimers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRTimers.h; sourceTree = "<group>"; }; + C84828AE156CFCD8005A996F /* GUIViewStatePVR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIViewStatePVR.cpp; sourceTree = "<group>"; }; + C84828AF156CFCD8005A996F /* GUIViewStatePVR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIViewStatePVR.h; sourceTree = "<group>"; }; + C84828B0156CFCD8005A996F /* GUIWindowPVR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVR.cpp; sourceTree = "<group>"; }; + C84828B1156CFCD8005A996F /* GUIWindowPVR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVR.h; sourceTree = "<group>"; }; + C84828B2156CFCD8005A996F /* GUIWindowPVRChannels.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRChannels.cpp; sourceTree = "<group>"; }; + C84828B3156CFCD8005A996F /* GUIWindowPVRChannels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRChannels.h; sourceTree = "<group>"; }; + C84828B4156CFCD8005A996F /* GUIWindowPVRCommon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRCommon.cpp; sourceTree = "<group>"; }; + C84828B5156CFCD8005A996F /* GUIWindowPVRCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRCommon.h; sourceTree = "<group>"; }; + C84828B6156CFCD8005A996F /* GUIWindowPVRGuide.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRGuide.cpp; sourceTree = "<group>"; }; + C84828B7156CFCD8005A996F /* GUIWindowPVRGuide.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRGuide.h; sourceTree = "<group>"; }; + C84828B8156CFCD8005A996F /* GUIWindowPVRRecordings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRRecordings.cpp; sourceTree = "<group>"; }; + C84828B9156CFCD8005A996F /* GUIWindowPVRRecordings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRRecordings.h; sourceTree = "<group>"; }; + C84828BA156CFCD8005A996F /* GUIWindowPVRSearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRSearch.cpp; sourceTree = "<group>"; }; + C84828BB156CFCD8005A996F /* GUIWindowPVRSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRSearch.h; sourceTree = "<group>"; }; + C84828BC156CFCD8005A996F /* GUIWindowPVRTimers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowPVRTimers.cpp; sourceTree = "<group>"; }; + C84828BD156CFCD8005A996F /* GUIWindowPVRTimers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowPVRTimers.h; sourceTree = "<group>"; }; + C84828E8156CFD5E005A996F /* Epg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Epg.cpp; sourceTree = "<group>"; }; + C84828E9156CFD5E005A996F /* Epg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Epg.h; sourceTree = "<group>"; }; + C84828EA156CFD5E005A996F /* EpgContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EpgContainer.cpp; sourceTree = "<group>"; }; + C84828EB156CFD5E005A996F /* EpgContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EpgContainer.h; sourceTree = "<group>"; }; + C84828EC156CFD5E005A996F /* EpgDatabase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EpgDatabase.cpp; sourceTree = "<group>"; }; + C84828ED156CFD5E005A996F /* EpgDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EpgDatabase.h; sourceTree = "<group>"; }; + C84828EE156CFD5E005A996F /* EpgInfoTag.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EpgInfoTag.cpp; sourceTree = "<group>"; }; + C84828EF156CFD5E005A996F /* EpgInfoTag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EpgInfoTag.h; sourceTree = "<group>"; }; + C84828F0156CFD5E005A996F /* EpgSearchFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EpgSearchFilter.cpp; sourceTree = "<group>"; }; + C84828F1156CFD5E005A996F /* EpgSearchFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EpgSearchFilter.h; sourceTree = "<group>"; }; + C84828F2156CFD5E005A996F /* GUIEPGGridContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIEPGGridContainer.cpp; sourceTree = "<group>"; }; + C84828F3156CFD5E005A996F /* GUIEPGGridContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIEPGGridContainer.h; sourceTree = "<group>"; }; + C84828FC156CFDC3005A996F /* GUIDialogExtendedProgressBar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogExtendedProgressBar.cpp; sourceTree = "<group>"; }; + C84828FD156CFDC3005A996F /* GUIDialogExtendedProgressBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogExtendedProgressBar.h; sourceTree = "<group>"; }; + C84828FF156CFE4B005A996F /* Observer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Observer.cpp; sourceTree = "<group>"; }; + C8482900156CFE4B005A996F /* Observer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Observer.h; sourceTree = "<group>"; }; + C8482902156CFED9005A996F /* DVDDemuxPVRClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDDemuxPVRClient.cpp; sourceTree = "<group>"; }; + C8482903156CFED9005A996F /* DVDDemuxPVRClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDDemuxPVRClient.h; sourceTree = "<group>"; }; + C8482905156CFF24005A996F /* PVRDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRDirectory.cpp; sourceTree = "<group>"; }; + C8482906156CFF24005A996F /* PVRDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRDirectory.h; sourceTree = "<group>"; }; + C8482907156CFF24005A996F /* PVRFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRFile.cpp; sourceTree = "<group>"; }; + C8482908156CFF24005A996F /* PVRFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRFile.h; sourceTree = "<group>"; }; + C848290E156CFFA0005A996F /* DVDInputStreamPVRManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDInputStreamPVRManager.cpp; sourceTree = "<group>"; }; + C848290F156CFFA0005A996F /* DVDInputStreamPVRManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDInputStreamPVRManager.h; sourceTree = "<group>"; }; + C8482911156CFFE7005A996F /* AddonCallbacks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AddonCallbacks.cpp; sourceTree = "<group>"; }; + C8482912156CFFE7005A996F /* AddonCallbacks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddonCallbacks.h; sourceTree = "<group>"; }; + C8482913156CFFE7005A996F /* AddonCallbacksAddon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AddonCallbacksAddon.cpp; sourceTree = "<group>"; }; + C8482914156CFFE7005A996F /* AddonCallbacksAddon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddonCallbacksAddon.h; sourceTree = "<group>"; }; + C8482915156CFFE7005A996F /* AddonCallbacksGUI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AddonCallbacksGUI.cpp; sourceTree = "<group>"; }; + C8482916156CFFE7005A996F /* AddonCallbacksGUI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddonCallbacksGUI.h; sourceTree = "<group>"; }; + C8482917156CFFE7005A996F /* AddonCallbacksPVR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AddonCallbacksPVR.cpp; sourceTree = "<group>"; }; + C8482918156CFFE7005A996F /* AddonCallbacksPVR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddonCallbacksPVR.h; sourceTree = "<group>"; }; + C848291D156D003E005A996F /* TextSearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextSearch.cpp; sourceTree = "<group>"; }; + C848291E156D003E005A996F /* TextSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextSearch.h; sourceTree = "<group>"; }; C84BF7321349BB74006D6FC9 /* JSONServiceDescription.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSONServiceDescription.cpp; sourceTree = "<group>"; }; C84BF7331349BB74006D6FC9 /* JSONServiceDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONServiceDescription.h; sourceTree = "<group>"; }; C85EB75A1174614E0008E5A5 /* Repository.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Repository.cpp; sourceTree = "<group>"; }; @@ -3221,6 +3368,14 @@ children = ( 18B49FF11152BFA5001AF8A6 /* Addon.cpp */, 18B49FF21152BFA5001AF8A6 /* Addon.h */, + C8482911156CFFE7005A996F /* AddonCallbacks.cpp */, + C8482912156CFFE7005A996F /* AddonCallbacks.h */, + C8482913156CFFE7005A996F /* AddonCallbacksAddon.cpp */, + C8482914156CFFE7005A996F /* AddonCallbacksAddon.h */, + C8482915156CFFE7005A996F /* AddonCallbacksGUI.cpp */, + C8482916156CFFE7005A996F /* AddonCallbacksGUI.h */, + C8482917156CFFE7005A996F /* AddonCallbacksPVR.cpp */, + C8482918156CFFE7005A996F /* AddonCallbacksPVR.h */, 18B7C3821294203F009E7A26 /* AddonDatabase.cpp */, 18B7C3831294203F009E7A26 /* AddonDatabase.h */, 18B49FF31152BFA5001AF8A6 /* AddonDll.h */, @@ -3779,6 +3934,8 @@ 431376FF12D6455C00680C15 /* GUIDialogCache.h */, E38E17A40D25F9FA00618676 /* GUIDialogContextMenu.cpp */, E38E17A50D25F9FA00618676 /* GUIDialogContextMenu.h */, + C84828FC156CFDC3005A996F /* GUIDialogExtendedProgressBar.cpp */, + C84828FD156CFDC3005A996F /* GUIDialogExtendedProgressBar.h */, E38E17A60D25F9FA00618676 /* GUIDialogFavourites.cpp */, E38E17A70D25F9FA00618676 /* GUIDialogFavourites.h */, E38E17A80D25F9FA00618676 /* GUIDialogFileBrowser.cpp */, @@ -4177,6 +4334,144 @@ name = Documentation; sourceTree = "<group>"; }; + C8482871156CFCD8005A996F /* pvr */ = { + isa = PBXGroup; + children = ( + C8482872156CFCD8005A996F /* addons */, + C8482878156CFCD8005A996F /* channels */, + C8482884156CFCD8005A996F /* dialogs */, + C84828A1156CFCD8005A996F /* recordings */, + C84828A7156CFCD8005A996F /* timers */, + C84828AD156CFCD8005A996F /* windows */, + C848289B156CFCD8005A996F /* PVRDatabase.cpp */, + C848289C156CFCD8005A996F /* PVRDatabase.h */, + C848289D156CFCD8005A996F /* PVRGUIInfo.cpp */, + C848289E156CFCD8005A996F /* PVRGUIInfo.h */, + C848289F156CFCD8005A996F /* PVRManager.cpp */, + C84828A0156CFCD8005A996F /* PVRManager.h */, + ); + path = pvr; + sourceTree = "<group>"; + }; + C8482872156CFCD8005A996F /* addons */ = { + isa = PBXGroup; + children = ( + C8482874156CFCD8005A996F /* PVRClient.cpp */, + C8482875156CFCD8005A996F /* PVRClient.h */, + C8482876156CFCD8005A996F /* PVRClients.cpp */, + C8482877156CFCD8005A996F /* PVRClients.h */, + ); + path = addons; + sourceTree = "<group>"; + }; + C8482878156CFCD8005A996F /* channels */ = { + isa = PBXGroup; + children = ( + C848287A156CFCD8005A996F /* PVRChannel.cpp */, + C848287B156CFCD8005A996F /* PVRChannel.h */, + C848287C156CFCD8005A996F /* PVRChannelGroup.cpp */, + C848287D156CFCD8005A996F /* PVRChannelGroup.h */, + C848287E156CFCD8005A996F /* PVRChannelGroupInternal.cpp */, + C848287F156CFCD8005A996F /* PVRChannelGroupInternal.h */, + C8482880156CFCD8005A996F /* PVRChannelGroups.cpp */, + C8482881156CFCD8005A996F /* PVRChannelGroups.h */, + C8482882156CFCD8005A996F /* PVRChannelGroupsContainer.cpp */, + C8482883156CFCD8005A996F /* PVRChannelGroupsContainer.h */, + ); + path = channels; + sourceTree = "<group>"; + }; + C8482884156CFCD8005A996F /* dialogs */ = { + isa = PBXGroup; + children = ( + C8482885156CFCD8005A996F /* GUIDialogPVRChannelManager.cpp */, + C8482886156CFCD8005A996F /* GUIDialogPVRChannelManager.h */, + C8482887156CFCD8005A996F /* GUIDialogPVRChannelsOSD.cpp */, + C8482888156CFCD8005A996F /* GUIDialogPVRChannelsOSD.h */, + C8482889156CFCD8005A996F /* GUIDialogPVRCutterOSD.cpp */, + C848288A156CFCD8005A996F /* GUIDialogPVRCutterOSD.h */, + C848288B156CFCD8005A996F /* GUIDialogPVRDirectorOSD.cpp */, + C848288C156CFCD8005A996F /* GUIDialogPVRDirectorOSD.h */, + C848288D156CFCD8005A996F /* GUIDialogPVRGroupManager.cpp */, + C848288E156CFCD8005A996F /* GUIDialogPVRGroupManager.h */, + C848288F156CFCD8005A996F /* GUIDialogPVRGuideInfo.cpp */, + C8482890156CFCD8005A996F /* GUIDialogPVRGuideInfo.h */, + C8482891156CFCD8005A996F /* GUIDialogPVRGuideOSD.cpp */, + C8482892156CFCD8005A996F /* GUIDialogPVRGuideOSD.h */, + C8482893156CFCD8005A996F /* GUIDialogPVRGuideSearch.cpp */, + C8482894156CFCD8005A996F /* GUIDialogPVRGuideSearch.h */, + C8482895156CFCD8005A996F /* GUIDialogPVRRecordingInfo.cpp */, + C8482896156CFCD8005A996F /* GUIDialogPVRRecordingInfo.h */, + C8482897156CFCD8005A996F /* GUIDialogPVRTimerSettings.cpp */, + C8482898156CFCD8005A996F /* GUIDialogPVRTimerSettings.h */, + ); + path = dialogs; + sourceTree = "<group>"; + }; + C84828A1156CFCD8005A996F /* recordings */ = { + isa = PBXGroup; + children = ( + C84828A3156CFCD8005A996F /* PVRRecording.cpp */, + C84828A4156CFCD8005A996F /* PVRRecording.h */, + C84828A5156CFCD8005A996F /* PVRRecordings.cpp */, + C84828A6156CFCD8005A996F /* PVRRecordings.h */, + ); + path = recordings; + sourceTree = "<group>"; + }; + C84828A7156CFCD8005A996F /* timers */ = { + isa = PBXGroup; + children = ( + C84828A9156CFCD8005A996F /* PVRTimerInfoTag.cpp */, + C84828AA156CFCD8005A996F /* PVRTimerInfoTag.h */, + C84828AB156CFCD8005A996F /* PVRTimers.cpp */, + C84828AC156CFCD8005A996F /* PVRTimers.h */, + ); + path = timers; + sourceTree = "<group>"; + }; + C84828AD156CFCD8005A996F /* windows */ = { + isa = PBXGroup; + children = ( + C84828AE156CFCD8005A996F /* GUIViewStatePVR.cpp */, + C84828AF156CFCD8005A996F /* GUIViewStatePVR.h */, + C84828B0156CFCD8005A996F /* GUIWindowPVR.cpp */, + C84828B1156CFCD8005A996F /* GUIWindowPVR.h */, + C84828B2156CFCD8005A996F /* GUIWindowPVRChannels.cpp */, + C84828B3156CFCD8005A996F /* GUIWindowPVRChannels.h */, + C84828B4156CFCD8005A996F /* GUIWindowPVRCommon.cpp */, + C84828B5156CFCD8005A996F /* GUIWindowPVRCommon.h */, + C84828B6156CFCD8005A996F /* GUIWindowPVRGuide.cpp */, + C84828B7156CFCD8005A996F /* GUIWindowPVRGuide.h */, + C84828B8156CFCD8005A996F /* GUIWindowPVRRecordings.cpp */, + C84828B9156CFCD8005A996F /* GUIWindowPVRRecordings.h */, + C84828BA156CFCD8005A996F /* GUIWindowPVRSearch.cpp */, + C84828BB156CFCD8005A996F /* GUIWindowPVRSearch.h */, + C84828BC156CFCD8005A996F /* GUIWindowPVRTimers.cpp */, + C84828BD156CFCD8005A996F /* GUIWindowPVRTimers.h */, + ); + path = windows; + sourceTree = "<group>"; + }; + C84828E7156CFD5E005A996F /* epg */ = { + isa = PBXGroup; + children = ( + C84828E8156CFD5E005A996F /* Epg.cpp */, + C84828E9156CFD5E005A996F /* Epg.h */, + C84828EA156CFD5E005A996F /* EpgContainer.cpp */, + C84828EB156CFD5E005A996F /* EpgContainer.h */, + C84828EC156CFD5E005A996F /* EpgDatabase.cpp */, + C84828ED156CFD5E005A996F /* EpgDatabase.h */, + C84828EE156CFD5E005A996F /* EpgInfoTag.cpp */, + C84828EF156CFD5E005A996F /* EpgInfoTag.h */, + C84828F0156CFD5E005A996F /* EpgSearchFilter.cpp */, + C84828F1156CFD5E005A996F /* EpgSearchFilter.h */, + C84828F2156CFD5E005A996F /* GUIEPGGridContainer.cpp */, + C84828F3156CFD5E005A996F /* GUIEPGGridContainer.h */, + ); + path = epg; + sourceTree = "<group>"; + }; DF527729151BAF4C00B5B63B /* websocket */ = { isa = PBXGroup; children = ( @@ -4314,6 +4609,7 @@ EC720A91155091CA00FFD782 /* commons */, E38E149A0D25F9F900618676 /* cores */, 4313773012D647BB00680C15 /* dbwrappers */, + C84828E7156CFD5E005A996F /* epg */, 431376E912D6439900680C15 /* dialogs */, E38E16940D25F9FA00618676 /* filesystem */, 18B7C3AA1294219F009E7A26 /* guilib */, @@ -4328,6 +4624,7 @@ 18B7C91B129428CA009E7A26 /* playlists */, 430C880812D649B10098821A /* powermanagement */, 4313769112D63F9E00680C15 /* programs */, + C8482871156CFCD8005A996F /* pvr */, 43FAC8BF12D63B7400F67914 /* rendering */, E38E1DF60D25F9FD00618676 /* screensavers */, E38E1E000D25F9FD00618676 /* settings */, @@ -4690,6 +4987,8 @@ E38E154C0D25F9F900618676 /* DVDDemuxFFmpeg.h */, F55110440F5C3C0000955236 /* DVDDemuxHTSP.cpp */, F55110430F5C3C0000955236 /* DVDDemuxHTSP.h */, + C8482902156CFED9005A996F /* DVDDemuxPVRClient.cpp */, + C8482903156CFED9005A996F /* DVDDemuxPVRClient.h */, E38E154D0D25F9F900618676 /* DVDDemuxShoutcast.cpp */, E38E154E0D25F9F900618676 /* DVDDemuxShoutcast.h */, E38E154F0D25F9F900618676 /* DVDDemuxUtils.cpp */, @@ -4723,6 +5022,8 @@ E38E15640D25F9FA00618676 /* DVDInputStreamMemory.h */, E38E15650D25F9FA00618676 /* DVDInputStreamNavigator.cpp */, E38E15660D25F9FA00618676 /* DVDInputStreamNavigator.h */, + C848290E156CFFA0005A996F /* DVDInputStreamPVRManager.cpp */, + C848290F156CFFA0005A996F /* DVDInputStreamPVRManager.h */, 815EE6330E17F1DC009FBE3C /* DVDInputStreamRTMP.cpp */, 815EE6340E17F1DC009FBE3C /* DVDInputStreamRTMP.h */, E33979940D62FD47004ECDDA /* DVDInputStreamTV.cpp */, @@ -4999,6 +5300,10 @@ E38E17430D25F9FA00618676 /* PlaylistFileDirectory.h */, E38E17440D25F9FA00618676 /* PluginDirectory.cpp */, E38E17450D25F9FA00618676 /* PluginDirectory.h */, + C8482905156CFF24005A996F /* PVRDirectory.cpp */, + C8482906156CFF24005A996F /* PVRDirectory.h */, + C8482907156CFF24005A996F /* PVRFile.cpp */, + C8482908156CFF24005A996F /* PVRFile.h */, E38E17460D25F9FA00618676 /* RarDirectory.cpp */, E38E17470D25F9FA00618676 /* RarDirectory.h */, DF93D6811444A8B0007C6459 /* RarFile.cpp */, @@ -5892,6 +6197,8 @@ F5F8E1E70E427F6700A8E96F /* md5.h */, 188F75FC152217BC009870CE /* Mime.cpp */, 188F75FD152217BC009870CE /* Mime.h */, + C84828FF156CFE4B005A996F /* Observer.cpp */, + C8482900156CFE4B005A996F /* Observer.h */, E38E1E6F0D25F9FD00618676 /* PerformanceSample.cpp */, E38E1E700D25F9FD00618676 /* PerformanceSample.h */, E38E1E710D25F9FD00618676 /* PerformanceStats.cpp */, @@ -5927,6 +6234,8 @@ 18B7C8F21294261F009E7A26 /* StringUtils.h */, E38E1E830D25F9FD00618676 /* SystemInfo.cpp */, E38E1E840D25F9FD00618676 /* SystemInfo.h */, + C848291D156D003E005A996F /* TextSearch.cpp */, + C848291E156D003E005A996F /* TextSearch.h */, 7CEE2E5913D6B71E000ABF2A /* TimeSmoother.cpp */, 7CEE2E5A13D6B71E000ABF2A /* TimeSmoother.h */, 7CCF7FC7106A0DF500992676 /* TimeUtils.cpp */, @@ -7329,6 +7638,55 @@ F5ED9509155D855200842059 /* CoreAudioGraph.cpp in Sources */, 7C6EB330155BD1D40080368A /* ImageFile.cpp in Sources */, 7C6EB6FA155F32C30080368A /* HTTPImageHandler.cpp in Sources */, + C84828C0156CFCD8005A996F /* PVRClient.cpp in Sources */, + C84828C1156CFCD8005A996F /* PVRClients.cpp in Sources */, + C84828C3156CFCD8005A996F /* PVRChannel.cpp in Sources */, + C84828C4156CFCD8005A996F /* PVRChannelGroup.cpp in Sources */, + C84828C5156CFCD8005A996F /* PVRChannelGroupInternal.cpp in Sources */, + C84828C6156CFCD8005A996F /* PVRChannelGroups.cpp in Sources */, + C84828C7156CFCD8005A996F /* PVRChannelGroupsContainer.cpp in Sources */, + C84828C8156CFCD8005A996F /* GUIDialogPVRChannelManager.cpp in Sources */, + C84828C9156CFCD8005A996F /* GUIDialogPVRChannelsOSD.cpp in Sources */, + C84828CA156CFCD8005A996F /* GUIDialogPVRCutterOSD.cpp in Sources */, + C84828CB156CFCD8005A996F /* GUIDialogPVRDirectorOSD.cpp in Sources */, + C84828CC156CFCD8005A996F /* GUIDialogPVRGroupManager.cpp in Sources */, + C84828CD156CFCD8005A996F /* GUIDialogPVRGuideInfo.cpp in Sources */, + C84828CE156CFCD8005A996F /* GUIDialogPVRGuideOSD.cpp in Sources */, + C84828CF156CFCD8005A996F /* GUIDialogPVRGuideSearch.cpp in Sources */, + C84828D0156CFCD8005A996F /* GUIDialogPVRRecordingInfo.cpp in Sources */, + C84828D1156CFCD8005A996F /* GUIDialogPVRTimerSettings.cpp in Sources */, + C84828D4156CFCD8005A996F /* PVRDatabase.cpp in Sources */, + C84828D5156CFCD8005A996F /* PVRGUIInfo.cpp in Sources */, + C84828D6156CFCD8005A996F /* PVRManager.cpp in Sources */, + C84828D8156CFCD8005A996F /* PVRRecording.cpp in Sources */, + C84828D9156CFCD8005A996F /* PVRRecordings.cpp in Sources */, + C84828DB156CFCD8005A996F /* PVRTimerInfoTag.cpp in Sources */, + C84828DC156CFCD8005A996F /* PVRTimers.cpp in Sources */, + C84828DD156CFCD8005A996F /* GUIViewStatePVR.cpp in Sources */, + C84828DE156CFCD8005A996F /* GUIWindowPVR.cpp in Sources */, + C84828DF156CFCD8005A996F /* GUIWindowPVRChannels.cpp in Sources */, + C84828E0156CFCD8005A996F /* GUIWindowPVRCommon.cpp in Sources */, + C84828E1156CFCD8005A996F /* GUIWindowPVRGuide.cpp in Sources */, + C84828E2156CFCD8005A996F /* GUIWindowPVRRecordings.cpp in Sources */, + C84828E3156CFCD8005A996F /* GUIWindowPVRSearch.cpp in Sources */, + C84828E4156CFCD8005A996F /* GUIWindowPVRTimers.cpp in Sources */, + C84828F5156CFD5E005A996F /* Epg.cpp in Sources */, + C84828F6156CFD5E005A996F /* EpgContainer.cpp in Sources */, + C84828F7156CFD5E005A996F /* EpgDatabase.cpp in Sources */, + C84828F8156CFD5E005A996F /* EpgInfoTag.cpp in Sources */, + C84828F9156CFD5E005A996F /* EpgSearchFilter.cpp in Sources */, + C84828FA156CFD5E005A996F /* GUIEPGGridContainer.cpp in Sources */, + C84828FE156CFDC3005A996F /* GUIDialogExtendedProgressBar.cpp in Sources */, + C8482901156CFE4B005A996F /* Observer.cpp in Sources */, + C8482904156CFED9005A996F /* DVDDemuxPVRClient.cpp in Sources */, + C8482909156CFF24005A996F /* PVRDirectory.cpp in Sources */, + C848290A156CFF24005A996F /* PVRFile.cpp in Sources */, + C8482910156CFFA0005A996F /* DVDInputStreamPVRManager.cpp in Sources */, + C8482919156CFFE7005A996F /* AddonCallbacks.cpp in Sources */, + C848291A156CFFE7005A996F /* AddonCallbacksAddon.cpp in Sources */, + C848291B156CFFE7005A996F /* AddonCallbacksGUI.cpp in Sources */, + C848291C156CFFE7005A996F /* AddonCallbacksPVR.cpp in Sources */, + C848291F156D003E005A996F /* TextSearch.cpp in Sources */, 18E7CACB1578C26D001D4554 /* CDDARipJob.cpp in Sources */, F5DA82D915803129003EE43C /* main.cpp in Sources */, 36A9443D15821E2800727135 /* DatabaseUtils.cpp in Sources */, diff --git a/addons/library.xbmc.addon/dlfcn-win32.cpp b/addons/library.xbmc.addon/dlfcn-win32.cpp new file mode 100644 index 0000000000..583992120f --- /dev/null +++ b/addons/library.xbmc.addon/dlfcn-win32.cpp @@ -0,0 +1,263 @@ +/* + * dlfcn-win32 + * Copyright (c) 2007 Ramiro Polla + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <windows.h> +#include <stdio.h> + +#include "dlfcn-win32.h" + +/* Note: + * MSDN says these functions are not thread-safe. We make no efforts to have + * any kind of thread safety. + */ + +/* I have no special reason to have set MAX_GLOBAL_OBJECTS to this value. Any + * comments are welcome. + */ +#define MAX_OBJECTS 255 + +static HMODULE global_objects[MAX_OBJECTS]; + +/* This function adds an object to the list of global objects. + * The implementation is very simple and slow. + * TODO: should failing this function be enough to fail the call to dlopen( )? + */ +static void global_object_add( HMODULE hModule ) +{ + int i; + + for( i = 0 ; i < MAX_OBJECTS ; i++ ) + { + if( !global_objects[i] ) + { + global_objects[i] = hModule; + break; + } + } +} + +static void global_object_rem( HMODULE hModule ) +{ + int i; + + for( i = 0 ; i < MAX_OBJECTS ; i++ ) + { + if( global_objects[i] == hModule ) + { + global_objects[i] = 0; + break; + } + } +} + +/* Argument to last function. Used in dlerror( ) */ +static char last_name[MAX_PATH]; + +static int copy_string( char *dest, int dest_size, const char *src ) +{ + int i = 0; + + if( src && dest ) + { + for( i = 0 ; i < dest_size-1 ; i++ ) + { + if( !src[i] ) + break; + else + dest[i] = src[i]; + } + } + dest[i] = '\0'; + + return i; +} + +void *dlopen( const char *file, int mode ) +{ + HMODULE hModule; + UINT uMode; + + /* Do not let Windows display the critical-error-handler message box */ + uMode = SetErrorMode( SEM_FAILCRITICALERRORS ); + + if( file == 0 ) + { + /* Save NULL pointer for error message */ + _snprintf_s( last_name, MAX_PATH, MAX_PATH, "0x%p", file ); + + /* POSIX says that if the value of file is 0, a handle on a global + * symbol object must be provided. That object must be able to access + * all symbols from the original program file, and any objects loaded + * with the RTLD_GLOBAL flag. + * The return value from GetModuleHandle( ) allows us to retrieve + * symbols only from the original program file. For objects loaded with + * the RTLD_GLOBAL flag, we create our own list later on. + */ + hModule = GetModuleHandle( NULL ); + } + else + { + char lpFileName[MAX_PATH]; + int i; + + /* MSDN says backslashes *must* be used instead of forward slashes. */ + for( i = 0 ; i < sizeof(lpFileName)-1 ; i++ ) + { + if( !file[i] ) + break; + else if( file[i] == '/' ) + lpFileName[i] = '\\'; + else + lpFileName[i] = file[i]; + } + lpFileName[i] = '\0'; + + /* Save file name for error message */ + copy_string( last_name, sizeof(last_name), lpFileName ); + + /* POSIX says the search path is implementation-defined. + * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely + * to UNIX's search paths (start with system folders instead of current + * folder). + */ + hModule = LoadLibraryEx( (LPSTR) lpFileName, NULL, + LOAD_WITH_ALTERED_SEARCH_PATH ); + /* If the object was loaded with RTLD_GLOBAL, add it to list of global + * objects, so that its symbols may be retrieved even if the handle for + * the original program file is passed. POSIX says that if the same + * file is specified in multiple invocations, and any of them are + * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the + * symbols will remain global. + */ + + if( hModule && (mode & RTLD_GLOBAL) ) + global_object_add( hModule ); + } + + /* Return to previous state of the error-mode bit flags. */ + SetErrorMode( uMode ); + + return (void *) hModule; +} + +int dlclose( void *handle ) +{ + HMODULE hModule = (HMODULE) handle; + BOOL ret; + + /* Save handle for error message */ + _snprintf_s( last_name, MAX_PATH, MAX_PATH, "0x%p", handle ); + + ret = FreeLibrary( hModule ); + + /* If the object was loaded with RTLD_GLOBAL, remove it from list of global + * objects. + */ + if( ret ) + global_object_rem( hModule ); + + /* dlclose's return value in inverted in relation to FreeLibrary's. */ + ret = !ret; + + return (int) ret; +} + +void *dlsym( void *handle, const char *name ) +{ + FARPROC symbol; + HMODULE myhandle = (HMODULE) handle; + + /* Save symbol name for error message */ + copy_string( last_name, sizeof(last_name), name ); + + symbol = GetProcAddress( myhandle, name ); +#if 0 + if( symbol == NULL ) + { + HMODULE hModule; + + /* If the handle for the original program file is passed, also search + * in all globally loaded objects. + */ + + hModule = GetModuleHandle( NULL ); + + if( hModule == handle ) + { + int i; + + for( i = 0 ; i < MAX_OBJECTS ; i++ ) + { + if( global_objects[i] != 0 ) + { + symbol = GetProcAddress( global_objects[i], name ); + if( symbol != NULL ) + break; + } + } + } + + + CloseHandle( hModule ); + } +#endif + return (void*) symbol; +} + +char *dlerror( void ) +{ + DWORD dwMessageId; + /* POSIX says this function doesn't have to be thread-safe, so we use one + * static buffer. + * MSDN says the buffer cannot be larger than 64K bytes, so we set it to + * the limit. + */ + static char lpBuffer[65535]; + DWORD ret; + + dwMessageId = GetLastError( ); + + if( dwMessageId == 0 ) + return NULL; + + /* Format error message to: + * "<argument to function that failed>": <Windows localized error message> + */ + ret = copy_string( lpBuffer, sizeof(lpBuffer), "\"" ); + ret += copy_string( lpBuffer+ret, sizeof(lpBuffer)-ret, last_name ); + ret += copy_string( lpBuffer+ret, sizeof(lpBuffer)-ret, "\": " ); + ret += FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwMessageId, + MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), + lpBuffer+ret, sizeof(lpBuffer)-ret, NULL ); + + if( ret > 1 ) + { + /* POSIX says the string must not have trailing <newline> */ + if( lpBuffer[ret-2] == '\r' && lpBuffer[ret-1] == '\n' ) + lpBuffer[ret-2] = '\0'; + } + + /* POSIX says that invoking dlerror( ) a second time, immediately following + * a prior invocation, shall result in NULL being returned. + */ + SetLastError(0); + + return lpBuffer; +} + diff --git a/addons/library.xbmc.addon/dlfcn-win32.h b/addons/library.xbmc.addon/dlfcn-win32.h new file mode 100644 index 0000000000..b93a029f29 --- /dev/null +++ b/addons/library.xbmc.addon/dlfcn-win32.h @@ -0,0 +1,46 @@ +#pragma once +/* + * dlfcn-win32 + * Copyright (c) 2007 Ramiro Polla + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DLFCN_H +#define DLFCN_H + +/* POSIX says these are implementation-defined. + * To simplify use with Windows API, we treat them the same way. + */ + +#define RTLD_LAZY 0 +#define RTLD_NOW 0 + +#define RTLD_GLOBAL (1 << 1) +#define RTLD_LOCAL (1 << 2) + +/* These two were added in The Open Group Base Specifications Issue 6. + * Note: All other RTLD_* flags in any dlfcn.h are not standard compliant. + */ + +#define RTLD_DEFAULT 0 +#define RTLD_NEXT 0 + +void *dlopen ( const char *file, int mode ); +int dlclose( void *handle ); +void *dlsym ( void *handle, const char *name ); +char *dlerror( void ); + +#endif /* DLFCN-WIN32_H */ diff --git a/addons/library.xbmc.addon/libXBMC_addon.h b/addons/library.xbmc.addon/libXBMC_addon.h new file mode 100644 index 0000000000..75012e9aff --- /dev/null +++ b/addons/library.xbmc.addon/libXBMC_addon.h @@ -0,0 +1,182 @@ +#pragma once +/* + * Copyright (C) 2005-2010 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include <string> +#include <vector> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#ifdef _WIN32 // windows +#include "dlfcn-win32.h" +#define ADDON_DLL "\\library.xbmc.addon\\libXBMC_addon" ADDON_HELPER_EXT +#define ADDON_HELPER_PLATFORM "win32" +#define ADDON_HELPER_EXT ".dll" +#else +#if defined(__APPLE__) // osx +#define ADDON_HELPER_PLATFORM "osx" +#if defined(__POWERPC__) +#define ADDON_HELPER_ARCH "powerpc" +#elif defined(__arm__) +#define ADDON_HELPER_ARCH "arm" +#else +#define ADDON_HELPER_ARCH "x86" +#endif +#else // linux +#define ADDON_HELPER_PLATFORM "linux" +#if defined(__x86_64__) +#define ADDON_HELPER_ARCH "x86_64" +#elif defined(_POWERPC) +#define ADDON_HELPER_ARCH "powerpc" +#elif defined(_POWERPC64) +#define ADDON_HELPER_ARCH "powerpc64" +#elif defined(__ARMEL__) +#define ADDON_HELPER_ARCH "arm" +#elif defined(_MIPSEL) +#define ADDON_HELPER_ARCH "mipsel" +#else +#define ADDON_HELPER_ARCH "i486" +#endif +#endif +#include <dlfcn.h> // linux+osx +#define ADDON_HELPER_EXT ".so" +#define ADDON_DLL "/library.xbmc.addon/libXBMC_addon-" ADDON_HELPER_ARCH "-" ADDON_HELPER_PLATFORM ADDON_HELPER_EXT +#endif + +#ifdef LOG_DEBUG +#undef LOG_DEBUG +#endif +#ifdef LOG_INFO +#undef LOG_INFO +#endif +#ifdef LOG_NOTICE +#undef LOG_NOTICE +#endif +#ifdef LOG_ERROR +#undef LOG_ERROR +#endif + +namespace ADDON +{ + typedef enum addon_log + { + LOG_DEBUG, + LOG_INFO, + LOG_NOTICE, + LOG_ERROR + } addon_log_t; + + typedef enum queue_msg + { + QUEUE_INFO, + QUEUE_WARNING, + QUEUE_ERROR + } queue_msg_t; + + class CHelper_libXBMC_addon + { + public: + CHelper_libXBMC_addon() + { + m_libXBMC_addon = NULL; + m_Handle = NULL; + } + + ~CHelper_libXBMC_addon() + { + if (m_libXBMC_addon) + { + XBMC_unregister_me(); + dlclose(m_libXBMC_addon); + } + } + + bool RegisterMe(void *Handle) + { + m_Handle = Handle; + + std::string libBasePath; + libBasePath = ((cb_array*)m_Handle)->libPath; + libBasePath += ADDON_DLL; + + m_libXBMC_addon = dlopen(libBasePath.c_str(), RTLD_LAZY); + if (m_libXBMC_addon == NULL) + { + fprintf(stderr, "Unable to load %s\n", dlerror()); + return false; + } + + XBMC_register_me = (int (*)(void *HANDLE)) + dlsym(m_libXBMC_addon, "XBMC_register_me"); + if (XBMC_register_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_unregister_me = (void (*)()) + dlsym(m_libXBMC_addon, "XBMC_unregister_me"); + if (XBMC_unregister_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + Log = (void (*)(const addon_log_t loglevel, const char *format, ... )) + dlsym(m_libXBMC_addon, "XBMC_log"); + if (Log == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GetSetting = (bool (*)(const char* settingName, void *settingValue)) + dlsym(m_libXBMC_addon, "XBMC_get_setting"); + if (GetSetting == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + QueueNotification = (void (*)(const queue_msg_t loglevel, const char *format, ... )) + dlsym(m_libXBMC_addon, "XBMC_queue_notification"); + if (QueueNotification == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + UnknownToUTF8 = (void (*)(std::string &str)) + dlsym(m_libXBMC_addon, "XBMC_unknown_to_utf8"); + if (UnknownToUTF8 == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GetLocalizedString = (const char* (*)(int dwCode)) + dlsym(m_libXBMC_addon, "XBMC_get_localized_string"); + if (GetLocalizedString == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GetDVDMenuLanguage = (const char* (*)()) + dlsym(m_libXBMC_addon, "XBMC_get_dvd_menu_language"); + if (GetDVDMenuLanguage == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + return XBMC_register_me(m_Handle) > 0; + } + + void (*Log)(const addon_log_t loglevel, const char *format, ... ); + bool (*GetSetting)(const char* settingName, void *settingValue); + void (*QueueNotification)(const queue_msg_t type, const char *format, ... ); + void (*UnknownToUTF8)(std::string &str); + const char* (*GetLocalizedString)(int dwCode); + const char* (*GetDVDMenuLanguage)(); + + protected: + int (*XBMC_register_me)(void *HANDLE); + void (*XBMC_unregister_me)(); + + private: + void *m_libXBMC_addon; + void *m_Handle; + struct cb_array + { + const char* libPath; + }; + }; +}; diff --git a/addons/library.xbmc.gui/libXBMC_gui.h b/addons/library.xbmc.gui/libXBMC_gui.h new file mode 100644 index 0000000000..afde37814d --- /dev/null +++ b/addons/library.xbmc.gui/libXBMC_gui.h @@ -0,0 +1,310 @@ +#pragma once +/* + * Copyright (C) 2005-2010 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include <string> +#include <vector> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include "../library.xbmc.addon/libXBMC_addon.h" + +typedef void* GUIHANDLE; + +#ifdef _WIN32 +#define GUI_HELPER_DLL "\\library.xbmc.gui\\libXBMC_gui" ADDON_HELPER_EXT +#else +#define GUI_HELPER_DLL "/library.xbmc.gui/libXBMC_gui-" ADDON_HELPER_ARCH "-" ADDON_HELPER_PLATFORM ADDON_HELPER_EXT +#endif + +#define ADDON_ACTION_PREVIOUS_MENU 10 +#define ADDON_ACTION_CLOSE_DIALOG 51 + +class CAddonGUIWindow; +class CAddonGUISpinControl; +class CAddonGUIRadioButton; +class CAddonGUIProgressControl; +class CAddonListItem; + +class CHelper_libXBMC_gui +{ +public: + CHelper_libXBMC_gui() + { + m_libXBMC_gui = NULL; + m_Handle = NULL; + } + + ~CHelper_libXBMC_gui() + { + if (m_libXBMC_gui) + { + GUI_unregister_me(); + dlclose(m_libXBMC_gui); + } + } + + bool RegisterMe(void *Handle) + { + m_Handle = Handle; + + std::string libBasePath; + libBasePath = ((cb_array*)m_Handle)->libPath; + libBasePath += GUI_HELPER_DLL; + + m_libXBMC_gui = dlopen(libBasePath.c_str(), RTLD_LAZY); + if (m_libXBMC_gui == NULL) + { + fprintf(stderr, "Unable to load %s\n", dlerror()); + return false; + } + + GUI_register_me = (int (*)(void *HANDLE)) + dlsym(m_libXBMC_gui, "GUI_register_me"); + if (GUI_register_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_unregister_me = (void (*)()) + dlsym(m_libXBMC_gui, "GUI_unregister_me"); + if (GUI_unregister_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + Lock = (void (*)()) + dlsym(m_libXBMC_gui, "GUI_lock"); + if (Lock == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + Unlock = (void (*)()) + dlsym(m_libXBMC_gui, "GUI_unlock"); + if (Unlock == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GetScreenHeight = (int (*)()) + dlsym(m_libXBMC_gui, "GUI_get_screen_height"); + if (GetScreenHeight == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GetScreenWidth = (int (*)()) + dlsym(m_libXBMC_gui, "GUI_get_screen_width"); + if (GetScreenWidth == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GetVideoResolution = (int (*)()) + dlsym(m_libXBMC_gui, "GUI_get_video_resolution"); + if (GetVideoResolution == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + Window_create = (CAddonGUIWindow* (*)(const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog)) + dlsym(m_libXBMC_gui, "GUI_Window_create"); + if (Window_create == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + Window_destroy = (void (*)(CAddonGUIWindow* p)) + dlsym(m_libXBMC_gui, "GUI_Window_destroy"); + if (Window_destroy == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + Control_getSpin = (CAddonGUISpinControl* (*)(CAddonGUIWindow *window, int controlId)) + dlsym(m_libXBMC_gui, "GUI_control_get_spin"); + if (Control_getSpin == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + Control_releaseSpin = (void (*)(CAddonGUISpinControl* p)) + dlsym(m_libXBMC_gui, "GUI_control_release_spin"); + if (Control_releaseSpin == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + Control_getRadioButton = (CAddonGUIRadioButton* (*)(CAddonGUIWindow *window, int controlId)) + dlsym(m_libXBMC_gui, "GUI_control_get_radiobutton"); + if (Control_getRadioButton == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + Control_releaseRadioButton = (void (*)(CAddonGUIRadioButton* p)) + dlsym(m_libXBMC_gui, "GUI_control_release_radiobutton"); + if (Control_releaseRadioButton == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + Control_getProgress = (CAddonGUIProgressControl* (*)(CAddonGUIWindow *window, int controlId)) + dlsym(m_libXBMC_gui, "GUI_control_get_progress"); + if (Control_getProgress == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + Control_releaseProgress = (void (*)(CAddonGUIProgressControl* p)) + dlsym(m_libXBMC_gui, "GUI_control_release_progress"); + if (Control_releaseProgress == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + ListItem_create = (CAddonListItem* (*)(const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path)) + dlsym(m_libXBMC_gui, "GUI_ListItem_create"); + if (ListItem_create == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + ListItem_destroy = (void (*)(CAddonListItem* p)) + dlsym(m_libXBMC_gui, "GUI_ListItem_destroy"); + if (ListItem_destroy == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + + return GUI_register_me(m_Handle) > 0; + } + + void (*Lock)(); + void (*Unlock)(); + int (*GetScreenHeight)(); + int (*GetScreenWidth)(); + int (*GetVideoResolution)(); + CAddonGUIWindow* (*Window_create)(const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog); + void (*Window_destroy)(CAddonGUIWindow* p); + CAddonGUISpinControl* (*Control_getSpin)(CAddonGUIWindow *window, int controlId); + void (*Control_releaseSpin)(CAddonGUISpinControl* p); + CAddonGUIRadioButton* (*Control_getRadioButton)(CAddonGUIWindow *window, int controlId); + void (*Control_releaseRadioButton)(CAddonGUIRadioButton* p); + CAddonGUIProgressControl* (*Control_getProgress)(CAddonGUIWindow *window, int controlId); + void (*Control_releaseProgress)(CAddonGUIProgressControl* p); + CAddonListItem* (*ListItem_create)(const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path); + void (*ListItem_destroy)(CAddonListItem* p); + +protected: + int (*GUI_register_me)(void *HANDLE); + void (*GUI_unregister_me)(); + +private: + void *m_libXBMC_gui; + void *m_Handle; + struct cb_array + { + const char* libPath; + }; +}; + +class CAddonGUISpinControl +{ +public: + CAddonGUISpinControl(CAddonGUIWindow *window, int controlId); + virtual ~CAddonGUISpinControl(void) {} + + virtual void SetVisible(bool yesNo); + virtual void SetText(const char *label); + virtual void Clear(); + virtual void AddLabel(const char *label, int iValue); + virtual int GetValue(); + virtual void SetValue(int iValue); + +private: + CAddonGUIWindow *m_Window; + int m_ControlId; + GUIHANDLE m_SpinHandle; +}; + +class CAddonGUIRadioButton +{ +public: + CAddonGUIRadioButton(CAddonGUIWindow *window, int controlId); + ~CAddonGUIRadioButton() {} + + virtual void SetVisible(bool yesNo); + virtual void SetText(const char *label); + virtual void SetSelected(bool yesNo); + virtual bool IsSelected(); + +private: + CAddonGUIWindow *m_Window; + int m_ControlId; + GUIHANDLE m_ButtonHandle; +}; + +class CAddonGUIProgressControl +{ +public: + CAddonGUIProgressControl(CAddonGUIWindow *window, int controlId); + virtual ~CAddonGUIProgressControl(void) {} + + virtual void SetPercentage(float fPercent); + virtual float GetPercentage() const; + virtual void SetInfo(int iInfo); + virtual int GetInfo() const; + virtual std::string GetDescription() const; + +private: + CAddonGUIWindow *m_Window; + int m_ControlId; + GUIHANDLE m_ProgressHandle; +}; + +class CAddonListItem +{ +friend class CAddonGUIWindow; + +public: + CAddonListItem(const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path); + virtual ~CAddonListItem(void) {} + + virtual const char *GetLabel(); + virtual void SetLabel(const char *label); + virtual const char *GetLabel2(); + virtual void SetLabel2(const char *label); + virtual void SetIconImage(const char *image); + virtual void SetThumbnailImage(const char *image); + virtual void SetInfo(const char *Info); + virtual void SetProperty(const char *key, const char *value); + virtual const char *GetProperty(const char *key) const; + virtual void SetPath(const char *Path); + +// {(char*)"select(); +// {(char*)"isSelected(); +protected: + GUIHANDLE m_ListItemHandle; +}; + +class CAddonGUIWindow +{ +friend class CAddonGUISpinControl; +friend class CAddonGUIRadioButton; +friend class CAddonGUIProgressControl; + +public: + CAddonGUIWindow(const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog); + ~CAddonGUIWindow(); + + virtual bool Show(); + virtual void Close(); + virtual void DoModal(); + virtual bool SetFocusId(int iControlId); + virtual int GetFocusId(); + virtual bool SetCoordinateResolution(int res); + virtual void SetProperty(const char *key, const char *value); + virtual void SetPropertyInt(const char *key, int value); + virtual void SetPropertyBool(const char *key, bool value); + virtual void SetPropertyDouble(const char *key, double value); + virtual const char *GetProperty(const char *key) const; + virtual int GetPropertyInt(const char *key) const; + virtual bool GetPropertyBool(const char *key) const; + virtual double GetPropertyDouble(const char *key) const; + virtual void ClearProperties(); + virtual int GetListSize(); + virtual void ClearList(); + virtual GUIHANDLE AddStringItem(const char *name, int itemPosition = -1); + virtual void AddItem(GUIHANDLE item, int itemPosition = -1); + virtual void AddItem(CAddonListItem *item, int itemPosition = -1); + virtual void RemoveItem(int itemPosition); + virtual GUIHANDLE GetListItem(int listPos); + virtual void SetCurrentListPosition(int listPos); + virtual int GetCurrentListPosition(); + virtual void SetControlLabel(int controlId, const char *label); + + virtual bool OnClick(int controlId); + virtual bool OnFocus(int controlId); + virtual bool OnInit(); + virtual bool OnAction(int actionId); + + GUIHANDLE m_cbhdl; + bool (*CBOnInit)(GUIHANDLE cbhdl); + bool (*CBOnFocus)(GUIHANDLE cbhdl, int controlId); + bool (*CBOnClick)(GUIHANDLE cbhdl, int controlId); + bool (*CBOnAction)(GUIHANDLE cbhdl, int actionId); + +protected: + GUIHANDLE m_WindowHandle; +}; + diff --git a/addons/library.xbmc.pvr/libXBMC_pvr.h b/addons/library.xbmc.pvr/libXBMC_pvr.h new file mode 100644 index 0000000000..a485de302d --- /dev/null +++ b/addons/library.xbmc.pvr/libXBMC_pvr.h @@ -0,0 +1,170 @@ +#pragma once +/* + * Copyright (C) 2005-2010 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include <string> +#include <vector> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include "xbmc_pvr_types.h" +#include "../library.xbmc.addon/libXBMC_addon.h" + +#ifdef _WIN32 +#define PVR_HELPER_DLL "\\library.xbmc.pvr\\libXBMC_pvr" ADDON_HELPER_EXT +#else +#define PVR_HELPER_DLL "/library.xbmc.pvr/libXBMC_pvr-" ADDON_HELPER_ARCH "-" ADDON_HELPER_PLATFORM ADDON_HELPER_EXT +#endif + +#define DVD_TIME_BASE 1000000 +#define DVD_NOPTS_VALUE (-1LL<<52) // should be possible to represent in both double and __int64 + +class CHelper_libXBMC_pvr +{ +public: + CHelper_libXBMC_pvr() + { + m_libXBMC_pvr = NULL; + m_Handle = NULL; + } + + ~CHelper_libXBMC_pvr() + { + if (m_libXBMC_pvr) + { + PVR_unregister_me(); + dlclose(m_libXBMC_pvr); + } + } + + bool RegisterMe(void *Handle) + { + m_Handle = Handle; + + std::string libBasePath; + libBasePath = ((cb_array*)m_Handle)->libPath; + libBasePath += PVR_HELPER_DLL; + + m_libXBMC_pvr = dlopen(libBasePath.c_str(), RTLD_LAZY); + if (m_libXBMC_pvr == NULL) + { + fprintf(stderr, "Unable to load %s\n", dlerror()); + return false; + } + + PVR_register_me = (int (*)(void *HANDLE)) + dlsym(m_libXBMC_pvr, "PVR_register_me"); + if (PVR_register_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + PVR_unregister_me = (void (*)()) + dlsym(m_libXBMC_pvr, "PVR_unregister_me"); + if (PVR_unregister_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + TransferEpgEntry = (void (*)(const ADDON_HANDLE handle, const EPG_TAG *epgentry)) + dlsym(m_libXBMC_pvr, "PVR_transfer_epg_entry"); + if (TransferEpgEntry == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + TransferChannelEntry = (void (*)(const ADDON_HANDLE handle, const PVR_CHANNEL *chan)) + dlsym(m_libXBMC_pvr, "PVR_transfer_channel_entry"); + if (TransferChannelEntry == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + TransferTimerEntry = (void (*)(const ADDON_HANDLE handle, const PVR_TIMER *timer)) + dlsym(m_libXBMC_pvr, "PVR_transfer_timer_entry"); + if (TransferTimerEntry == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + TransferRecordingEntry = (void (*)(const ADDON_HANDLE handle, const PVR_RECORDING *recording)) + dlsym(m_libXBMC_pvr, "PVR_transfer_recording_entry"); + if (TransferRecordingEntry == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + AddMenuHook = (void (*)(PVR_MENUHOOK *hook)) + dlsym(m_libXBMC_pvr, "PVR_add_menu_hook"); + if (AddMenuHook == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + Recording = (void (*)(const char *Name, const char *FileName, bool On)) + dlsym(m_libXBMC_pvr, "PVR_recording"); + if (Recording == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + TriggerTimerUpdate = (void (*)()) + dlsym(m_libXBMC_pvr, "PVR_trigger_timer_update"); + if (TriggerTimerUpdate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + TriggerRecordingUpdate = (void (*)()) + dlsym(m_libXBMC_pvr, "PVR_trigger_recording_update"); + if (TriggerRecordingUpdate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + TriggerChannelUpdate = (void (*)()) + dlsym(m_libXBMC_pvr, "PVR_trigger_channel_update"); + if (TriggerChannelUpdate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + TriggerChannelGroupsUpdate = (void (*)()) + dlsym(m_libXBMC_pvr, "PVR_trigger_channel_groups_update"); + if (TriggerChannelGroupsUpdate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + TransferChannelGroup = (void (*)(const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP *group)) + dlsym(m_libXBMC_pvr, "PVR_transfer_channel_group"); + if (TransferChannelGroup == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + TransferChannelGroupMember = (void (*)(const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER *member)) + dlsym(m_libXBMC_pvr, "PVR_transfer_channel_group_member"); + if (TransferChannelGroupMember == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + +#ifdef USE_DEMUX + FreeDemuxPacket = (void (*)(DemuxPacket* pPacket)) + dlsym(m_libXBMC_pvr, "PVR_free_demux_packet"); + if (FreeDemuxPacket == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + AllocateDemuxPacket = (DemuxPacket* (*)(int iDataSize)) + dlsym(m_libXBMC_pvr, "PVR_allocate_demux_packet"); + if (AllocateDemuxPacket == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } +#endif + + return PVR_register_me(m_Handle) > 0; + } + + void (*TransferEpgEntry)(const ADDON_HANDLE handle, const EPG_TAG *epgentry); + void (*TransferChannelEntry)(const ADDON_HANDLE handle, const PVR_CHANNEL *chan); + void (*TransferTimerEntry)(const ADDON_HANDLE handle, const PVR_TIMER *timer); + void (*TransferRecordingEntry)(const ADDON_HANDLE handle, const PVR_RECORDING *recording); + void (*AddMenuHook)(PVR_MENUHOOK *hook); + void (*Recording)(const char *Name, const char *FileName, bool On); + void (*TriggerTimerUpdate)(); + void (*TriggerRecordingUpdate)(); + void (*TriggerChannelUpdate)(); + void (*TriggerChannelGroupsUpdate)(); + void (*TransferChannelGroup)(const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP *group); + void (*TransferChannelGroupMember)(const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER *member); +#ifdef USE_DEMUX + void (*FreeDemuxPacket)(DemuxPacket* pPacket); + DemuxPacket* (*AllocateDemuxPacket)(int iDataSize); +#endif + +protected: + int (*PVR_register_me)(void *HANDLE); + void (*PVR_unregister_me)(); + +private: + void *m_libXBMC_pvr; + void *m_Handle; + struct cb_array + { + const char* libPath; + }; +}; diff --git a/addons/skin.confluence/720p/DialogButtonMenu.xml b/addons/skin.confluence/720p/DialogButtonMenu.xml index 08848834fc..0ae22e8b58 100644 --- a/addons/skin.confluence/720p/DialogButtonMenu.xml +++ b/addons/skin.confluence/720p/DialogButtonMenu.xml @@ -63,9 +63,9 @@ <onclick>PreviousMenu</onclick> <texturefocus>DialogCloseButton-focus.png</texturefocus> <texturenofocus>DialogCloseButton.png</texturenofocus> - <onleft>13</onleft> + <onleft>2</onleft> <onright>13</onright> - <onup>9</onup> + <onup>13</onup> <ondown>2</ondown> <visible>system.getbool(input.enablemouse)</visible> </control> @@ -222,7 +222,7 @@ <font>font13</font> <visible>System.HasLocks</visible> </control> - <control type="group" id="11"> + <control type="group" id="10"> <width>340</width> <height>70</height> <visible>System.HasAlarm(shutdowntimer)</visible> @@ -246,7 +246,39 @@ <label>$LOCALIZE[31329] [B]$INFO[System.Alarmpos][/B]</label> </control> </control> - <control type="image" id="12"> + <control type="button" id="11"> + <description>Inhibit idle shutdown</description> + <width>340</width> + <height>40</height> + <textcolor>grey2</textcolor> + <focusedcolor>white</focusedcolor> + <align>center</align> + <textwidth>290</textwidth> + <texturefocus border="25,5,25,5">ShutdownButtonFocus.png</texturefocus> + <texturenofocus border="25,5,25,5">ShutdownButtonNoFocus.png</texturenofocus> + <onclick>XBMC.InhibitIdleShutdown(true)</onclick> + <pulseonselect>no</pulseonselect> + <font>font13</font> + <label>13017</label> + <visible>System.HasShutdown +!System.IsInhibit</visible> + </control> + <control type="button" id="12"> + <description>Allow idle shutdown</description> + <width>340</width> + <height>40</height> + <textcolor>grey2</textcolor> + <focusedcolor>white</focusedcolor> + <align>center</align> + <textwidth>290</textwidth> + <texturefocus border="25,5,25,5">ShutdownButtonFocus.png</texturefocus> + <texturenofocus border="25,5,25,5">ShutdownButtonNoFocus.png</texturenofocus> + <onclick>XBMC.InhibitIdleShutdown(false)</onclick> + <pulseonselect>no</pulseonselect> + <font>font13</font> + <label>13018</label> + <visible>System.HasShutdown + System.IsInhibit</visible> + </control> + <control type="image" id="13"> <description>background bottom image</description> <posx>0</posx> <width>340</width> diff --git a/addons/skin.confluence/720p/DialogExtendedProgressBar.xml b/addons/skin.confluence/720p/DialogExtendedProgressBar.xml new file mode 100644 index 0000000000..412d92c894 --- /dev/null +++ b/addons/skin.confluence/720p/DialogExtendedProgressBar.xml @@ -0,0 +1,48 @@ +<window id="614"> + <defaultcontrol></defaultcontrol> + <animation effect="slide" start="0,-70" end="0,0" time="100">WindowOpen</animation> + <animation effect="slide" start="0,0" end="0,-70" delay="400" time="100">WindowClose</animation> + <controls> + <control type="group"> + <posx>720</posx> + <posy>0</posy> + <animation effect="slide" end="-400,0" time="200" condition="Window.IsVisible(133)">conditional</animation> + <animation effect="slide" end="0,-80" time="200" condition="Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation)">conditional</animation> + <control type="image"> + <posx>0</posx> + <posy>-10</posy> + <width>400</width> + <height>70</height> + <texture flipy="true" border="20,20,20,2">InfoMessagePanel.png</texture> + </control> + <control type="label" id="30"> + <description>Header Label</description> + <posx>15</posx> + <posy>4</posy> + <width>370</width> + <height>18</height> + <font>font10_title</font> + <textcolor>selected</textcolor> + <align>left</align> + <aligny>center</aligny> + </control> + <control type="label" id="31"> + <description>Title Label</description> + <posx>15</posx> + <posy>20</posy> + <width>370</width> + <height>20</height> + <font>font10</font> + <align>left</align> + <aligny>center</aligny> + </control> + <control type="progress" id="32"> + <description>progress control</description> + <posx>15</posx> + <posy>42</posy> + <width>370</width> + <height>8</height> + </control> + </control> + </controls> +</window>
\ No newline at end of file diff --git a/addons/skin.confluence/720p/DialogPVRChannelManager.xml b/addons/skin.confluence/720p/DialogPVRChannelManager.xml new file mode 100644 index 0000000000..4093206466 --- /dev/null +++ b/addons/skin.confluence/720p/DialogPVRChannelManager.xml @@ -0,0 +1,560 @@ +<window id="605"> + <defaultcontrol always="true">20</defaultcontrol> + <allowoverlay>no</allowoverlay> + <coordinates> + <system>1</system> + <posx>190</posx> + <posy>30</posy> + </coordinates> + <include>dialogeffect</include> + + <controls> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>900</width> + <height>660</height> + <texture border="40">DialogBack.png</texture> + </control> + <control type="image"> + <description>Dialog Header image</description> + <posx>40</posx> + <posy>16</posy> + <width>820</width> + <height>40</height> + <texture>dialogheader.png</texture> + </control> + <control type="label"> + <description>header label</description> + <posx>40</posx> + <posy>20</posy> + <width>820</width> + <height>30</height> + <font>font13_title</font> + <label>$LOCALIZE[19199] - $LOCALIZE[19023]</label> + <align>center</align> + <aligny>center</aligny> + <textcolor>selected</textcolor> + <shadowcolor>black</shadowcolor> + <visible>IsEmpty(Window.Property(IsRadio))</visible> + </control> + <control type="label"> + <description>header label</description> + <posx>40</posx> + <posy>20</posy> + <width>820</width> + <height>30</height> + <font>font13_title</font> + <label>$LOCALIZE[19199] - $LOCALIZE[19024]</label> + <align>center</align> + <aligny>center</aligny> + <textcolor>selected</textcolor> + <shadowcolor>black</shadowcolor> + <visible>!IsEmpty(Window.Property(IsRadio))</visible> + </control> + <control type="button"> + <description>Close Window button</description> + <posx>810</posx> + <posy>15</posy> + <width>64</width> + <height>32</height> + <label>-</label> + <font>-</font> + <onclick>PreviousMenu</onclick> + <texturefocus>DialogCloseButton-focus.png</texturefocus> + <texturenofocus>DialogCloseButton.png</texturenofocus> + <onleft>10</onleft> + <onright>10</onright> + <onup>10</onup> + <ondown>10</ondown> + <visible>system.getbool(input.enablemouse)</visible> + </control> + <control type="group"> + <posx>20</posx> + <posy>70</posy> + <control type="scrollbar" id="60"> + <posx>0</posx> + <posy>5</posy> + <width>25</width> + <height>470</height> + <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground> + <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar> + <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus> + <textureslidernib>ScrollBarNib.png</textureslidernib> + <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus> + <onleft>9002</onleft> + <onright>20</onright> + <showonepage>false</showonepage> + <orientation>vertical</orientation> + </control> + <control type="image"> + <posx>25</posx> + <posy>0</posy> + <width>430</width> + <height>475</height> + <texture border="5">button-nofocus.png</texture> + </control> + <control type="list" id="20"> + <posx>30</posx> + <posy>5</posy> + <width>420</width> + <height>470</height> + <onup>20</onup> + <ondown>20</ondown> + <onleft>60</onleft> + <onright>9002</onright> + <pagecontrol>60</pagecontrol> + <scrolltime>200</scrolltime> + <itemlayout height="45" width="420"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>420</width> + <height>40</height> + <texture border="5">button-nofocus.png</texture> + </control> + <control type="image"> + <posx>5</posx> + <posy>5</posy> + <width>30</width> + <height>30</height> + <texture>$INFO[ListItem.Property(Icon)]</texture> + <visible>ListItem.Property(ActiveChannel)</visible> + </control> + <control type="image"> + <posx>5</posx> + <posy>5</posy> + <width>30</width> + <height>30</height> + <colordiffuse>77FFFFFF</colordiffuse> + <texture>$INFO[ListItem.Property(Icon)]</texture> + <visible>!ListItem.Property(ActiveChannel)</visible> + </control> + <control type="label"> + <posx>45</posx> + <posy>0</posy> + <width>335</width> + <height>40</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <label>$INFO[ListItem.Property(Number),(,) - ]$INFO[ListItem.Property(Name)]</label> + <visible>ListItem.Property(ActiveChannel)</visible> + </control> + <control type="label"> + <posx>45</posx> + <posy>0</posy> + <width>335</width> + <height>40</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey3</textcolor> + <selectedcolor>selected</selectedcolor> + <label>$INFO[ListItem.Property(Number),(,) - ]$INFO[ListItem.Property(Name)]</label> + <visible>!ListItem.Property(ActiveChannel)</visible> + </control> + <control type="image"> + <posx>390</posx> + <posy>10</posy> + <width>20</width> + <height>20</height> + <texture>OverlayWatched.png</texture> + <visible>ListItem.Property(Changed)</visible> + </control> + </itemlayout> + <focusedlayout height="65" width="420"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>420</width> + <height>60</height> + <texture border="5">button-focus2.png</texture> + <animation effect="fade" start="100" end="30" time="0" condition="!Control.HasFocus(20)">conditional</animation> + </control> + <control type="image"> + <posx>5</posx> + <posy>5</posy> + <width>30</width> + <height>30</height> + <texture>$INFO[ListItem.Property(Icon)]</texture> + <visible>ListItem.Property(ActiveChannel)</visible> + </control> + <control type="image"> + <posx>5</posx> + <posy>5</posy> + <width>30</width> + <height>30</height> + <colordiffuse>77FFFFFF</colordiffuse> + <texture>$INFO[ListItem.Property(Icon)]</texture> + <visible>!ListItem.Property(ActiveChannel)</visible> + </control> + <control type="label"> + <posx>45</posx> + <posy>0</posy> + <width>335</width> + <height>40</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <label>$INFO[ListItem.Property(Number),(,) - ]$INFO[ListItem.Property(Name)]</label> + <visible>ListItem.Property(ActiveChannel)</visible> + </control> + <control type="label"> + <posx>45</posx> + <posy>0</posy> + <width>335</width> + <height>40</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey3</textcolor> + <selectedcolor>selected</selectedcolor> + <label>$INFO[ListItem.Property(Number),(,) - ]$INFO[ListItem.Property(Name)]</label> + <visible>!ListItem.Property(ActiveChannel)</visible> + </control> + <control type="image"> + <posx>390</posx> + <posy>10</posy> + <width>20</width> + <height>20</height> + <texture>OverlayWatched.png</texture> + <visible>ListItem.Property(Changed)</visible> + </control> + <control type="label"> + <posx>5</posx> + <posy>30</posy> + <width>410</width> + <height>30</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <label>$LOCALIZE[19210]: $INFO[ListItem.Property(ClientName)]</label> + </control> + </focusedlayout> + </control> + <control type="label"> + <description>Page Count Label</description> + <posx>30</posx> + <posy>485</posy> + <width>420</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <scroll>false</scroll> + <align>center</align> + <aligny>center</aligny> + <label>([COLOR=blue]$INFO[Container(20).NumItems][/COLOR]) $LOCALIZE[19019] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(20).CurrentPage]/$INFO[Container(20).NumPages][/COLOR])</label> + </control> + </control> + <control type="group" id="9002"> + <control type="group"> + <posx>490</posx> + <posy>70</posy> + <control type="label"> + <description>channel options Header</description> + <posx>0</posx> + <posy>0</posy> + <width>380</width> + <height>20</height> + <font>font12</font> + <label>$LOCALIZE[31511]</label> + <align>left</align> + <aligny>center</aligny> + <textcolor>blue</textcolor> + <shadowcolor>black</shadowcolor> + </control> + <control type="radiobutton" id ="7"> + <description>Channel activated</description> + <posx>0</posx> + <posy>25</posy> + <width>380</width> + <height>35</height> + <font>font12</font> + <textcolor>white</textcolor> + <focusedcolor>white</focusedcolor> + <shadowcolor>black</shadowcolor> + <align>left</align> + <aligny>center</aligny> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <pulseonselect>no</pulseonselect> + <label>19074</label> + <onleft>20</onleft> + <onright>60</onright> + <onup>9000</onup> + <ondown>8</ondown> + </control> + <control type="edit" id ="8"> + <description>Channel name</description> + <posx>0</posx> + <posy>65</posy> + <width>380</width> + <height>35</height> + <font>font12</font> + <textcolor>white</textcolor> + <focusedcolor>white</focusedcolor> + <shadowcolor>black</shadowcolor> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <label>19201</label> + <onright>60</onright> + <onleft>20</onleft> + <onup>7</onup> + <ondown>9</ondown> + </control> + <control type="button" id ="9"> + <description>Channel logo Button</description> + <posx>0</posx> + <posy>105</posy> + <width>380</width> + <height>35</height> + <font>font12</font> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <label>19202</label> + <onleft>20</onleft> + <onright>60</onright> + <onup>8</onup> + <ondown>12</ondown> + </control> + <control type="image" id ="10"> + <description>Current Channel Icon</description> + <posx>345</posx> + <posy>107</posy> + <width>30</width> + <height>30</height> + <aspectratio>keep</aspectratio> + <info>ListItem.Property(Icon)</info> + </control> + <control type="radiobutton" id ="12"> + <description>EPG activated</description> + <posx>0</posx> + <posy>145</posy> + <width>380</width> + <height>35</height> + <font>font12</font> + <textcolor>white</textcolor> + <focusedcolor>white</focusedcolor> + <shadowcolor>black</shadowcolor> + <align>left</align> + <aligny>center</aligny> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <pulseonselect>no</pulseonselect> + <label>19206</label> + <onleft>20</onleft> + <onright>60</onright> + <onup>9</onup> + <ondown>13</ondown> + </control> + <control type="spincontrolex" id ="13"> + <description>EPG source</description> + <posx>0</posx> + <posy>185</posy> + <width>380</width> + <height>35</height> + <font>font12</font> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <label>19200</label> + <onright>60</onright> + <onleft>20</onleft> + <onup>12</onup> + <ondown>14</ondown> + </control> + <control type="radiobutton" id ="14"> + <description>Parental locked</description> + <posx>0</posx> + <posy>225</posy> + <width>380</width> + <height>35</height> + <font>font12</font> + <textcolor>white</textcolor> + <focusedcolor>white</focusedcolor> + <shadowcolor>black</shadowcolor> + <align>left</align> + <aligny>center</aligny> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <pulseonselect>no</pulseonselect> + <label>19267</label> + <onleft>20</onleft> + <onright>60</onright> + <onup>13</onup> + <ondown>30</ondown> + </control> + </control> + <control type="group"> + <posx>490</posx> + <posy>360</posy> + <control type="label"> + <description>channel options Header</description> + <posx>0</posx> + <posy>0</posy> + <width>380</width> + <height>20</height> + <font>font12</font> + <label>$LOCALIZE[31026]</label> + <align>left</align> + <aligny>center</aligny> + <textcolor>blue</textcolor> + <shadowcolor>black</shadowcolor> + </control> + <control type="button" id ="30"> + <description>Group Manager Button</description> + <posx>0</posx> + <posy>25</posy> + <width>190</width> + <height>35</height> + <font>font12</font> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <align>center</align> + <label>19205</label> + <onleft>20</onleft> + <onright>34</onright> + <onup>14</onup> + <ondown>31</ondown> + </control> + <control type="button" id ="34"> + <description>TV/Radio Button</description> + <posx>195</posx> + <posy>25</posy> + <width>185</width> + <height>35</height> + <font>font12</font> + <visible>IsEmpty(Window.Property(IsRadio))</visible> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <align>center</align> + <label>19024</label> + <onleft>30</onleft> + <onright>60</onright> + <onup>14</onup> + <ondown>31</ondown> + </control> + <control type="button" id ="34"> + <description>TV/Radio Button</description> + <posx>195</posx> + <posy>25</posy> + <width>185</width> + <height>35</height> + <font>font12</font> + <visible>!IsEmpty(Window.Property(IsRadio))</visible> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <align>center</align> + <label>19023</label> + <onleft>30</onleft> + <onright>60</onright> + <onup>14</onup> + <ondown>31</ondown> + </control> + <control type="button" id ="31"> + <description>Edit channel Button</description> + <posx>0</posx> + <posy>65</posy> + <width>380</width> + <height>35</height> + <font>font12</font> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <align>center</align> + <label>19203</label> + <onleft>20</onleft> + <onright>60</onright> + <onup>30</onup> + <ondown>32</ondown> + </control> + <control type="button" id ="32"> + <description>Delete channel Button</description> + <posx>0</posx> + <posy>105</posy> + <width>380</width> + <height>35</height> + <font>font12</font> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <align>center</align> + <label>19211</label> + <onleft>20</onleft> + <onright>60</onright> + <onup>31</onup> + <ondown>33</ondown> + </control> + <control type="button" id ="33"> + <description>New channel Button</description> + <posx>0</posx> + <posy>145</posy> + <width>380</width> + <height>35</height> + <font>font12</font> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <align>center</align> + <label>19204</label> + <onleft>20</onleft> + <onright>60</onright> + <onup>32</onup> + <ondown>9000</ondown> + </control> + </control> + </control> + <control type="group" id="9000"> + <posx>70</posx> + <posy>590</posy> + <control type="button" id ="4"> + <description>OK Button</description> + <posx>0</posx> + <posy>0</posy> + <width>250</width> + <height>40</height> + <label>186</label> + <font>font12_title</font> + <align>center</align> + <aligny>center</aligny> + <onleft>6</onleft> + <onright>5</onright> + <onup>33</onup> + <ondown>7</ondown> + </control> + <control type="button" id ="5"> + <description>Apply changes Button</description> + <posx>260</posx> + <posy>0</posy> + <width>250</width> + <height>40</height> + <label>14070</label> + <font>font12_title</font> + <align>center</align> + <aligny>center</aligny> + <onleft>4</onleft> + <onright>6</onright> + <onup>33</onup> + <ondown>7</ondown> + </control> + <control type="button" id ="6"> + <description>Cancel Button</description> + <posx>520</posx> + <posy>0</posy> + <width>250</width> + <height>40</height> + <label>222</label> + <font>font12_title</font> + <align>center</align> + <aligny>center</aligny> + <onleft>5</onleft> + <onright>4</onright> + <onup>33</onup> + <ondown>7</ondown> + </control> + </control> + </controls> +</window> diff --git a/addons/skin.confluence/720p/DialogPVRChannelsOSD.xml b/addons/skin.confluence/720p/DialogPVRChannelsOSD.xml new file mode 100644 index 0000000000..fa54573f83 --- /dev/null +++ b/addons/skin.confluence/720p/DialogPVRChannelsOSD.xml @@ -0,0 +1,359 @@ +<window id="609"> + <defaultcontrol always="true">11</defaultcontrol> + <coordinates> + <system>1</system> + <posx>780</posx> + <posy>30</posy> + </coordinates> + <include>dialogeffect</include> + <controls> + <control type="group"> + <control type="image"> + <description>background image</description> + <posx>0</posx> + <posy>0</posy> + <width>480</width> + <height>660</height> + <texture border="40">DialogBack2.png</texture> + </control> + <control type="button"> + <description>Close Window button</description> + <posx>390</posx> + <posy>15</posy> + <width>64</width> + <height>32</height> + <label>-</label> + <font>-</font> + <onclick>PreviousMenu</onclick> + <texturefocus>DialogCloseButton-focus.png</texturefocus> + <texturenofocus>DialogCloseButton.png</texturenofocus> + <onleft>2</onleft> + <onright>2</onright> + <onup>2</onup> + <ondown>2</ondown> + <visible>system.getbool(input.enablemouse)</visible> + </control> + <control type="image"> + <description>Dialog Header image</description> + <posx>40</posx> + <posy>16</posy> + <width>400</width> + <height>50</height> + <texture>dialogheader.png</texture> + </control> + <control type="label"> + <description>header label</description> + <posx>40</posx> + <posy>16</posy> + <width>430</width> + <height>40</height> + <font>font12_title</font> + <label>$LOCALIZE[19023] - $INFO[VideoPlayer.ChannelGroup]</label> + <align>center</align> + <aligny>center</aligny> + <textcolor>blue</textcolor> + <shadowcolor>black</shadowcolor> + <visible>!pvr.IsPlayingRadio</visible> + </control> + <control type="label"> + <description>header label</description> + <posx>40</posx> + <posy>16</posy> + <width>430</width> + <height>40</height> + <font>font12_title</font> + <label>$LOCALIZE[19024] - $INFO[MusicPlayer.ChannelGroup]</label> + <align>center</align> + <aligny>center</aligny> + <textcolor>blue</textcolor> + <shadowcolor>black</shadowcolor> + <visible>pvr.IsPlayingRadio</visible> + </control> + <control type="label"> + <description>header label</description> + <posx>40</posx> + <posy>-7</posy> + <width>430</width> + <height>120</height> + <font>font10_title</font> + <label>$INFO[System.Date(DDD)], $INFO[System.Date(d)] $INFO[System.Date(mmm)] $INFO[System.Date(yyyy)] • $INFO[System.Time]</label> + <align>center</align> + <aligny>center</aligny> + <textcolor>grey</textcolor> + <shadowcolor>black</shadowcolor> + </control> + <control type="list" id="11"> + <posx>30</posx> + <posy>70</posy> + <width>410</width> + <height>520</height> + <onleft>60</onleft> + <onright>60</onright> + <onup>11</onup> + <ondown>11</ondown> + <viewtype label="535">list</viewtype> + <pagecontrol>60</pagecontrol> + <scrolltime>200</scrolltime> + <itemlayout height="70" width="410"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>410</width> + <height>65</height> + <texture border="5">button-nofocus.png</texture> + <include>VisibleFadeEffect</include> + </control> + <control type="label"> + <posx>5</posx> + <posy>0</posy> + <width>40</width> + <height>30</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <info>ListItem.ChannelNumber</info> + </control> + <control type="image"> + <posx>5</posx> + <posy>35</posy> + <width>30</width> + <height>20</height> + <texture>PVR-IsRecording.png</texture> + <visible>ListItem.IsRecording</visible> + </control> + <control type="label"> + <posx>50</posx> + <posy>0</posy> + <width>350</width> + <height>25</height> + <font>font13</font> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Label]</label> + </control> + <control type="label"> + <posx>50</posx> + <posy>25</posy> + <width>350</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Title]</label> + <visible>IsEmpty(Listitem.Icon)</visible> + </control> + <control type="label"> + <posx>50</posx> + <posy>25</posy> + <width>300</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Title]</label> + <visible>!IsEmpty(Listitem.Icon)</visible> + </control> + <control type="label"> + <posx>50</posx> + <posy>44</posy> + <width>60</width> + <height>20</height> + <font>font10_title</font> + <textcolor>blue</textcolor> + <selectedcolor>blue</selectedcolor> + <aligny>center</aligny> + <label>$INFO[ListItem.StartTime]</label> + <visible>ListItem.HasEpg</visible> + </control> + <control type="progress"> + <description>Progressbar</description> + <posx>110</posx> + <posy>53</posy> + <width>230</width> + <height>6</height> + <colordiffuse>88FFFFFF</colordiffuse> + <info>ListItem.Progress</info> + <visible>ListItem.HasEpg</visible> + </control> + <control type="label"> + <posx>355</posx> + <posy>44</posy> + <width>60</width> + <height>20</height> + <font>font10_title</font> + <textcolor>blue</textcolor> + <selectedcolor>blue</selectedcolor> + <aligny>center</aligny> + <label>$INFO[ListItem.EndTime]</label> + <visible>ListItem.HasEpg</visible> + </control> + <control type="image"> + <posx>360</posx> + <posy>4</posy> + <width>40</width> + <height>40</height> + <texture>$INFO[ListItem.Icon]</texture> + </control> + </itemlayout> + <focusedlayout height="70" width="410"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>410</width> + <height>65</height> + <texture border="5">button-nofocus.png</texture> + <visible>!Control.HasFocus(11)</visible> + <include>VisibleFadeEffect</include> + </control> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>410</width> + <height>65</height> + <texture border="5">button-focus2.png</texture> + <visible>Control.HasFocus(11)</visible> + <include>VisibleFadeEffect</include> + </control> + <control type="label"> + <posx>5</posx> + <posy>0</posy> + <width>40</width> + <height>30</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <info>ListItem.ChannelNumber</info> + </control> + <control type="image"> + <posx>5</posx> + <posy>35</posy> + <width>30</width> + <height>20</height> + <texture>PVR-IsRecording.png</texture> + <visible>ListItem.IsRecording</visible> + </control> + <control type="label"> + <posx>50</posx> + <posy>0</posy> + <width>350</width> + <height>25</height> + <font>font13</font> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Label]</label> + </control> + <control type="label"> + <posx>50</posx> + <posy>25</posy> + <width>350</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Title]</label> + <visible>IsEmpty(Listitem.Icon)</visible> + </control> + <control type="label"> + <posx>50</posx> + <posy>25</posy> + <width>300</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Title]</label> + <visible>!IsEmpty(Listitem.Icon)</visible> + </control> + <control type="label"> + <posx>50</posx> + <posy>44</posy> + <width>60</width> + <height>20</height> + <font>font10_title</font> + <textcolor>blue</textcolor> + <selectedcolor>blue</selectedcolor> + <aligny>center</aligny> + <label>$INFO[ListItem.StartTime]</label> + <visible>ListItem.HasEpg</visible> + </control> + <control type="progress"> + <description>Progressbar</description> + <posx>110</posx> + <posy>53</posy> + <width>230</width> + <height>6</height> + <colordiffuse>88FFFFFF</colordiffuse> + <info>ListItem.Progress</info> + <visible>ListItem.HasEpg</visible> + </control> + <control type="label"> + <posx>355</posx> + <posy>44</posy> + <width>60</width> + <height>20</height> + <font>font10_title</font> + <textcolor>blue</textcolor> + <selectedcolor>blue</selectedcolor> + <aligny>center</aligny> + <label>$INFO[ListItem.EndTime]</label> + <visible>ListItem.HasEpg</visible> + </control> + <control type="image"> + <posx>360</posx> + <posy>4</posy> + <width>40</width> + <height>40</height> + <texture>$INFO[ListItem.Icon]</texture> + </control> + </focusedlayout> + </control> + <control type="scrollbar" id="60"> + <posx>440</posx> + <posy>70</posy> + <width>25</width> + <height>520</height> + <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground> + <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar> + <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus> + <textureslidernib>ScrollBarNib.png</textureslidernib> + <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus> + <onleft>11</onleft> + <onright>11</onright> + <ondown>61</ondown> + <onup>61</onup> + <showonepage>false</showonepage> + <orientation>vertical</orientation> + </control> + <control type="label"> + <description>Page Count Label</description> + <posx>450</posx> + <posy>610</posy> + <width>400</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <scroll>false</scroll> + <align>right</align> + <aligny>center</aligny> + <label>([COLOR=blue]$INFO[Container(11).NumItems][/COLOR]) $LOCALIZE[19019] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(11).CurrentPage]/$INFO[Container(11).NumPages][/COLOR])</label> + <include>Window_OpenClose_Animation</include> + </control> + </control> + </controls> +</window>
\ No newline at end of file diff --git a/addons/skin.confluence/720p/DialogPVRGroupManager.xml b/addons/skin.confluence/720p/DialogPVRGroupManager.xml new file mode 100644 index 0000000000..5cdaaabcff --- /dev/null +++ b/addons/skin.confluence/720p/DialogPVRGroupManager.xml @@ -0,0 +1,433 @@ +<window id="604"> + <defaultcontrol always="true">29</defaultcontrol> + <controls> + <control type="group"> + <visible>!Window.IsVisible(FileBrowser)</visible> + <animation effect="slide" start="1150,0" end="0,0" time="400" tween="quadratic" easing="out">WindowOpen</animation> + <animation effect="slide" start="0,0" end="1150,0" time="400" tween="quadratic" easing="out">WindowClose</animation> + <control type="image"> + <posx>130</posx> + <posy>0</posy> + <width>1150</width> + <height>720</height> + <texture border="15,0,0,0" flipx="true">MediaBladeSub.png</texture> + </control> + <control type="button"> + <description>Close Window button</description> + <posx>180</posx> + <posy>0</posy> + <width>64</width> + <height>32</height> + <label>-</label> + <font>-</font> + <onclick>PreviousMenu</onclick> + <texturefocus>DialogCloseButton-focus.png</texturefocus> + <texturenofocus>DialogCloseButton.png</texturenofocus> + <onleft>9000</onleft> + <onright>9000</onright> + <onup>9000</onup> + <ondown>9000</ondown> + <visible>system.getbool(input.enablemouse)</visible> + </control> + <control type="group"> + <animation effect="fade" delay="400" start="0" end="100" time="200">WindowOpen</animation> + <animation effect="fade" start="100" end="0" time="200">WindowClose</animation> + <control type="label"> + <description>header label</description> + <posx>160</posx> + <posy>40</posy> + <width>1080</width> + <height>30</height> + <font>font24_title</font> + <label>19143</label> + <align>center</align> + <aligny>center</aligny> + <textcolor>selected</textcolor> + <shadowcolor>black</shadowcolor> + </control> + <control type="group"> + <description>Group list</description> + <posx>160</posx> + <posy>80</posy> + <control type="label"> + <description>name label</description> + <posx>0</posx> + <posy>0</posy> + <width>340</width> + <height>70</height> + <font>font13</font> + <label>31506</label> + <align>center</align> + <aligny>center</aligny> + <textcolor>blue</textcolor> + </control> + <control type="image"> + <posx>0</posx> + <posy>75</posy> + <width>340</width> + <height>460</height> + <texture border="5">button-nofocus.png</texture> + </control> + <control type="list" id="13"> + <posx>5</posx> + <posy>80</posy> + <width>330</width> + <height>450</height> + <onup>13</onup> + <ondown>13</ondown> + <onleft>29</onleft> + <onright>73</onright> + <pagecontrol>73</pagecontrol> + <scrolltime>200</scrolltime> + <itemlayout height="45"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>330</width> + <height>40</height> + <texture border="5">button-nofocus.png</texture> + </control> + <control type="label"> + <posx>10</posx> + <posy>0</posy> + <width>310</width> + <height>40</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Label</info> + </control> + </itemlayout> + <focusedlayout height="45"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>330</width> + <height>40</height> + <texture border="5">button-nofocus.png</texture> + <visible>!Control.HasFocus(13)</visible> + </control> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>330</width> + <height>40</height> + <texture border="5">button-focus2.png</texture> + <visible>Control.HasFocus(13)</visible> + </control> + <control type="label"> + <posx>10</posx> + <posy>0</posy> + <width>310</width> + <height>40</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Label</info> + </control> + </focusedlayout> + </control> + <control type="scrollbar" id="73"> + <posx>340</posx> + <posy>75</posy> + <width>25</width> + <height>460</height> + <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground> + <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar> + <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus> + <textureslidernib>ScrollBarNib.png</textureslidernib> + <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus> + <onleft>13</onleft> + <onright>11</onright> + <ondown>73</ondown> + <onup>73</onup> + <showonepage>false</showonepage> + <orientation>vertical</orientation> + </control> + </control> + <control type="group"> + <description>Channels list</description> + <posx>525</posx> + <posy>80</posy> + <control type="label" id="21"> + <description>name label</description> + <posx>0</posx> + <posy>0</posy> + <width>340</width> + <height>70</height> + <font>font13</font> + <align>center</align> + <aligny>center</aligny> + <textcolor>blue</textcolor> + </control> + <control type="image"> + <posx>0</posx> + <posy>75</posy> + <width>340</width> + <height>460</height> + <texture border="5">button-nofocus.png</texture> + </control> + <control type="list" id="11"> + <posx>5</posx> + <posy>85</posy> + <width>330</width> + <height>450</height> + <onup>11</onup> + <ondown>11</ondown> + <onleft>73</onleft> + <onright>71</onright> + <pagecontrol>71</pagecontrol> + <scrolltime>200</scrolltime> + <itemlayout height="45"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>330</width> + <height>40</height> + <texture border="5">button-nofocus.png</texture> + </control> + <control type="image"> + <width>32</width> + <height>32</height> + <posx>5</posx> + <posy>4</posy> + <texture>$INFO[ListItem.Icon]</texture> + </control> + <control type="label"> + <posx>40</posx> + <posy>0</posy> + <width>280</width> + <height>40</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <label>$INFO[ListItem.ChannelNumber,(,) - ]$INFO[ListItem.ChannelName]</label> + </control> + </itemlayout> + <focusedlayout height="45"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>330</width> + <height>40</height> + <texture border="5">button-nofocus.png</texture> + <visible>!Control.HasFocus(11)</visible> + </control> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>330</width> + <height>40</height> + <texture border="5">button-focus2.png</texture> + <visible>Control.HasFocus(11)</visible> + </control> + <control type="image"> + <width>32</width> + <height>32</height> + <posx>5</posx> + <posy>4</posy> + <texture>$INFO[ListItem.Icon]</texture> + </control> + <control type="label"> + <posx>40</posx> + <posy>0</posy> + <width>280</width> + <height>40</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <label>$INFO[ListItem.ChannelNumber,(,) - ]$INFO[ListItem.ChannelName]</label> + </control> + </focusedlayout> + </control> + <control type="scrollbar" id="71"> + <posx>340</posx> + <posy>75</posy> + <width>25</width> + <height>460</height> + <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground> + <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar> + <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus> + <textureslidernib>ScrollBarNib.png</textureslidernib> + <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus> + <onleft>11</onleft> + <onright>12</onright> + <ondown>71</ondown> + <onup>71</onup> + <showonepage>false</showonepage> + <orientation>vertical</orientation> + </control> + </control> + <control type="group"> + <description>Grouped Channels list</description> + <posx>890</posx> + <posy>80</posy> + <control type="label" id="22"> + <description>name label</description> + <posx>0</posx> + <posy>0</posy> + <width>340</width> + <height>70</height> + <font>font13</font> + <align>center</align> + <aligny>center</aligny> + <textcolor>blue</textcolor> + </control> + <control type="image"> + <posx>0</posx> + <posy>75</posy> + <width>340</width> + <height>460</height> + <texture border="5">button-nofocus.png</texture> + </control> + <control type="list" id="12"> + <posx>5</posx> + <posy>85</posy> + <width>330</width> + <height>450</height> + <onup>12</onup> + <ondown>12</ondown> + <onleft>71</onleft> + <onright>72</onright> + <pagecontrol>72</pagecontrol> + <scrolltime>200</scrolltime> + <itemlayout height="45"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>330</width> + <height>40</height> + <texture border="5">button-nofocus.png</texture> + </control> + <control type="image"> + <width>32</width> + <height>32</height> + <posx>5</posx> + <posy>4</posy> + <texture>$INFO[ListItem.Icon]</texture> + </control> + <control type="label"> + <posx>40</posx> + <posy>0</posy> + <width>280</width> + <height>40</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <label>$INFO[ListItem.ChannelNumber,(,) - ]$INFO[ListItem.ChannelName]</label> + </control> + </itemlayout> + <focusedlayout height="45"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>330</width> + <height>40</height> + <texture border="5">button-nofocus.png</texture> + <visible>!Control.HasFocus(12)</visible> + </control> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>330</width> + <height>40</height> + <texture border="5">button-focus2.png</texture> + <visible>Control.HasFocus(12)</visible> + </control> + <control type="image"> + <width>32</width> + <height>32</height> + <posx>5</posx> + <posy>4</posy> + <texture>$INFO[ListItem.Icon]</texture> + </control> + <control type="label"> + <posx>40</posx> + <posy>0</posy> + <width>280</width> + <height>40</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <label>$INFO[ListItem.ChannelNumber,(,) - ]$INFO[ListItem.ChannelName]</label> + </control> + </focusedlayout> + </control> + <control type="scrollbar" id="72"> + <posx>340</posx> + <posy>75</posy> + <width>25</width> + <height>460</height> + <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground> + <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar> + <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus> + <textureslidernib>ScrollBarNib.png</textureslidernib> + <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus> + <onleft>12</onleft> + <onright>26</onright> + <ondown>72</ondown> + <onup>72</onup> + <showonepage>false</showonepage> + <orientation>vertical</orientation> + </control> + </control> + <control type="grouplist" id="9000"> + <posx>160</posx> + <posy>660</posy> + <width>1080</width> + <height>40</height> + <itemgap>2</itemgap> + <align>center</align> + <orientation>horizontal</orientation> + <onleft>72</onleft> + <onright>13</onright> + <onup>9000</onup> + <ondown>9000</ondown> + <control type="button" id="26"> + <description>Add Group</description> + <width>230</width> + <include>ButtonInfoDialogsCommonValues</include> + <label>31503</label> + </control> + <control type="button" id="27"> + <description>Rename Group</description> + <width>230</width> + <include>ButtonInfoDialogsCommonValues</include> + <label>31504</label> + </control> + <control type="button" id="28"> + <description>Delete Group</description> + <width>230</width> + <include>ButtonInfoDialogsCommonValues</include> + <label>31505</label> + </control> + <control type="button" id="29"> + <description>OK</description> + <width>230</width> + <include>ButtonInfoDialogsCommonValues</include> + <label>186</label> + </control> + </control> + </control> + </control> + <include>SideBladeRight</include> + <include>Clock</include> + + <control type="label" id="20"> + <description>Fake Label used to pass on name label</description> + <visible>false</visible> + </control> + </controls> +</window>
\ No newline at end of file diff --git a/addons/skin.confluence/720p/DialogPVRGuideInfo.xml b/addons/skin.confluence/720p/DialogPVRGuideInfo.xml new file mode 100644 index 0000000000..24cc03cb78 --- /dev/null +++ b/addons/skin.confluence/720p/DialogPVRGuideInfo.xml @@ -0,0 +1,226 @@ +<window id="601"> + <defaultcontrol always="true">7</defaultcontrol> + <coordinates> + <system>1</system> + <posx>20</posx> + <posy>30</posy> + <origin x="275" y="30">![Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation)]</origin> + </coordinates> + <include>dialogeffect</include> + <controls> + <control type="group"> + <control type="image"> + <description>background image</description> + <posx>0</posx> + <posy>0</posy> + <width>730</width> + <height>660</height> + <texture border="40">DialogBack2.png</texture> + <visible>Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation)</visible> + </control> + <control type="image"> + <description>background image</description> + <posx>0</posx> + <posy>0</posy> + <width>730</width> + <height>660</height> + <texture border="40">DialogBack.png</texture> + <visible>![Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation)]</visible> + </control> + <control type="image"> + <description>Dialog Header image</description> + <posx>40</posx> + <posy>16</posy> + <width>650</width> + <height>40</height> + <texture>dialogheader.png</texture> + </control> + <control type="label"> + <description>header label</description> + <posx>40</posx> + <posy>20</posy> + <width>650</width> + <height>30</height> + <font>font13_title</font> + <label>$LOCALIZE[19047]</label> + <align>center</align> + <aligny>center</aligny> + <textcolor>selected</textcolor> + <shadowcolor>black</shadowcolor> + </control> + <control type="button"> + <description>Close Window button</description> + <posx>640</posx> + <posy>15</posy> + <width>64</width> + <height>32</height> + <label>-</label> + <font>-</font> + <onclick>PreviousMenu</onclick> + <texturefocus>DialogCloseButton-focus.png</texturefocus> + <texturenofocus>DialogCloseButton.png</texturenofocus> + <onleft>10</onleft> + <onright>10</onright> + <onup>10</onup> + <ondown>10</ondown> + <visible>system.getbool(input.enablemouse)</visible> + </control> + <control type="label"> + <description>Title label</description> + <posx>40</posx> + <posy>70</posy> + <width>650</width> + <height>30</height> + <font>font13_title</font> + <label>$INFO[ListItem.Title]</label> + <align>center</align> + <aligny>center</aligny> + <textcolor>blue</textcolor> + <shadowcolor>black</shadowcolor> + </control> + <control type="group"> + <control type="group"> + <posx>40</posx> + <posy>120</posy> + <control type="label"> + <description>Time description</description> + <posx>170</posx> + <posy>0</posy> + <width>170</width> + <height>25</height> + <align>right</align> + <aligny>center</aligny> + <font>font13</font> + <textcolor>blue</textcolor> + <label>$LOCALIZE[142]</label> + </control> + <control type="label"> + <description>Time value</description> + <posx>180</posx> + <posy>0</posy> + <width>470</width> + <height>25</height> + <align>left</align> + <aligny>center</aligny> + <font>font13</font> + <textcolor>white</textcolor> + <label>$INFO[ListItem.StartTime] - $INFO[ListItem.EndTime] ($INFO[ListItem.StartDate])</label> + </control> + <control type="label"> + <description>Duration</description> + <posx>170</posx> + <posy>35</posy> + <width>170</width> + <height>25</height> + <align>right</align> + <aligny>center</aligny> + <font>font13</font> + <textcolor>blue</textcolor> + <label>$LOCALIZE[180]:</label> + </control> + <control type="label"> + <description>Duration value</description> + <posx>180</posx> + <posy>35</posy> + <width>470</width> + <height>25</height> + <align>left</align> + <aligny>center</aligny> + <font>font13</font> + <textcolor>white</textcolor> + <label>$INFO[ListItem.Duration]</label> + </control> + <control type="label"> + <description>Channel Name</description> + <posx>170</posx> + <posy>70</posy> + <width>170</width> + <height>25</height> + <align>right</align> + <aligny>center</aligny> + <font>font13</font> + <textcolor>blue</textcolor> + <label>$LOCALIZE[19148]:</label> + </control> + <control type="fadelabel"> + <description>Channel Value</description> + <posx>180</posx> + <posy>70</posy> + <width>470</width> + <height>25</height> + <align>left</align> + <aligny>center</aligny> + <font>font13</font> + <textcolor>white</textcolor> + <label>$INFO[ListItem.ChannelName]</label> + </control> + <control type="label"> + <description>Genre</description> + <posx>170</posx> + <posy>105</posy> + <width>170</width> + <height>25</height> + <align>right</align> + <aligny>center</aligny> + <font>font13</font> + <textcolor>blue</textcolor> + <label>$LOCALIZE[135]:</label> + </control> + <control type="label"> + <description>Genre value</description> + <posx>180</posx> + <posy>105</posy> + <width>470</width> + <label fallback="161">$INFO[ListItem.Genre]</label> + <align>left</align> + <font>font13</font> + <scroll>true</scroll> + </control> + </control> + <control type="textbox" id="400"> + <description>Plot value</description> + <posx>40</posx> + <posy>275</posy> + <width>650</width> + <height>295</height> + <font>font12</font> + <align>justify</align> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <pagecontrol>-</pagecontrol> + <autoscroll time="2000" delay="3000" repeat="5000">true</autoscroll> + <label fallback="161">$INFO[ListItem.Plot]</label> + </control> + <control type="grouplist" id="9000"> + <posx>40</posx> + <posy>590</posy> + <width>640</width> + <height>40</height> + <itemgap>5</itemgap> + <align>center</align> + <orientation>horizontal</orientation> + <onleft>9000</onleft> + <onright>9000</onright> + <onup>60</onup> + <ondown>60</ondown> + <control type="button" id="5"> + <description>Switch to Channel</description> + <include>ButtonInfoDialogsCommonValues</include> + <label>19165</label> + </control> + <control type="button" id="6"> + <description>Record</description> + <include>ButtonInfoDialogsCommonValues</include> + <label>-</label> + </control> + <control type="button" id="7"> + <description>OK</description> + <include>ButtonInfoDialogsCommonValues</include> + <label>186</label> + </control> + </control> + </control> + </control> + <include>SideBladeRight</include> + </controls> +</window>
\ No newline at end of file diff --git a/addons/skin.confluence/720p/DialogPVRGuideOSD.xml b/addons/skin.confluence/720p/DialogPVRGuideOSD.xml new file mode 100644 index 0000000000..8cd50eab2a --- /dev/null +++ b/addons/skin.confluence/720p/DialogPVRGuideOSD.xml @@ -0,0 +1,253 @@ +<window id="610"> + <defaultcontrol always="true">11</defaultcontrol> + <coordinates> + <system>1</system> + <posx>780</posx> + <posy>30</posy> + </coordinates> + <include>dialogeffect</include> + <controls> + <control type="group"> + <control type="image"> + <description>background image</description> + <posx>0</posx> + <posy>0</posy> + <width>480</width> + <height>660</height> + <texture border="40">DialogBack2.png</texture> + </control> + <control type="button"> + <description>Close Window button</description> + <posx>390</posx> + <posy>15</posy> + <width>64</width> + <height>32</height> + <label>-</label> + <font>-</font> + <onclick>PreviousMenu</onclick> + <texturefocus>DialogCloseButton-focus.png</texturefocus> + <texturenofocus>DialogCloseButton.png</texturenofocus> + <onleft>2</onleft> + <onright>2</onright> + <onup>2</onup> + <ondown>2</ondown> + <visible>system.getbool(input.enablemouse)</visible> + </control> + <control type="image"> + <description>Dialog Header image</description> + <posx>40</posx> + <posy>16</posy> + <width>400</width> + <height>40</height> + <texture>dialogheader.png</texture> + </control> + <control type="label"> + <description>header label</description> + <posx>40</posx> + <posy>16</posy> + <width>430</width> + <height>40</height> + <font>font12_title</font> + <label>$LOCALIZE[19222] - $INFO[VideoPlayer.ChannelName]</label> + <align>center</align> + <aligny>center</aligny> + <textcolor>blue</textcolor> + <shadowcolor>black</shadowcolor> + </control> + <control type="label"> + <description>Selected item's date</description> + <posx>40</posx> + <posy>60</posy> + <width>430</width> + <height>30</height> + <font>font11</font> + <textcolor>grey2</textcolor> + <label>$INFO[Container(11).ListItem.StartDate]</label> + <align>center</align> + <aligny>center</aligny> + </control> + <control type="list" id="11"> + <posx>30</posx> + <posy>100</posy> + <width>410</width> + <height>490</height> + <onleft>60</onleft> + <onright>60</onright> + <onup>11</onup> + <ondown>11</ondown> + <viewtype label="535">list</viewtype> + <pagecontrol>60</pagecontrol> + <scrolltime>200</scrolltime> + <itemlayout height="35" width="410"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>410</width> + <height>30</height> + <texture border="5">button-nofocus.png</texture> + <include>VisibleFadeEffect</include> + </control> + <control type="label"> + <posx>10</posx> + <posy>0</posy> + <width>100</width> + <height>30</height> + <font>font13</font> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.StartTime]</label> + </control> + <control type="image"> + <posx>120</posx> + <posy>5</posy> + <width>30</width> + <height>20</height> + <texture>PVR-IsRecording.png</texture> + <visible>ListItem.IsRecording</visible> + </control> + <control type="image"> + <posx>120</posx> + <posy>5</posy> + <width>20</width> + <height>20</height> + <texture>PVR-HasTimer.png</texture> + <visible>ListItem.HasTimer + !ListItem.IsRecording</visible> + </control> + <control type="label"> + <posx>400</posx> + <posy>0</posy> + <width>300</width> + <height>30</height> + <font>font12</font> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <align>right</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Label]</label> + <visible>![ListItem.IsRecording | ListItem.HasTimer]</visible> + </control> + <control type="label"> + <posx>400</posx> + <posy>0</posy> + <width>250</width> + <height>30</height> + <font>font12</font> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <align>right</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Label]</label> + <visible>ListItem.IsRecording | ListItem.HasTimer</visible> + </control> + </itemlayout> + <focusedlayout height="35" width="410"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>410</width> + <height>30</height> + <texture border="5">button-nofocus.png</texture> + <visible>!Control.HasFocus(11)</visible> + <include>VisibleFadeEffect</include> + </control> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>410</width> + <height>30</height> + <texture border="5">button-focus2.png</texture> + <visible>Control.HasFocus(11)</visible> + <include>VisibleFadeEffect</include> + </control> + <control type="label"> + <posx>10</posx> + <posy>0</posy> + <width>100</width> + <height>30</height> + <font>font13</font> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.StartTime]</label> + </control> + <control type="image"> + <posx>120</posx> + <posy>5</posy> + <width>30</width> + <height>20</height> + <texture>PVR-IsRecording.png</texture> + <visible>ListItem.IsRecording</visible> + </control> + <control type="image"> + <posx>120</posx> + <posy>5</posy> + <width>20</width> + <height>20</height> + <texture>PVR-HasTimer.png</texture> + <visible>ListItem.HasTimer + !ListItem.IsRecording</visible> + </control> + <control type="label"> + <posx>400</posx> + <posy>0</posy> + <width>300</width> + <height>30</height> + <font>font12</font> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <align>right</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Label]</label> + <visible>![ListItem.IsRecording | ListItem.HasTimer]</visible> + </control> + <control type="label"> + <posx>400</posx> + <posy>0</posy> + <width>250</width> + <height>30</height> + <font>font12</font> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <align>right</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Label]</label> + <visible>ListItem.IsRecording | ListItem.HasTimer</visible> + </control> + </focusedlayout> + </control> + <control type="scrollbar" id="60"> + <posx>440</posx> + <posy>100</posy> + <width>25</width> + <height>490</height> + <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground> + <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar> + <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus> + <textureslidernib>ScrollBarNib.png</textureslidernib> + <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus> + <onleft>11</onleft> + <onright>11</onright> + <ondown>61</ondown> + <onup>61</onup> + <showonepage>false</showonepage> + <orientation>vertical</orientation> + </control> + <control type="label"> + <description>Page Count Label</description> + <posx>450</posx> + <posy>610</posy> + <width>400</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <scroll>false</scroll> + <align>right</align> + <aligny>center</aligny> + <label>([COLOR=blue]$INFO[Container(11).NumItems][/COLOR]) $LOCALIZE[31025] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(11).CurrentPage]/$INFO[Container(11).NumPages][/COLOR])</label> + <include>Window_OpenClose_Animation</include> + </control> + </control> + </controls> +</window> diff --git a/addons/skin.confluence/720p/DialogPVRGuideSearch.xml b/addons/skin.confluence/720p/DialogPVRGuideSearch.xml new file mode 100644 index 0000000000..9eda47748b --- /dev/null +++ b/addons/skin.confluence/720p/DialogPVRGuideSearch.xml @@ -0,0 +1,456 @@ +<window id="606"> + <defaultcontrol always="true">9</defaultcontrol> + <coordinates> + <system>1</system> + <posx>210</posx> + <posy>65</posy> + </coordinates> + <include>dialogeffect</include> + <controls> + <control type="image"> + <description>background image</description> + <posx>0</posx> + <posy>0</posy> + <width>865</width> + <height>605</height> + <texture border="40">DialogBack.png</texture> + </control> + <control type="image"> + <description>Dialog Header image</description> + <posx>40</posx> + <posy>16</posy> + <width>785</width> + <height>40</height> + <texture>dialogheader.png</texture> + </control> + <control type="label"> + <description>header label</description> + <posx>40</posx> + <posy>20</posy> + <width>785</width> + <height>30</height> + <font>font13_title</font> + <label>$LOCALIZE[19142]</label> + <align>center</align> + <aligny>center</aligny> + <textcolor>selected</textcolor> + <shadowcolor>black</shadowcolor> + <visible>!pvr.IsPlayingRadio</visible> + </control> + <control type="button"> + <description>Close Window button</description> + <posx>775</posx> + <posy>15</posy> + <width>64</width> + <height>32</height> + <label>-</label> + <font>-</font> + <onclick>PreviousMenu</onclick> + <texturefocus>DialogCloseButton-focus.png</texturefocus> + <texturenofocus>DialogCloseButton.png</texturenofocus> + <onleft>3</onleft> + <onright>3</onright> + <onup>3</onup> + <ondown>3</ondown> + <visible>system.getbool(input.enablemouse)</visible> + </control> + <control type="label"> + <description>Search string</description> + <posx>30</posx> + <posy>60</posy> + <width>320</width> + <height>40</height> + <font>font12caps</font> + <label>$LOCALIZE[19133]</label> + <align>left</align> + <aligny>center</aligny> + <textcolor>blue</textcolor> + <shadowcolor>black</shadowcolor> + </control> + <control type="image"> + <posx>30</posx> + <posy>100</posy> + <width>800</width> + <height>50</height> + <aspectratio>stretch</aspectratio> + <texture border="20">KeyboardEditArea.png</texture> + </control> + <control type="edit" id="9"> + <description>Search string</description> + <posx>35</posx> + <posy>105</posy> + <width>790</width> + <height>40</height> + <font>font13</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <texturefocus>-</texturefocus> + <texturenofocus>-</texturenofocus> + <align>left</align> + <onleft>9</onleft> + <onright>9</onright> + <onup>26</onup> + <ondown>10</ondown> + </control> + <control type="textbox"> + <description>Search help</description> + <posx>30</posx> + <posy>155</posy> + <width>800</width> + <height>70</height> + <align>left</align> + <font>font12</font> + <textcolor>grey2</textcolor> + <label>$LOCALIZE[19001] $LOCALIZE[19002]</label> + </control> + <control type="group"> + <posx>30</posx> + <posy>230</posy> + <control type="radiobutton" id="10"> + <description>Include Description</description> + <posx>0</posx> + <posy>0</posy> + <height>35</height> + <width>400</width> + <font>font12</font> + <textcolor>white</textcolor> + <focusedcolor>white</focusedcolor> + <shadowcolor>black</shadowcolor> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <align>left</align> + <aligny>center</aligny> + <label>19134</label> + <onleft>12</onleft> + <onright>12</onright> + <onup>9</onup> + <ondown>11</ondown> + </control> + <control type="radiobutton" id="11"> + <description>Case Sensitive</description> + <posx>0</posx> + <posy>35</posy> + <height>35</height> + <width>400</width> + <font>font12</font> + <textcolor>white</textcolor> + <focusedcolor>white</focusedcolor> + <shadowcolor>black</shadowcolor> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <align>left</align> + <aligny>center</aligny> + <label>19135</label> + <onleft>13</onleft> + <onright>13</onright> + <onup>10</onup> + <ondown>14</ondown> + </control> + <control type="edit" id="14"> + <description>Start Date</description> + <posx>0</posx> + <posy>70</posy> + <width>400</width> + <height>35</height> + <font>font12</font> + <textcolor>white</textcolor> + <focusedcolor>white</focusedcolor> + <shadowcolor>black</shadowcolor> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <label>19128</label> + <onright>16</onright> + <onleft>16</onleft> + <onup>11</onup> + <ondown>15</ondown> + </control> + <control type="edit" id="15"> + <description>Stop Date</description> + <posx>0</posx> + <posy>105</posy> + <width>400</width> + <height>35</height> + <font>font12</font> + <textcolor>white</textcolor> + <focusedcolor>white</focusedcolor> + <shadowcolor>black</shadowcolor> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <label>19129</label> + <onright>17</onright> + <onleft>17</onleft> + <onup>14</onup> + <ondown>18</ondown> + </control> + <control type="spincontrolex" id="18"> + <description>Genre</description> + <posx>0</posx> + <posy>140</posy> + <width>400</width> + <height>35</height> + <font>font12</font> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <label>515</label> + <onright>19</onright> + <onleft>19</onleft> + <onup>15</onup> + <ondown>20</ondown> + </control> + <control type="radiobutton" id="20"> + <description>Include unknown Genres</description> + <posx>0</posx> + <posy>175</posy> + <height>35</height> + <width>400</width> + <font>font12</font> + <textcolor>white</textcolor> + <focusedcolor>white</focusedcolor> + <shadowcolor>black</shadowcolor> + <align>left</align> + <aligny>center</aligny> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <pulseonselect>no</pulseonselect> + <label>19132</label> + <onleft>21</onleft> + <onright>21</onright> + <onup>18</onup> + <ondown>22</ondown> + </control> + <control type="radiobutton" id="22"> + <description>FTA only</description> + <posx>0</posx> + <posy>210</posy> + <height>35</height> + <width>400</width> + <font>font12</font> + <textcolor>white</textcolor> + <focusedcolor>white</focusedcolor> + <shadowcolor>black</shadowcolor> + <align>left</align> + <aligny>center</aligny> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <pulseonselect>no</pulseonselect> + <label>19123</label> + <onleft>23</onleft> + <onright>23</onright> + <onup>20</onup> + <ondown>24</ondown> + </control> + <control type="radiobutton" id="24"> + <description>Ignore Timers</description> + <posx>0</posx> + <posy>245</posy> + <height>35</height> + <width>400</width> + <font>font12</font> + <textcolor>white</textcolor> + <focusedcolor>white</focusedcolor> + <shadowcolor>black</shadowcolor> + <align>left</align> + <aligny>center</aligny> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <pulseonselect>no</pulseonselect> + <label>19124</label> + <onleft>27</onleft> + <onright>27</onright> + <onup>22</onup> + <ondown>26</ondown> + </control> + </control> + <control type="group"> + <posx>440</posx> + <posy>230</posy> + <control type="spincontrolex" id="12"> + <description>Min Duration</description> + <posx>0</posx> + <posy>0</posy> + <width>400</width> + <height>35</height> + <font>font12</font> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <label>19130</label> + <onright>10</onright> + <onleft>10</onleft> + <onup>9</onup> + <ondown>13</ondown> + </control> + <control type="spincontrolex" id="13"> + <description>Max Duration</description> + <posx>0</posx> + <posy>35</posy> + <width>400</width> + <height>35</height> + <font>font12</font> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <label>19131</label> + <onright>11</onright> + <onleft>11</onleft> + <onup>12</onup> + <ondown>16</ondown> + </control> + <control type="edit" id="16"> + <description>Start time</description> + <posx>0</posx> + <posy>70</posy> + <width>400</width> + <height>35</height> + <font>font12</font> + <textcolor>white</textcolor> + <focusedcolor>white</focusedcolor> + <shadowcolor>black</shadowcolor> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <label>19126</label> + <onright>14</onright> + <onleft>14</onleft> + <onup>13</onup> + <ondown>17</ondown> + </control> + <control type="edit" id="17"> + <description>Stop time</description> + <posx>0</posx> + <posy>105</posy> + <width>400</width> + <height>35</height> + <font>font12</font> + <textcolor>white</textcolor> + <focusedcolor>white</focusedcolor> + <shadowcolor>black</shadowcolor> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <label>19127</label> + <onright>15</onright> + <onleft>15</onleft> + <onup>16</onup> + <ondown>19</ondown> + </control> + <control type="radiobutton" id="19"> + <description>avoid repeats</description> + <posx>0</posx> + <posy>140</posy> + <width>400</width> + <height>35</height> + <font>font12</font> + <textcolor>white</textcolor> + <focusedcolor>white</focusedcolor> + <shadowcolor>black</shadowcolor> + <align>left</align> + <aligny>center</aligny> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <pulseonselect>no</pulseonselect> + <label>19121</label> + <onright>18</onright> + <onleft>18</onleft> + <onup>17</onup> + <ondown>21</ondown> + </control> + <control type="spincontrolex" id="21"> + <description>Groups</description> + <posx>0</posx> + <posy>175</posy> + <width>400</width> + <height>35</height> + <font>font12</font> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <label>19141</label> + <onright>20</onright> + <onleft>20</onleft> + <onup>19</onup> + <ondown>23</ondown> + </control> + <control type="spincontrolex" id="23"> + <description>Channels</description> + <posx>0</posx> + <posy>210</posy> + <width>400</width> + <height>35</height> + <font>font12</font> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <label>19148</label> + <onleft>22</onleft> + <onright>22</onright> + <onup>21</onup> + <ondown>27</ondown> + </control> + <control type="radiobutton" id="27"> + <description>Ignore Recordings</description> + <posx>0</posx> + <posy>245</posy> + <height>35</height> + <width>400</width> + <font>font12</font> + <textcolor>white</textcolor> + <focusedcolor>white</focusedcolor> + <shadowcolor>black</shadowcolor> + <align>left</align> + <aligny>center</aligny> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <pulseonselect>no</pulseonselect> + <label>19125</label> + <onleft>24</onleft> + <onright>24</onright> + <onup>23</onup> + <ondown>26</ondown> + </control> + </control> + <control type="group" id="9000"> + <posy>540</posy> + <posx>125</posx> + <control type="button" id="28"> + <description>Defaults Button</description> + <posx>0</posx> + <posy>0</posy> + <width>200</width> + <height>40</height> + <align>center</align> + <aligny>center</aligny> + <font>font12_title</font> + <label>409</label> + <onleft>26</onleft> + <onright>25</onright> + <onup>24</onup> + <ondown>9</ondown> + </control> + <control type="button" id="25"> + <description>Cancel Button</description> + <posx>210</posx> + <posy>0</posy> + <width>200</width> + <height>40</height> + <align>center</align> + <aligny>center</aligny> + <font>font12_title</font> + <label>222</label> + <onleft>28</onleft> + <onright>26</onright> + <onup>27</onup> + <ondown>9</ondown> + </control> + <control type="button" id="26"> + <description>Search Button</description> + <posx>420</posx> + <posy>0</posy> + <width>200</width> + <height>40</height> + <align>center</align> + <aligny>center</aligny> + <font>font12_title</font> + <label>137</label> + <onleft>25</onleft> + <onright>28</onright> + <onup>27</onup> + <ondown>9</ondown> + </control> + </control> + </controls> +</window>
\ No newline at end of file diff --git a/addons/skin.confluence/720p/DialogPVRRecordingInfo.xml b/addons/skin.confluence/720p/DialogPVRRecordingInfo.xml new file mode 100644 index 0000000000..83c3d6b590 --- /dev/null +++ b/addons/skin.confluence/720p/DialogPVRRecordingInfo.xml @@ -0,0 +1,267 @@ +<window id="602"> + <defaultcontrol always="true">10</defaultcontrol> + <coordinates> + <system>1</system> + <posx>275</posx> + <posy>30</posy> + </coordinates> + <include>dialogeffect</include> + <controls> + <control type="image"> + <description>background image</description> + <posx>0</posx> + <posy>0</posy> + <width>730</width> + <height>660</height> + <texture border="40">DialogBack2.png</texture> + <visible>Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation)</visible> + </control> + <control type="image"> + <description>background image</description> + <posx>0</posx> + <posy>0</posy> + <width>730</width> + <height>660</height> + <texture border="40">DialogBack.png</texture> + <visible>![Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation)]</visible> + </control> + <control type="image"> + <description>Dialog Header image</description> + <posx>40</posx> + <posy>16</posy> + <width>650</width> + <height>40</height> + <texture>dialogheader.png</texture> + </control> + <control type="label"> + <description>header label</description> + <posx>40</posx> + <posy>20</posy> + <width>650</width> + <height>30</height> + <font>font13_title</font> + <label>$LOCALIZE[19053]</label> + <align>center</align> + <aligny>center</aligny> + <textcolor>selected</textcolor> + <shadowcolor>black</shadowcolor> + </control> + <control type="button"> + <description>Close Window button</description> + <posx>640</posx> + <posy>15</posy> + <width>64</width> + <height>32</height> + <label>-</label> + <font>-</font> + <onclick>PreviousMenu</onclick> + <texturefocus>DialogCloseButton-focus.png</texturefocus> + <texturenofocus>DialogCloseButton.png</texturenofocus> + <onleft>10</onleft> + <onright>10</onright> + <onup>10</onup> + <ondown>10</ondown> + <visible>system.getbool(input.enablemouse)</visible> + </control> + <control type="label"> + <description>Title label</description> + <posx>40</posx> + <posy>70</posy> + <width>650</width> + <height>30</height> + <font>font13_title</font> + <label>$INFO[ListItem.Title]</label> + <align>center</align> + <aligny>center</aligny> + <textcolor>blue</textcolor> + <shadowcolor>black</shadowcolor> + </control> + <control type="group"> + <posx>40</posx> + <posy>140</posy> + <control type="label"> + <description>Start Date</description> + <posx>170</posx> + <posy>0</posy> + <width>160</width> + <height>25</height> + <align>right</align> + <aligny>center</aligny> + <font>font13</font> + <textcolor>blue</textcolor> + <label>$LOCALIZE[552]:</label> + </control> + <control type="label"> + <description>Start date value</description> + <posx>180</posx> + <posy>0</posy> + <width>470</width> + <height>25</height> + <align>left</align> + <aligny>center</aligny> + <font>font13</font> + <textcolor>white</textcolor> + <label>$INFO[ListItem.StartDate]</label> + </control> + <control type="label"> + <description>Start time</description> + <posx>170</posx> + <posy>35</posy> + <width>160</width> + <height>25</height> + <align>right</align> + <aligny>center</aligny> + <font>font13</font> + <textcolor>blue</textcolor> + <label>$LOCALIZE[142]</label> + </control> + <control type="label"> + <description>Start Time value</description> + <posx>180</posx> + <posy>35</posy> + <width>470</width> + <height>25</height> + <align>left</align> + <aligny>center</aligny> + <font>font13</font> + <textcolor>white</textcolor> + <label>$INFO[ListItem.StartTime]</label> + </control> + <control type="label"> + <description>Channel Name</description> + <posx>170</posx> + <posy>70</posy> + <width>160</width> + <height>25</height> + <align>right</align> + <aligny>center</aligny> + <font>font13</font> + <textcolor>blue</textcolor> + <label>$LOCALIZE[19148]:</label> + </control> + <control type="fadelabel"> + <description>Channel Value</description> + <posx>180</posx> + <posy>70</posy> + <width>470</width> + <height>25</height> + <align>left</align> + <aligny>center</aligny> + <font>font13</font> + <textcolor>white</textcolor> + <label>$INFO[ListItem.ChannelName]</label> + </control> + <control type="label"> + <description>Duration</description> + <posx>170</posx> + <posy>105</posy> + <width>160</width> + <height>25</height> + <align>right</align> + <aligny>center</aligny> + <font>font13</font> + <textcolor>blue</textcolor> + <label>$LOCALIZE[180]:</label> + </control> + <control type="label"> + <description>Duration value</description> + <posx>180</posx> + <posy>105</posy> + <width>470</width> + <label>$INFO[ListItem.Duration]</label> + <align>left</align> + <font>font13</font> + <scroll>true</scroll> + </control> + <control type="label"> + <description>Genre</description> + <posx>170</posx> + <posy>140</posy> + <width>160</width> + <height>25</height> + <align>right</align> + <aligny>center</aligny> + <font>font13</font> + <textcolor>blue</textcolor> + <label>$LOCALIZE[135]:</label> + </control> + <control type="label"> + <description>Genre value</description> + <posx>180</posx> + <posy>140</posy> + <width>470</width> + <label fallback="161">$INFO[ListItem.Genre]</label> + <align>left</align> + <font>font13</font> + <scroll>true</scroll> + </control> + <control type="label"> + <description>Subtitle value</description> + <posx>40</posx> + <posy>185</posy> + <width>650</width> + <label>$INFO[ListItem.PlotOutline]</label> + <align>center</align> + <font>font13</font> + <textcolor>blue</textcolor> + <scroll>true</scroll> + <visible>!IsEmpty(ListItem.PlotOutline)</visible> + </control> + </control> + <control type="label"> + <posx>610</posx> + <posy>370</posy> + <width>400</width> + <height>30</height> + <font>font12</font> + <textcolor>grey</textcolor> + <shadowcolor>black</shadowcolor> + <scroll>true</scroll> + <align>right</align> + <aligny>center</aligny> + <label>[COLOR=blue]$LOCALIZE[207][/COLOR]$INFO[Container(400).CurrentPage, ( $LOCALIZE[31024] ]$INFO[Container(400).NumPages,/, )]</label> + </control> + <control type="spincontrol" id="60"> + <description>Next page button</description> + <posx>620</posx> + <posy>375</posy> + <subtype>page</subtype> + <font>-</font> + <onleft>60</onleft> + <onright>60</onright> + <ondown>9000</ondown> + <onup>9000</onup> + <textcolor>-</textcolor> + <showonepage>true</showonepage> + </control> + <control type="textbox" id="400"> + <description>PLOT</description> + <posx>40</posx> + <posy>400</posy> + <width>650</width> + <height>180</height> + <font>font12</font> + <align>justify</align> + <pagecontrol>60</pagecontrol> + <label fallback="161">$INFO[ListItem.Plot]</label> + </control> + <control type="grouplist" id="9000"> + <posx>40</posx> + <posy>590</posy> + <width>640</width> + <height>40</height> + <itemgap>5</itemgap> + <align>center</align> + <orientation>horizontal</orientation> + <onleft>9000</onleft> + <onright>9000</onright> + <onup>60</onup> + <ondown>60</ondown> + <control type="button" id="10"> + <description>OK</description> + <include>ButtonInfoDialogsCommonValues</include> + <label>186</label> + </control> + </control> + </controls> +</window>
\ No newline at end of file diff --git a/addons/skin.confluence/720p/DialogPVRTimerSettings.xml b/addons/skin.confluence/720p/DialogPVRTimerSettings.xml new file mode 100644 index 0000000000..219ac5cd7e --- /dev/null +++ b/addons/skin.confluence/720p/DialogPVRTimerSettings.xml @@ -0,0 +1,155 @@ +<window id="603"> + <defaultcontrol>29</defaultcontrol> + <coordinates> + <system>1</system> + <posx>275</posx> + <posy>30</posy> + </coordinates> + <include>dialogeffect</include> + <controls> + <control type="image"> + <description>background image</description> + <posx>0</posx> + <posy>0</posy> + <width>730</width> + <height>660</height> + <texture border="40">DialogBack.png</texture> + </control> + <control type="image"> + <description>Dialog Header image</description> + <posx>40</posx> + <posy>16</posy> + <width>650</width> + <height>40</height> + <texture>dialogheader.png</texture> + </control> + <control type="label" id="2"> + <description>header label</description> + <posx>40</posx> + <posy>20</posy> + <width>650</width> + <height>30</height> + <font>font13_title</font> + <label>-</label> + <align>center</align> + <aligny>center</aligny> + <textcolor>selected</textcolor> + <shadowcolor>black</shadowcolor> + </control> + <control type="button"> + <description>Close Window button</description> + <posx>640</posx> + <posy>15</posy> + <width>64</width> + <height>32</height> + <label>-</label> + <font>-</font> + <onclick>PreviousMenu</onclick> + <texturefocus>DialogCloseButton-focus.png</texturefocus> + <texturenofocus>DialogCloseButton.png</texturenofocus> + <onleft>10</onleft> + <onright>10</onright> + <onup>10</onup> + <ondown>10</ondown> + <visible>system.getbool(input.enablemouse)</visible> + </control> + <control type="grouplist" id="5"> + <description>control area</description> + <posx>30</posx> + <posy>70</posy> + <width>670</width> + <height>510</height> + <itemgap>5</itemgap> + <onup>9001</onup> + <ondown>9001</ondown> + <onleft>9001</onleft> + <onright>9001</onright> + </control> + <control type="group" id="9001"> + <posx>160</posx> + <posy>590</posy> + <control type="button" id="28"> + <description>OK Button</description> + <posx>0</posx> + <posy>0</posy> + <width>200</width> + <height>40</height> + <align>center</align> + <aligny>center</aligny> + <font>font12_title</font> + <label>186</label> + <onleft>29</onleft> + <onright>29</onright> + <onup>5</onup> + <ondown>5</ondown> + </control> + <control type="button" id="29"> + <description>Cancel Button</description> + <posx>210</posx> + <posy>0</posy> + <width>200</width> + <height>40</height> + <align>center</align> + <aligny>center</aligny> + <font>font12_title</font> + <label>222</label> + <onleft>28</onleft> + <onright>28</onright> + <onup>5</onup> + <ondown>5</ondown> + </control> + </control> + <control type="button" id="7"> + <description>Default Button</description> + <height>40</height> + <font>font13</font> + <textcolor>grey2</textcolor> + <focusedcolor>white</focusedcolor> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + </control> + <control type="radiobutton" id="8"> + <description>Default RadioButton</description> + <height>40</height> + <font>font13</font> + <textcolor>grey2</textcolor> + <focusedcolor>white</focusedcolor> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + </control> + <control type="spincontrolex" id="9"> + <description>Default spincontrolex</description> + <height>40</height> + <textcolor>grey2</textcolor> + <focusedcolor>white</focusedcolor> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <font>font13</font> + <aligny>center</aligny> + <reverse>yes</reverse> + </control> + <control type="sliderex" id="10"> + <description>Default Slider</description> + <height>40</height> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + <font>font13</font> + <textcolor>grey2</textcolor> + <focusedcolor>white</focusedcolor> + </control> + <control type="image" id="11"> + <description>Default Seperator</description> + <height>2</height> + <texture>separator2.png</texture> + </control> + <control type="edit" id="12"> + <description>Default Edit</description> + <height>40</height> + <font>font13</font> + <textcolor>grey2</textcolor> + <focusedcolor>white</focusedcolor> + <texturefocus border="5">button-focus2.png</texturefocus> + <texturenofocus border="5">button-nofocus.png</texturenofocus> + </control> + </controls> +</window>
\ No newline at end of file diff --git a/addons/skin.confluence/720p/Home.xml b/addons/skin.confluence/720p/Home.xml index 0001a3abd3..fb62336446 100644 --- a/addons/skin.confluence/720p/Home.xml +++ b/addons/skin.confluence/720p/Home.xml @@ -84,6 +84,150 @@ <shadowcolor>black</shadowcolor> </control> </control> + <!-- LiveTV Info --> + <control type="group"> + <posx>490r</posx> + <posy>70</posy> + <visible>Container(9000).HasFocus(12) + [PVR.IsRecording | PVR.HasNonRecordingTimer]</visible> + <include>VisibleFadeEffect</include> + <include>Window_OpenClose_Animation</include> + <animation effect="fade" start="100" end="0" time="200" condition="Window.IsActive(Favourites)">conditional</animation> + <control type="group"> + <animation effect="slide" start="0,0" end="0,100" time="0" condition="PVR.IsRecording">conditional</animation> + <visible>PVR.HasNonRecordingTimer</visible> + <control type="image"> + <posx>0</posx> + <posy>-5</posy> + <width>490</width> + <height>90</height> + <texture>gradient.png</texture> + </control> + <control type="image"> + <posx>400</posx> + <posy>0</posy> + <width>80</width> + <height>80</height> + <aspectratio>keep</aspectratio> + <texture background="true" fallback="DefaultVideoCover.png">$INFO[PVR.NextRecordingChannelIcon]</texture> + <bordertexture border="8">ThumbBorder.png</bordertexture> + <bordersize>4</bordersize> + </control> + <control type="image"> + <posx>365</posx> + <posy>5</posy> + <width>25</width> + <height>25</height> + <aspectratio>keep</aspectratio> + <texture>PVR-HasTimer.png</texture> + </control> + <control type="label"> + <description>Next Timer Header label</description> + <posx>355</posx> + <posy>5</posy> + <height>25</height> + <width>400</width> + <label>$LOCALIZE[19157]</label> + <align>right</align> + <aligny>center</aligny> + <font>font12_title</font> + <textcolor>blue</textcolor> + <shadowcolor>black</shadowcolor> + </control> + <control type="label"> + <description>NextRecordingDateTime</description> + <posx>390</posx> + <posy>30</posy> + <height>25</height> + <width>400</width> + <label>$INFO[PVR.NextRecordingDateTime,$LOCALIZE[19126] - ]</label> + <align>right</align> + <aligny>center</aligny> + <font>font12</font> + <scroll>true</scroll> + <shadowcolor>black</shadowcolor> + </control> + <control type="label"> + <description>NextRecordingTitle Channel</description> + <posx>390</posx> + <posy>50</posy> + <height>25</height> + <width>800</width> + <label>$INFO[PVR.NextRecordingTitle][COLOR=grey]$INFO[PVR.NextRecordingChannel, - [COLOR=blue]([/COLOR],[COLOR=blue])[/COLOR]][/COLOR]</label> + <align>right</align> + <aligny>center</aligny> + <font>font12_title</font> + <shadowcolor>black</shadowcolor> + <scroll>true</scroll> + </control> + </control> + <control type="group"> + <visible>PVR.IsRecording</visible> + <control type="image"> + <posx>0</posx> + <posy>-5</posy> + <width>490</width> + <height>90</height> + <texture>gradient.png</texture> + </control> + <control type="image"> + <posx>400</posx> + <posy>0</posy> + <width>80</width> + <height>80</height> + <aspectratio>keep</aspectratio> + <texture background="true" fallback="DefaultVideoCover.png">$INFO[PVR.NowRecordingChannelIcon]</texture> + <bordertexture border="8">ThumbBorder.png</bordertexture> + <bordersize>4</bordersize> + </control> + <control type="image"> + <posx>360</posx> + <posy>5</posy> + <width>30</width> + <height>25</height> + <aspectratio>keep</aspectratio> + <texture>PVR-IsRecording.png</texture> + </control> + <control type="label"> + <description>Is Recording Header label</description> + <posx>350</posx> + <posy>5</posy> + <height>25</height> + <width>400</width> + <label>$LOCALIZE[19158]</label> + <align>right</align> + <aligny>center</aligny> + <font>font12_title</font> + <textcolor>blue</textcolor> + <shadowcolor>black</shadowcolor> + </control> + <control type="label"> + <description>NextRecordingDateTime</description> + <posx>390</posx> + <posy>30</posy> + <height>25</height> + <width>400</width> + <label>$INFO[PVR.NowRecordingDateTime,$LOCALIZE[19126] - ]</label> + <align>right</align> + <aligny>center</aligny> + <font>font12</font> + <scroll>true</scroll> + <shadowcolor>black</shadowcolor> + </control> + <control type="label"> + <description>NextRecordingTitle Channel</description> + <posx>390</posx> + <posy>50</posy> + <height>30</height> + <width>800</width> + <label>$INFO[PVR.NowRecordingTitle][COLOR=grey]$INFO[PVR.NowRecordingChannel, - [COLOR=blue]([/COLOR],[COLOR=blue])[/COLOR]][/COLOR]</label> + <align>right</align> + <aligny>center</aligny> + <font>font12_title</font> + <shadowcolor>black</shadowcolor> + <scroll>true</scroll> + </control> + </control> + </control> <!-- Video Info --> <control type="group"> <posx>0</posx> @@ -92,7 +236,47 @@ <include>VisibleFadeEffect</include> <include>Window_OpenClose_Animation</include> <control type="group"> - <visible>!VideoPlayer.Content(Movies) + !VideoPlayer.Content(Episodes)</visible> + <visible>!VideoPlayer.Content(Movies) + !VideoPlayer.Content(Episodes) + !VideoPlayer.Content(LiveTV)</visible> + <control type="image"> + <description>Cover image</description> + <posx>20</posx> + <posy>45</posy> + <width>150</width> + <height>300</height> + <aspectratio aligny="bottom">keep</aspectratio> + <texture>$INFO[VideoPlayer.Cover]</texture> + <bordertexture border="8">ThumbBorder.png</bordertexture> + <bordersize>5</bordersize> + </control> + <control type="label"> + <description>Title label</description> + <posx>190</posx> + <posy>285</posy> + <height>30</height> + <width>1000</width> + <label>$INFO[VideoPlayer.Title]</label> + <align>left</align> + <aligny>center</aligny> + <font>font13_title</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + </control> + <control type="label"> + <description>Time Label</description> + <posx>190</posx> + <posy>310</posy> + <height>30</height> + <width>300</width> + <label>$INFO[Player.Time]$INFO[Player.Duration,[COLOR=blue] / [/COLOR]]</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + </control> + </control> + <control type="group"> + <visible>VideoPlayer.Content(LiveTV)</visible> <control type="image"> <description>Cover image</description> <posx>20</posx> @@ -105,6 +289,19 @@ <bordersize>5</bordersize> </control> <control type="label"> + <description>Channel label</description> + <posx>160</posx> + <posy>265</posy> + <height>25</height> + <width>660</width> + <label>$INFO[VideoPlayer.ChannelName]</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + </control> + <control type="label"> <description>Title label</description> <posx>160</posx> <posy>285</posy> @@ -320,93 +517,156 @@ <texture flipy="true" border="0,5,0,0">HomeSubNF.png</texture> <colordiffuse>CCFFFFFF</colordiffuse> </control> - <control type="button" id="601"> - <posx>10</posx> - <posy>2</posy> - <width>30</width> - <height>30</height> - <label>-</label> - <texturefocus>OSDPrevTrackFO.png</texturefocus> - <texturenofocus>OSDPrevTrackNF.png</texturenofocus> - <onleft>608</onleft> - <onright>602</onright> - <onup>9003</onup> - <ondown>9000</ondown> - <onclick>XBMC.PlayerControl(Previous)</onclick> - </control> - <control type="button" id="602"> - <posx>40</posx> - <posy>2</posy> - <width>30</width> - <height>30</height> - <label>-</label> - <texturefocus>OSDRewindFO.png</texturefocus> - <texturenofocus>OSDRewindNF.png</texturenofocus> - <onleft>601</onleft> - <onright>603</onright> - <onup>9003</onup> - <ondown>9000</ondown> - <onclick>XBMC.PlayerControl(Rewind)</onclick> - </control> - <control type="togglebutton" id="603"> - <posx>70</posx> - <posy>2</posy> - <width>30</width> - <height>30</height> - <label>-</label> - <texturefocus>OSDPauseFO.png</texturefocus> - <texturenofocus>OSDPauseNF.png</texturenofocus> - <usealttexture>Player.Paused | Player.Forwarding | Player.Rewinding</usealttexture> - <alttexturefocus>OSDPlayFO.png</alttexturefocus> - <alttexturenofocus>OSDPlayNF.png</alttexturenofocus> - <onleft>602</onleft> - <onright>604</onright> - <onup>9003</onup> - <ondown>9000</ondown> - <onclick>XBMC.PlayerControl(Play)</onclick> - </control> - <control type="button" id="604"> - <posx>100</posx> - <posy>2</posy> - <width>30</width> - <height>30</height> - <label>-</label> - <texturefocus>OSDStopFO.png</texturefocus> - <texturenofocus>OSDStopNF.png</texturenofocus> - <onleft>603</onleft> - <onright>605</onright> - <onup>9003</onup> - <ondown>9000</ondown> - <onclick>down</onclick> - <onclick>XBMC.PlayerControl(Stop)</onclick> - </control> - <control type="button" id="605"> - <posx>130</posx> - <posy>2</posy> - <width>30</width> - <height>30</height> - <label>-</label> - <texturefocus>OSDForwardFO.png</texturefocus> - <texturenofocus>OSDForwardNF.png</texturenofocus> - <onleft>604</onleft> - <onright>606</onright> - <onup>9003</onup> - <ondown>9000</ondown> - <onclick>XBMC.PlayerControl(Forward)</onclick> - </control> - <control type="button" id="606"> - <posx>160</posx> - <posy>2</posy> - <width>30</width> - <height>30</height> - <label>-</label> - <texturefocus>OSDNextTrackFO.png</texturefocus> - <texturenofocus>OSDNextTrackNF.png</texturenofocus> - <onleft>605</onleft> - <onright>607</onright> - <onup>9003</onup> - <ondown>9000</ondown> - <onclick>XBMC.PlayerControl(Next)</onclick> + <control type="group"> + <visible>!VideoPlayer.Content(LiveTV)</visible> + <control type="button" id="601"> + <posx>10</posx> + <posy>2</posy> + <width>30</width> + <height>30</height> + <label>-</label> + <texturefocus>OSDPrevTrackFO.png</texturefocus> + <texturenofocus>OSDPrevTrackNF.png</texturenofocus> + <onleft>608</onleft> + <onright>602</onright> + <onup>9003</onup> + <ondown>9000</ondown> + <onclick>XBMC.PlayerControl(Previous)</onclick> + </control> + <control type="button" id="602"> + <posx>40</posx> + <posy>2</posy> + <width>30</width> + <height>30</height> + <label>-</label> + <texturefocus>OSDRewindFO.png</texturefocus> + <texturenofocus>OSDRewindNF.png</texturenofocus> + <onleft>601</onleft> + <onright>603</onright> + <onup>9003</onup> + <ondown>9000</ondown> + <onclick>XBMC.PlayerControl(Rewind)</onclick> + </control> + <control type="togglebutton" id="603"> + <posx>70</posx> + <posy>2</posy> + <width>30</width> + <height>30</height> + <label>-</label> + <texturefocus>OSDPauseFO.png</texturefocus> + <texturenofocus>OSDPauseNF.png</texturenofocus> + <usealttexture>Player.Paused | Player.Forwarding | Player.Rewinding</usealttexture> + <alttexturefocus>OSDPlayFO.png</alttexturefocus> + <alttexturenofocus>OSDPlayNF.png</alttexturenofocus> + <onleft>602</onleft> + <onright>604</onright> + <onup>9003</onup> + <ondown>9000</ondown> + <onclick>XBMC.PlayerControl(Play)</onclick> + </control> + <control type="button" id="604"> + <posx>100</posx> + <posy>2</posy> + <width>30</width> + <height>30</height> + <label>-</label> + <texturefocus>OSDStopFO.png</texturefocus> + <texturenofocus>OSDStopNF.png</texturenofocus> + <onleft>603</onleft> + <onright>605</onright> + <onup>9003</onup> + <ondown>9000</ondown> + <onclick>down</onclick> + <onclick>XBMC.PlayerControl(Stop)</onclick> + </control> + <control type="button" id="605"> + <posx>130</posx> + <posy>2</posy> + <width>30</width> + <height>30</height> + <label>-</label> + <texturefocus>OSDForwardFO.png</texturefocus> + <texturenofocus>OSDForwardNF.png</texturenofocus> + <onleft>604</onleft> + <onright>606</onright> + <onup>9003</onup> + <ondown>9000</ondown> + <onclick>XBMC.PlayerControl(Forward)</onclick> + </control> + <control type="button" id="606"> + <posx>160</posx> + <posy>2</posy> + <width>30</width> + <height>30</height> + <label>-</label> + <texturefocus>OSDNextTrackFO.png</texturefocus> + <texturenofocus>OSDNextTrackNF.png</texturenofocus> + <onleft>605</onleft> + <onright>607</onright> + <onup>9003</onup> + <ondown>9000</ondown> + <onclick>XBMC.PlayerControl(Next)</onclick> + </control> + </control> + <control type="group" id="600"> + <visible>VideoPlayer.Content(LiveTV)</visible> + <control type="button" id="601"> + <posx>10</posx> + <posy>2</posy> + <width>30</width> + <height>30</height> + <label>-</label> + <texturefocus>OSDChannelUPFO.png</texturefocus> + <texturenofocus>OSDChannelUPNF.png</texturenofocus> + <onleft>608</onleft> + <onright>602</onright> + <onup>9003</onup> + <ondown>9000</ondown> + <onclick>XBMC.PlayerControl(Previous)</onclick> + </control> + <control type="button" id="602"> + <posx>40</posx> + <posy>2</posy> + <width>30</width> + <height>30</height> + <label>-</label> + <texturefocus>OSDChannelDownFO.png</texturefocus> + <texturenofocus>OSDChannelDownNF.png</texturenofocus> + <onleft>601</onleft> + <onright>603</onright> + <onup>9003</onup> + <ondown>9000</ondown> + <onclick>XBMC.PlayerControl(Next)</onclick> + </control> + <control type="button" id="603"> + <posx>70</posx> + <posy>2</posy> + <width>30</width> + <height>30</height> + <label>-</label> + <texturefocus>OSDStopFO.png</texturefocus> + <texturenofocus>OSDStopNF.png</texturenofocus> + <onleft>602</onleft> + <onright>606</onright> + <onup>9003</onup> + <ondown>9000</ondown> + <onclick>down</onclick> + <onclick>XBMC.PlayerControl(Stop)</onclick> + </control> + <control type="button" id="606"> + <posx>130</posx> + <posy>2</posy> + <width>30</width> + <height>30</height> + <label>-</label> + <texturefocus>OSDRecordOffFO.png</texturefocus> + <texturenofocus>OSDRecordOffNF.png</texturenofocus> + <onleft>603</onleft> + <onright>607</onright> + <onup>9003</onup> + <ondown>9000</ondown> + <onclick>XBMC.PlayerControl(record)</onclick> + </control> </control> </control> <control type="radiobutton" id="607"> @@ -496,6 +756,13 @@ </control> <control type="grouplist" id="9014"> <include>HomeSubMenuCommonValues</include> + <onleft>9014</onleft> + <onright>9014</onright> + <visible>Container(9000).HasFocus(12)</visible> + <include>HomeSubMenuLiveTV</include> <!-- Buttons for the grouplist --> + </control> + <control type="grouplist" id="9015"> + <include>HomeSubMenuCommonValues</include> <onleft>9013</onleft> <onright>9013</onright> <visible>Container(9000).HasFocus(4)</visible> @@ -609,10 +876,17 @@ <item id="4"> <label>1</label> <onclick>ActivateWindow(Pictures)</onclick> - <icon>special://skin/backgrounds/pictures.jpg</icon> - <thumb>$INFO[Skin.String(Home_Custom_Back_Pictures_Folder)]</thumb> + <icon>-</icon> + <thumb>-</thumb> <visible>!Skin.HasSetting(HomeMenuNoPicturesButton)</visible> </item> + <item id="12"> + <label>31502</label> + <onclick>ActivateWindow(PVR)</onclick> + <icon>-</icon> + <thumb>-</thumb> + <visible>System.GetBool(pvrmanager.enabled)</visible> + </item> <item id="2"> <label>3</label> <onclick condition="StringCompare(Window.Property(VideosDirectLink),True)">ActivateWindow(Videos)</onclick> diff --git a/addons/skin.confluence/720p/IncludesBackgroundBuilding.xml b/addons/skin.confluence/720p/IncludesBackgroundBuilding.xml index 74b202eb37..c583fbc15e 100644 --- a/addons/skin.confluence/720p/IncludesBackgroundBuilding.xml +++ b/addons/skin.confluence/720p/IncludesBackgroundBuilding.xml @@ -295,4 +295,101 @@ </control> </control> </include> -</includes>
\ No newline at end of file + <include name="ContentPanelBackgroundsPVR"> + <control type="image"> + <posx>0</posx> + <posy>100r</posy> + <width>1280</width> + <height>100</height> + <texture>floor.png</texture> + <animation effect="fade" time="250" condition="Window.Previous(Home)">WindowOpen</animation> + <animation effect="fade" time="250" condition="Window.Next(Home)">WindowClose</animation> + </control> + <control type="group"> + <include>Window_OpenClose_Animation</include> + <control type="group"> + <include>VisibleFadeEffect</include> + <visible>Control.IsVisible(10) | Control.IsVisible(14) | Control.IsVisible(15) | Control.IsVisible(16) | Control.IsVisible(17)</visible> + <control type="image"> + <posx>55</posx> + <posy>60</posy> + <width>1170</width> + <height>600</height> + <texture border="15">ContentPanel.png</texture> + </control> + <control type="image"> + <posx>55</posx> + <posy>652</posy> + <width>1170</width> + <height>64</height> + <texture border="15">ContentPanelMirror.png</texture> + </control> + </control> + <control type="group"> + <include>VisibleFadeEffect</include> + <visible>Control.IsVisible(11) | Control.IsVisible(12)</visible> + <control type="image"> + <posx>50</posx> + <posy>60</posy> + <width>450</width> + <height>600</height> + <texture border="15">ContentPanel.png</texture> + </control> + <control type="image"> + <posx>50</posx> + <posy>652</posy> + <width>450</width> + <height>64</height> + <texture border="15">ContentPanelMirror.png</texture> + </control> + <control type="image"> + <posx>510</posx> + <posy>60</posy> + <width>730</width> + <height>600</height> + <texture border="15">ContentPanel.png</texture> + </control> + <control type="image"> + <posx>510</posx> + <posy>652</posy> + <width>730</width> + <height>64</height> + <texture border="15">ContentPanelMirror.png</texture> + </control> + </control> + <control type="group"> + <include>VisibleFadeEffect</include> + <visible>Control.IsVisible(13)</visible> + <control type="image"> + <posx>50</posx> + <posy>60</posy> + <width>840</width> + <height>600</height> + <texture border="15">ContentPanel.png</texture> + </control> + <control type="image"> + <posx>50</posx> + <posy>652</posy> + <width>840</width> + <height>64</height> + <texture border="15">ContentPanelMirror.png</texture> + </control> + <control type="image"> + <posx>900</posx> + <posy>60</posy> + <width>330</width> + <height>600</height> + <texture border="15">ContentPanel.png</texture> + </control> + <control type="image"> + <posx>900</posx> + <posy>652</posy> + <width>330</width> + <height>64</height> + <texture border="15">ContentPanelMirror.png</texture> + </control> + </control> + </control> + </include> + +</includes> diff --git a/addons/skin.confluence/720p/IncludesHomeMenuItems.xml b/addons/skin.confluence/720p/IncludesHomeMenuItems.xml index 317cd7480c..7bad6f0cbb 100644 --- a/addons/skin.confluence/720p/IncludesHomeMenuItems.xml +++ b/addons/skin.confluence/720p/IncludesHomeMenuItems.xml @@ -229,16 +229,56 @@ </control> </include> <include name="HomeSubMenuPictures"> - <control type="image" id="90141"> + <control type="image" id="90147"> <width>35</width> <height>35</height> <texture border="0,0,0,3" flipx="true">HomeSubEnd.png</texture> </control> - <control type="button" id="90142"> + <control type="button" id="90148"> <include>ButtonHomeSubCommonValues</include> <label>24001</label> <onclick>ActivateWindow(Pictures,Addons,return)</onclick> </control> + <control type="image" id="90149"> + <width>35</width> + <height>35</height> + <texture border="0,0,0,3">HomeSubEnd.png</texture> + </control> + </include> + <include name="HomeSubMenuLiveTV"> + <control type="image" id="90141"> + <width>35</width> + <height>35</height> + <texture border="0,0,0,3" flipx="true">HomeSubEnd.png</texture> + </control> + <control type="button" id="90142"> + <include>ButtonHomeSubCommonValues</include> + <label>19023</label> + <onclick>ActivateWindow(PVR)</onclick> + <onclick>Setfocus(32)</onclick> + <onclick>Setfocus(11)</onclick> + </control> + <control type="button" id="90143"> + <include>ButtonHomeSubCommonValues</include> + <label>19024</label> + <onclick>ActivateWindow(PVR)</onclick> + <onclick>Setfocus(33)</onclick> + <onclick>Setfocus(12)</onclick> + </control> + <control type="button" id="90144"> + <include>ButtonHomeSubCommonValues</include> + <label>19163</label> + <onclick>ActivateWindow(PVR)</onclick> + <onclick>Setfocus(34)</onclick> + <onclick>Setfocus(13)</onclick> + </control> + <control type="button" id="90145"> + <include>ButtonHomeSubCommonValues</include> + <label>19040</label> + <onclick>ActivateWindow(PVR)</onclick> + <onclick>Setfocus(35)</onclick> + <onclick>Setfocus(14)</onclick> + </control> <control type="image" id="90146"> <width>35</width> <height>35</height> diff --git a/addons/skin.confluence/720p/MusicOSD.xml b/addons/skin.confluence/720p/MusicOSD.xml index 9171ff70d0..49fc84ba7b 100644 --- a/addons/skin.confluence/720p/MusicOSD.xml +++ b/addons/skin.confluence/720p/MusicOSD.xml @@ -306,8 +306,8 @@ <height>45</height> <label>264</label> <font>-</font> - <texturefocus>OSDRecordFO.png</texturefocus> - <texturenofocus>OSDRecordNF.png</texturenofocus> + <texturefocus>OSDRecordOffFO.png</texturefocus> + <texturenofocus>OSDRecordOffNF.png</texturenofocus> <onleft>703</onleft> <onright>600</onright> <onup>1000</onup> @@ -318,4 +318,4 @@ </control> </control> </controls> - </window>
\ No newline at end of file + </window> diff --git a/addons/skin.confluence/720p/MyPVR.xml b/addons/skin.confluence/720p/MyPVR.xml new file mode 100644 index 0000000000..f0b0d9c4fd --- /dev/null +++ b/addons/skin.confluence/720p/MyPVR.xml @@ -0,0 +1,258 @@ +<window id="600"> + <defaultcontrol>32</defaultcontrol> + <allowoverlay>no</allowoverlay> + <controls> + <control type="image"> + <description>Normal Default Background Image</description> + <posx>0</posx> + <posy>0</posy> + <width>1280</width> + <height>720</height> + <aspectratio>scale</aspectratio> + <texture>$INFO[Skin.CurrentTheme,special://skin/backgrounds/,.jpg]</texture> + <visible>![Skin.HasSetting(UseCustomBackground) + !IsEmpty(Skin.String(CustomBackgroundPath))]</visible> + <include>VisibleFadeEffect</include> + </control> + <control type="image"> + <description>User Set Background Image</description> + <posx>0</posx> + <posy>0</posy> + <width>1280</width> + <height>720</height> + <aspectratio>scale</aspectratio> + <texture>$INFO[Skin.String(CustomBackgroundPath)]</texture> + <visible>Skin.HasSetting(UseCustomBackground) + !IsEmpty(Skin.String(CustomBackgroundPath))</visible> + <include>VisibleFadeEffect</include> + </control> + <control type="group"> + <visible>!Control.IsVisible(11) + !Control.IsVisible(12)</visible> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>1280</width> + <height>720</height> + <texture>special://skin/backgrounds/media-overlay.png</texture> + <visible>Player.HasVideo + !Skin.HasSetting(ShowBackgroundVideo)</visible> + </control> + <control type="visualisation"> + <posx>0</posx> + <posy>0</posy> + <width>1280</width> + <height>720</height> + <visible>Player.HasAudio + !Skin.HasSetting(ShowBackgroundVis)</visible> + </control> + <control type="videowindow"> + <posx>0</posx> + <posy>0</posy> + <width>1280</width> + <height>720</height> + <visible>Player.HasVideo + !Skin.HasSetting(ShowBackgroundVideo)</visible> + </control> + </control> + <include>ContentPanelBackgroundsPVR</include> + <include>MainWindowMouseButtons</include> + <control type="image"> + <description>Section header image</description> + <posx>20</posx> + <posy>3</posy> + <width>35</width> + <height>35</height> + <aspectratio>keep</aspectratio> + <texture>icon_video.png</texture> + </control> + <control type="grouplist"> + <posx>65</posx> + <posy>5</posy> + <width>1000</width> + <height>30</height> + <orientation>horizontal</orientation> + <align>left</align> + <itemgap>5</itemgap> + <control type="label"> + <include>WindowTitleCommons</include> + <label>$LOCALIZE[31502]</label> + </control> + <control type="label"> + <include>WindowTitleCommons</include> + <label>$INFO[Control.GetLabel(29),[COLOR=blue] - [/COLOR]]</label> + </control> + <control type="label"> + <include>WindowTitleCommons</include> + <label>$INFO[Control.GetLabel(30),[COLOR=blue] - [/COLOR]]</label> + </control> + </control> + <control type="label" id="29"> + <description>Empty so we can pass the values up one level</description> + <visible>False</visible> + </control> + <control type="label" id="30"> + <description>Empty so we can pass the values up one level</description> + <visible>False</visible> + </control> + <control type="group"> + <description>Small Media Window</description> + <posx>530</posx> + <posy>80</posy> + <visible>Control.IsVisible(11) | Control.IsVisible(12)</visible> + <include>VisibleFadeEffect</include> + <include>Window_OpenClose_Animation</include> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>690</width> + <height>400</height> + <texture border="5">button-nofocus.png</texture> + </control> + <control type="image"> + <posx>5</posx> + <posy>5</posy> + <width>680</width> + <height>390</height> + <texture fallback="special://skin/backgrounds/tv.jpg">$INFO[Skin.String(Home_Custom_Back_TV_Folder)]</texture> + <include>VisibleFadeEffect</include> + <visible>!Player.HasVideo</visible> + </control> + <control type="videowindow"> + <posx>5</posx> + <posy>5</posy> + <width>680</width> + <height>390</height> + <visible>Player.HasVideo</visible> + <animation effect="slide" start="0,0" end="-2000,0" time="0">WindowClose</animation> + </control> + <control type="image"> + <posx>1</posx> + <posy>1</posy> + <width>688</width> + <height>35</height> + <texture>black-back.png</texture> + <colordiffuse>DDFFFFFF</colordiffuse> + <visible>Player.HasVideo</visible> + </control> + <control type="label"> + <description>Current Video label</description> + <posx>30</posx> + <posy>1</posy> + <width>650</width> + <height>35</height> + <font>font12</font> + <textcolor>grey2</textcolor> + <align>center</align> + <aligny>center</aligny> + <label>$INFO[VideoPlayer.Title]</label> + <visible>Player.HasVideo</visible> + </control> + <!-- control type="visualisation"> + <posx>85</posx> + <posy>85</posy> + <width>700</width> + <height>390</height> + <visible>Player.HasAudio</visible> + </control --> + </control> + + <control type="group" id="50"> + <include>Window_OpenClose_Animation</include> + <include>EPGTimelineView</include>--> <!-- view id = 10 --> + <include>LiveTVChannelView</include> <!-- view id = 11 --> + <include>LiveRadioChannelView</include> <!-- view id = 12 --> + <include>LiveTVRecordingsView</include> <!-- view id = 13 --> + <include>LiveTVTimersView</include> <!-- view id = 14 --> + <include>LiveTVGuideChannelView</include> <!-- view id = 15 --> + <include>LiveTVGuideNowNextView</include> <!-- view id = 16 --> + <include>LiveTVSearchView</include> <!-- view id = 17 --> + </control> + <include>CommonNowPlaying</include> + <include>BehindDialogFadeOut</include> + <include>ScrollOffsetLabel</include> + + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>1280</width> + <height>720</height> + <texture>black-back.png</texture> + <animation effect="fade" time="400">Visible</animation> + <animation effect="fade" time="200">Hidden</animation> + <visible>Window.IsActive(FileBrowser) | Window.IsActive(PVRGuideInfo) | Window.IsActive(PVRRecordingInfo) | Window.IsActive(PVRTimerSetting) | Window.IsActive(PVRGroupManager) | Window.IsActive(PVRGuideSearch)</visible> + </control> + + <control type="group"> + <posx>-250</posx> + <include>SideBladeLeft</include> + <control type="grouplist" id="9000"> + <posx>0</posx> + <posy>110</posy> + <width>250</width> + <height>600</height> + <onleft>9000</onleft> + <onright>50</onright> + <onup>9000</onup> + <ondown>9000</ondown> + <itemgap>0</itemgap> + <control type="label" id="200"> + <width>250</width> + <height>35</height> + <font>font12caps</font> + <label>31006</label> + <textcolor>blue</textcolor> + <align>center</align> + <aligny>center</aligny> + </control> + <control type="button" id="32"> + <description>TV Channels</description> + <posx>0</posx> + <posy>0</posy> + <textwidth>235</textwidth> + <include>ButtonCommonValues</include> + <label>19023</label> + </control> + <control type="button" id="33"> + <description>Radio Channels</description> + <posx>0</posx> + <posy>40</posy> + <textwidth>235</textwidth> + <include>ButtonCommonValues</include> + <label>19024</label> + <onleft>12</onleft> + <onright>12</onright> + </control> + <control type="button" id="31"> + <description>TV Guide</description> + <posx>0</posx> + <posy>80</posy> + <textwidth>235</textwidth> + <include>ButtonCommonValues</include> + <label>$LOCALIZE[19222]: $LOCALIZE[19030]</label> + </control> + <control type="button" id="34"> + <description>Recordings</description> + <posx>0</posx> + <posy>120</posy> + <textwidth>235</textwidth> + <include>ButtonCommonValues</include> + <label>19163</label> + </control> + <control type="button" id="35"> + <description>Timers</description> + <posx>0</posx> + <posy>160</posy> + <textwidth>235</textwidth> + <include>ButtonCommonValues</include> + <label>19040</label> + </control> + <control type="button" id="36"> + <description>Search</description> + <posx>0</posx> + <posy>200</posy> + <textwidth>235</textwidth> + <include>ButtonCommonValues</include> + <label>137</label> + </control> + <include>CommonNowPlaying_Controls</include> + </control> + </control> + + <include>Clock</include> + </controls> +</window> diff --git a/addons/skin.confluence/720p/PlayerControls.xml b/addons/skin.confluence/720p/PlayerControls.xml index 56beee14ca..e6a6241779 100644 --- a/addons/skin.confluence/720p/PlayerControls.xml +++ b/addons/skin.confluence/720p/PlayerControls.xml @@ -1,5 +1,5 @@ <window type="dialog" id="114"> - <defaultcontrol always="true">603</defaultcontrol> + <defaultcontrol always="true">100</defaultcontrol> <include>dialogeffect</include> <visible>Player.HasMedia + Window.IsActive(PlayerControls) + !Window.IsActive(FullscreenVideo) + !Window.IsActive(Visualisation)</visible> <coordinates> @@ -18,6 +18,8 @@ <control type="group" id="100"> <posx>25</posx> <posy>162</posy> + <defaultcontrol always="true">603</defaultcontrol> + <visible>!VideoPlayer.Content(LiveTV)</visible> <control type="button" id="600"> <posx>0</posx> <posy>0</posy> @@ -31,6 +33,7 @@ <onup>300</onup> <ondown>200</ondown> <onclick>XBMC.PlayerControl(Previous)</onclick> + <visible>!VideoPlayer.Content(LiveTV)</visible> </control> <control type="button" id="601"> <posx>40</posx> @@ -45,6 +48,7 @@ <onup>300</onup> <ondown>200</ondown> <onclick>XBMC.PlayerControl(Rewind)</onclick> + <visible>!VideoPlayer.Content(LiveTV)</visible> </control> <control type="togglebutton" id="603"> <posx>80</posx> @@ -62,6 +66,7 @@ <onup>300</onup> <ondown>200</ondown> <onclick>XBMC.PlayerControl(Play)</onclick> + <visible>!VideoPlayer.Content(LiveTV)</visible> </control> <control type="button" id="602"> <posx>120</posx> @@ -77,6 +82,7 @@ <ondown>200</ondown> <onclick>down</onclick> <onclick>XBMC.PlayerControl(Stop)</onclick> + <visible>!VideoPlayer.Content(LiveTV)</visible> </control> <control type="button" id="604"> <posx>160</posx> @@ -91,6 +97,7 @@ <onup>300</onup> <ondown>200</ondown> <onclick>XBMC.PlayerControl(Forward)</onclick> + <visible>!VideoPlayer.Content(LiveTV)</visible> </control> <control type="button" id="605"> <posx>200</posx> @@ -105,6 +112,7 @@ <onup>300</onup> <ondown>200</ondown> <onclick>XBMC.PlayerControl(Next)</onclick> + <visible>!VideoPlayer.Content(LiveTV)</visible> </control> <control type="button" id="606"> <posx>240</posx> @@ -112,8 +120,8 @@ <width>40</width> <height>40</height> <label>-</label> - <texturefocus>OSDRecordFO.png</texturefocus> - <texturenofocus>OSDRecordNF.png</texturenofocus> + <texturefocus>OSDRecordOffFO.png</texturefocus> + <texturenofocus>OSDRecordOffNF.png</texturenofocus> <onleft>605</onleft> <onright>607</onright> <onup>300</onup> @@ -121,6 +129,7 @@ <onclick>XBMC.PlayerControl(record)</onclick> <enable>Player.CanRecord</enable> <animation effect="fade" start="100" end="30" time="100" condition="!Player.CanRecord">Conditional</animation> + <visible>!VideoPlayer.Content(LiveTV)</visible> </control> <control type="group"> <animation effect="slide" start="0,0" end="40,0" time="0" condition="!Player.HasAudio">Conditional</animation> @@ -137,6 +146,7 @@ <onright>608</onright> <onup>100</onup> <ondown>100</ondown> + <visible>!VideoPlayer.Content(LiveTV)</visible> </control> <control type="image"> <posx>325</posx> @@ -146,6 +156,7 @@ <texture>OSDRepeatNF.png</texture> <visible>!Playlist.IsRepeat + !Playlist.IsRepeatOne</visible> <visible>!Control.HasFocus(607)</visible> + <visible>!VideoPlayer.Content(LiveTV)</visible> </control> <control type="image"> <posx>325</posx> @@ -155,6 +166,7 @@ <texture>OSDRepeatFO.png</texture> <visible>!Playlist.IsRepeat + !Playlist.IsRepeatOne</visible> <visible>Control.HasFocus(607)</visible> + <visible>!VideoPlayer.Content(LiveTV)</visible> </control> <control type="image"> <posx>325</posx> @@ -164,6 +176,7 @@ <texture>OSDRepeatOneNF.png</texture> <visible>Playlist.IsRepeatOne</visible> <visible>!Control.HasFocus(607)</visible> + <visible>!VideoPlayer.Content(LiveTV)</visible> </control> <control type="image"> <posx>325</posx> @@ -173,6 +186,7 @@ <texture>OSDRepeatOneFO.png</texture> <visible>Playlist.IsRepeatOne</visible> <visible>Control.HasFocus(607)</visible> + <visible>!VideoPlayer.Content(LiveTV)</visible> </control> <control type="image"> <posx>325</posx> @@ -182,6 +196,7 @@ <texture>OSDRepeatAllNF.png</texture> <visible>Playlist.IsRepeat</visible> <visible>!Control.HasFocus(607)</visible> + <visible>!VideoPlayer.Content(LiveTV)</visible> </control> <control type="image"> <posx>325</posx> @@ -191,6 +206,7 @@ <texture>OSDRepeatAllFO.png</texture> <visible>Playlist.IsRepeat</visible> <visible>Control.HasFocus(607)</visible> + <visible>!VideoPlayer.Content(LiveTV)</visible> </control> <control type="togglebutton" id="608"> <posx>365</posx> @@ -208,6 +224,7 @@ <onright>609</onright> <onup>100</onup> <ondown>100</ondown> + <visible>!VideoPlayer.Content(LiveTV)</visible> </control> <control type="togglebutton" id="609"> <posx>405</posx> @@ -231,9 +248,133 @@ <altclick>XBMC.RunScript($INFO[Skin.String(LyricScript_Path)])</altclick> <usealttexture>IsEmpty(Skin.String(LyricScript_Path))</usealttexture> <visible>Player.HasAudio</visible> + <visible>!VideoPlayer.Content(LiveTV)</visible> </control> </control> </control> + <control type="group" id="100"> + <posx>25</posx> + <posy>162</posy> + <defaultcontrol always="true">700</defaultcontrol> + <visible>VideoPlayer.Content(LiveTV)</visible> + <control type="button" id="701"> + <posx>0</posx> + <posy>0</posy> + <width>40</width> + <height>40</height> + <label>-</label> + <texturefocus>OSDRewindFO.png</texturefocus> + <texturenofocus>OSDRewindNF.png</texturenofocus> + <onleft>706</onleft> + <onright>702</onright> + <onup>300</onup> + <ondown>200</ondown> + <onclick>XBMC.PlayerControl(Rewind)</onclick> + <visible>VideoPlayer.Content(LiveTV)</visible> + <enable>false</enable> + <animation effect="fade" start="100" end="50" time="100" condition="true">Conditional</animation> + </control> + <control type="button" id="702"> + <posx>40</posx> + <posy>0</posy> + <width>40</width> + <height>40</height> + <label>-</label> + <texturefocus>OSDStopFO.png</texturefocus> + <texturenofocus>OSDStopNF.png</texturenofocus> + <onleft>701</onleft> + <onright>703</onright> + <onup>300</onup> + <ondown>200</ondown> + <onclick>down</onclick> + <onclick>XBMC.PlayerControl(Stop)</onclick> + <visible>VideoPlayer.Content(LiveTV)</visible> + </control> + <control type="togglebutton" id="703"> + <posx>80</posx> + <posy>0</posy> + <width>40</width> + <height>40</height> + <label>-</label> + <texturefocus>OSDPauseFO.png</texturefocus> + <texturenofocus>OSDPauseNF.png</texturenofocus> + <usealttexture>Player.Paused | Player.Forwarding | Player.Rewinding</usealttexture> + <alttexturefocus>OSDPlayFO.png</alttexturefocus> + <alttexturenofocus>OSDPlayNF.png</alttexturenofocus> + <onleft>702</onleft> + <onright>704</onright> + <onup>300</onup> + <ondown>200</ondown> + <onclick>XBMC.PlayerControl(Play)</onclick> + <visible>VideoPlayer.Content(LiveTV)</visible> + <enable>false</enable> + <animation effect="fade" start="100" end="50" time="100" condition="true">Conditional</animation> + </control> + <control type="button" id="704"> + <posx>120</posx> + <posy>0</posy> + <width>40</width> + <height>40</height> + <label>-</label> + <texturefocus>OSDForwardFO.png</texturefocus> + <texturenofocus>OSDForwardNF.png</texturenofocus> + <onleft>703</onleft> + <onright>700</onright> + <onup>300</onup> + <ondown>200</ondown> + <onclick>XBMC.PlayerControl(Forward)</onclick> + <visible>VideoPlayer.Content(LiveTV)</visible> + <enable>false</enable> + <animation effect="fade" start="100" end="50" time="100" condition="true">Conditional</animation> + </control> + <control type="button" id="700"> + <posx>200</posx> + <posy>0</posy> + <width>40</width> + <height>40</height> + <label>-</label> + <texturefocus>OSDChannelUPFO.png</texturefocus> + <texturenofocus>OSDChannelUPNF.png</texturenofocus> + <onleft>704</onleft> + <onright>705</onright> + <onup>300</onup> + <ondown>200</ondown> + <onclick>XBMC.PlayerControl(Previous)</onclick> + <visible>VideoPlayer.Content(LiveTV)</visible> + </control> + <control type="button" id="705"> + <posx>240</posx> + <posy>0</posy> + <width>40</width> + <height>40</height> + <label>-</label> + <texturefocus>OSDChannelDownFO.png</texturefocus> + <texturenofocus>OSDChannelDownNF.png</texturenofocus> + <onleft>700</onleft> + <onright>706</onright> + <onup>300</onup> + <ondown>200</ondown> + <onclick>XBMC.PlayerControl(Next)</onclick> + <visible>VideoPlayer.Content(LiveTV)</visible> + </control> + <control type="button" id="706"> + <posx>280</posx> + <posy>0</posy> + <width>40</width> + <height>40</height> + <label>-</label> + <texturefocus>OSDRecordOffFO.png</texturefocus> + <texturenofocus>OSDRecordOffNF.png</texturenofocus> + <onleft>705</onleft> + <onright>701</onright> + <onup>300</onup> + <ondown>200</ondown> + <onclick>XBMC.PlayerControl(record)</onclick> + <enable>Player.CanRecord</enable> + <animation effect="fade" start="100" end="30" time="100" condition="!Player.CanRecord">Conditional</animation> + <visible>VideoPlayer.Content(LiveTV)</visible> + </control> + </control> <!-- Music Info --> <control type="image"> <description>gradient</description> @@ -272,4 +413,4 @@ <visible>system.getbool(input.enablemouse)</visible> </control> </controls> -</window>
\ No newline at end of file +</window> diff --git a/addons/skin.confluence/720p/Settings.xml b/addons/skin.confluence/720p/Settings.xml index a34673c7ff..c2328c94e0 100644 --- a/addons/skin.confluence/720p/Settings.xml +++ b/addons/skin.confluence/720p/Settings.xml @@ -128,42 +128,48 @@ <icon>-</icon> </item> <item id="3"> + <label>31502</label> + <label2>31409</label2> + <onclick>ActivateWindow(PVRSettings)</onclick> + <icon>special://skin/backgrounds/tv.jpg</icon> + </item> + <item id="4"> <label>2</label> <label2>31402</label2> <onclick>ActivateWindow(MusicSettings)</onclick> <icon>-</icon> </item> - <item id="4"> + <item id="5"> <label>1</label> <label2>31403</label2> <onclick>ActivateWindow(PicturesSettings)</onclick> <icon>-</icon> </item> - <item id="5"> + <item id="6"> <label>8</label> <label2>31404</label2> <onclick>ActivateWindow(WeatherSettings)</onclick> <icon>-</icon> </item> - <item id="6"> + <item id="7"> <label>24001</label> <label2>31408</label2> <onclick>ActivateWindow(AddonBrowser)</onclick> <icon>-</icon> </item> - <item id="7"> + <item id="8"> <label>14036</label> <label2>31409</label2> <onclick>ActivateWindow(ServiceSettings)</onclick> <icon>-</icon> </item> - <item id="8"> + <item id="9"> <label>13000</label> <label2>31406</label2> <onclick>ActivateWindow(SystemSettings)</onclick> <icon>-</icon> </item> - <item id="9"> + <item id="10"> <label>166</label> <label2>31407</label2> <onclick>ActivateWindow(1111)</onclick> diff --git a/addons/skin.confluence/720p/SettingsSystemInfo.xml b/addons/skin.confluence/720p/SettingsSystemInfo.xml index 0f89b5dcfb..c8748adb4e 100644 --- a/addons/skin.confluence/720p/SettingsSystemInfo.xml +++ b/addons/skin.confluence/720p/SettingsSystemInfo.xml @@ -146,6 +146,21 @@ <pulseonselect>false</pulseonselect> <label>13281</label> </control> + <control type="button" id="99"> + <description>Button PVR</description> + <height>60</height> + <width>241</width> + <textoffsetx>0</textoffsetx> + <align>right</align> + <aligny>center</aligny> + <font>font13_title</font> + <textcolor>grey2</textcolor> + <focusedcolor>white</focusedcolor> + <texturefocus border="5">MenuItemFO.png</texturefocus> + <texturenofocus border="5">MenuItemNF.png</texturenofocus> + <pulseonselect>false</pulseonselect> + <label>19191</label> + </control> </control> <control type="image"> <posx>268</posx> diff --git a/addons/skin.confluence/720p/VideoFullScreen.xml b/addons/skin.confluence/720p/VideoFullScreen.xml index e12ec819f6..703d424026 100644 --- a/addons/skin.confluence/720p/VideoFullScreen.xml +++ b/addons/skin.confluence/720p/VideoFullScreen.xml @@ -3,7 +3,7 @@ <controls> <!-- media infos --> <control type="group" id="1"> - <visible>[Player.ShowInfo | Window.IsActive(VideoOSD)] + ![Window.IsVisible(123) | Window.IsVisible(124) | Window.IsVisible(125)]</visible> + <visible>[Player.ShowInfo | Window.IsActive(VideoOSD)] + ![Window.IsVisible(123) | Window.IsVisible(124) | Window.IsVisible(125) | Window.IsVisible(PVROSDChannels) | Window.IsVisible(PVROSDGuide)]</visible> <animation effect="fade" time="200">VisibleChange</animation> <control type="image" id="1"> <posx>0</posx> @@ -24,7 +24,21 @@ <textcolor>white</textcolor> <shadowcolor>black</shadowcolor> <label>$INFO[Player.Chapter,$LOCALIZE[21396]: ]$INFO[Player.ChapterCount, / ]$INFO[Player.ChapterName,[COLOR=grey] - (,)[/COLOR]]</label> - <visible>Player.ChapterCount</visible> + <visible>Player.ChapterCount + !VideoPlayer.Content(LiveTV)</visible> + </control> + <control type="label" id="1"> + <description>Channel Group label</description> + <posx>30</posx> + <posy>5</posy> + <width>1000</width> + <height>25</height> + <align>left</align> + <aligny>center</aligny> + <font>font13</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <label>$INFO[VideoPlayer.ChannelGroup,$LOCALIZE[31509]: ]</label> + <visible>VideoPlayer.Content(LiveTV)</visible> </control> <control type="label" id="1"> <description>Clock label</description> @@ -81,11 +95,35 @@ <width>910</width> <height>25</height> <align>left</align> + <aligny>center</aligny> <font>font13</font> <label>$LOCALIZE[31040]</label> <textcolor>white</textcolor> <shadowcolor>black</shadowcolor> - <animation effect="slide" start="0,0" end="0,25" time="0" condition="!VideoPlayer.Content(Movies) + !VideoPlayer.Content(Episodes) + !VideoPlayer.Content(MusicVideos)">conditional</animation> + <visible>![VideoPlayer.Content(LiveTV) + Player.Recording]</visible> + <animation effect="slide" start="0,0" end="0,25" time="0" condition="!VideoPlayer.Content(Movies) + !VideoPlayer.Content(Episodes) + !VideoPlayer.Content(MusicVideos) + !VideoPlayer.Content(LiveTV)">conditional</animation> + </control> + <control type="image" id="1"> + <posy>0</posy> + <width>50</width> + <height>25</height> + <aspectratio align="center" aligny="center">keep</aspectratio> + <texture>PVR-IsRecording.png</texture> + <visible>VideoPlayer.Content(LiveTV) + Player.Recording</visible> + </control> + <control type="label" id="1"> + <description>Heading label</description> + <posx>50</posx> + <posy>0</posy> + <width>860</width> + <height>25</height> + <align>left</align> + <aligny>center</aligny> + <font>font13</font> + <label>$LOCALIZE[19158]</label> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <visible>VideoPlayer.Content(LiveTV) + Player.Recording</visible> </control> <control type="label" id="1"> <description>Studio label</description> @@ -126,6 +164,19 @@ <shadowcolor>black</shadowcolor> <visible>VideoPlayer.Content(MusicVideos)</visible> </control> + <control type="label" id="1"> + <description>LiveTV Info label</description> + <posx>20</posx> + <posy>30</posy> + <width>910</width> + <height>25</height> + <align>left</align> + <font>font12</font> + <label>$INFO[VideoPlayer.ChannelName]$INFO[VideoPlayer.ChannelNumber, - ([COLOR=blue],[/COLOR])]</label> + <textcolor>grey2</textcolor> + <shadowcolor>black</shadowcolor> + <visible>VideoPlayer.Content(LiveTV)</visible> + </control> <control type="grouplist" id="1"> <posx>20</posx> <posy>60</posy> @@ -176,13 +227,28 @@ <font>font12</font> <textcolor>grey</textcolor> <scroll>true</scroll> - <visible>!Window.IsVisible(VideoOSD)</visible> + <visible>!Window.IsVisible(VideoOSD) + !VideoPlayer.Content(LiveTV)</visible> + <animation effect="fade" time="200">VisibleChange</animation> + </control> + <control type="label" id="1"> + <posx>0</posx> + <posy>120</posy> + <width>910</width> + <height>25</height> + <label>$INFO[VideoPlayer.NextTitle,$LOCALIZE[209]: ]</label> + <align>center</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>grey</textcolor> + <scroll>true</scroll> + <visible>!Window.IsVisible(VideoOSD) + VideoPlayer.Content(LiveTV)</visible> <animation effect="fade" time="200">VisibleChange</animation> </control> </control> <control type="group" id="1"> <posx>330</posx> <posy>85r</posy> + <visible>!VideoPlayer.Content(LiveTV) | [VideoPlayer.Content(LiveTV) + VideoPlayer.HasEpg]</visible> <control type="label" id="1"> <posx>0</posx> <posy>0</posy> @@ -233,7 +299,6 @@ <posy>0</posy> <width>1280</width> <height>140</height> - <colordiffuse>AAFFFFFF</colordiffuse> <texture>black-back.png</texture> </control> <control type="label" id="10"> @@ -270,5 +335,252 @@ <label>-</label> </control> </control> + <control type="selectbutton" id="503"> + <posx>440</posx> + <posy>100</posy> + <width>400</width> + <height>100</height> + <font>font13caps</font> + <description>TV Channel Group Select Button</description> + <texturebg border="20">OverlayDialogBackground.png</texturebg> + <onleft>503</onleft> + <onright>503</onright> + <onup>500</onup> + <ondown>500</ondown> + <include>VisibleFadeEffect</include> + </control> + <control type="group"> + <visible>Player.ShowCodec + VideoPlayer.Content(LiveTV) + system.getbool(pvrplayback.signalquality)</visible> + <posy>165</posy> + <control type="image"> + <description>media info background image</description> + <posx>0</posx> + <posy>0</posy> + <width>1280</width> + <height>220</height> + <texture>black-back.png</texture> + </control> + <control type="label"> + <description>Header</description> + <posx>50</posx> + <posy>5</posy> + <width>1200</width> + <height>25</height> + <label>$LOCALIZE[19005]</label> + <align>left</align> + <aligny>center</aligny> + <font>font13_title</font> + <textcolor>blue</textcolor> + </control> + <control type="label"> + <description>Backend</description> + <posx>50</posx> + <posy>40</posy> + <width>165</width> + <height>25</height> + <label>$LOCALIZE[19012]:</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>grey2</textcolor> + </control> + <control type="label"> + <description>Backend value</description> + <posx>220</posx> + <posy>40</posy> + <width>1000</width> + <height>25</height> + <label>$INFO[PVR.ActStreamClient]</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>white</textcolor> + </control> + <control type="label"> + <description>Device</description> + <posx>50</posx> + <posy>65</posy> + <width>165</width> + <height>25</height> + <label>$LOCALIZE[19006]:</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>grey2</textcolor> + </control> + <control type="label"> + <description>Device value</description> + <posx>220</posx> + <posy>65</posy> + <width>1000</width> + <height>25</height> + <label>$INFO[PVR.ActStreamDevice]</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>white</textcolor> + </control> + <control type="label"> + <description>Status</description> + <posx>50</posx> + <posy>90</posy> + <width>165</width> + <height>25</height> + <label>$LOCALIZE[19007]:</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>grey2</textcolor> + </control> + <control type="label"> + <description>Status value</description> + <posx>220</posx> + <posy>90</posy> + <width>1000</width> + <height>25</height> + <label>$INFO[PVR.ActStreamStatus]</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>white</textcolor> + </control> + <control type="label"> + <description>Signal</description> + <posx>50</posx> + <posy>115</posy> + <width>165</width> + <height>25</height> + <label>$LOCALIZE[19008]:</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>grey2</textcolor> + </control> + <control type="progress"> + <description>Progressbar</description> + <posx>220</posx> + <posy>122</posy> + <width>910</width> + <height>14</height> + <info>PVR.ActStreamProgrSignal</info> + </control> + <control type="label"> + <description>Signal value</description> + <posx>1200</posx> + <posy>115</posy> + <width>180</width> + <height>25</height> + <label>$INFO[PVR.ActStreamSignal]</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>white</textcolor> + </control> + <control type="label"> + <description>SNR</description> + <posx>50</posx> + <posy>140</posy> + <width>165</width> + <height>25</height> + <label>$LOCALIZE[19009]:</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>grey2</textcolor> + </control> + <control type="progress"> + <description>Progressbar</description> + <posx>220</posx> + <posy>147</posy> + <width>910</width> + <height>14</height> + <overlaytexture>-</overlaytexture> + <info>PVR.ActStreamProgrSNR</info> + </control> + <control type="label"> + <description>SNR value</description> + <posx>1200</posx> + <posy>140</posy> + <width>180</width> + <height>25</height> + <label>$INFO[PVR.ActStreamSNR]</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>white</textcolor> + </control> + <control type="label"> + <description>BER</description> + <posx>50</posx> + <posy>165</posy> + <width>165</width> + <height>25</height> + <label>$LOCALIZE[19010]:</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>grey2</textcolor> + </control> + <control type="label"> + <description>BER value</description> + <posx>220</posx> + <posy>165</posy> + <width>1000</width> + <height>25</height> + <label>$INFO[PVR.ActStreamBER]</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>white</textcolor> + </control> + <control type="label"> + <description>UNC</description> + <posx>430</posx> + <posy>165</posy> + <width>165</width> + <height>25</height> + <label>$LOCALIZE[19011]:</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>grey2</textcolor> + </control> + <control type="label"> + <description>UNC value</description> + <posx>600</posx> + <posy>165</posy> + <width>1000</width> + <height>25</height> + <label>$INFO[PVR.ActStreamUNC]</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>white</textcolor> + </control> + <control type="label"> + <description>Encryption</description> + <posx>50</posx> + <posy>190</posy> + <width>165</width> + <height>25</height> + <label>$LOCALIZE[19015]:</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>grey2</textcolor> + </control> + <control type="label"> + <description>Encryption value</description> + <posx>220</posx> + <posy>190</posy> + <width>1000</width> + <height>25</height> + <label>$INFO[PVR.ActStreamEncryptionName]</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>white</textcolor> + </control> + </control> </controls> -</window>
\ No newline at end of file +</window> diff --git a/addons/skin.confluence/720p/VideoOSD.xml b/addons/skin.confluence/720p/VideoOSD.xml index 3cb999aeb2..2602be6bc6 100644 --- a/addons/skin.confluence/720p/VideoOSD.xml +++ b/addons/skin.confluence/720p/VideoOSD.xml @@ -1,5 +1,5 @@ <window id="2901"> - <defaultcontrol always="true">602</defaultcontrol> + <defaultcontrol always="true">100</defaultcontrol> <include>dialogeffect</include> <coordinates> <system>1</system> @@ -39,13 +39,16 @@ <textureslidernib>osd_slider_nibNF.png</textureslidernib> <textureslidernibfocus>osd_slider_nib.png</textureslidernibfocus> <animation effect="fade" time="200">VisibleChange</animation> - <visible>![Window.IsVisible(SliderDialog) | Window.IsVisible(OSDVideoSettings) | Window.IsVisible(OSDAudioSettings) | Window.IsVisible(VideoBookmarks)]</visible> + <visible>![Window.IsVisible(SliderDialog) | Window.IsVisible(OSDVideoSettings) | Window.IsVisible(OSDAudioSettings) | Window.IsVisible(VideoBookmarks) | VideoPlayer.Content(LiveTV)]</visible> </control> + <!-- !LiveTV --> <control type="group" id="100"> <posx>325</posx> <posy>50r</posy> + <defaultcontrol always="true">602</defaultcontrol> <animation effect="fade" time="200">VisibleChange</animation> <visible>![Window.IsVisible(SliderDialog) | Window.IsVisible(OSDVideoSettings) | Window.IsVisible(OSDAudioSettings) | Window.IsVisible(VideoBookmarks)]</visible> + <visible>!VideoPlayer.Content(LiveTV)</visible> <control type="button" id="600"> <posx>0</posx> <posy>0</posy> @@ -141,11 +144,134 @@ <onclick>PlayerControl(Next)</onclick> </control> </control> + + <!-- LiveTV --> + <control type="group" id="100"> + <posx>325</posx> + <posy>50r</posy> + <defaultcontrol always="true">601</defaultcontrol> + <animation effect="fade" time="200">VisibleChange</animation> + <visible>![Window.IsVisible(SliderDialog) | Window.IsVisible(OSDVideoSettings) | Window.IsVisible(OSDAudioSettings) | Window.IsVisible(VideoBookmarks) | Window.IsVisible(PVROSDChannels) | Window.IsVisible(PVROSDGuide)]</visible> + <visible>VideoPlayer.Content(LiveTV)</visible> + <control type="button" id="600"> + <posx>0</posx> + <posy>0</posy> + <width>45</width> + <height>45</height> + <label>210</label> + <font>-</font> + <texturefocus>OSDChannelUPFO.png</texturefocus> + <texturenofocus>OSDChannelUPNF.png</texturenofocus> + <onleft>704</onleft> + <onright>601</onright> + <onup>1000</onup> + <ondown>1000</ondown> + <onclick>PlayerControl(Previous)</onclick> + </control> + <control type="button" id="601"> + <posx>45</posx> + <posy>0</posy> + <width>45</width> + <height>45</height> + <label>31354</label> + <font>-</font> + <texturefocus>OSDChannelDownFO.png</texturefocus> + <texturenofocus>OSDChannelDownNF.png</texturenofocus> + <onleft>600</onleft> + <onright>602</onright> + <onup>1000</onup> + <ondown>1000</ondown> + <onclick>PlayerControl(Next)</onclick> + </control> + <control type="togglebutton" id="602"> + <posx>90</posx> + <posy>0</posy> + <width>45</width> + <height>45</height> + <label>31351</label> + <altlabel>208</altlabel> + <font>-</font> + <texturefocus>OSDPauseFO.png</texturefocus> + <texturenofocus>OSDPauseNF.png</texturenofocus> + <usealttexture>Player.Paused | Player.Forwarding | Player.Rewinding</usealttexture> + <alttexturefocus>OSDPlayFO.png</alttexturefocus> + <alttexturenofocus>OSDPlayNF.png</alttexturenofocus> + <onleft>601</onleft> + <onright>603</onright> + <onup>1000</onup> + <ondown>1000</ondown> + <onclick>PlayerControl(Play)</onclick> + <visible>False</visible> + </control> + <control type="button" id="603"> + <posx>135</posx> + <posy>0</posy> + <width>45</width> + <height>45</height> + <label>31351</label> + <altlabel>208</altlabel> + <font>-</font> + <texturefocus>OSDStopFO.png</texturefocus> + <texturenofocus>OSDStopNF.png</texturenofocus> + <onleft>602</onleft> + <onright>604</onright> + <onup>1000</onup> + <ondown>1000</ondown> + <onclick>PlayerControl(Stop)</onclick> + </control> + <control type="button" id="604"> + <posx>180</posx> + <posy>0</posy> + <width>45</width> + <height>45</height> + <label>19019</label> + <font>-</font> + <texturefocus>OSDChannelListFO.png</texturefocus> + <texturenofocus>OSDChannelListNF.png</texturenofocus> + <onleft>603</onleft> + <onright>605</onright> + <onup>1000</onup> + <ondown>1000</ondown> + <onclick>ActivateWindow(PVROSDChannels)</onclick> + <onclick>Dialog.Close(VideoOSD)</onclick> + </control> + <control type="button" id="605"> + <posx>225</posx> + <posy>0</posy> + <width>45</width> + <height>45</height> + <label>$LOCALIZE[19029]$INFO[VideoPlayer.ChannelName, - ]</label> + <font>-</font> + <texturefocus>OSDepgFO.png</texturefocus> + <texturenofocus>OSDepgNF.png</texturenofocus> + <onleft>604</onleft> + <onright>701</onright> + <onup>1000</onup> + <ondown>1000</ondown> + <onclick>ActivateWindow(PVROSDGuide)</onclick> + <onclick>Dialog.Close(VideoOSD)</onclick> + </control> + <control type="label" id="606"> + <posx>290</posx> + <posy>0</posy> + <width>450</width> + <height>45</height> + <label>$INFO[VideoPlayer.NextTitle,$LOCALIZE[209]: ]</label> + <align>center</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>grey</textcolor> + <scroll>false</scroll> + </control> + </control> + + <!-- !LiveTV --> <control type="group"> <posx>250r</posx> <posy>50r</posy> <animation effect="fade" time="200">VisibleChange</animation> <visible>![Window.IsVisible(SliderDialog) | Window.IsVisible(OSDVideoSettings) | Window.IsVisible(OSDAudioSettings) | Window.IsVisible(VideoBookmarks)]</visible> + <visible>!VideoPlayer.Content(LiveTV)</visible> <control type="togglebutton" id="701"> <posx>0</posx> <posy>0</posy> @@ -231,5 +357,80 @@ <animation effect="fade" start="100" end="50" time="100" condition="!VideoPlayer.HasMenu">Conditional</animation> </control> </control> + + <!-- LiveTV --> + <control type="group"> + <posx>200r</posx> + <posy>50r</posy> + <animation effect="fade" time="200">VisibleChange</animation> + <visible>![Window.IsVisible(SliderDialog) | Window.IsVisible(OSDVideoSettings) | Window.IsVisible(OSDAudioSettings) | Window.IsVisible(VideoBookmarks) | Window.IsVisible(PVROSDChannels) | Window.IsVisible(PVROSDGuide)]</visible> + <visible>VideoPlayer.Content(LiveTV)</visible> + <control type="button" id="701"> + <posx>0</posx> + <posy>0</posy> + <width>45</width> + <height>45</height> + <label>31356</label> + <font>-</font> + <texturefocus>OSDTeleTextFO.png</texturefocus> + <texturenofocus>OSDTeleTextNF.png</texturenofocus> + <onleft>605</onleft> + <onright>702</onright> + <onup>1000</onup> + <ondown>1000</ondown> + <onclick>ActivateWindow(Teletext)</onclick> + </control> + <control type="button" id="702"> + <posx>45</posx> + <posy>0</posy> + <width>45</width> + <height>45</height> + <label>13395</label> + <font>-</font> + <texturefocus>OSDVideoFO.png</texturefocus> + <texturenofocus>OSDVideoNF.png</texturenofocus> + <onleft>701</onleft> + <onright>703</onright> + <onup>1000</onup> + <ondown>1000</ondown> + <onclick>ActivateWindow(OSDVideoSettings)</onclick> + </control> + <control type="button" id="703"> + <posx>90</posx> + <posy>0</posy> + <width>45</width> + <height>45</height> + <label>13396</label> + <font>-</font> + <texturefocus>OSDAudioFO.png</texturefocus> + <texturenofocus>OSDAudioNF.png</texturenofocus> + <onleft>702</onleft> + <onright>704</onright> + <onup>1000</onup> + <ondown>1000</ondown> + <onclick>ActivateWindow(OSDAudioSettings)</onclick> + </control> + <control type="togglebutton" id="704"> + <posx>135</posx> + <posy>0</posy> + <width>45</width> + <height>45</height> + <label>31351</label> + <altlabel>208</altlabel> + <font>-</font> + <texturefocus>OSDRecordOffFO.png</texturefocus> + <texturenofocus>OSDRecordOffNF.png</texturenofocus> + <usealttexture>Player.Recording</usealttexture> + <alttexturefocus>OSDRecordOnFO.png</alttexturefocus> + <alttexturenofocus>OSDRecordOnNF.png</alttexturenofocus> + <onleft>703</onleft> + <onright>600</onright> + <onup>1000</onup> + <ondown>1000</ondown> + <onclick>PlayerControl(Record)</onclick> + <enable>Player.CanRecord</enable> + <animation effect="fade" start="100" end="50" time="100" condition="!Player.CanRecord">Conditional</animation> + </control> + </control> </controls> - </window>
\ No newline at end of file + </window> diff --git a/addons/skin.confluence/720p/ViewsPVR.xml b/addons/skin.confluence/720p/ViewsPVR.xml new file mode 100644 index 0000000000..003009e3bb --- /dev/null +++ b/addons/skin.confluence/720p/ViewsPVR.xml @@ -0,0 +1,2392 @@ +<includes> + <include name="LiveTVChannelView"> + <control type="group"> + <description>TV Channels group</description> + <visible>Control.IsVisible(11)</visible> + <include>VisibleFadeEffect</include> + <control type="group"> + <posx>530</posx> + <posy>490</posy> + <control type="label"> + <posx>0</posx> + <posy>0</posy> + <width>690</width> + <height>20</height> + <font>font13</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <scroll>true</scroll> + <align>center</align> + <aligny>center</aligny> + <label>[B]$INFO[Container(11).ListItem.Title][/B]</label> + </control> + <control type="label"> + <posx>80</posx> + <posy>22</posy> + <width>80</width> + <height>20</height> + <align>right</align> + <aligny>center</aligny> + <font>font10_title</font> + <textcolor>blue</textcolor> + <visible>Container(11).ListItem.HasEpg</visible> + <label>$INFO[Container(11).ListItem.StartTime]</label> + </control> + <control type="progress"> + <description>Progressbar</description> + <posx>85</posx> + <posy>30</posy> + <width>510</width> + <height>8</height> + <visible>Container(11).ListItem.HasEpg</visible> + <info>Container(11).ListItem.Progress</info> + </control> + <control type="label"> + <posx>600</posx> + <posy>22</posy> + <width>80</width> + <height>20</height> + <align>left</align> + <aligny>center</aligny> + <font>font10_title</font> + <textcolor>blue</textcolor> + <visible>Container(11).ListItem.HasEpg</visible> + <label>$INFO[Container(11).ListItem.EndTime]</label> + </control> + <control type="textbox"> + <description>Plot Value for TVShow</description> + <posx>0</posx> + <posy>43</posy> + <width>690</width> + <height>80</height> + <font>font12</font> + <align>justify</align> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <pagecontrol>-</pagecontrol> + <label>$INFO[Container(11).ListItem.Plot]</label> + <autoscroll time="2000" delay="3000" repeat="5000">true</autoscroll> + </control> + <control type="label"> + <posx>690</posx> + <posy>140</posy> + <width>690</width> + <height>20</height> + <font>font12</font> + <textcolor>grey2</textcolor> + <align>right</align> + <aligny>center</aligny> + <scroll>false</scroll> + <visible>!IsEmpty(Container(11).ListItem.NextTitle)</visible> + <label>$LOCALIZE[19031]: $INFO[Container(11).ListItem.NextTitle]</label> + </control> + </control> + <control type="list" id="11"> + <posx>70</posx> + <posy>85</posy> + <width>390</width> + <height>541</height> + <onleft>32</onleft> + <onright>70</onright> + <onup>11</onup> + <ondown>11</ondown> + <viewtype label="535">list</viewtype> + <pagecontrol>70</pagecontrol> + <scrolltime>200</scrolltime> + <itemlayout height="60" width="390"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>390</width> + <height>61</height> + <texture border="2">MenuItemNF.png</texture> + <include>VisibleFadeEffect</include> + </control> + <control type="label"> + <posx>5</posx> + <posy>-4</posy> + <width>40</width> + <height>35</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <info>ListItem.ChannelNumber</info> + </control> + <control type="label"> + <posx>50</posx> + <posy>0</posy> + <width>270</width> + <height>25</height> + <font>font13</font> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Label]</label> + </control> + <control type="label"> + <posx>50</posx> + <posy>25</posy> + <width>330</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Title]</label> + <visible>IsEmpty(Listitem.Icon)</visible> + </control> + <control type="label"> + <posx>50</posx> + <posy>25</posy> + <width>280</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Title]</label> + <visible>!IsEmpty(Listitem.Icon)</visible> + </control> + <control type="progress"> + <description>Progressbar</description> + <posx>50</posx> + <posy>48</posy> + <width>280</width> + <height>6</height> + <colordiffuse>88FFFFFF</colordiffuse> + <visible>ListItem.HasEpg</visible> + <info>ListItem.Progress</info> + </control> + <control type="image"> + <posx>340</posx> + <posy>4</posy> + <width>50</width> + <height>50</height> + <texture>$INFO[ListItem.Icon]</texture> + </control> + <control type="image"> + <posx>5</posx> + <posy>37</posy> + <width>30</width> + <height>20</height> + <texture>PVR-IsRecording.png</texture> + <visible>ListItem.IsRecording</visible> + </control> + </itemlayout> + <focusedlayout height="60" width="390"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>390</width> + <height>61</height> + <texture border="2">MenuItemNF.png</texture> + <visible>!Control.HasFocus(11)</visible> + <include>VisibleFadeEffect</include> + </control> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>390</width> + <height>61</height> + <texture border="2">MenuItemFO.png</texture> + <visible>Control.HasFocus(11)</visible> + <include>VisibleFadeEffect</include> + </control> + <control type="label"> + <posx>5</posx> + <posy>-4</posy> + <width>40</width> + <height>35</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <info>ListItem.ChannelNumber</info> + </control> + <control type="label"> + <posx>50</posx> + <posy>0</posy> + <width>270</width> + <height>25</height> + <font>font13</font> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Label]</label> + </control> + <control type="label"> + <posx>50</posx> + <posy>25</posy> + <width>330</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Title]</label> + <visible>IsEmpty(Listitem.Icon)</visible> + </control> + <control type="label"> + <posx>50</posx> + <posy>25</posy> + <width>280</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Title]</label> + <visible>!IsEmpty(Listitem.Icon)</visible> + </control> + <control type="progress"> + <description>Progressbar</description> + <posx>50</posx> + <posy>48</posy> + <width>280</width> + <height>6</height> + <colordiffuse>88FFFFFF</colordiffuse> + <visible>ListItem.HasEpg</visible> + <info>ListItem.Progress</info> + </control> + <control type="image"> + <posx>340</posx> + <posy>4</posy> + <width>50</width> + <height>50</height> + <texture>$INFO[ListItem.Icon]</texture> + </control> + <control type="image"> + <posx>5</posx> + <posy>37</posy> + <width>30</width> + <height>20</height> + <texture>PVR-IsRecording.png</texture> + <visible>ListItem.IsRecording</visible> + </control> + </focusedlayout> + </control> + <control type="scrollbar" id="70"> + <posx>465</posx> + <posy>85</posy> + <width>25</width> + <height>540</height> + <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground> + <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar> + <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus> + <textureslidernib>ScrollBarNib.png</textureslidernib> + <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus> + <onleft>11</onleft> + <onright>32</onright> + <showonepage>false</showonepage> + <orientation>vertical</orientation> + </control> + <control type="label"> + <animation effect="slide" start="0,0" end="-90,0" time="0" condition="system.getbool(input.enablemouse)">Conditional</animation> + <description>Page Count Label</description> + <posx>40r</posx> + <posy>53r</posy> + <width>500</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <scroll>false</scroll> + <align>right</align> + <aligny>center</aligny> + <label>([COLOR=blue]$INFO[Container(11).NumItems][/COLOR]) $LOCALIZE[19019] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(11).CurrentPage]/$INFO[Container(11).NumPages][/COLOR])</label> + <include>Window_OpenClose_Animation</include> + </control> + </control> + </include> + + <include name="LiveRadioChannelView"> + <control type="group"> + <description>Radio Channels group</description> + <visible>Control.IsVisible(12)</visible> + <include>VisibleFadeEffect</include> + <control type="group"> + <posx>530</posx> + <posy>490</posy> + <control type="label"> + <posx>0</posx> + <posy>0</posy> + <width>690</width> + <height>20</height> + <font>font13</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <scroll>true</scroll> + <align>center</align> + <aligny>center</aligny> + <label>[B]$INFO[Container(12).ListItem.Title][/B]</label> + </control> + <control type="label"> + <posx>80</posx> + <posy>22</posy> + <width>80</width> + <height>20</height> + <align>right</align> + <aligny>center</aligny> + <font>font10_title</font> + <textcolor>blue</textcolor> + <visible>Container(12).ListItem.HasEpg</visible> + <label>$INFO[Container(12).ListItem.StartTime]</label> + </control> + <control type="progress"> + <description>Progressbar</description> + <posx>85</posx> + <posy>30</posy> + <width>510</width> + <height>8</height> + <visible>Container(12).ListItem.HasEpg</visible> + <info>Container(12).ListItem.Progress</info> + </control> + <control type="label"> + <posx>600</posx> + <posy>22</posy> + <width>80</width> + <height>20</height> + <align>left</align> + <aligny>center</aligny> + <font>font10_title</font> + <textcolor>blue</textcolor> + <visible>Container(12).ListItem.HasEpg</visible> + <label>$INFO[Container(12).ListItem.EndTime]</label> + </control> + <control type="textbox"> + <description>Plot Value for TVShow</description> + <posx>0</posx> + <posy>43</posy> + <width>690</width> + <height>80</height> + <font>font12</font> + <align>justify</align> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <pagecontrol>-</pagecontrol> + <label>$INFO[Container(12).ListItem.Plot]</label> + <autoscroll time="2000" delay="3000" repeat="5000">true</autoscroll> + </control> + <control type="label"> + <posx>690</posx> + <posy>140</posy> + <width>690</width> + <height>20</height> + <font>font12</font> + <textcolor>grey2</textcolor> + <align>right</align> + <aligny>center</aligny> + <scroll>false</scroll> + <visible>!IsEmpty(Container(12).ListItem.NextTitle)</visible> + <label>$LOCALIZE[19031]: $INFO[Container(12).ListItem.NextTitle]</label> + </control> + </control> + <control type="list" id="12"> + <posx>70</posx> + <posy>85</posy> + <width>390</width> + <height>541</height> + <onleft>33</onleft> + <onright>71</onright> + <onup>12</onup> + <ondown>12</ondown> + <viewtype label="535">list</viewtype> + <pagecontrol>71</pagecontrol> + <scrolltime>200</scrolltime> + <itemlayout height="60" width="390"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>390</width> + <height>61</height> + <texture border="2">MenuItemNF.png</texture> + <include>VisibleFadeEffect</include> + </control> + <control type="label"> + <posx>5</posx> + <posy>-4</posy> + <width>40</width> + <height>35</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <info>ListItem.ChannelNumber</info> + </control> + <control type="label"> + <posx>50</posx> + <posy>0</posy> + <width>270</width> + <height>25</height> + <font>font13</font> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Label]</label> + </control> + <control type="label"> + <posx>50</posx> + <posy>25</posy> + <width>330</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Title]</label> + <visible>IsEmpty(Listitem.Icon)</visible> + </control> + <control type="label"> + <posx>50</posx> + <posy>25</posy> + <width>280</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Title]</label> + <visible>!IsEmpty(Listitem.Icon)</visible> + </control> + <control type="progress"> + <description>Progressbar</description> + <posx>50</posx> + <posy>48</posy> + <width>280</width> + <height>6</height> + <colordiffuse>88FFFFFF</colordiffuse> + <info>ListItem.Progress</info> + <visible>ListItem.HasEpg</visible> + </control> + <control type="image"> + <posx>340</posx> + <posy>4</posy> + <width>50</width> + <height>50</height> + <texture>$INFO[ListItem.Icon]</texture> + </control> + <control type="image"> + <posx>5</posx> + <posy>37</posy> + <width>30</width> + <height>20</height> + <texture>PVR-IsRecording.png</texture> + <visible>ListItem.IsRecording</visible> + </control> + </itemlayout> + <focusedlayout height="60" width="390"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>390</width> + <height>61</height> + <texture border="2">MenuItemNF.png</texture> + <visible>!Control.HasFocus(12)</visible> + <include>VisibleFadeEffect</include> + </control> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>390</width> + <height>61</height> + <texture border="2">MenuItemFO.png</texture> + <visible>Control.HasFocus(12)</visible> + <include>VisibleFadeEffect</include> + </control> + <control type="label"> + <posx>5</posx> + <posy>-4</posy> + <width>40</width> + <height>35</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <info>ListItem.ChannelNumber</info> + </control> + <control type="label"> + <posx>50</posx> + <posy>0</posy> + <width>270</width> + <height>25</height> + <font>font13</font> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Label]</label> + </control> + <control type="label"> + <posx>50</posx> + <posy>25</posy> + <width>330</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Title]</label> + <visible>IsEmpty(Listitem.Icon)</visible> + </control> + <control type="label"> + <posx>50</posx> + <posy>25</posy> + <width>280</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Title]</label> + <visible>!IsEmpty(Listitem.Icon)</visible> + </control> + <control type="progress"> + <description>Progressbar</description> + <posx>50</posx> + <posy>48</posy> + <width>280</width> + <height>6</height> + <colordiffuse>88FFFFFF</colordiffuse> + <info>ListItem.Progress</info> + <visible>ListItem.HasEpg</visible> + </control> + <control type="image"> + <posx>340</posx> + <posy>4</posy> + <width>50</width> + <height>50</height> + <texture>$INFO[ListItem.Icon]</texture> + </control> + <control type="image"> + <posx>5</posx> + <posy>37</posy> + <width>30</width> + <height>20</height> + <texture>PVR-IsRecording.png</texture> + <visible>ListItem.IsRecording</visible> + </control> + </focusedlayout> + </control> + <control type="scrollbar" id="71"> + <posx>465</posx> + <posy>85</posy> + <width>25</width> + <height>540</height> + <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground> + <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar> + <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus> + <textureslidernib>ScrollBarNib.png</textureslidernib> + <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus> + <onleft>12</onleft> + <onright>33</onright> + <showonepage>false</showonepage> + <orientation>vertical</orientation> + </control> + <control type="label"> + <animation effect="slide" start="0,0" end="-90,0" time="0" condition="system.getbool(input.enablemouse)">Conditional</animation> + <description>Page Count Label</description> + <posx>40r</posx> + <posy>53r</posy> + <width>500</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <scroll>false</scroll> + <align>right</align> + <aligny>center</aligny> + <label>([COLOR=blue]$INFO[Container(12).NumItems][/COLOR]) $LOCALIZE[19019] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(12).CurrentPage]/$INFO[Container(12).NumPages][/COLOR])</label> + <include>Window_OpenClose_Animation</include> + </control> + </control> + </include> + + <include name="LiveTVRecordingsView"> + <control type="group"> + <description>Recordings group</description> + <visible>Control.IsVisible(13)</visible> + <include>VisibleFadeEffect</include> + <control type="list" id="13"> + <posx>70</posx> + <posy>75</posy> + <width>760</width> + <height>561</height> + <onleft>34</onleft> + <onright>72</onright> + <onup>13</onup> + <ondown>13</ondown> + <viewtype label="535">list</viewtype> + <pagecontrol>72</pagecontrol> + <scrolltime>200</scrolltime> + <itemlayout height="40" width="760"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>760</width> + <height>41</height> + <texture border="2">MenuItemNF.png</texture> + <include>VisibleFadeEffect</include> + </control> + <control type="image"> + <posx>10</posx> + <posy>5</posy> + <width>30</width> + <height>30</height> + <texture background="true" fallback="DefaultVideoCover.png">$INFO[ListItem.Icon]</texture> + </control> + <control type="label"> + <posx>50</posx> + <posy>0</posy> + <width>605</width> + <height>40</height> + <font>font13</font> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Label]</label> + </control> + <control type="label"> + <posx>725</posx> + <posy>0</posy> + <width>500</width> + <height>40</height> + <font>font12</font> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <align>right</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Date]</label> + </control> + <control type="image"> + <posx>730</posx> + <posy>14</posy> + <width>20</width> + <height>16</height> + <texture>$INFO[ListItem.Overlay]</texture> + </control> + </itemlayout> + <focusedlayout height="40" width="760"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>760</width> + <height>41</height> + <texture border="2">MenuItemFO.png</texture> + <visible>Control.HasFocus(13)</visible> + <include>VisibleFadeEffect</include> + </control> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>760</width> + <height>41</height> + <texture border="2">MenuItemNF.png</texture> + <include>VisibleFadeEffect</include> + <visible>!Control.HasFocus(13)</visible> + </control> + <control type="image"> + <posx>560</posx> + <posy>5</posy> + <width>200</width> + <height>31</height> + <texture border="0,0,14,0">MediaItemDetailBG.png</texture> + <visible>Control.HasFocus(13) + !IsEmpty(ListItem.Date)</visible> + </control> + <control type="image"> + <posx>10</posx> + <posy>5</posy> + <width>30</width> + <height>30</height> + <texture background="true" fallback="DefaultVideoCover.png">$INFO[ListItem.Icon]</texture> + </control> + <control type="label"> + <posx>50</posx> + <posy>0</posy> + <width>630</width> + <height>40</height> + <font>font13</font> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <align>left</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Label]</label> + </control> + <control type="label"> + <posx>725</posx> + <posy>0</posy> + <width>500</width> + <height>40</height> + <font>font12</font> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <align>right</align> + <aligny>center</aligny> + <label>$INFO[ListItem.Date]</label> + </control> + <control type="image"> + <posx>730</posx> + <posy>14</posy> + <width>20</width> + <height>16</height> + <texture>$INFO[ListItem.Overlay]</texture> + </control> + </focusedlayout> + </control> + <control type="scrollbar" id="72"> + <posx>850</posx> + <posy>78</posy> + <width>25</width> + <height>560</height> + <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground> + <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar> + <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus> + <textureslidernib>ScrollBarNib.png</textureslidernib> + <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus> + <onleft>13</onleft> + <onright>34</onright> + <showonepage>false</showonepage> + <orientation>vertical</orientation> + </control> + <control type="label"> + <animation effect="slide" start="0,0" end="-90,0" time="0" condition="system.getbool(input.enablemouse)">Conditional</animation> + <description>Page Count Label</description> + <posx>40r</posx> + <posy>53r</posy> + <width>500</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <scroll>false</scroll> + <align>right</align> + <aligny>center</aligny> + <label>([COLOR=blue]$INFO[Container(13).NumItems][/COLOR]) $LOCALIZE[19163] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(13).CurrentPage]/$INFO[Container(13).NumPages][/COLOR])</label> + <include>Window_OpenClose_Animation</include> + </control> + <control type="group"> + <posx>910</posx> + <posy>80</posy> + <control type="image"> + <posx>10</posx> + <posy>0</posy> + <width>290</width> + <height>230</height> + <aspectratio aligny="bottom">keep</aspectratio> + <fadetime>IconCrossfadeTime</fadetime> + <texture fallback="DefaultVideoCover.png">$INFO[Container(13).ListItem.Icon]</texture> + <bordertexture border="8">ThumbShadow.png</bordertexture> + <bordersize>8</bordersize> + </control> + <control type="fadelabel"> + <posx>10</posx> + <posy>230</posy> + <width>290</width> + <height>25</height> + <label>$INFO[Container(13).ListItem.Title]</label> + <align>center</align> + <aligny>center</aligny> + <font>font13</font> + <textcolor>blue</textcolor> + <shadowcolor>black</shadowcolor> + <scrollout>false</scrollout> + <pauseatend>1000</pauseatend> + </control> + <control type="textbox"> + <description>Description Value for TV Show</description> + <posx>10</posx> + <posy>270</posy> + <width>290</width> + <height>280</height> + <font>font12</font> + <align>justify</align> + <textcolor>white</textcolor> + <label>$INFO[Container(13).ListItem.Plot]</label> + <autoscroll time="2000" delay="3000" repeat="5000">Skin.HasSetting(AutoScroll)</autoscroll> + </control> + </control> + </control> + </include> + + <include name="EPGTimelineView"> + <control type="group"> + <description>TV Guide Timeline</description> + <visible>Control.IsVisible(10)</visible> + <include>VisibleFadeEffect</include> + <control type="epggrid" id="10"> + <description>EPG Grid</description> + <posx>80</posx> + <posy>81</posy> + <width>1120</width> + <height>555</height> + <pagecontrol>10</pagecontrol> + <scrolltime>350</scrolltime> + <timeblocks>40</timeblocks> + <rulerunit>6</rulerunit> + <onleft>31</onleft> + <onright>31</onright> + <onup>10</onup> + <ondown>10</ondown> + <rulerlayout height="35" width="40"> + <control type="image" id="1"> + <width>40</width> + <height>29</height> + <posx>0</posx> + <posy>0</posy> + <texture border="5">button-nofocus.png</texture> + </control> + <control type="label" id="2"> + <posx>10</posx> + <posy>0</posy> + <width>34</width> + <height>29</height> + <font>font12</font> + <aligny>center</aligny> + <selectedcolor>selected</selectedcolor> + <align>left</align> + <label>$INFO[ListItem.Label]</label> + </control> + </rulerlayout> + <channellayout height="52" width="280"> + <animation effect="fade" start="110" time="200">UnFocus</animation> + <control type="image" id="1"> + <posx>0</posx> + <posy>0</posy> + <width>270</width> + <height>52</height> + <texture border="5">button-nofocus.png</texture> + </control> + <control type="label"> + <posx>5</posx> + <posy>5</posy> + <width>40</width> + <height>35</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <info>ListItem.ChannelNumber</info> + </control> + <control type="image"> + <posx>45</posx> + <posy>4</posy> + <width>45</width> + <height>44</height> + <texture>$INFO[ListItem.Icon]</texture> + </control> + <control type="label" id="1"> + <posx>94</posx> + <posy>0</posy> + <width>160</width> + <height>52</height> + <font>special12</font> + <aligny>center</aligny> + <selectedcolor>selected</selectedcolor> + <align>left</align> + <label>$INFO[ListItem.ChannelName]</label> + </control> + </channellayout> + <focusedchannellayout height="52" width="280"> + <animation effect="fade" start="110" time="200">OnFocus</animation> + <control type="image" id="1"> + <posx>0</posx> + <posy>0</posy> + <width>270</width> + <height>52</height> + <texture border="5">button-focus.png</texture> + </control> + <control type="label"> + <posx>5</posx> + <posy>5</posy> + <width>40</width> + <height>35</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey</textcolor> + <selectedcolor>grey</selectedcolor> + <info>ListItem.ChannelNumber</info> + </control> + <control type="image"> + <posx>45</posx> + <posy>4</posy> + <width>45</width> + <height>44</height> + <texture>$INFO[ListItem.Icon]</texture> + </control> + <control type="label" id="1"> + <posx>94</posx> + <posy>0</posy> + <width>160</width> + <height>52</height> + <font>special12</font> + <aligny>center</aligny> + <selectedcolor>selected</selectedcolor> + <align>left</align> + <label>$INFO[ListItem.ChannelName]</label> + </control> + </focusedchannellayout> + <itemlayout height="52" width="40"> + <control type="image" id="2"> + <width>40</width> + <height>52</height> + <posx>0</posx> + <posy>0</posy> + <aspectratio>stretch</aspectratio> + <texture border="3">epg-genres/$INFO[ListItem.Property(GenreType)].png</texture> + </control> + <control type="label" id="1"> + <posx>6</posx> + <posy>3</posy> + <width>30</width> + <height>25</height> + <font>font12</font> + <aligny>center</aligny> + <selectedcolor>selected</selectedcolor> + <align>left</align> + <info>ListItem.Label</info> + </control> + <control type="image"> + <posx>5</posx> + <posy>28</posy> + <width>30</width> + <height>20</height> + <texture>PVR-IsRecording.png</texture> + <visible>ListItem.IsRecording</visible> + </control> + <control type="image"> + <posx>5</posx> + <posy>28</posy> + <width>20</width> + <height>20</height> + <texture>PVR-HasTimer.png</texture> + <visible>ListItem.HasTimer + !ListItem.IsRecording</visible> + </control> + </itemlayout> + <focusedlayout height="52" width="40"> + <control type="image" id="14"> + <width>40</width> + <height>52</height> + <posx>0</posx> + <posy>0</posy> + <texture border="5">folder-focus.png</texture> + </control> + <control type="image" id="2"> + <width>40</width> + <height>52</height> + <posx>0</posx> + <posy>0</posy> + <aspectratio>stretch</aspectratio> + <texture border="3">epg-genres/$INFO[ListItem.Property(GenreType)].png</texture> + </control> + <control type="label" id="1"> + <posx>6</posx> + <posy>3</posy> + <width>30</width> + <height>25</height> + <font>font12</font> + <aligny>center</aligny> + <selectedcolor>selected</selectedcolor> + <align>left</align> + <info>ListItem.Label</info> + </control> + <control type="image"> + <posx>5</posx> + <posy>28</posy> + <width>30</width> + <height>20</height> + <texture>PVR-IsRecording.png</texture> + <visible>ListItem.IsRecording</visible> + </control> + <control type="image"> + <posx>5</posx> + <posy>28</posy> + <width>20</width> + <height>20</height> + <texture>PVR-HasTimer.png</texture> + <visible>ListItem.HasTimer + !ListItem.IsRecording</visible> + </control> + </focusedlayout> + </control> + </control> + </include> + + <include name="LiveTVTimersView"> + <control type="group"> + <description>Timers group</description> + <visible>Control.IsVisible(14)</visible> + <include>VisibleFadeEffect</include> + <control type="group"> + <posx>80</posx> + <posy>60</posy> + <control type="label"> + <description>Channel header label</description> + <posx>0</posx> + <posy>20</posy> + <width>220</width> + <height>20</height> + <font>font13_title</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <align>center</align> + <aligny>center</aligny> + <label>19029</label> + </control> + <control type="label"> + <description>Title header label</description> + <posx>220</posx> + <posy>20</posy> + <width>300</width> + <height>20</height> + <font>font13_title</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <align>center</align> + <aligny>center</aligny> + <label>369</label> + </control> + <control type="label"> + <description>Schedule Time header label</description> + <posx>580</posx> + <posy>20</posy> + <width>300</width> + <height>20</height> + <font>font13_title</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <align>center</align> + <aligny>center</aligny> + <label>31501</label> + </control> + <control type="label"> + <description>Status header label</description> + <posx>940</posx> + <posy>20</posy> + <width>150</width> + <height>20</height> + <font>font13_title</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <align>center</align> + <aligny>center</aligny> + <label>126</label> + </control> + <control type="image"> + <description>separator image</description> + <posx>0</posx> + <posy>50</posy> + <width>1100</width> + <height>1</height> + <colordiffuse>88FFFFFF</colordiffuse> + <texture>separator2.png</texture> + </control> + <control type="list" id="14"> + <posx>0</posx> + <posy>55</posy> + <width>1100</width> + <height>480</height> + <onup>14</onup> + <ondown>14</ondown> + <onleft>35</onleft> + <onright>73</onright> + <pagecontrol>73</pagecontrol> + <scrolltime>200</scrolltime> + <itemlayout height="40"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>1100</width> + <height>41</height> + <texture border="5">MenuItemNF.png</texture> + </control> + <control type="image"> + <posx>220</posx> + <posy>0</posy> + <width>300</width> + <height>40</height> + <colordiffuse>33FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + </control> + <control type="image"> + <posx>940</posx> + <posy>0</posy> + <width>155</width> + <height>40</height> + <colordiffuse>33FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + </control> + <control type="image"> + <posx>0</posx> + <posy>8</posy> + <width>50</width> + <height>26</height> + <visible>!IsEmpty(ListItem.Date)</visible> + <texture border="1">$INFO[ListItem.Icon]</texture> + </control> + <control type="label"> + <posx>50</posx> + <posy>0</posy> + <width>150</width> + <height>40</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <selectedcolor>selected</selectedcolor> + <info>ListItem.ChannelName</info> + </control> + <control type="label"> + <posx>370</posx> + <posy>0</posy> + <width>290</width> + <height>40</height> + <font>font12</font> + <align>center</align> + <aligny>center</aligny> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Label</info> + </control> + <control type="label"> + <posx>730</posx> + <posy>0</posy> + <width>400</width> + <height>40</height> + <font>font12</font> + <align>center</align> + <aligny>center</aligny> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Date</info> + </control> + <control type="label"> + <posx>1018</posx> + <posy>0</posy> + <width>170</width> + <height>40</height> + <font>font12</font> + <align>center</align> + <aligny>center</aligny> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Comment</info> + </control> + </itemlayout> + <focusedlayout height="40"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>1100</width> + <height>41</height> + <texture border="5">MenuItemNF.png</texture> + </control> + <control type="image"> + <posx>220</posx> + <posy>0</posy> + <width>300</width> + <height>40</height> + <colordiffuse>33FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + <visible>!Control.HasFocus(14)</visible> + </control> + <control type="image"> + <posx>940</posx> + <posy>0</posy> + <width>155</width> + <height>40</height> + <colordiffuse>33FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + <visible>!Control.HasFocus(14)</visible> + </control> + <control type="image"> + <posx>220</posx> + <posy>0</posy> + <width>300</width> + <height>40</height> + <colordiffuse>88FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + <visible>Control.HasFocus(14)</visible> + </control> + <control type="image"> + <posx>940</posx> + <posy>0</posy> + <width>155</width> + <height>40</height> + <colordiffuse>88FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + <visible>Control.HasFocus(14)</visible> + </control> + <control type="image"> + <posx>0</posx> + <posy>8</posy> + <width>50</width> + <height>26</height> + <visible>!IsEmpty(ListItem.Date)</visible> + <texture border="1">$INFO[ListItem.Icon]</texture> + </control> + <control type="label"> + <posx>50</posx> + <posy>0</posy> + <width>150</width> + <height>40</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <selectedcolor>selected</selectedcolor> + <info>ListItem.ChannelName</info> + </control> + <control type="label"> + <posx>370</posx> + <posy>0</posy> + <width>290</width> + <height>40</height> + <font>font12</font> + <align>center</align> + <aligny>center</aligny> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Label</info> + </control> + <control type="label"> + <posx>730</posx> + <posy>0</posy> + <width>400</width> + <height>40</height> + <font>font12</font> + <align>center</align> + <aligny>center</aligny> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Date</info> + </control> + <control type="label"> + <posx>1018</posx> + <posy>0</posy> + <width>150</width> + <height>40</height> + <font>font12</font> + <align>center</align> + <aligny>center</aligny> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Comment</info> + </control> + </focusedlayout> + </control> + <control type="scrollbar" id="73"> + <posx>1105</posx> + <posy>50</posy> + <width>25</width> + <height>480</height> + <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground> + <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar> + <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus> + <textureslidernib>ScrollBarNib.png</textureslidernib> + <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus> + <onleft>11</onleft> + <onright>35</onright> + <showonepage>false</showonepage> + <orientation>vertical</orientation> + </control> + <control type="image"> + <description>separator image</description> + <posx>55</posx> + <posy>540</posy> + <width>1010</width> + <height>1</height> + <colordiffuse>88FFFFFF</colordiffuse> + <texture>separator2.png</texture> + </control> + <control type="label"> + <description>Next timer date</description> + <posx>55</posx> + <posy>545</posy> + <width>1010</width> + <height>30</height> + <font>font13</font> + <align>center</align> + <aligny>center</aligny> + <scroll>true</scroll> + <textcolor>white</textcolor> + <label>$INFO[PVR.NextTimer]</label> + <visible>PVR.HasTimer</visible> + </control> + </control> + <control type="label"> + <animation effect="slide" start="0,0" end="-90,0" time="0" condition="system.getbool(input.enablemouse)">Conditional</animation> + <description>Page Count Label</description> + <posx>40r</posx> + <posy>53r</posy> + <width>500</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <scroll>false</scroll> + <align>right</align> + <aligny>center</aligny> + <label>([COLOR=blue]$INFO[Container(14).NumItems][/COLOR]) $LOCALIZE[19040] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(14).CurrentPage]/$INFO[Container(14).NumPages][/COLOR])</label> + <include>Window_OpenClose_Animation</include> + </control> + </control> + </include> + + <include name="LiveTVSearchView"> + <control type="group"> + <description>TV Search group</description> + <visible>Control.IsVisible(17)</visible> + <include>VisibleFadeEffect</include> + <control type="group"> + <posx>80</posx> + <posy>60</posy> + <control type="label"> + <description>Channel label</description> + <posx>0</posx> + <posy>20</posy> + <width>250</width> + <height>20</height> + <font>font13_title</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <align>center</align> + <aligny>center</aligny> + <label>19148</label> + </control> + <control type="label"> + <description>Title</description> + <posx>290</posx> + <posy>20</posy> + <width>350</width> + <height>20</height> + <font>font13_title</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <align>left</align> + <aligny>center</aligny> + <label>369</label> + </control> + <control type="label"> + <description>Time label</description> + <posx>920</posx> + <posy>20</posy> + <width>300</width> + <height>20</height> + <font>font13_title</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <align>right</align> + <aligny>center</aligny> + <label>21820</label> + </control> + <control type="label"> + <description>Status header label</description> + <posx>960</posx> + <posy>20</posy> + <width>140</width> + <height>20</height> + <font>font13_title</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <align>center</align> + <aligny>center</aligny> + <label>126</label> + </control> + <control type="image"> + <description>separator image</description> + <posx>0</posx> + <posy>50</posy> + <width>1100</width> + <height>1</height> + <colordiffuse>88FFFFFF</colordiffuse> + <texture>separator2.png</texture> + </control> + <control type="list" id="17"> + <posx>0</posx> + <posy>55</posy> + <width>1100</width> + <height>520</height> + <onup>17</onup> + <ondown>17</ondown> + <onleft>36</onleft> + <onright>77</onright> + <pagecontrol>77</pagecontrol> + <scrolltime>200</scrolltime> + <itemlayout height="40"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>1100</width> + <height>41</height> + <texture border="5">MenuItemNF.png</texture> + </control> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>250</width> + <height>40</height> + <colordiffuse>33FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + </control> + <control type="image"> + <posx>960</posx> + <posy>0</posy> + <width>140</width> + <height>40</height> + <colordiffuse>33FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + </control> + <control type="image"> + <posx>10</posx> + <posy>5</posy> + <width>30</width> + <height>30</height> + <info>ListItem.Icon</info> + </control> + <control type="label"> + <posx>50</posx> + <posy>0</posy> + <width>190</width> + <height>35</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.ChannelName</info> + </control> + <control type="label"> + <posx>260</posx> + <posy>0</posy> + <width>650</width> + <height>35</height> + <font>font13</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Label</info> + </control> + <control type="label"> + <posx>950</posx> + <posy>0</posy> + <width>500</width> + <height>40</height> + <font>font12</font> + <align>right</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Date</info> + </control> + <control type="image"> + <posx>970</posx> + <posy>10</posy> + <width>30</width> + <height>20</height> + <texture>PVR-IsRecording.png</texture> + <visible>ListItem.IsRecording</visible> + </control> + <control type="label"> + <posx>1005</posx> + <posy>0</posy> + <width>80</width> + <height>40</height> + <font>font10</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <label>19043</label> + <visible>ListItem.IsRecording</visible> + </control> + <control type="image"> + <posx>970</posx> + <posy>10</posy> + <width>20</width> + <height>20</height> + <texture>PVR-HasTimer.png</texture> + <visible>ListItem.HasTimer + !ListItem.IsRecording</visible> + </control> + <control type="label"> + <posx>1000</posx> + <posy>0</posy> + <width>80</width> + <height>40</height> + <font>font10</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <label>31510</label> + <visible>ListItem.HasTimer</visible> + </control> + </itemlayout> + <focusedlayout height="40"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>1100</width> + <height>41</height> + <texture border="5">MenuItemNF.png</texture> + </control> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>250</width> + <height>40</height> + <colordiffuse>33FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + <visible>!Control.HasFocus(17)</visible> + </control> + <control type="image"> + <posx>960</posx> + <posy>0</posy> + <width>140</width> + <height>40</height> + <colordiffuse>33FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + <visible>!Control.HasFocus(17)</visible> + </control> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>250</width> + <height>40</height> + <colordiffuse>88FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + <visible>Control.HasFocus(17)</visible> + </control> + <control type="image"> + <posx>960</posx> + <posy>0</posy> + <width>140</width> + <height>40</height> + <colordiffuse>88FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + <visible>Control.HasFocus(17)</visible> + </control> + <control type="image"> + <posx>10</posx> + <posy>5</posy> + <width>30</width> + <height>30</height> + <info>ListItem.Icon</info> + </control> + <control type="label"> + <posx>50</posx> + <posy>0</posy> + <width>190</width> + <height>35</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.ChannelName</info> + </control> + <control type="label"> + <posx>260</posx> + <posy>0</posy> + <width>650</width> + <height>35</height> + <font>font13</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Label</info> + </control> + <control type="label"> + <posx>950</posx> + <posy>0</posy> + <width>500</width> + <height>40</height> + <font>font12</font> + <align>right</align> + <aligny>center</aligny> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Date</info> + </control> + <control type="image"> + <posx>970</posx> + <posy>10</posy> + <width>30</width> + <height>20</height> + <texture>PVR-IsRecording.png</texture> + <visible>ListItem.IsRecording</visible> + </control> + <control type="label"> + <posx>1005</posx> + <posy>0</posy> + <width>80</width> + <height>40</height> + <font>font10</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <label>19043</label> + <visible>ListItem.IsRecording</visible> + </control> + <control type="image"> + <posx>970</posx> + <posy>10</posy> + <width>20</width> + <height>20</height> + <texture>PVR-HasTimer.png</texture> + <visible>ListItem.HasTimer + !ListItem.IsRecording</visible> + </control> + <control type="label"> + <posx>1000</posx> + <posy>0</posy> + <width>80</width> + <height>40</height> + <font>font10</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <label>31510</label> + <visible>ListItem.HasTimer</visible> + </control> + </focusedlayout> + </control> + <control type="scrollbar" id="77"> + <posx>1105</posx> + <posy>50</posy> + <width>25</width> + <height>520</height> + <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground> + <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar> + <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus> + <textureslidernib>ScrollBarNib.png</textureslidernib> + <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus> + <onleft>16</onleft> + <onright>36</onright> + <showonepage>false</showonepage> + <orientation>vertical</orientation> + </control> + </control> + <control type="label"> + <animation effect="slide" start="0,0" end="-90,0" time="0" condition="system.getbool(input.enablemouse)">Conditional</animation> + <description>Page Count Label</description> + <posx>40r</posx> + <posy>53r</posy> + <width>500</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <scroll>false</scroll> + <align>right</align> + <aligny>center</aligny> + <label>([COLOR=blue]$INFO[Container(17).NumItems][/COLOR]) $LOCALIZE[31025] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(17).CurrentPage]/$INFO[Container(17).NumPages][/COLOR])</label> + </control> + </control> + </include> + + <include name="LiveTVGuideChannelView"> + <control type="group"> + <description>TV Guide Channel</description> + <visible>Control.IsVisible(15)</visible> + <include>VisibleFadeEffect</include> + <control type="group"> + <posx>80</posx> + <posy>60</posy> + <control type="label"> + <description>Date Time label</description> + <posx>0</posx> + <posy>20</posy> + <width>300</width> + <height>20</height> + <font>font13_title</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <align>center</align> + <aligny>center</aligny> + <label>21820</label> + </control> + <control type="label"> + <description>Title</description> + <posx>300</posx> + <posy>20</posy> + <width>600</width> + <height>20</height> + <font>font13_title</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <align>center</align> + <aligny>center</aligny> + <label>369</label> + </control> + <control type="label"> + <description>Status header label</description> + <posx>960</posx> + <posy>20</posy> + <width>140</width> + <height>20</height> + <font>font13_title</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <align>center</align> + <aligny>center</aligny> + <label>126</label> + </control> + <control type="image"> + <description>separator image</description> + <posx>0</posx> + <posy>50</posy> + <width>1100</width> + <height>1</height> + <colordiffuse>88FFFFFF</colordiffuse> + <texture>separator2.png</texture> + </control> + <control type="list" id="15"> + <posx>0</posx> + <posy>60</posy> + <width>1100</width> + <height>500</height> + <onup>15</onup> + <ondown>15</ondown> + <onleft>31</onleft> + <onright>75</onright> + <pagecontrol>75</pagecontrol> + <scrolltime>200</scrolltime> + <itemlayout height="40"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>1100</width> + <height>41</height> + <texture border="5">MenuItemNF.png</texture> + </control> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>300</width> + <height>40</height> + <colordiffuse>33FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + </control> + <control type="image"> + <posx>960</posx> + <posy>0</posy> + <width>140</width> + <height>40</height> + <colordiffuse>33FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + </control> + <control type="label"> + <posx>150</posx> + <posy>0</posy> + <width>280</width> + <height>40</height> + <font>font12</font> + <align>center</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Label2</info> + </control> + <control type="label"> + <posx>310</posx> + <posy>0</posy> + <width>640</width> + <height>40</height> + <font>font13</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Label</info> + </control> + <control type="image"> + <posx>970</posx> + <posy>10</posy> + <width>30</width> + <height>20</height> + <texture>PVR-IsRecording.png</texture> + <visible>ListItem.IsRecording</visible> + </control> + <control type="label"> + <posx>1005</posx> + <posy>0</posy> + <width>80</width> + <height>40</height> + <font>font10</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <label>19043</label> + <visible>ListItem.IsRecording</visible> + </control> + <control type="image"> + <posx>970</posx> + <posy>10</posy> + <width>20</width> + <height>20</height> + <texture>PVR-HasTimer.png</texture> + <visible>ListItem.HasTimer + !ListItem.IsRecording</visible> + </control> + <control type="label"> + <posx>1000</posx> + <posy>0</posy> + <width>80</width> + <height>40</height> + <font>font10</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <label>31510</label> + <visible>ListItem.HasTimer</visible> + </control> + </itemlayout> + <focusedlayout height="100"> + <control type="image"> + <posx>0</posx> + <posy>1</posy> + <width>1100</width> + <height>98</height> + <colordiffuse>AAFFFFFF</colordiffuse> + <texture border="5">black-back2.png</texture> + </control> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>1100</width> + <height>101</height> + <texture border="5">MenuItemNF.png</texture> + </control> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>300</width> + <height>40</height> + <colordiffuse>AAFFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + <visible>!Control.HasFocus(15)</visible> + </control> + <control type="image"> + <posx>960</posx> + <posy>0</posy> + <width>140</width> + <height>40</height> + <colordiffuse>AAFFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + <visible>!Control.HasFocus(15)</visible> + </control> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>300</width> + <height>40</height> + <colordiffuse>88FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + <visible>Control.HasFocus(15)</visible> + </control> + <control type="image"> + <posx>960</posx> + <posy>0</posy> + <width>140</width> + <height>40</height> + <colordiffuse>88FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + <visible>Control.HasFocus(15)</visible> + </control> + <control type="label"> + <posx>150</posx> + <posy>0</posy> + <width>280</width> + <height>40</height> + <font>font12</font> + <align>center</align> + <aligny>center</aligny> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Label2</info> + </control> + <control type="label"> + <posx>310</posx> + <posy>0</posy> + <width>640</width> + <height>40</height> + <font>font13</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Label</info> + </control> + <control type="textbox"> + <description>Plot Value for TVShow</description> + <posx>50</posx> + <posy>40</posy> + <width>1000</width> + <height>60</height> + <font>font12</font> + <align>justify</align> + <textcolor>grey2</textcolor> + <shadowcolor>black</shadowcolor> + <pagecontrol>-</pagecontrol> + <label>$INFO[ListItem.Plot]</label> + </control> + <control type="image"> + <posx>970</posx> + <posy>10</posy> + <width>30</width> + <height>20</height> + <texture>PVR-IsRecording.png</texture> + <visible>ListItem.IsRecording</visible> + </control> + <control type="label"> + <posx>1005</posx> + <posy>0</posy> + <width>80</width> + <height>40</height> + <font>font10</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <label>19043</label> + <visible>ListItem.IsRecording</visible> + </control> + <control type="image"> + <posx>970</posx> + <posy>10</posy> + <width>20</width> + <height>20</height> + <texture>PVR-HasTimer.png</texture> + <visible>ListItem.HasTimer + !ListItem.IsRecording</visible> + </control> + <control type="label"> + <posx>1000</posx> + <posy>0</posy> + <width>80</width> + <height>40</height> + <font>font10</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <label>31510</label> + <visible>ListItem.HasTimer</visible> + </control> + </focusedlayout> + </control> + <control type="scrollbar" id="75"> + <posx>1105</posx> + <posy>60</posy> + <width>25</width> + <height>500</height> + <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground> + <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar> + <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus> + <textureslidernib>ScrollBarNib.png</textureslidernib> + <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus> + <onleft>15</onleft> + <onright>31</onright> + <showonepage>false</showonepage> + <orientation>vertical</orientation> + </control> + </control> + <control type="label"> + <animation effect="slide" start="0,0" end="-90,0" time="0" condition="system.getbool(input.enablemouse)">Conditional</animation> + <description>Page Count Label</description> + <posx>40r</posx> + <posy>53r</posy> + <width>500</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <scroll>false</scroll> + <align>right</align> + <aligny>center</aligny> + <label>([COLOR=blue]$INFO[Container(15).NumItems][/COLOR]) $LOCALIZE[31025] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(15).CurrentPage]/$INFO[Container(15).NumPages][/COLOR])</label> + </control> + </control> + </include> + + <include name="LiveTVGuideNowNextView"> + <control type="group"> + <description>TV Guide Now/Next</description> + <visible>Control.IsVisible(16)</visible> + <include>VisibleFadeEffect</include> + <control type="group"> + <posx>80</posx> + <posy>60</posy> + <control type="label"> + <description>Time label</description> + <posx>0</posx> + <posy>20</posy> + <width>100</width> + <height>20</height> + <font>font13_title</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <align>center</align> + <aligny>center</aligny> + <label>555</label> + </control> + <control type="label"> + <description>Channel label</description> + <posx>100</posx> + <posy>20</posy> + <width>250</width> + <height>20</height> + <font>font13_title</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <align>center</align> + <aligny>center</aligny> + <label>19148</label> + </control> + <control type="label"> + <description>Title</description> + <posx>350</posx> + <posy>20</posy> + <width>550</width> + <height>20</height> + <font>font13_title</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <align>center</align> + <aligny>center</aligny> + <label>369</label> + </control> + <control type="label"> + <description>Status header label</description> + <posx>960</posx> + <posy>20</posy> + <width>140</width> + <height>20</height> + <font>font13_title</font> + <textcolor>white</textcolor> + <shadowcolor>black</shadowcolor> + <align>center</align> + <aligny>center</aligny> + <label>126</label> + </control> + <control type="image"> + <description>separator image</description> + <posx>0</posx> + <posy>50</posy> + <width>1100</width> + <height>1</height> + <colordiffuse>88FFFFFF</colordiffuse> + <texture>separator2.png</texture> + </control> + <control type="list" id="16"> + <posx>0</posx> + <posy>60</posy> + <width>1100</width> + <height>500</height> + <onup>16</onup> + <ondown>16</ondown> + <onleft>31</onleft> + <onright>76</onright> + <pagecontrol>76</pagecontrol> + <scrolltime>200</scrolltime> + <itemlayout height="40"> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>1100</width> + <height>41</height> + <texture border="5">MenuItemNF.png</texture> + </control> + <control type="image"> + <posx>100</posx> + <posy>0</posy> + <width>250</width> + <height>40</height> + <colordiffuse>33FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + </control> + <control type="image"> + <posx>960</posx> + <posy>0</posy> + <width>140</width> + <height>40</height> + <colordiffuse>33FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + </control> + <control type="label"> + <posx>50</posx> + <posy>0</posy> + <width>100</width> + <height>40</height> + <font>font12</font> + <align>center</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.StartTime</info> + </control> + <control type="image"> + <posx>110</posx> + <posy>5</posy> + <width>30</width> + <height>30</height> + <info>ListItem.Icon</info> + </control> + <control type="label"> + <posx>150</posx> + <posy>0</posy> + <width>190</width> + <height>35</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.ChannelName</info> + </control> + <control type="label"> + <posx>360</posx> + <posy>0</posy> + <width>590</width> + <height>35</height> + <font>font13</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Label</info> + </control> + <control type="image"> + <posx>970</posx> + <posy>10</posy> + <width>30</width> + <height>20</height> + <texture>PVR-IsRecording.png</texture> + <visible>ListItem.IsRecording</visible> + </control> + <control type="label"> + <posx>1005</posx> + <posy>0</posy> + <width>80</width> + <height>40</height> + <font>font10</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <label>19043</label> + <visible>ListItem.IsRecording</visible> + </control> + <control type="image"> + <posx>970</posx> + <posy>10</posy> + <width>20</width> + <height>20</height> + <texture>PVR-HasTimer.png</texture> + <visible>ListItem.HasTimer + !ListItem.IsRecording</visible> + </control> + <control type="label"> + <posx>1000</posx> + <posy>0</posy> + <width>80</width> + <height>40</height> + <font>font10</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <label>31510</label> + <visible>ListItem.HasTimer</visible> + </control> + </itemlayout> + <focusedlayout height="100"> + <control type="image"> + <posx>0</posx> + <posy>1</posy> + <width>1100</width> + <height>98</height> + <colordiffuse>AAFFFFFF</colordiffuse> + <texture border="5">black-back2.png</texture> + </control> + <control type="image"> + <posx>0</posx> + <posy>0</posy> + <width>1100</width> + <height>100</height> + <texture border="5">MenuItemNF.png</texture> + </control> + <control type="image"> + <posx>100</posx> + <posy>0</posy> + <width>250</width> + <height>40</height> + <colordiffuse>AAFFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + <visible>!Control.HasFocus(16)</visible> + </control> + <control type="image"> + <posx>960</posx> + <posy>0</posy> + <width>140</width> + <height>40</height> + <colordiffuse>AAFFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + <visible>!Control.HasFocus(16)</visible> + </control> + <control type="image"> + <posx>100</posx> + <posy>0</posy> + <width>250</width> + <height>40</height> + <colordiffuse>88FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + <visible>Control.HasFocus(16)</visible> + </control> + <control type="image"> + <posx>960</posx> + <posy>0</posy> + <width>140</width> + <height>40</height> + <colordiffuse>88FFFFFF</colordiffuse> + <texture border="5">StackFO.png</texture> + <visible>Control.HasFocus(16)</visible> + </control> + <control type="label"> + <posx>50</posx> + <posy>0</posy> + <width>100</width> + <height>40</height> + <font>font12</font> + <align>center</align> + <aligny>center</aligny> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.StartTime</info> + </control> + <control type="image"> + <posx>110</posx> + <posy>5</posy> + <width>30</width> + <height>30</height> + <info>ListItem.Icon</info> + </control> + <control type="label"> + <posx>150</posx> + <posy>0</posy> + <width>190</width> + <height>35</height> + <font>font12</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.ChannelName</info> + </control> + <control type="label"> + <posx>360</posx> + <posy>0</posy> + <width>590</width> + <height>35</height> + <font>font13</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>white</textcolor> + <selectedcolor>selected</selectedcolor> + <info>ListItem.Label</info> + </control> + <control type="textbox"> + <description>Plot Value for TVShow</description> + <posx>50</posx> + <posy>40</posy> + <width>1000</width> + <height>60</height> + <font>font12</font> + <align>justify</align> + <textcolor>grey2</textcolor> + <shadowcolor>black</shadowcolor> + <pagecontrol>-</pagecontrol> + <label>$INFO[ListItem.Plot]</label> + </control> + <control type="image"> + <posx>970</posx> + <posy>10</posy> + <width>30</width> + <height>20</height> + <texture>PVR-IsRecording.png</texture> + <visible>ListItem.IsRecording</visible> + </control> + <control type="label"> + <posx>1005</posx> + <posy>0</posy> + <width>80</width> + <height>40</height> + <font>font10</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <label>19043</label> + <visible>ListItem.IsRecording</visible> + </control> + <control type="image"> + <posx>970</posx> + <posy>10</posy> + <width>20</width> + <height>20</height> + <texture>PVR-HasTimer.png</texture> + <visible>ListItem.HasTimer + !ListItem.IsRecording</visible> + </control> + <control type="label"> + <posx>1000</posx> + <posy>0</posy> + <width>80</width> + <height>40</height> + <font>font10</font> + <align>left</align> + <aligny>center</aligny> + <textcolor>grey2</textcolor> + <selectedcolor>selected</selectedcolor> + <label>31510</label> + <visible>ListItem.HasTimer</visible> + </control> + </focusedlayout> + </control> + <control type="scrollbar" id="76"> + <posx>1105</posx> + <posy>60</posy> + <width>25</width> + <height>500</height> + <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground> + <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar> + <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus> + <textureslidernib>ScrollBarNib.png</textureslidernib> + <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus> + <onleft>16</onleft> + <onright>31</onright> + <showonepage>false</showonepage> + <orientation>vertical</orientation> + </control> + </control> + <control type="label"> + <animation effect="slide" start="0,0" end="-90,0" time="0" condition="system.getbool(input.enablemouse)">Conditional</animation> + <description>Page Count Label</description> + <posx>40r</posx> + <posy>53r</posy> + <width>500</width> + <height>20</height> + <font>font12</font> + <textcolor>grey</textcolor> + <scroll>false</scroll> + <align>right</align> + <aligny>center</aligny> + <label>([COLOR=blue]$INFO[Container(16).NumItems][/COLOR]) $LOCALIZE[31025] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(16).CurrentPage]/$INFO[Container(16).NumPages][/COLOR])</label> + </control> + </control> + </include> +</includes>
\ No newline at end of file diff --git a/addons/skin.confluence/720p/defaults.xml b/addons/skin.confluence/720p/defaults.xml index ae5c683a64..512b94b181 100644 --- a/addons/skin.confluence/720p/defaults.xml +++ b/addons/skin.confluence/720p/defaults.xml @@ -125,6 +125,7 @@ <disabledcolor>grey3</disabledcolor> <textoffsetx>7</textoffsetx> <aligny>center</aligny> + <pulseonselect>no</pulseonselect> </default> <default type="selectbutton"> <posx>490</posx> diff --git a/addons/skin.confluence/720p/includes.xml b/addons/skin.confluence/720p/includes.xml index d7606ddd73..6fa3b0cedd 100644 --- a/addons/skin.confluence/720p/includes.xml +++ b/addons/skin.confluence/720p/includes.xml @@ -6,6 +6,7 @@ <include file="ViewsPictures.xml" /> <include file="ViewsAddonBrowser.xml" /> <include file="ViewsLiveTV.xml" /> + <include file="ViewsPVR.xml" /> <include file="IncludesCodecFlagging.xml" /> <include file="IncludesHomeRecentlyAdded.xml" /> <include file="IncludesHomeMenuItems.xml" /> @@ -28,7 +29,7 @@ <texture>black-back.png</texture> <animation effect="fade" time="400">Visible</animation> <animation effect="fade" time="200">Hidden</animation> - <visible>Window.IsActive(MovieInformation) | Window.IsActive(MusicInformation) | Window.IsActive(SongInformation) | Window.IsActive(FileBrowser) | Window.IsActive(TextViewer) | Window.IsActive(AddonSettings) | Window.IsActive(ContentSettings) | Window.IsActive(SelectDialog) | Window.IsActive(FileStackingDialog) | Window.IsActive(MediaSource) | Window.IsActive(PictureInfo) | Window.IsActive(PlayerControls) | Window.IsActive(VirtualKeyboard) | Window.IsActive(NumericInput) | Window.IsActive(ProfileSettings) | Window.IsActive(LockSettings) | Window.IsActive(SmartPlaylistEditor) | Window.IsActive(SmartPlaylistRule) | Window.IsActive(script-RSS_Editor-rssEditor.xml) | Window.IsActive(script-RSS_Editor-setEditor.xml) | Window.IsActive(AddonInformation) | Window.IsActive(Peripherals) | Window.IsActive(PeripheralSettings) | Window.IsActive(script-XBMC_Lyrics-main.xml)</visible> + <visible>Window.IsActive(MovieInformation) | Window.IsActive(MusicInformation) | Window.IsActive(SongInformation) | Window.IsActive(FileBrowser) | Window.IsActive(TextViewer) | Window.IsActive(AddonSettings) | Window.IsActive(ContentSettings) | Window.IsActive(SelectDialog) | Window.IsActive(FileStackingDialog) | Window.IsActive(MediaSource) | Window.IsActive(PictureInfo) | Window.IsActive(PlayerControls) | Window.IsActive(VirtualKeyboard) | Window.IsActive(NumericInput) | Window.IsActive(ProfileSettings) | Window.IsActive(LockSettings) | Window.IsActive(SmartPlaylistEditor) | Window.IsActive(SmartPlaylistRule) | Window.IsActive(script-RSS_Editor-rssEditor.xml) | Window.IsActive(script-RSS_Editor-setEditor.xml) | Window.IsActive(AddonInformation) | Window.IsActive(Peripherals) | Window.IsActive(PeripheralSettings) | Window.IsActive(script-XBMC_Lyrics-main.xml) | Window.IsActive(PVRChannelManager)</visible> </control> </include> <include name="WindowTitleCommons"> @@ -302,6 +303,19 @@ <shadowcolor>black</shadowcolor> <visible>Player.HasVideo + VideoPlayer.Content(MusicVideos)</visible> </control> + <control type="label"> + <posx>85</posx> + <posy>30r</posy> + <width>700</width> + <height>20</height> + <label>$INFO[VideoPlayer.ChannelName]$INFO[VideoPlayer.ChannelNumber, - ([COLOR=blue],[/COLOR])]</label> + <align>left</align> + <aligny>center</aligny> + <font>font12</font> + <textcolor>grey</textcolor> + <shadowcolor>black</shadowcolor> + <visible>Player.HasVideo + VideoPlayer.Content(LiveTV)</visible> + </control> </control> </include> <include name="CommonPageCount"> @@ -572,8 +586,74 @@ <description>Fake Button to fix Player Controls Navigation</description> <visible>false</visible> </control> + <control type="group" id="9006"> + <width>250</width> + <height>45</height> + <visible>VideoPlayer.Content(LiveTV)</visible> + <include>VisibleFadeEffect</include> + <control type="button" id="600"> + <posx>20</posx> + <posy>2</posy> + <width>39</width> + <height>39</height> + <label>-</label> + <texturefocus>OSDChannelUPFO.png</texturefocus> + <texturenofocus>OSDChannelUPNF.png</texturenofocus> + <onleft>50</onleft> + <onright>601</onright> + <onup>610</onup> + <ondown>611</ondown> + <onclick>XBMC.PlayerControl(Previous)</onclick> + </control> + <control type="button" id="601"> + <posx>60</posx> + <posy>2</posy> + <width>39</width> + <height>39</height> + <label>-</label> + <texturefocus>OSDChannelDownFO.png</texturefocus> + <texturenofocus>OSDChannelDownNF.png</texturenofocus> + <onleft>600</onleft> + <onright>603</onright> + <onup>610</onup> + <ondown>611</ondown> + <onclick>XBMC.PlayerControl(Next)</onclick> + </control> + <control type="button" id="603"> + <posx>100</posx> + <posy>2</posy> + <width>39</width> + <height>39</height> + <label>-</label> + <texturefocus>OSDStopFO.png</texturefocus> + <texturenofocus>OSDStopNF.png</texturenofocus> + <onleft>601</onleft> + <onright>604</onright> + <onup>610</onup> + <ondown>611</ondown> + <onclick>down</onclick> + <onclick>XBMC.PlayerControl(Stop)</onclick> + </control> + <control type="button" id="604"> + <posx>180</posx> + <posy>2</posy> + <width>39</width> + <height>39</height> + <label>-</label> + <texturefocus>OSDRecordOffFO.png</texturefocus> + <texturenofocus>OSDRecordOffNF.png</texturenofocus> + <onleft>603</onleft> + <onright>50</onright> + <onup>610</onup> + <ondown>611</ondown> + <onclick>XBMC.PlayerControl(record)</onclick> + <enable>Player.CanRecord</enable> + <animation effect="fade" start="100" end="30" time="100" condition="!Player.CanRecord">Conditional</animation> + </control> + </control> <control type="group" id="9005"> <visible>[Player.HasAudio | Player.HasVideo]</visible> + <visible>!VideoPlayer.Content(LiveTV)</visible> <include>VisibleFadeEffect</include> <width>250</width> <height>45</height> diff --git a/addons/skin.confluence/backgrounds/tv.jpg b/addons/skin.confluence/backgrounds/tv.jpg Binary files differnew file mode 100644 index 0000000000..9414690563 --- /dev/null +++ b/addons/skin.confluence/backgrounds/tv.jpg diff --git a/addons/skin.confluence/language/Dutch/strings.po b/addons/skin.confluence/language/Dutch/strings.po index edbb654da0..d567de4d13 100644 --- a/addons/skin.confluence/language/Dutch/strings.po +++ b/addons/skin.confluence/language/Dutch/strings.po @@ -567,6 +567,46 @@ msgctxt "#31421" msgid "Select your XBMC user Profile[CR]to login and continue" msgstr "Selecteer uw XBMC-gebruikersprofiel[CR]om in te loggen" +msgctxt "#31500" +msgid "Recording Timers" +msgstr "Geplande opnames" + +msgctxt "#31501" +msgid "Scheduled Time" +msgstr "Opname starttijd" + +msgctxt "#31502" +msgid "Live TV" +msgstr "Televisie" + +msgctxt "#31503" +msgid "Add Group" +msgstr "Groep toevoegen" + +msgctxt "#31504" +msgid "Rename Group" +msgstr "Groep hernoemen" + +msgctxt "#31505" +msgid "Delete Group" +msgstr "Groep verwijderen" + +msgctxt "#31506" +msgid "Available[CR]Groups" +msgstr "Beschikbare[CR]Groepen" + +msgctxt "#31509" +msgid "Channel Group" +msgstr "Groep" + +msgctxt "#31510" +msgid "Timer Set" +msgstr "Geplande opname ingesteld" + +msgctxt "#31511" +msgid "Channel Options" +msgstr "Kanaal opties" + msgctxt "#31900" msgid "Weather Maps" msgstr "Weer Kaarten" diff --git a/addons/skin.confluence/language/English/strings.po b/addons/skin.confluence/language/English/strings.po index 7e51873d12..a074d74351 100644 --- a/addons/skin.confluence/language/English/strings.po +++ b/addons/skin.confluence/language/English/strings.po @@ -513,17 +513,64 @@ msgid "[B]CONFIGURE ADD-ONS[/B][CR][CR]Manage your installed Add-ons · Browse f msgstr "" msgctxt "#31409" +msgid "[B]CONFIGURE TV SETTINGS[/B][CR][CR]Change fullscreen info · Manage EPG data settings" +msgstr "" + +msgctxt "#31410" msgid "[B]CONFIGURE SERVICE SETTINGS[/B][CR][CR]Setup control of XBMC via UPnP and HTTP · Configure file sharing[CR]Enable Zeroconf · Configure AirPlay" msgstr "" -#empty strings from id 31410 to 31420 +#empty strings from id 31411 to 31420 msgctxt "#31421" msgid "Select your XBMC user Profile[CR]to login and continue" msgstr "" +#PVR labels +#empty strings from id 31422 to 31499 + +msgctxt "#31500" +msgid "Recording Timers" +msgstr "" + +msgctxt "#31501" +msgid "Scheduled Time" +msgstr "" + +msgctxt "#31502" +msgid "Live TV" +msgstr "" + +msgctxt "#31503" +msgid "Add Group" +msgstr "" + +msgctxt "#31504" +msgid "Rename Group" +msgstr "" + +msgctxt "#31505" +msgid "Delete Group" +msgstr "" + +msgctxt "#31506" +msgid "Available[CR]Groups" +msgstr "" + +msgctxt "#31509" +msgid "Channel Group" +msgstr "" + +msgctxt "#31510" +msgid "Timer Set" +msgstr "" + +msgctxt "#31511" +msgid "Channel Options" +msgstr "" + #Weather plugin -#empty strings from id 31422 to 31899 +#empty strings from id 31512 to 31899 msgctxt "#31901" msgid "36 Hour Forecast" diff --git a/addons/skin.confluence/language/Finnish/strings.po b/addons/skin.confluence/language/Finnish/strings.po index 133c8685a8..a02ed9a5f9 100644 --- a/addons/skin.confluence/language/Finnish/strings.po +++ b/addons/skin.confluence/language/Finnish/strings.po @@ -558,6 +558,10 @@ msgid "[B]CONFIGURE ADD-ONS[/B][CR][CR]Manage your installed Add-ons · Browse f msgstr "[B]Muokkaa lisäosia[/B][CR][CR]Hallitse asennettuja lisäosia · Valitse ja asenna lisäosia xbmc.org:sta[CR]Muokkaa lisäosien asetuksia" msgctxt "#31409" +msgid "[B]CONFIGURE TV SETTINGS[/B][CR][CR]Change fullscreen info · Manage EPG data settings" +msgstr "[B]Muokkaa TV-asetuksia[/B][CR][CR]Muokkaa kokoruudun tietoja · Hallitse ohjelmaoppaan asetuksia" + +msgctxt "#31410" msgid "[B]CONFIGURE SERVICE SETTINGS[/B][CR][CR]Setup control of XBMC via UPnP and HTTP · Configure file sharing[CR]Enable Zeroconf · Configure AirPlay" msgstr "" @@ -565,6 +569,46 @@ msgctxt "#31421" msgid "Select your XBMC user Profile[CR]to login and continue" msgstr "Valitse XBMC-käyttäjäprofiili[CR]kirjautuaksesi sisään" +msgctxt "#31500" +msgid "Recording Timers" +msgstr "Nauhoitusajastukset" + +msgctxt "#31501" +msgid "Scheduled Time" +msgstr "Kellonaika" + +msgctxt "#31502" +msgid "Live TV" +msgstr "TV-lähetys" + +msgctxt "#31503" +msgid "Add Group" +msgstr "Lisää ryhmä" + +msgctxt "#31504" +msgid "Rename Group" +msgstr "Muuta ryhmän nimeä" + +msgctxt "#31505" +msgid "Delete Group" +msgstr "Poista ryhmä" + +msgctxt "#31506" +msgid "Available[CR]Groups" +msgstr "Saatavilla olevat[CR]ryhmät" + +msgctxt "#31509" +msgid "Channel Group" +msgstr "Kanavaryhmä" + +msgctxt "#31510" +msgid "Timer Set" +msgstr "Ajastettu" + +msgctxt "#31511" +msgid "Channel Options" +msgstr "Kanavan valinnat" + msgctxt "#31900" msgid "Weather Maps" msgstr "Sääkartat" diff --git a/addons/skin.confluence/media/OSDChannelDownFO.png b/addons/skin.confluence/media/OSDChannelDownFO.png Binary files differnew file mode 100644 index 0000000000..5116c327ba --- /dev/null +++ b/addons/skin.confluence/media/OSDChannelDownFO.png diff --git a/addons/skin.confluence/media/OSDChannelDownNF.png b/addons/skin.confluence/media/OSDChannelDownNF.png Binary files differnew file mode 100644 index 0000000000..6795c43328 --- /dev/null +++ b/addons/skin.confluence/media/OSDChannelDownNF.png diff --git a/addons/skin.confluence/media/OSDChannelListFO.png b/addons/skin.confluence/media/OSDChannelListFO.png Binary files differnew file mode 100644 index 0000000000..a08bc13a7d --- /dev/null +++ b/addons/skin.confluence/media/OSDChannelListFO.png diff --git a/addons/skin.confluence/media/OSDChannelListNF.png b/addons/skin.confluence/media/OSDChannelListNF.png Binary files differnew file mode 100644 index 0000000000..8339fdced4 --- /dev/null +++ b/addons/skin.confluence/media/OSDChannelListNF.png diff --git a/addons/skin.confluence/media/OSDChannelUPFO.png b/addons/skin.confluence/media/OSDChannelUPFO.png Binary files differnew file mode 100644 index 0000000000..a3e6dbad5f --- /dev/null +++ b/addons/skin.confluence/media/OSDChannelUPFO.png diff --git a/addons/skin.confluence/media/OSDChannelUPNF.png b/addons/skin.confluence/media/OSDChannelUPNF.png Binary files differnew file mode 100644 index 0000000000..47e6e331bc --- /dev/null +++ b/addons/skin.confluence/media/OSDChannelUPNF.png diff --git a/addons/skin.confluence/media/OSDRecordOff.png b/addons/skin.confluence/media/OSDRecordOff.png Binary files differdeleted file mode 100644 index cb73c77605..0000000000 --- a/addons/skin.confluence/media/OSDRecordOff.png +++ /dev/null diff --git a/addons/skin.confluence/media/OSDRecordFO.png b/addons/skin.confluence/media/OSDRecordOffFO.png Binary files differindex fd2bb18fa9..fd2bb18fa9 100644 --- a/addons/skin.confluence/media/OSDRecordFO.png +++ b/addons/skin.confluence/media/OSDRecordOffFO.png diff --git a/addons/skin.confluence/media/OSDRecordNF.png b/addons/skin.confluence/media/OSDRecordOffNF.png Binary files differindex db3d5753e1..db3d5753e1 100644 --- a/addons/skin.confluence/media/OSDRecordNF.png +++ b/addons/skin.confluence/media/OSDRecordOffNF.png diff --git a/addons/skin.confluence/media/OSDRecord2.png b/addons/skin.confluence/media/OSDRecordOnFO.png Binary files differindex cb4fcf9edf..cb4fcf9edf 100644 --- a/addons/skin.confluence/media/OSDRecord2.png +++ b/addons/skin.confluence/media/OSDRecordOnFO.png diff --git a/addons/skin.confluence/media/OSDRecordOnNF.png b/addons/skin.confluence/media/OSDRecordOnNF.png Binary files differnew file mode 100644 index 0000000000..b3358180db --- /dev/null +++ b/addons/skin.confluence/media/OSDRecordOnNF.png diff --git a/addons/skin.confluence/media/OSDTeleTextFO.png b/addons/skin.confluence/media/OSDTeleTextFO.png Binary files differnew file mode 100644 index 0000000000..53eb5762fd --- /dev/null +++ b/addons/skin.confluence/media/OSDTeleTextFO.png diff --git a/addons/skin.confluence/media/OSDTeleTextNF.png b/addons/skin.confluence/media/OSDTeleTextNF.png Binary files differnew file mode 100644 index 0000000000..111c0685b2 --- /dev/null +++ b/addons/skin.confluence/media/OSDTeleTextNF.png diff --git a/addons/skin.confluence/media/OSDepgFO.png b/addons/skin.confluence/media/OSDepgFO.png Binary files differnew file mode 100644 index 0000000000..141f7adfc7 --- /dev/null +++ b/addons/skin.confluence/media/OSDepgFO.png diff --git a/addons/skin.confluence/media/OSDepgNF.png b/addons/skin.confluence/media/OSDepgNF.png Binary files differnew file mode 100644 index 0000000000..cf9a86b656 --- /dev/null +++ b/addons/skin.confluence/media/OSDepgNF.png diff --git a/addons/skin.confluence/media/PVR-HasTimer.png b/addons/skin.confluence/media/PVR-HasTimer.png Binary files differnew file mode 100644 index 0000000000..99c2ee9b44 --- /dev/null +++ b/addons/skin.confluence/media/PVR-HasTimer.png diff --git a/addons/skin.confluence/media/PVR-IsRecording.png b/addons/skin.confluence/media/PVR-IsRecording.png Binary files differnew file mode 100644 index 0000000000..e64b346327 --- /dev/null +++ b/addons/skin.confluence/media/PVR-IsRecording.png diff --git a/addons/skin.confluence/media/StackNF.png b/addons/skin.confluence/media/StackNF.png Binary files differindex 17e5052671..0cbcd832fc 100644 --- a/addons/skin.confluence/media/StackNF.png +++ b/addons/skin.confluence/media/StackNF.png diff --git a/addons/skin.confluence/media/epg-genres/0.png b/addons/skin.confluence/media/epg-genres/0.png Binary files differnew file mode 100644 index 0000000000..80c6192880 --- /dev/null +++ b/addons/skin.confluence/media/epg-genres/0.png diff --git a/addons/skin.confluence/media/epg-genres/112.png b/addons/skin.confluence/media/epg-genres/112.png Binary files differnew file mode 100644 index 0000000000..7701fc6fb8 --- /dev/null +++ b/addons/skin.confluence/media/epg-genres/112.png diff --git a/addons/skin.confluence/media/epg-genres/128.png b/addons/skin.confluence/media/epg-genres/128.png Binary files differnew file mode 100644 index 0000000000..3071d4e6af --- /dev/null +++ b/addons/skin.confluence/media/epg-genres/128.png diff --git a/addons/skin.confluence/media/epg-genres/144.png b/addons/skin.confluence/media/epg-genres/144.png Binary files differnew file mode 100644 index 0000000000..a9325a699e --- /dev/null +++ b/addons/skin.confluence/media/epg-genres/144.png diff --git a/addons/skin.confluence/media/epg-genres/16.png b/addons/skin.confluence/media/epg-genres/16.png Binary files differnew file mode 100644 index 0000000000..ffaae9eff4 --- /dev/null +++ b/addons/skin.confluence/media/epg-genres/16.png diff --git a/addons/skin.confluence/media/epg-genres/160.png b/addons/skin.confluence/media/epg-genres/160.png Binary files differnew file mode 100644 index 0000000000..8d825ab34b --- /dev/null +++ b/addons/skin.confluence/media/epg-genres/160.png diff --git a/addons/skin.confluence/media/epg-genres/176.png b/addons/skin.confluence/media/epg-genres/176.png Binary files differnew file mode 100644 index 0000000000..7ae6320ab2 --- /dev/null +++ b/addons/skin.confluence/media/epg-genres/176.png diff --git a/addons/skin.confluence/media/epg-genres/192.png b/addons/skin.confluence/media/epg-genres/192.png Binary files differnew file mode 100644 index 0000000000..80c6192880 --- /dev/null +++ b/addons/skin.confluence/media/epg-genres/192.png diff --git a/addons/skin.confluence/media/epg-genres/208.png b/addons/skin.confluence/media/epg-genres/208.png Binary files differnew file mode 100644 index 0000000000..80c6192880 --- /dev/null +++ b/addons/skin.confluence/media/epg-genres/208.png diff --git a/addons/skin.confluence/media/epg-genres/224.png b/addons/skin.confluence/media/epg-genres/224.png Binary files differnew file mode 100644 index 0000000000..80c6192880 --- /dev/null +++ b/addons/skin.confluence/media/epg-genres/224.png diff --git a/addons/skin.confluence/media/epg-genres/240.png b/addons/skin.confluence/media/epg-genres/240.png Binary files differnew file mode 100644 index 0000000000..80c6192880 --- /dev/null +++ b/addons/skin.confluence/media/epg-genres/240.png diff --git a/addons/skin.confluence/media/epg-genres/32.png b/addons/skin.confluence/media/epg-genres/32.png Binary files differnew file mode 100644 index 0000000000..be2bc8ee5f --- /dev/null +++ b/addons/skin.confluence/media/epg-genres/32.png diff --git a/addons/skin.confluence/media/epg-genres/48.png b/addons/skin.confluence/media/epg-genres/48.png Binary files differnew file mode 100644 index 0000000000..57b4dee631 --- /dev/null +++ b/addons/skin.confluence/media/epg-genres/48.png diff --git a/addons/skin.confluence/media/epg-genres/64.png b/addons/skin.confluence/media/epg-genres/64.png Binary files differnew file mode 100644 index 0000000000..d85eb051b9 --- /dev/null +++ b/addons/skin.confluence/media/epg-genres/64.png diff --git a/addons/skin.confluence/media/epg-genres/80.png b/addons/skin.confluence/media/epg-genres/80.png Binary files differnew file mode 100644 index 0000000000..cdd6da3b1d --- /dev/null +++ b/addons/skin.confluence/media/epg-genres/80.png diff --git a/addons/skin.confluence/media/epg-genres/96.png b/addons/skin.confluence/media/epg-genres/96.png Binary files differnew file mode 100644 index 0000000000..ba92ae36d7 --- /dev/null +++ b/addons/skin.confluence/media/epg-genres/96.png diff --git a/addons/skin.confluence/media/epg-genres/genre-numbers.txt b/addons/skin.confluence/media/epg-genres/genre-numbers.txt new file mode 100644 index 0000000000..031732b9d7 --- /dev/null +++ b/addons/skin.confluence/media/epg-genres/genre-numbers.txt @@ -0,0 +1,18 @@ +Genre Numbers set internally by XBMC + +0 = other/unknown +16 = moviedrama +32 = news +48 = show +64 = sports +80 = child +96 = music +112 = arts +128 = social +144 = science +160 = hobby +176 = special +192 = other/unknown +208 = other/unknown +224 = other/unknown +240 = other/unknown diff --git a/addons/skin.confluence/media/gradient.png b/addons/skin.confluence/media/gradient.png Binary files differnew file mode 100644 index 0000000000..7db158f2ea --- /dev/null +++ b/addons/skin.confluence/media/gradient.png diff --git a/addons/skin.confluence/media/home-power-inhibit-FO.png b/addons/skin.confluence/media/home-power-inhibit-FO.png Binary files differnew file mode 100644 index 0000000000..3656ffa5de --- /dev/null +++ b/addons/skin.confluence/media/home-power-inhibit-FO.png diff --git a/addons/skin.confluence/media/home-power-inhibit.png b/addons/skin.confluence/media/home-power-inhibit.png Binary files differnew file mode 100644 index 0000000000..b90d844202 --- /dev/null +++ b/addons/skin.confluence/media/home-power-inhibit.png diff --git a/configure.in b/configure.in index 81425ea2b0..4c4b1252d1 100644 --- a/configure.in +++ b/configure.in @@ -2162,6 +2162,9 @@ OUTPUT_FILES="Makefile \ xbmc/visualizations/OpenGLSpectrum/Makefile \ xbmc/visualizations/WaveForm/Makefile \ xbmc/visualizations/iTunes/Makefile \ + lib/addons/library.xbmc.addon/Makefile \ + lib/addons/library.xbmc.gui/Makefile \ + lib/addons/library.xbmc.pvr/Makefile \ xbmc/visualizations/EGLHelpers/Makefile \ tools/Linux/xbmc.sh \ tools/Linux/xbmc-standalone.sh \ diff --git a/docs/README.pvr b/docs/README.pvr new file mode 100644 index 0000000000..ea6bfa196a --- /dev/null +++ b/docs/README.pvr @@ -0,0 +1,4 @@ +PVR add-ons are not included in XBMC's tree. + +They can be found here: +https://github.com/opdenkamp/xbmc-pvr-addons diff --git a/language/English/strings.po b/language/English/strings.po index 12ad2c7cd3..8d1c6c2b5d 100644 --- a/language/English/strings.po +++ b/language/English/strings.po @@ -3962,7 +3962,15 @@ msgctxt "#13016" msgid "Power off System" msgstr "" -#empty strings from id 13017 to 13019 +msgctxt "#13017" +msgid "Inhibit idle shutdown" +msgstr "" + +msgctxt "#13018" +msgid "Allow idle shutdown" +msgstr "" + +#empty string with id 13019 msgctxt "#13020" msgid "Is another session active, perhaps over ssh?" @@ -5996,7 +6004,11 @@ msgctxt "#16324" msgid "Software Blend" msgstr "" -#empty strings from id 16325 to 16399 +msgctxt "#16325" +msgid "Auto - ION Optimized" +msgstr "" + +#empty strings from id 16326 to 16399 msgctxt "#16400" msgid "Post-processing" @@ -6008,13 +6020,1471 @@ msgctxt "#17500" msgid "Display sleep timeout" msgstr "" -#empty strings from id 17501 to 18999 +#empty strings from id 17501 to 17996 + +msgctxt "#17997" +msgid "%i MByte" +msgstr "" + +msgctxt "#17998" +msgid "%i hours" +msgstr "" + +msgctxt "#17999" +msgid "%i days" +msgstr "" + +#empty strings from id 18000 to 18999 msgctxt "#19000" msgid "Switch to channel" msgstr "" -#empty strings from id 19001 to 19999 +msgctxt "#19001" +msgid "Separate the search words by using AND, OR and/or NOT." +msgstr "" + +msgctxt "#19002" +msgid "or use phrases to find an exact match, like \"The wizard of Oz\"." +msgstr "" + +msgctxt "#19003" +msgid "Find similar programme" +msgstr "" + +msgctxt "#19004" +msgid "Importing EPG from clients" +msgstr "" + +msgctxt "#19005" +msgid "PVR stream information" +msgstr "" + +msgctxt "#19006" +msgid "Receiving device" +msgstr "" + +msgctxt "#19007" +msgid "Device status" +msgstr "" + +msgctxt "#19008" +msgid "Signal quality" +msgstr "" + +msgctxt "#19009" +msgid "SNR" +msgstr "" + +msgctxt "#19010" +msgid "BER" +msgstr "" + +msgctxt "#19011" +msgid "UNC" +msgstr "" + +msgctxt "#19012" +msgid "PVR Backend" +msgstr "" + +msgctxt "#19013" +msgid "Free to air" +msgstr "" + +msgctxt "#19014" +msgid "Fixed" +msgstr "" + +msgctxt "#19015" +msgid "Encryption" +msgstr "" + +msgctxt "#19016" +msgid "PVR Backend %i - %s" +msgstr "" + +msgctxt "#19017" +msgid "TV recordings" +msgstr "" + +msgctxt "#19018" +msgid "Default folder for PVR thumbnails" +msgstr "" + +msgctxt "#19019" +msgid "Channels" +msgstr "" + +msgctxt "#19020" +msgid "TV" +msgstr "" + +msgctxt "#19021" +msgid "Radio" +msgstr "" + +msgctxt "#19022" +msgid "Hidden" +msgstr "" + +msgctxt "#19023" +msgid "TV channels" +msgstr "" + +msgctxt "#19024" +msgid "Radio channels" +msgstr "" + +msgctxt "#19025" +msgid "Upcoming recordings" +msgstr "" + +msgctxt "#19026" +msgid "Add timer..." +msgstr "" + +msgctxt "#19027" +msgid "No search results" +msgstr "" + +msgctxt "#19028" +msgid "No EPG entries" +msgstr "" + +msgctxt "#19029" +msgid "Channel" +msgstr "" + +msgctxt "#19030" +msgid "Now" +msgstr "" + +msgctxt "#19031" +msgid "Next" +msgstr "" + +msgctxt "#19032" +msgid "Timeline" +msgstr "" + +msgctxt "#19033" +msgid "Information" +msgstr "" + +msgctxt "#19034" +msgid "Already started recording on this channel" +msgstr "" + +msgctxt "#19035" +msgid "This channel cannot be played. Check the log for details." +msgstr "" + +msgctxt "#19036" +msgid "This recording cannot be played. Check the log for details." +msgstr "" + +msgctxt "#19037" +msgid "Show signal quality" +msgstr "" + +msgctxt "#19038" +msgid "Not supported by the PVR backend." +msgstr "" + +msgctxt "#19039" +msgid "Are you sure you want to hide this channel?" +msgstr "" + +msgctxt "#19040" +msgid "Timer" +msgstr "" + +msgctxt "#19041" +msgid "Are you sure you want to rename this recording?" +msgstr "" + +msgctxt "#19042" +msgid "Are you sure you want to rename this timer?" +msgstr "" + +msgctxt "#19043" +msgid "Recording" +msgstr "" + +msgctxt "#19044" +msgid "Please check your configuration or check the log for details." +msgstr "" + +msgctxt "#19045" +msgid "No PVR clients have been started yet. Wait for the PVR clients to start up or check the log for details." +msgstr "" + +msgctxt "#19046" +msgid "New channel" +msgstr "" + +msgctxt "#19047" +msgid "Programme info" +msgstr "" + +msgctxt "#19048" +msgid "Group management" +msgstr "" + +msgctxt "#19049" +msgid "Show channel" +msgstr "" + +msgctxt "#19050" +msgid "Show visible channels" +msgstr "" + +msgctxt "#19051" +msgid "Show hidden channels" +msgstr "" + +msgctxt "#19052" +msgid "Move channel to:" +msgstr "" + +msgctxt "#19053" +msgid "Recording information" +msgstr "" + +msgctxt "#19054" +msgid "Hide channel" +msgstr "" + +msgctxt "#19055" +msgid "No information available" +msgstr "" + +msgctxt "#19056" +msgid "New timer" +msgstr "" + +msgctxt "#19057" +msgid "Edit timer" +msgstr "" + +msgctxt "#19058" +msgid "Timer enabled" +msgstr "" + +msgctxt "#19059" +msgid "Stop recording" +msgstr "" + +msgctxt "#19060" +msgid "Delete timer" +msgstr "" + +msgctxt "#19061" +msgid "Add timer" +msgstr "" + +msgctxt "#19062" +msgid "Sort by: Channel" +msgstr "" + +msgctxt "#19063" +msgid "Go to begin" +msgstr "" + +msgctxt "#19064" +msgid "Go to end" +msgstr "" + +msgctxt "#19065" +msgid "Default EPG window" +msgstr "" + +#empty string with id 19066 + +msgctxt "#19067" +msgid "This event is already being recorded." +msgstr "" + +msgctxt "#19068" +msgid "This recording could not be deleted. Check the log for details." +msgstr "" + +msgctxt "#19069" +msgid "EPG" +msgstr "" + +#empty string with id 19070 + +msgctxt "#19071" +msgid "EPG update interval" +msgstr "" + +msgctxt "#19072" +msgid "Do not store the EPG in the database" +msgstr "" + +msgctxt "#19073" +msgid "Delay channel switch" +msgstr "" + +msgctxt "#19074" +msgid "Active:" +msgstr "" + +msgctxt "#19075" +msgid "Name:" +msgstr "" + +msgctxt "#19076" +msgid "Folder:" +msgstr "" + +msgctxt "#19077" +msgid "Radio:" +msgstr "" + +msgctxt "#19078" +msgid "Channel:" +msgstr "" + +msgctxt "#19079" +msgid "Day:" +msgstr "" + +msgctxt "#19080" +msgid "Begin:" +msgstr "" + +msgctxt "#19081" +msgid "End:" +msgstr "" + +msgctxt "#19082" +msgid "Priority:" +msgstr "" + +msgctxt "#19083" +msgid "Lifetime (days):" +msgstr "" + +msgctxt "#19084" +msgid "First day:" +msgstr "" + +msgctxt "#19085" +msgid "Unknown channel %u" +msgstr "" + +msgctxt "#19086" +msgid "Mo-__-__-__-__-__-__" +msgstr "" + +msgctxt "#19087" +msgid "__-Tu-__-__-__-__-__" +msgstr "" + +msgctxt "#19088" +msgid "__-__-We-__-__-__-__" +msgstr "" + +msgctxt "#19089" +msgid "__-__-__-Th-__-__-__" +msgstr "" + +msgctxt "#19090" +msgid "__-__-__-__-Fr-__-__" +msgstr "" + +msgctxt "#19091" +msgid "__-__-__-__-__-Sa-__" +msgstr "" + +msgctxt "#19092" +msgid "__-__-__-__-__-__-Su" +msgstr "" + +msgctxt "#19093" +msgid "Mo-Tu-We-Th-Fr-__-__" +msgstr "" + +msgctxt "#19094" +msgid "Mo-Tu-We-Th-Fr-Sa-__" +msgstr "" + +msgctxt "#19095" +msgid "Mo-Tu-We-Th-Fr-Sa-Su" +msgstr "" + +msgctxt "#19096" +msgid "__-__-__-__-__-Sa-Su" +msgstr "" + +msgctxt "#19097" +msgid "Enter the name for the recording" +msgstr "" + +msgctxt "#19098" +msgid "Warning" +msgstr "" + +#empty strings from id 19099 to 19100 + +msgctxt "#19102" +msgid "Please switch to another channel." +msgstr "" + +msgctxt "#19103" +msgid "Scan for missing icons" +msgstr "" + +msgctxt "#19104" +msgid "Enter the name of the folder for the recording" +msgstr "" + +#empty string with id 19105 + +msgctxt "#19106" +msgid "Next timer on" +msgstr "" + +msgctxt "#19107" +msgid "at" +msgstr "" + +#empty string with id 19108 + +msgctxt "#19109" +msgid "Couldn't save timer. Check the log for details." +msgstr "" + +msgctxt "#19110" +msgid "An unexpected error occurred. Try again later or check the log for details." +msgstr "" + +msgctxt "#19111" +msgid "PVR backend error. Check the log for details." +msgstr "" + +#empty strings from id 19112 to 19113 + +msgctxt "#19114" +msgid "Version" +msgstr "" + +msgctxt "#19115" +msgid "Address" +msgstr "" + +msgctxt "#19116" +msgid "Disksize" +msgstr "" + +msgctxt "#19117" +msgid "Search for channels" +msgstr "" + +msgctxt "#19118" +msgid "Cannot use PVR functions while searching." +msgstr "" + +msgctxt "#19119" +msgid "On which server you want to search?" +msgstr "" + +msgctxt "#19120" +msgid "Client number" +msgstr "" + +msgctxt "#19121" +msgid "Avoid repeats" +msgstr "" + +msgctxt "#19122" +msgid "This timer is still recording. Are you sure you want to delete this timer?" +msgstr "" + +msgctxt "#19123" +msgid "Free to air channels only" +msgstr "" + +msgctxt "#19124" +msgid "Ignore present timers" +msgstr "" + +msgctxt "#19125" +msgid "Ignore present recordings" +msgstr "" + +msgctxt "#19126" +msgid "Start time" +msgstr "" + +msgctxt "#19127" +msgid "End time" +msgstr "" + +msgctxt "#19128" +msgid "Start date" +msgstr "" + +msgctxt "#19129" +msgid "End date" +msgstr "" + +msgctxt "#19130" +msgid "Minimum duration" +msgstr "" + +msgctxt "#19131" +msgid "Maximum duration" +msgstr "" + +msgctxt "#19132" +msgid "Include unknown genres" +msgstr "" + +msgctxt "#19133" +msgid "Search string" +msgstr "" + +msgctxt "#19134" +msgid "Include description" +msgstr "" + +msgctxt "#19135" +msgid "Case sensitive" +msgstr "" + +msgctxt "#19136" +msgid "Channel unavailable" +msgstr "" + +msgctxt "#19137" +msgid "No groups defined" +msgstr "" + +msgctxt "#19138" +msgid "Please create a group first" +msgstr "" + +msgctxt "#19139" +msgid "Name of the new group" +msgstr "" + +msgctxt "#19141" +msgid "Group" +msgstr "" + +msgctxt "#19142" +msgid "Search guide" +msgstr "" + +msgctxt "#19143" +msgid "Group management" +msgstr "" + +msgctxt "#19144" +msgid "No groups defined" +msgstr "" + +msgctxt "#19145" +msgid "Grouped" +msgstr "" + +msgctxt "#19146" +msgid "Groups" +msgstr "" + +msgctxt "#19147" +msgid "The PVR backend does not support this action. Check the log for details." +msgstr "" + +msgctxt "#19148" +msgid "Channel" +msgstr "" + +msgctxt "#19149" +msgid "Mo" +msgstr "" + +msgctxt "#19150" +msgid "Tu" +msgstr "" + +msgctxt "#19151" +msgid "We" +msgstr "" + +msgctxt "#19152" +msgid "Th" +msgstr "" + +msgctxt "#19153" +msgid "Fr" +msgstr "" + +msgctxt "#19154" +msgid "Sa" +msgstr "" + +msgctxt "#19155" +msgid "Su" +msgstr "" + +msgctxt "#19156" +msgid "from" +msgstr "" + +msgctxt "#19157" +msgid "Next recording" +msgstr "" + +msgctxt "#19158" +msgid "Currently recording" +msgstr "" + +msgctxt "#19159" +msgid "from" +msgstr "" + +msgctxt "#19160" +msgid "to" +msgstr "" + +msgctxt "#19161" +msgid "On" +msgstr "" + +msgctxt "#19162" +msgid "Recording active" +msgstr "" + +msgctxt "#19163" +msgid "Recordings" +msgstr "" + +msgctxt "#19164" +msgid "Cannot start recording. Check the log for details." +msgstr "" + +msgctxt "#19165" +msgid "Switch" +msgstr "" + +msgctxt "#19166" +msgid "PVR information" +msgstr "" + +msgctxt "#19167" +msgid "Scan for missing icons" +msgstr "" + +msgctxt "#19168" +msgid "Switch channel without pressing OK" +msgstr "" + +msgctxt "#19169" +msgid "Hide video information box" +msgstr "" + +msgctxt "#19170" +msgid "Timeout when starting playback" +msgstr "" + +msgctxt "#19171" +msgid "Start playback minimized" +msgstr "" + +msgctxt "#19172" +msgid "Instant recording duration" +msgstr "" + +msgctxt "#19173" +msgid "Default recording priority" +msgstr "" + +msgctxt "#19174" +msgid "Default recording lifetime" +msgstr "" + +msgctxt "#19175" +msgid "Margin at the start of a recording" +msgstr "" + +msgctxt "#19176" +msgid "Margin at the end of a recording" +msgstr "" + +msgctxt "#19177" +msgid "Playback" +msgstr "" + +msgctxt "#19178" +msgid "Show channel information when switching channels" +msgstr "" + +msgctxt "#19179" +msgid "Automatically hide channel information" +msgstr "" + +msgctxt "#19180" +msgid "TV" +msgstr "" + +msgctxt "#19181" +msgid "Menu/OSD" +msgstr "" + +msgctxt "#19182" +msgid "Days to display in the EPG" +msgstr "" + +msgctxt "#19184" +msgid "Channel information duration" +msgstr "" + +msgctxt "#19185" +msgid "Reset the PVR database" +msgstr "" + +msgctxt "#19186" +msgid "All data in the PVR database is being erased" +msgstr "" + +msgctxt "#19187" +msgid "Reset the EPG database" +msgstr "" + +msgctxt "#19188" +msgid "EPG is being reset" +msgstr "" + +msgctxt "#19189" +msgid "Continue last channel on startup" +msgstr "" + +msgctxt "#19190" +msgid "Minimized" +msgstr "" + +msgctxt "#19191" +msgid "PVR service" +msgstr "" + +msgctxt "#19192" +msgid "None of the connected PVR backends supports scanning for channels." +msgstr "" + +msgctxt "#19193" +msgid "The channel scan cannot be started. Check the log for details." +msgstr "" + +msgctxt "#19194" +msgid "Continue?" +msgstr "" + +msgctxt "#19195" +msgid "Client actions" +msgstr "" + +msgctxt "#19196" +msgid "PVR client specific actions" +msgstr "" + +msgctxt "#19197" +msgid "Recording started on: %s" +msgstr "" + +msgctxt "#19198" +msgid "Recording finished on: %s" +msgstr "" + +msgctxt "#19199" +msgid "Channel manager" +msgstr "" + +msgctxt "#19200" +msgid "EPG source:" +msgstr "" + +msgctxt "#19201" +msgid "Channel name:" +msgstr "" + +msgctxt "#19202" +msgid "Channel icon:" +msgstr "" + +msgctxt "#19203" +msgid "Edit channel" +msgstr "" + +msgctxt "#19204" +msgid "New channel" +msgstr "" + +msgctxt "#19205" +msgid "Group management" +msgstr "" + +msgctxt "#19206" +msgid "Activate EPG:" +msgstr "" + +msgctxt "#19207" +msgid "Group:" +msgstr "" + +msgctxt "#19208" +msgid "Enter the name of the new channel" +msgstr "" + +msgctxt "#19209" +msgid "XBMC virtual backend" +msgstr "" + +msgctxt "#19210" +msgid "Client" +msgstr "" + +msgctxt "#19211" +msgid "Delete channel" +msgstr "" + +msgctxt "#19212" +msgid "This list contains changes" +msgstr "" + +msgctxt "#19213" +msgid "Select backend" +msgstr "" + +msgctxt "#19214" +msgid "Enter a valid URL for the new channel" +msgstr "" + +msgctxt "#19215" +msgid "The PVR backend does not support timers." +msgstr "" + +msgctxt "#19216" +msgid "All radio channels" +msgstr "" + +msgctxt "#19217" +msgid "All TV channels" +msgstr "" + +msgctxt "#19218" +msgid "Visible" +msgstr "" + +msgctxt "#19219" +msgid "Ungrouped channels" +msgstr "" + +msgctxt "#19220" +msgid "Channels in" +msgstr "" + +msgctxt "#19221" +msgid "Synchronise channel groups with backends" +msgstr "" + +msgctxt "#19222" +msgid "EPG" +msgstr "" + +msgctxt "#19223" +msgid "No PVR add-on could be enabled. Check your settings or the log for more info." +msgstr "" + +msgctxt "#19224" +msgid "Recording aborted" +msgstr "" + +msgctxt "#19225" +msgid "Recording scheduled" +msgstr "" + +msgctxt "#19226" +msgid "Recording started" +msgstr "" + +msgctxt "#19227" +msgid "Recording completed" +msgstr "" + +msgctxt "#19228" +msgid "Recording deleted" +msgstr "" + +msgctxt "#19229" +msgid "Close channel OSD after switching channels" +msgstr "" + +msgctxt "#19230" +msgid "Prevent EPG updates while playing a TV stream" +msgstr "" + +msgctxt "#19231" +msgid "Always use the channel order from the backend(s)" +msgstr "" + +msgctxt "#19232" +msgid "Clear search results" +msgstr "" + +msgctxt "#19233" +msgid "Display a notification on timer updates" +msgstr "" + +msgctxt "#19234" +msgid "Use backend channels numbers (only works with 1 enabled PVR addon)" +msgstr "" + +msgctxt "#19235" +msgid "PVR manager is starting up" +msgstr "" + +msgctxt "#19236" +msgid "Loading channels from clients" +msgstr "" + +msgctxt "#19237" +msgid "Loading timers from clients" +msgstr "" + +msgctxt "#19238" +msgid "Loading recordings from clients" +msgstr "" + +msgctxt "#19239" +msgid "Starting background threads" +msgstr "" + +msgctxt "#19240" +msgid "No PVR add-on enabled" +msgstr "" + +msgctxt "#19241" +msgid "The PVR manager has been enabled without any" +msgstr "" + +msgctxt "#19242" +msgid "enabled PVR add-on. Enable at least one add-on" +msgstr "" + +msgctxt "#19243" +msgid "in order to use the PVR functionality." +msgstr "" + +msgctxt "#19244" +msgid "Backend idle time" +msgstr "" + +msgctxt "#19245" +msgid "Set wakup command (cmd [timestamp])" +msgstr "" + +msgctxt "#19246" +msgid "Wakup before recording" +msgstr "" + +msgctxt "#19247" +msgid "Daily wakeup" +msgstr "" + +msgctxt "#19248" +msgid "Daily wakeup time (HH:MM:SS)" +msgstr "" + +msgctxt "#19249" +msgid "Filter channels" +msgstr "" + +msgctxt "#19250" +msgid "Loading EPG from database" +msgstr "" + +msgctxt "#19251" +msgid "Update EPG information" +msgstr "" + +msgctxt "#19252" +msgid "Schedule EPG update for this channel?" +msgstr "" + +msgctxt "#19253" +msgid "EPG update scheduled for channel" +msgstr "" + +msgctxt "#19254" +msgid "EPG update failed for channel" +msgstr "" + +msgctxt "#19255" +msgid "Start recording" +msgstr "" + +msgctxt "#19256" +msgid "Stop recording" +msgstr "" + +msgctxt "#19257" +msgid "Lock channel" +msgstr "" + +msgctxt "#19258" +msgid "Unlock channel" +msgstr "" + +msgctxt "#19259" +msgid "Parental control" +msgstr "" + +msgctxt "#19260" +msgid "Unlock duration" +msgstr "" + +msgctxt "#19261" +msgid "Change PIN" +msgstr "" + +msgctxt "#19262" +msgid "Parental control. Enter PIN:" +msgstr "" + +msgctxt "#19263" +msgid "Locked channel. Enter PIN:" +msgstr "" + +msgctxt "#19264" +msgid "Incorrect PIN" +msgstr "" + +msgctxt "#19265" +msgid "The entered PIN number was incorrect." +msgstr "" + +msgctxt "#19266" +msgid "Parental locked" +msgstr "" + +msgctxt "#19267" +msgid "Parental locked:" +msgstr "" + +msgctxt "#19268" +msgid "Do not show 'no information available' labels" +msgstr "" + +msgctxt "#19269" +msgid "Do not show 'connection lost' warnings" +msgstr "" + +msgctxt "#19270" +msgid "* All recordings" +msgstr "" + +msgctxt "#19271" +msgid "No PVR add-ons could be found" +msgstr "" + +msgctxt "#19272" +msgid "You need a tuner, backend software, and an" +msgstr "" + +msgctxt "#19273" +msgid "add-on for the backend to be able to use PVR." +msgstr "" + +msgctxt "#19274" +msgid "Please visit xbmc.org/PVR to learn more." +msgstr "" + +#empty strings from id 19275 to 19499 + +msgctxt "#19499" +msgid "Other/Unknown" +msgstr "" + +msgctxt "#19500" +msgid "Movie/Drama" +msgstr "" + +msgctxt "#19501" +msgid "Detective/Thriller" +msgstr "" + +msgctxt "#19502" +msgid "Adventure/Western/War" +msgstr "" + +msgctxt "#19503" +msgid "Science Fiction/Fantasy/Horror" +msgstr "" + +msgctxt "#19504" +msgid "Comedy" +msgstr "" + +msgctxt "#19505" +msgid "Soap/Melodrama/Folkloric" +msgstr "" + +msgctxt "#19506" +msgid "Romance" +msgstr "" + +msgctxt "#19507" +msgid "Serious/Classical/Religious/Historical Movie/Drama" +msgstr "" + +msgctxt "#19508" +msgid "Adult Movie/Drama" +msgstr "" + +#empty strings from id 19509 to 19515 + +msgctxt "#19516" +msgid "News/Current Affairs" +msgstr "" + +msgctxt "#19517" +msgid "News/Weather Report" +msgstr "" + +msgctxt "#19518" +msgid "News Magazine" +msgstr "" + +msgctxt "#19519" +msgid "Documentary" +msgstr "" + +msgctxt "#19520" +msgid "Discussion/Interview/Debate" +msgstr "" + +#empty strings from id 19521 to 19531 + +msgctxt "#19532" +msgid "Show/Game Show" +msgstr "" + +msgctxt "#19533" +msgid "Game Show/Quiz/Contest" +msgstr "" + +msgctxt "#19534" +msgid "Variety Show" +msgstr "" + +msgctxt "#19535" +msgid "Talk Show" +msgstr "" + +#empty strings from id 19536 to 19547 + +msgctxt "#19548" +msgid "Sports" +msgstr "" + +msgctxt "#19549" +msgid "Special Event" +msgstr "" + +msgctxt "#19550" +msgid "Sport Magazine" +msgstr "" + +msgctxt "#19551" +msgid "Football" +msgstr "" + +msgctxt "#19552" +msgid "Tennis/Squash" +msgstr "" + +msgctxt "#19553" +msgid "Team Sports" +msgstr "" + +msgctxt "#19554" +msgid "Athletics" +msgstr "" + +msgctxt "#19555" +msgid "Motor Sport" +msgstr "" + +msgctxt "#19556" +msgid "Water Sport" +msgstr "" + +msgctxt "#19557" +msgid "Winter Sports" +msgstr "" + +msgctxt "#19558" +msgid "Equestrian" +msgstr "" + +msgctxt "#19559" +msgid "Martial Sports" +msgstr "" + +#empty strings from id 19560 to 19563 + +msgctxt "#19564" +msgid "Children's/Youth Programmes" +msgstr "" + +msgctxt "#19565" +msgid "Pre-school Children's Programmes" +msgstr "" + +msgctxt "#19566" +msgid "Entertainment Programmes for 6 to 14" +msgstr "" + +msgctxt "#19567" +msgid "Entertainment Programmes for 10 to 16" +msgstr "" + +msgctxt "#19568" +msgid "Informational/Educational/School Programme" +msgstr "" + +msgctxt "#19569" +msgid "Cartoons/Puppets" +msgstr "" + +#empty strings from id 19570 to 19579 + +msgctxt "#19580" +msgid "Music/Ballet/Dance" +msgstr "" + +msgctxt "#19581" +msgid "Rock/Pop" +msgstr "" + +msgctxt "#19582" +msgid "Serious/Classical Music" +msgstr "" + +msgctxt "#19583" +msgid "Folk/Traditional Music" +msgstr "" + +msgctxt "#19584" +msgid "Musical/Opera" +msgstr "" + +msgctxt "#19585" +msgid "Ballet" +msgstr "" + +#empty strings from id 19586 to 19595 + +msgctxt "#19596" +msgid "Arts/Culture" +msgstr "" + +msgctxt "#19597" +msgid "Performing Arts" +msgstr "" + +msgctxt "#19598" +msgid "Fine Arts" +msgstr "" + +msgctxt "#19599" +msgid "Religion" +msgstr "" + +msgctxt "#19600" +msgid "Popular Culture/Traditional Arts" +msgstr "" + +msgctxt "#19601" +msgid "Literature" +msgstr "" + +msgctxt "#19602" +msgid "Film/Cinema" +msgstr "" + +msgctxt "#19603" +msgid "Experimental Film/Video" +msgstr "" + +msgctxt "#19604" +msgid "Broadcasting/Press" +msgstr "" + +msgctxt "#19605" +msgid "New Media" +msgstr "" + +msgctxt "#19606" +msgid "Arts/Culture Magazines" +msgstr "" + +msgctxt "#19607" +msgid "Fashion" +msgstr "" + +#empty strings from id 19608 to 19611 + +msgctxt "#19612" +msgid "Social/Political/Economics" +msgstr "" + +msgctxt "#19613" +msgid "Magazines/Reports/Documentary" +msgstr "" + +msgctxt "#19614" +msgid "Economics/Social Advisory" +msgstr "" + +msgctxt "#19615" +msgid "Remarkable People" +msgstr "" + +#empty strings from id 19616 to 19627 + +msgctxt "#19628" +msgid "Education/Science/Factual" +msgstr "" + +msgctxt "#19629" +msgid "Nature/Animals/Environment" +msgstr "" + +msgctxt "#19630" +msgid "Technology/Natural Sciences" +msgstr "" + +msgctxt "#19631" +msgid "Medicine/Physiology/Psychology" +msgstr "" + +msgctxt "#19632" +msgid "Foreign Countries/Expeditions" +msgstr "" + +msgctxt "#19633" +msgid "Social/Spiritual Sciences" +msgstr "" + +msgctxt "#19634" +msgid "Further Education" +msgstr "" + +msgctxt "#19635" +msgid "Languages" +msgstr "" + +#empty strings from id 19636 to 19643 + +msgctxt "#19644" +msgid "Leisure/Hobbies" +msgstr "" + +msgctxt "#19645" +msgid "Tourism/Travel" +msgstr "" + +msgctxt "#19646" +msgid "Handicraft" +msgstr "" + +msgctxt "#19647" +msgid "Motoring" +msgstr "" + +msgctxt "#19648" +msgid "Fitness & Health" +msgstr "" + +msgctxt "#19649" +msgid "Cooking" +msgstr "" + +msgctxt "#19650" +msgid "Advertisement/Shopping" +msgstr "" + +msgctxt "#19651" +msgid "Gardening" +msgstr "" + +#empty strings from id 19652 to 19659 + +msgctxt "#19660" +msgid "Special Characteristics" +msgstr "" + +msgctxt "#19661" +msgid "Original Language" +msgstr "" + +msgctxt "#19662" +msgid "Black & White" +msgstr "" + +msgctxt "#19663" +msgid "Unpublished" +msgstr "" + +msgctxt "#19664" +msgid "Live Broadcast" +msgstr "" + +#empty strings from id 19665 to 19675 + +msgctxt "#19676" +msgid "Drama" +msgstr "" + +msgctxt "#19677" +msgid "Detective/Thriller" +msgstr "" + +msgctxt "#19678" +msgid "Adventure/Western/War" +msgstr "" + +msgctxt "#19679" +msgid "Science Fiction/Fantasy/Horror" +msgstr "" + +msgctxt "#19680" +msgid "Comedy" +msgstr "" + +msgctxt "#19681" +msgid "Soap/Melodrama/Folkloric" +msgstr "" + +msgctxt "#19682" +msgid "Romance" +msgstr "" + +msgctxt "#19683" +msgid "Serious/ClassicalReligion/Historical" +msgstr "" + +msgctxt "#19684" +msgid "Adult" +msgstr "" + +#empty strings from id 19685 to 19999 msgctxt "#20000" msgid "Saved music folder" @@ -8503,7 +9973,9 @@ msgctxt "#24018" msgid "Services" msgstr "" -#empty string with id 24019 +msgctxt "#24019" +msgid "PVR clients" +msgstr "" msgctxt "#24020" msgid "Configure" diff --git a/lib/addons/library.xbmc.addon/Makefile.in b/lib/addons/library.xbmc.addon/Makefile.in new file mode 100644 index 0000000000..cd8b00991b --- /dev/null +++ b/lib/addons/library.xbmc.addon/Makefile.in @@ -0,0 +1,27 @@ +ARCH=@ARCH@ +INCLUDES=-I. -I../../../xbmc/addons/include -I../../../xbmc +DEFINES+= +CXXFLAGS=-fPIC +LIBNAME=libXBMC_addon +OBJS=$(LIBNAME).o + +LIB_SHARED=../../../addons/library.xbmc.addon/$(LIBNAME)-$(ARCH).so + +all: $(LIB_SHARED) + +$(LIB_SHARED): $(OBJS) +ifeq ($(findstring osx,$(ARCH)), osx) + $(CXX) $(LDFLAGS) -Wl,-alias_list,@abs_top_srcdir@/xbmc/cores/DllLoader/exports/wrapper_mach_alias \ + -bundle -undefined dynamic_lookup -read_only_relocs suppress -o $@ \ + @abs_top_srcdir@/xbmc/cores/DllLoader/exports/wrapper.o $(OBJS) +else + $(CXX) $(CFLAGS) $(LDFLAGS) -shared -g -o $(LIB_SHARED) $(OBJS) +endif + +CLEAN_FILES = \ + $(LIBNAME).so \ + +DISTCLEAN_FILES= \ + Makefile \ + +include ../../../Makefile.include diff --git a/lib/addons/library.xbmc.addon/libXBMC_addon.cpp b/lib/addons/library.xbmc.addon/libXBMC_addon.cpp new file mode 100644 index 0000000000..8d8f026a95 --- /dev/null +++ b/lib/addons/library.xbmc.addon/libXBMC_addon.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string> +#include "../../../addons/library.xbmc.addon/libXBMC_addon.h" +#include "../../../xbmc/addons/AddonCallbacks.h" + +#ifdef _WIN32 +#include <windows.h> +#define DLLEXPORT __declspec(dllexport) +#else +#define DLLEXPORT +#endif + + +using namespace std; +using namespace ADDON; + +AddonCB *m_Handle = NULL; +CB_AddOnLib *m_cb = NULL; + +extern "C" +{ + +DLLEXPORT int XBMC_register_me(void *hdl) +{ + if (!hdl) + fprintf(stderr, "libXBMC_addon-ERROR: XBMC_register_me is called with NULL handle !!!\n"); + else + { + m_Handle = (AddonCB*) hdl; + m_cb = m_Handle->AddOnLib_RegisterMe(m_Handle->addonData); + if (!m_cb) + fprintf(stderr, "libXBMC_addon-ERROR: XBMC_register_me can't get callback table from XBMC !!!\n"); + else + return 1; + } + return 0; +} + +DLLEXPORT void XBMC_unregister_me() +{ + if (m_Handle && m_cb) + m_Handle->AddOnLib_UnRegisterMe(m_Handle->addonData, m_cb); +} + +DLLEXPORT void XBMC_log(const addon_log_t loglevel, const char *format, ... ) +{ + if (m_cb == NULL) + return; + + char buffer[16384]; + va_list args; + va_start (args, format); + vsprintf (buffer, format, args); + va_end (args); + m_cb->Log(m_Handle->addonData, loglevel, buffer); +} + +DLLEXPORT bool XBMC_get_setting(const char* settingName, void *settingValue) +{ + if (m_cb == NULL) + return false; + + return m_cb->GetSetting(m_Handle->addonData, settingName, settingValue); +} + +DLLEXPORT void XBMC_queue_notification(const queue_msg_t type, const char *format, ... ) +{ + if (m_cb == NULL) + return; + + char buffer[16384]; + va_list args; + va_start (args, format); + vsprintf (buffer, format, args); + va_end (args); + m_cb->QueueNotification(m_Handle->addonData, type, buffer); +} + +DLLEXPORT void XBMC_unknown_to_utf8(string &str) +{ + if (m_cb == NULL) + return; + + string buffer = m_cb->UnknownToUTF8(str.c_str()); + str = buffer; +} + +DLLEXPORT const char* XBMC_get_localized_string(int dwCode) +{ + if (m_cb == NULL) + return ""; + + return m_cb->GetLocalizedString(m_Handle->addonData, dwCode); +} + +DLLEXPORT const char* XBMC_get_dvd_menu_language() +{ + if (m_cb == NULL) + return ""; + + string buffer = m_cb->GetDVDMenuLanguage(m_Handle->addonData); + return buffer.c_str(); +} + +}; diff --git a/lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj b/lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj new file mode 100644 index 0000000000..af5c26fc7a --- /dev/null +++ b/lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}</ProjectGuid> + <RootNamespace>XBMC_VDR</RootNamespace> + <Keyword>Win32Proj</Keyword> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup> + <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> + <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\..\..\..\addons\library.xbmc.addon\</OutDir> + <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Debug\</IntDir> + <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\..\..\..\addons\library.xbmc.addon\</OutDir> + <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Release\</IntDir> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\addons;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;..\..\..\..\..\xbmc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32;_DEBUG;_WINDOWS;VDR_EXPORTS;_WIN32PC;_WINSOCKAPI_;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>true</MinimalRebuild> + <ExceptionHandling>Sync</ExceptionHandling> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + </ClCompile> + <Link> + <OutputFile>..\..\..\..\..\addons\library.xbmc.addon\$(ProjectName).dll</OutputFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <AdditionalIncludeDirectories>..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\addons;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;..\..\..\..\..\xbmc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;HAS_SDL_OPENGL;HAS_SDL;_USRDLL;XBMC_VDR_EXPORTS;_WIN32PC;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ExceptionHandling>Sync</ExceptionHandling> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + </ClCompile> + <Link> + <OutputFile>..\..\..\..\..\addons\library.xbmc.addon\$(ProjectName).dll</OutputFile> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\..\libXBMC_addon.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\..\..\..\addons\library.xbmc.addon\libXBMC_addon.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj.filters b/lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj.filters new file mode 100644 index 0000000000..916673ca41 --- /dev/null +++ b/lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj.filters @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\libXBMC_addon.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\..\..\..\addons\library.xbmc.addon\libXBMC_addon.h"> + <Filter>Source Files</Filter> + </ClInclude> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/lib/addons/library.xbmc.gui/Makefile.in b/lib/addons/library.xbmc.gui/Makefile.in new file mode 100644 index 0000000000..b5df484c2d --- /dev/null +++ b/lib/addons/library.xbmc.gui/Makefile.in @@ -0,0 +1,27 @@ +ARCH=@ARCH@ +INCLUDES=-I. -I../../../xbmc/addons/include -I../../../xbmc -I../../../xbmc/cores/dvdplayer/DVDDemuxers +DEFINES+= +CXXFLAGS=-fPIC +LIBNAME=libXBMC_gui +OBJS=$(LIBNAME).o + +LIB_SHARED=../../../addons/library.xbmc.gui/$(LIBNAME)-$(ARCH).so + +all: $(LIB_SHARED) + +$(LIB_SHARED): $(OBJS) +ifeq ($(findstring osx,$(ARCH)), osx) + $(CXX) $(LDFLAGS) -Wl,-alias_list,@abs_top_srcdir@/xbmc/cores/DllLoader/exports/wrapper_mach_alias \ + -bundle -undefined dynamic_lookup -read_only_relocs suppress -o $@ \ + @abs_top_srcdir@/xbmc/cores/DllLoader/exports/wrapper.o $(OBJS) +else + $(CXX) $(CFLAGS) $(LDFLAGS) -shared -g -o $(LIB_SHARED) $(OBJS) +endif + +CLEAN_FILES = \ + $(LIB_SHARED) \ + +DISTCLEAN_FILES= \ + Makefile \ + +include ../../../Makefile.include diff --git a/lib/addons/library.xbmc.gui/libXBMC_gui.cpp b/lib/addons/library.xbmc.gui/libXBMC_gui.cpp new file mode 100644 index 0000000000..752c2483cb --- /dev/null +++ b/lib/addons/library.xbmc.gui/libXBMC_gui.cpp @@ -0,0 +1,555 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string> +#include "../../../addons/library.xbmc.gui/libXBMC_gui.h" +#include "addons/AddonCallbacks.h" + +#ifdef _WIN32 +#include <windows.h> +#define DLLEXPORT __declspec(dllexport) +#else +#define DLLEXPORT +#endif + +using namespace std; + +AddonCB *m_Handle = NULL; +CB_GUILib *m_cb = NULL; + +extern "C" +{ + +DLLEXPORT int GUI_register_me(void *hdl) +{ + if (!hdl) + fprintf(stderr, "libXBMC_gui-ERROR: GUILib_register_me is called with NULL handle !!!\n"); + else + { + m_Handle = (AddonCB*) hdl; + m_cb = m_Handle->GUILib_RegisterMe(m_Handle->addonData); + if (!m_cb) + fprintf(stderr, "libXBMC_gui-ERROR: GUILib_register_me can't get callback table from XBMC !!!\n"); + else + return 1; + } + return 0; +} + +DLLEXPORT void GUI_unregister_me() +{ + if (m_Handle && m_cb) + m_Handle->GUILib_UnRegisterMe(m_Handle->addonData, m_cb); +} + +DLLEXPORT void GUI_lock() +{ + m_cb->Lock(); +} + +DLLEXPORT void GUI_unlock() +{ + m_cb->Unlock(); +} + +DLLEXPORT int GUI_get_screen_height() +{ + return m_cb->GetScreenHeight(); +} + +DLLEXPORT int GUI_get_screen_width() +{ + return m_cb->GetScreenWidth(); +} + +DLLEXPORT int GUI_get_video_resolution() +{ + return m_cb->GetVideoResolution(); +} + +DLLEXPORT CAddonGUIWindow* GUI_Window_create(const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog) +{ + return new CAddonGUIWindow(xmlFilename, defaultSkin, forceFallback, asDialog); +} + +DLLEXPORT void GUI_Window_destroy(CAddonGUIWindow* p) +{ + delete p; +} + +DLLEXPORT bool GUI_Window_OnClick(GUIHANDLE handle, int controlId) +{ + CAddonGUIWindow *window = (CAddonGUIWindow*) handle; + return window->OnClick(controlId); +} + +DLLEXPORT bool GUI_Window_OnFocus(GUIHANDLE handle, int controlId) +{ + CAddonGUIWindow *window = (CAddonGUIWindow*) handle; + return window->OnFocus(controlId); +} + +DLLEXPORT bool GUI_Window_OnInit(GUIHANDLE handle) +{ + CAddonGUIWindow *window = (CAddonGUIWindow*) handle; + return window->OnInit(); +} + +DLLEXPORT bool GUI_Window_OnAction(GUIHANDLE handle, int actionId) +{ + CAddonGUIWindow *window = (CAddonGUIWindow*) handle; + return window->OnAction(actionId); +} + + +CAddonGUIWindow::CAddonGUIWindow(const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog) +{ + CBOnInit = NULL; + CBOnClick = NULL; + CBOnFocus = NULL; + if (m_Handle && m_cb) + { + m_WindowHandle = m_cb->Window_New(m_Handle->addonData, xmlFilename, defaultSkin, forceFallback, asDialog); + if (!m_WindowHandle) + fprintf(stderr, "libXBMC_gui-ERROR: cGUIWindow can't create window class from XBMC !!!\n"); + + m_cb->Window_SetCallbacks(m_Handle->addonData, m_WindowHandle, this, GUI_Window_OnInit, GUI_Window_OnClick, GUI_Window_OnFocus, GUI_Window_OnAction); + } +} + +CAddonGUIWindow::~CAddonGUIWindow() +{ + if (m_Handle && m_cb && m_WindowHandle) + { + m_cb->Window_Delete(m_Handle->addonData, m_WindowHandle); + m_WindowHandle = NULL; + } +} + +bool CAddonGUIWindow::Show() +{ + return m_cb->Window_Show(m_Handle->addonData, m_WindowHandle); +} + +void CAddonGUIWindow::Close() +{ + m_cb->Window_Close(m_Handle->addonData, m_WindowHandle); +} + +void CAddonGUIWindow::DoModal() +{ + m_cb->Window_DoModal(m_Handle->addonData, m_WindowHandle); +} + +bool CAddonGUIWindow::OnInit() +{ + if (!CBOnInit) + return false; + + return CBOnInit(m_cbhdl); +} + +bool CAddonGUIWindow::OnClick(int controlId) +{ + if (!CBOnClick) + return false; + + return CBOnClick(m_cbhdl, controlId); +} + +bool CAddonGUIWindow::OnFocus(int controlId) +{ + if (!CBOnFocus) + return false; + + return CBOnFocus(m_cbhdl, controlId); +} + +bool CAddonGUIWindow::OnAction(int actionId) +{ + if (!CBOnAction) + return false; + + return CBOnAction(m_cbhdl, actionId); +} + +bool CAddonGUIWindow::SetFocusId(int iControlId) +{ + return m_cb->Window_SetFocusId(m_Handle->addonData, m_WindowHandle, iControlId); +} + +int CAddonGUIWindow::GetFocusId() +{ + return m_cb->Window_GetFocusId(m_Handle->addonData, m_WindowHandle); +} + +bool CAddonGUIWindow::SetCoordinateResolution(int res) +{ + return m_cb->Window_SetCoordinateResolution(m_Handle->addonData, m_WindowHandle, res); +} + +void CAddonGUIWindow::SetProperty(const char *key, const char *value) +{ + m_cb->Window_SetProperty(m_Handle->addonData, m_WindowHandle, key, value); +} + +void CAddonGUIWindow::SetPropertyInt(const char *key, int value) +{ + m_cb->Window_SetPropertyInt(m_Handle->addonData, m_WindowHandle, key, value); +} + +void CAddonGUIWindow::SetPropertyBool(const char *key, bool value) +{ + m_cb->Window_SetPropertyBool(m_Handle->addonData, m_WindowHandle, key, value); +} + +void CAddonGUIWindow::SetPropertyDouble(const char *key, double value) +{ + m_cb->Window_SetPropertyDouble(m_Handle->addonData, m_WindowHandle, key, value); +} + +const char *CAddonGUIWindow::GetProperty(const char *key) const +{ + return m_cb->Window_GetProperty(m_Handle->addonData, m_WindowHandle, key); +} + +int CAddonGUIWindow::GetPropertyInt(const char *key) const +{ + return m_cb->Window_GetPropertyInt(m_Handle->addonData, m_WindowHandle, key); +} + +bool CAddonGUIWindow::GetPropertyBool(const char *key) const +{ + return m_cb->Window_GetPropertyBool(m_Handle->addonData, m_WindowHandle, key); +} + +double CAddonGUIWindow::GetPropertyDouble(const char *key) const +{ + return m_cb->Window_GetPropertyDouble(m_Handle->addonData, m_WindowHandle, key); +} + +void CAddonGUIWindow::ClearProperties() +{ + m_cb->Window_ClearProperties(m_Handle->addonData, m_WindowHandle); +} + +int CAddonGUIWindow::GetListSize() +{ + return m_cb->Window_GetListSize(m_Handle->addonData, m_WindowHandle); +} + +void CAddonGUIWindow::ClearList() +{ + m_cb->Window_ClearList(m_Handle->addonData, m_WindowHandle); +} + +GUIHANDLE CAddonGUIWindow::AddStringItem(const char *name, int itemPosition) +{ + return m_cb->Window_AddStringItem(m_Handle->addonData, m_WindowHandle, name, itemPosition); +} + +void CAddonGUIWindow::AddItem(GUIHANDLE item, int itemPosition) +{ + m_cb->Window_AddItem(m_Handle->addonData, m_WindowHandle, item, itemPosition); +} + +void CAddonGUIWindow::AddItem(CAddonListItem *item, int itemPosition) +{ + m_cb->Window_AddItem(m_Handle->addonData, m_WindowHandle, item->m_ListItemHandle, itemPosition); +} + +void CAddonGUIWindow::RemoveItem(int itemPosition) +{ + m_cb->Window_RemoveItem(m_Handle->addonData, m_WindowHandle, itemPosition); +} + +GUIHANDLE CAddonGUIWindow::GetListItem(int listPos) +{ + return m_cb->Window_GetListItem(m_Handle->addonData, m_WindowHandle, listPos); +} + +void CAddonGUIWindow::SetCurrentListPosition(int listPos) +{ + m_cb->Window_SetCurrentListPosition(m_Handle->addonData, m_WindowHandle, listPos); +} + +int CAddonGUIWindow::GetCurrentListPosition() +{ + return m_cb->Window_GetCurrentListPosition(m_Handle->addonData, m_WindowHandle); +} + +void CAddonGUIWindow::SetControlLabel(int controlId, const char *label) +{ + m_cb->Window_SetControlLabel(m_Handle->addonData, m_WindowHandle, controlId, label); +} + +///------------------------------------- +/// cGUISpinControl + +DLLEXPORT CAddonGUISpinControl* GUI_control_get_spin(CAddonGUIWindow *window, int controlId) +{ + return new CAddonGUISpinControl(window, controlId); +} + +DLLEXPORT void GUI_control_release_spin(CAddonGUISpinControl* p) +{ + delete p; +} + +CAddonGUISpinControl::CAddonGUISpinControl(CAddonGUIWindow *window, int controlId) + : m_Window(window) + , m_ControlId(controlId) +{ + m_SpinHandle = m_cb->Window_GetControl_Spin(m_Handle->addonData, m_Window->m_WindowHandle, controlId); +} + +void CAddonGUISpinControl::SetVisible(bool yesNo) +{ + if (m_SpinHandle) + m_cb->Control_Spin_SetVisible(m_Handle->addonData, m_SpinHandle, yesNo); +} + +void CAddonGUISpinControl::SetText(const char *label) +{ + if (m_SpinHandle) + m_cb->Control_Spin_SetText(m_Handle->addonData, m_SpinHandle, label); +} + +void CAddonGUISpinControl::Clear() +{ + if (m_SpinHandle) + m_cb->Control_Spin_Clear(m_Handle->addonData, m_SpinHandle); +} + +void CAddonGUISpinControl::AddLabel(const char *label, int iValue) +{ + if (m_SpinHandle) + m_cb->Control_Spin_AddLabel(m_Handle->addonData, m_SpinHandle, label, iValue); +} + +int CAddonGUISpinControl::GetValue() +{ + if (!m_SpinHandle) + return -1; + + return m_cb->Control_Spin_GetValue(m_Handle->addonData, m_SpinHandle); +} + +void CAddonGUISpinControl::SetValue(int iValue) +{ + if (m_SpinHandle) + m_cb->Control_Spin_SetValue(m_Handle->addonData, m_SpinHandle, iValue); +} + +///------------------------------------- +/// cGUIRadioButton + +DLLEXPORT CAddonGUIRadioButton* GUI_control_get_radiobutton(CAddonGUIWindow *window, int controlId) +{ + return new CAddonGUIRadioButton(window, controlId); +} + +DLLEXPORT void GUI_control_release_radiobutton(CAddonGUIRadioButton* p) +{ + delete p; +} + +CAddonGUIRadioButton::CAddonGUIRadioButton(CAddonGUIWindow *window, int controlId) + : m_Window(window) + , m_ControlId(controlId) +{ + m_ButtonHandle = m_cb->Window_GetControl_RadioButton(m_Handle->addonData, m_Window->m_WindowHandle, controlId); +} + +void CAddonGUIRadioButton::SetVisible(bool yesNo) +{ + if (m_ButtonHandle) + m_cb->Control_RadioButton_SetVisible(m_Handle->addonData, m_ButtonHandle, yesNo); +} + +void CAddonGUIRadioButton::SetText(const char *label) +{ + if (m_ButtonHandle) + m_cb->Control_RadioButton_SetText(m_Handle->addonData, m_ButtonHandle, label); +} + +void CAddonGUIRadioButton::SetSelected(bool yesNo) +{ + if (m_ButtonHandle) + m_cb->Control_RadioButton_SetSelected(m_Handle->addonData, m_ButtonHandle, yesNo); +} + +bool CAddonGUIRadioButton::IsSelected() +{ + if (!m_ButtonHandle) + return false; + + return m_cb->Control_RadioButton_IsSelected(m_Handle->addonData, m_ButtonHandle); +} + + +///------------------------------------- +/// cGUIProgressControl + +DLLEXPORT CAddonGUIProgressControl* GUI_control_get_progress(CAddonGUIWindow *window, int controlId) +{ + return new CAddonGUIProgressControl(window, controlId); +} + +DLLEXPORT void GUI_control_release_progress(CAddonGUIProgressControl* p) +{ + delete p; +} + +CAddonGUIProgressControl::CAddonGUIProgressControl(CAddonGUIWindow *window, int controlId) + : m_Window(window) + , m_ControlId(controlId) +{ + m_ProgressHandle = m_cb->Window_GetControl_Progress(m_Handle->addonData, m_Window->m_WindowHandle, controlId); +} + +void CAddonGUIProgressControl::SetPercentage(float fPercent) +{ + if (m_ProgressHandle) + m_cb->Control_Progress_SetPercentage(m_Handle->addonData, m_ProgressHandle, fPercent); +} + +float CAddonGUIProgressControl::GetPercentage() const +{ + if (!m_ProgressHandle) + return 0.0; + + return m_cb->Control_Progress_GetPercentage(m_Handle->addonData, m_ProgressHandle); +} + +void CAddonGUIProgressControl::SetInfo(int iInfo) +{ + if (m_ProgressHandle) + m_cb->Control_Progress_SetInfo(m_Handle->addonData, m_ProgressHandle, iInfo); +} + +int CAddonGUIProgressControl::GetInfo() const +{ + if (!m_ProgressHandle) + return -1; + + return m_cb->Control_Progress_GetInfo(m_Handle->addonData, m_ProgressHandle); +} + +string CAddonGUIProgressControl::GetDescription() const +{ + if (!m_ProgressHandle) + return ""; + + return m_cb->Control_Progress_GetDescription(m_Handle->addonData, m_ProgressHandle); +} + + +///------------------------------------- +/// cListItem + +DLLEXPORT CAddonListItem* GUI_ListItem_create(const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path) +{ + return new CAddonListItem(label, label2, iconImage, thumbnailImage, path); +} + +DLLEXPORT void GUI_ListItem_destroy(CAddonListItem* p) +{ + delete p; +} + + +CAddonListItem::CAddonListItem(const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path) +{ + m_ListItemHandle = m_cb->ListItem_Create(m_Handle->addonData, label, label2, iconImage, thumbnailImage, path); +} + +const char *CAddonListItem::GetLabel() +{ + if (!m_ListItemHandle) + return ""; + + return m_cb->ListItem_GetLabel(m_Handle->addonData, m_ListItemHandle); +} + +void CAddonListItem::SetLabel(const char *label) +{ + if (m_ListItemHandle) + m_cb->ListItem_SetLabel(m_Handle->addonData, m_ListItemHandle, label); +} + +const char *CAddonListItem::GetLabel2() +{ + if (!m_ListItemHandle) + return ""; + + return m_cb->ListItem_GetLabel2(m_Handle->addonData, m_ListItemHandle); +} + +void CAddonListItem::SetLabel2(const char *label) +{ + if (m_ListItemHandle) + m_cb->ListItem_SetLabel2(m_Handle->addonData, m_ListItemHandle, label); +} + +void CAddonListItem::SetIconImage(const char *image) +{ + if (m_ListItemHandle) + m_cb->ListItem_SetIconImage(m_Handle->addonData, m_ListItemHandle, image); +} + +void CAddonListItem::SetThumbnailImage(const char *image) +{ + if (m_ListItemHandle) + m_cb->ListItem_SetThumbnailImage(m_Handle->addonData, m_ListItemHandle, image); +} + +void CAddonListItem::SetInfo(const char *Info) +{ + if (m_ListItemHandle) + m_cb->ListItem_SetInfo(m_Handle->addonData, m_ListItemHandle, Info); +} + +void CAddonListItem::SetProperty(const char *key, const char *value) +{ + if (m_ListItemHandle) + m_cb->ListItem_SetProperty(m_Handle->addonData, m_ListItemHandle, key, value); +} + +const char *CAddonListItem::GetProperty(const char *key) const +{ + if (!m_ListItemHandle) + return ""; + + return m_cb->ListItem_GetProperty(m_Handle->addonData, m_ListItemHandle, key); +} + +void CAddonListItem::SetPath(const char *Path) +{ + if (m_ListItemHandle) + m_cb->ListItem_SetPath(m_Handle->addonData, m_ListItemHandle, Path); +} + + +}; diff --git a/lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj b/lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj new file mode 100644 index 0000000000..1a09551b92 --- /dev/null +++ b/lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}</ProjectGuid> + <RootNamespace>XBMC_VDR</RootNamespace> + <Keyword>Win32Proj</Keyword> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup> + <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> + <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\..\..\..\addons\library.xbmc.gui\</OutDir> + <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Debug\</IntDir> + <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\..\..\..\addons\library.xbmc.gui\</OutDir> + <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Release\</IntDir> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32;_DEBUG;_WINDOWS;VDR_EXPORTS;_WIN32PC;_WINSOCKAPI_;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>true</MinimalRebuild> + <ExceptionHandling>Sync</ExceptionHandling> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + </ClCompile> + <Link> + <OutputFile>..\..\..\..\..\addons\library.xbmc.gui\$(ProjectName).dll</OutputFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <AdditionalIncludeDirectories>..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;HAS_SDL_OPENGL;HAS_SDL;_USRDLL;XBMC_VDR_EXPORTS;_WIN32PC;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ExceptionHandling>Sync</ExceptionHandling> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + </ClCompile> + <Link> + <OutputFile>../../../../../addons/library.xbmc.gui/$(ProjectName).dll</OutputFile> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\..\libXBMC_gui.cpp" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj.filters b/lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj.filters new file mode 100644 index 0000000000..166719621f --- /dev/null +++ b/lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj.filters @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\libXBMC_gui.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/lib/addons/library.xbmc.pvr/Makefile.in b/lib/addons/library.xbmc.pvr/Makefile.in new file mode 100644 index 0000000000..1edf41120a --- /dev/null +++ b/lib/addons/library.xbmc.pvr/Makefile.in @@ -0,0 +1,27 @@ +ARCH=@ARCH@ +INCLUDES=-I. -I../../../xbmc/addons/include -I../../../xbmc -I../../../xbmc/cores/dvdplayer/DVDDemuxers +DEFINES+= +CXXFLAGS=-fPIC +LIBNAME=libXBMC_pvr +OBJS=$(LIBNAME).o + +LIB_SHARED=../../../addons/library.xbmc.pvr/$(LIBNAME)-$(ARCH).so + +all: $(LIB_SHARED) + +$(LIB_SHARED): $(OBJS) +ifeq ($(findstring osx,$(ARCH)), osx) + $(CXX) $(LDFLAGS) -Wl,-alias_list,@abs_top_srcdir@/xbmc/cores/DllLoader/exports/wrapper_mach_alias \ + -bundle -undefined dynamic_lookup -read_only_relocs suppress -o $@ \ + @abs_top_srcdir@/xbmc/cores/DllLoader/exports/wrapper.o $(OBJS) +else + $(CXX) $(CFLAGS) $(LDFLAGS) -shared -g -o $(LIB_SHARED) $(OBJS) +endif + +CLEAN_FILES = \ + $(LIB_SHARED) \ + +DISTCLEAN_FILES= \ + Makefile \ + +include ../../../Makefile.include diff --git a/lib/addons/library.xbmc.pvr/libXBMC_pvr.cpp b/lib/addons/library.xbmc.pvr/libXBMC_pvr.cpp new file mode 100644 index 0000000000..8ef4b595f3 --- /dev/null +++ b/lib/addons/library.xbmc.pvr/libXBMC_pvr.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#define USE_DEMUX // enable including of the demuxer packet structure + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string> +#include "../../../addons/library.xbmc.pvr/libXBMC_pvr.h" +#include "addons/AddonCallbacks.h" +#include "cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h" + +#ifdef _WIN32 +#include <windows.h> +#define DLLEXPORT __declspec(dllexport) +#else +#define DLLEXPORT +#endif + +using namespace std; + +AddonCB *m_Handle = NULL; +CB_PVRLib *m_cb = NULL; + +extern "C" +{ + +DLLEXPORT int PVR_register_me(void *hdl) +{ + if (!hdl) + fprintf(stderr, "libXBMC_pvr-ERROR: PVRLib_register_me is called with NULL handle !!!\n"); + else + { + m_Handle = (AddonCB*) hdl; + m_cb = m_Handle->PVRLib_RegisterMe(m_Handle->addonData); + if (!m_cb) + fprintf(stderr, "libXBMC_pvr-ERROR: PVRLib_register_me can't get callback table from XBMC !!!\n"); + else + return 1; + } + return 0; +} + +DLLEXPORT void PVR_unregister_me() +{ + if (m_Handle && m_cb) + m_Handle->PVRLib_UnRegisterMe(m_Handle->addonData, m_cb); +} + +DLLEXPORT void PVR_transfer_epg_entry(const ADDON_HANDLE handle, const EPG_TAG *epgentry) +{ + if (m_cb == NULL) + return; + + m_cb->TransferEpgEntry(m_Handle->addonData, handle, epgentry); +} + +DLLEXPORT void PVR_transfer_channel_entry(const ADDON_HANDLE handle, const PVR_CHANNEL *chan) +{ + if (m_cb == NULL) + return; + + m_cb->TransferChannelEntry(m_Handle->addonData, handle, chan); +} + +DLLEXPORT void PVR_transfer_timer_entry(const ADDON_HANDLE handle, const PVR_TIMER *timer) +{ + if (m_cb == NULL) + return; + + m_cb->TransferTimerEntry(m_Handle->addonData, handle, timer); +} + +DLLEXPORT void PVR_transfer_recording_entry(const ADDON_HANDLE handle, const PVR_RECORDING *recording) +{ + if (m_cb == NULL) + return; + + m_cb->TransferRecordingEntry(m_Handle->addonData, handle, recording); +} + +DLLEXPORT void PVR_add_menu_hook(PVR_MENUHOOK *hook) +{ + if (m_cb == NULL) + return; + + m_cb->AddMenuHook(m_Handle->addonData, hook); +} + +DLLEXPORT void PVR_recording(const char *Name, const char *FileName, bool On) +{ + if (m_cb == NULL) + return; + + m_cb->Recording(m_Handle->addonData, Name, FileName, On); +} + +DLLEXPORT void PVR_trigger_channel_update() +{ + if (m_cb == NULL) + return; + + m_cb->TriggerChannelUpdate(m_Handle->addonData); +} + +DLLEXPORT void PVR_trigger_channel_groups_update() +{ + if (m_cb == NULL) + return; + + m_cb->TriggerChannelGroupsUpdate(m_Handle->addonData); +} + +DLLEXPORT void PVR_trigger_timer_update() +{ + if (m_cb == NULL) + return; + + m_cb->TriggerTimerUpdate(m_Handle->addonData); +} + +DLLEXPORT void PVR_trigger_recording_update() +{ + if (m_cb == NULL) + return; + + m_cb->TriggerRecordingUpdate(m_Handle->addonData); +} + +DLLEXPORT void PVR_free_demux_packet(DemuxPacket* pPacket) +{ + if (m_cb == NULL) + return; + + m_cb->FreeDemuxPacket(m_Handle->addonData, pPacket); +} + +DLLEXPORT DemuxPacket* PVR_allocate_demux_packet(int iDataSize) +{ + if (m_cb == NULL) + return NULL; + + return m_cb->AllocateDemuxPacket(m_Handle->addonData, iDataSize); +} + +DLLEXPORT void PVR_transfer_channel_group(const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP *group) +{ + if (m_cb == NULL) + return; + + m_cb->TransferChannelGroup(m_Handle->addonData, handle, group); +} + +DLLEXPORT void PVR_transfer_channel_group_member(const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER *member) +{ + if (m_cb == NULL) + return; + + m_cb->TransferChannelGroupMember(m_Handle->addonData, handle, member); +} + +}; diff --git a/lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj b/lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj new file mode 100644 index 0000000000..3e386a06e4 --- /dev/null +++ b/lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{6D8C91F8-992F-4C83-9DE3-485D64EF8420}</ProjectGuid> + <RootNamespace>XBMC_VDR</RootNamespace> + <Keyword>Win32Proj</Keyword> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup> + <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion> + <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\..\..\..\addons\library.xbmc.pvr\</OutDir> + <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Debug\</IntDir> + <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\..\..\..\addons\library.xbmc.pvr\</OutDir> + <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Release\</IntDir> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32;_DEBUG;_WINDOWS;VDR_EXPORTS;_WIN32PC;_WINSOCKAPI_;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>true</MinimalRebuild> + <ExceptionHandling>Sync</ExceptionHandling> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + </ClCompile> + <Link> + <OutputFile>..\..\..\..\..\addons\library.xbmc.pvr\$(ProjectName).dll</OutputFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <AdditionalIncludeDirectories>..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;HAS_SDL_OPENGL;HAS_SDL;_USRDLL;XBMC_VDR_EXPORTS;_WIN32PC;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ExceptionHandling>Sync</ExceptionHandling> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + </ClCompile> + <Link> + <OutputFile>..\..\..\..\..\addons\library.xbmc.pvr\$(ProjectName).dll</OutputFile> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\..\libXBMC_pvr.cpp" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj.filters b/lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj.filters new file mode 100644 index 0000000000..db93c59c90 --- /dev/null +++ b/lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj.filters @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\libXBMC_pvr.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/project/VS2010Express/XBMC for Windows.sln b/project/VS2010Express/XBMC for Windows.sln index 55b566427a..cc0be39cb6 100644 --- a/project/VS2010Express/XBMC for Windows.sln +++ b/project/VS2010Express/XBMC for Windows.sln @@ -7,6 +7,9 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnrarXLib", "UnrarXLib.vcxproj", "{FE0A91C0-E30A-47CD-8A92-A508C9292452}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libhts", "..\..\lib\libhts\Win32\libhts_2010.vcxproj", "{00700E12-A63B-4E54-B962-4011A90584BD}" + ProjectSection(ProjectDependencies) = postProject + {0F78AAF3-A188-4491-8BA6-203DC0B8D7F5} = {0F78AAF3-A188-4491-8BA6-203DC0B8D7F5} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libass_dll", "..\..\lib\libass\xbmc\libass_win32\libass_win32_vs2010.vcxproj", "{BA5B08FC-2ECB-4571-9F25-F8054522FC65}" EndProject @@ -72,6 +75,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmp3lame_dll", "..\..\lib EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libhdhomerun_dll", "..\..\lib\libhdhomerun\hdhomerun\hdhomerun.vcxproj", "{1E2FB608-3DD2-4021-A598-90008FA6DE85}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libXBMC_addon", "..\..\lib\addons\library.xbmc.addon\project\VS2010Express\libXBMC_addon.vcxproj", "{2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libXBMC_gui", "..\..\lib\addons\library.xbmc.gui\project\VS2010Express\libXBMC_gui.vcxproj", "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libXBMC_pvr", "..\..\lib\addons\library.xbmc.pvr\project\VS2010Express\libXBMC_pvr.vcxproj", "{6D8C91F8-992F-4C83-9DE3-485D64EF8420}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XbmcCommons", "XbmcCommons.vcxproj", "{87DA0A1E-3F33-4927-A5E5-2D58F2C58E17}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XbmcThreads", "XbmcThreads.vcxproj", "{034B1D02-CA92-455D-8866-DB95BEE49C10}" @@ -127,6 +136,22 @@ Global {BA5B08FC-2ECB-4571-9F25-F8054522FC65}.Release (DirectX)|Win32.Build.0 = Release|Win32 {BA5B08FC-2ECB-4571-9F25-F8054522FC65}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32 {BA5B08FC-2ECB-4571-9F25-F8054522FC65}.Release (OpenGL)|Win32.Build.0 = Release|Win32 + {0F78AAF3-A188-4491-8BA6-203DC0B8D7F5}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32 + {0F78AAF3-A188-4491-8BA6-203DC0B8D7F5}.Debug (DirectX)|Win32.Build.0 = Debug|Win32 + {0F78AAF3-A188-4491-8BA6-203DC0B8D7F5}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32 + {0F78AAF3-A188-4491-8BA6-203DC0B8D7F5}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32 + {0F78AAF3-A188-4491-8BA6-203DC0B8D7F5}.Release (DirectX)|Win32.ActiveCfg = Release|Win32 + {0F78AAF3-A188-4491-8BA6-203DC0B8D7F5}.Release (DirectX)|Win32.Build.0 = Release|Win32 + {0F78AAF3-A188-4491-8BA6-203DC0B8D7F5}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32 + {0F78AAF3-A188-4491-8BA6-203DC0B8D7F5}.Release (OpenGL)|Win32.Build.0 = Release|Win32 + {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32 + {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Debug (DirectX)|Win32.Build.0 = Debug|Win32 + {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32 + {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32 + {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Release (DirectX)|Win32.ActiveCfg = Release|Win32 + {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Release (DirectX)|Win32.Build.0 = Release|Win32 + {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32 + {3AD3147B-8E7F-4C70-B049-19FA1916BF12}.Release (OpenGL)|Win32.Build.0 = Release|Win32 {5E479372-4F34-426D-AA1E-9879E94C105D}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32 {5E479372-4F34-426D-AA1E-9879E94C105D}.Debug (DirectX)|Win32.Build.0 = Debug|Win32 {5E479372-4F34-426D-AA1E-9879E94C105D}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32 @@ -371,6 +396,54 @@ Global {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Release (DirectX)|Win32.Build.0 = Release|Win32 {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32 {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Release (OpenGL)|Win32.Build.0 = Release|Win32 + {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32 + {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Debug (DirectX)|Win32.Build.0 = Debug|Win32 + {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32 + {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32 + {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Release (DirectX)|Win32.ActiveCfg = Release|Win32 + {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Release (DirectX)|Win32.Build.0 = Release|Win32 + {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32 + {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Release (OpenGL)|Win32.Build.0 = Release|Win32 + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32 + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug (DirectX)|Win32.Build.0 = Debug|Win32 + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32 + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32 + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release (DirectX)|Win32.ActiveCfg = Release|Win32 + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release (DirectX)|Win32.Build.0 = Release|Win32 + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32 + {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release (OpenGL)|Win32.Build.0 = Release|Win32 + {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32 + {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Debug (DirectX)|Win32.Build.0 = Debug|Win32 + {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32 + {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32 + {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Release (DirectX)|Win32.ActiveCfg = Release|Win32 + {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Release (DirectX)|Win32.Build.0 = Release|Win32 + {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32 + {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Release (OpenGL)|Win32.Build.0 = Release|Win32 + {74C9642E-1988-48DC-8404-11717C355378}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32 + {74C9642E-1988-48DC-8404-11717C355378}.Debug (DirectX)|Win32.Build.0 = Debug|Win32 + {74C9642E-1988-48DC-8404-11717C355378}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32 + {74C9642E-1988-48DC-8404-11717C355378}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32 + {74C9642E-1988-48DC-8404-11717C355378}.Release (DirectX)|Win32.ActiveCfg = Release|Win32 + {74C9642E-1988-48DC-8404-11717C355378}.Release (DirectX)|Win32.Build.0 = Release|Win32 + {74C9642E-1988-48DC-8404-11717C355378}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32 + {74C9642E-1988-48DC-8404-11717C355378}.Release (OpenGL)|Win32.Build.0 = Release|Win32 + {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32 + {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Debug (DirectX)|Win32.Build.0 = Debug|Win32 + {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32 + {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32 + {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Release (DirectX)|Win32.ActiveCfg = Release|Win32 + {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Release (DirectX)|Win32.Build.0 = Release|Win32 + {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32 + {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Release (OpenGL)|Win32.Build.0 = Release|Win32 + {E2DF1AC8-E9BF-43B8-B5D9-7EC3B513EEF1}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32 + {E2DF1AC8-E9BF-43B8-B5D9-7EC3B513EEF1}.Debug (DirectX)|Win32.Build.0 = Debug|Win32 + {E2DF1AC8-E9BF-43B8-B5D9-7EC3B513EEF1}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32 + {E2DF1AC8-E9BF-43B8-B5D9-7EC3B513EEF1}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32 + {E2DF1AC8-E9BF-43B8-B5D9-7EC3B513EEF1}.Release (DirectX)|Win32.ActiveCfg = Release|Win32 + {E2DF1AC8-E9BF-43B8-B5D9-7EC3B513EEF1}.Release (DirectX)|Win32.Build.0 = Release|Win32 + {E2DF1AC8-E9BF-43B8-B5D9-7EC3B513EEF1}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32 + {E2DF1AC8-E9BF-43B8-B5D9-7EC3B513EEF1}.Release (OpenGL)|Win32.Build.0 = Release|Win32 {87DA0A1E-3F33-4927-A5E5-2D58F2C58E17}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32 {87DA0A1E-3F33-4927-A5E5-2D58F2C58E17}.Debug (DirectX)|Win32.Build.0 = Debug|Win32 {87DA0A1E-3F33-4927-A5E5-2D58F2C58E17}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32 diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj index a4deda38d4..5cfb0263b2 100644 --- a/project/VS2010Express/XBMC.vcxproj +++ b/project/VS2010Express/XBMC.vcxproj @@ -278,6 +278,10 @@ </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\lib\SlingboxLib\SlingboxLib.cpp" /> + <ClCompile Include="..\..\xbmc\addons\AddonCallbacks.cpp" /> + <ClCompile Include="..\..\xbmc\addons\AddonCallbacksAddon.cpp" /> + <ClCompile Include="..\..\xbmc\addons\AddonCallbacksGUI.cpp" /> + <ClCompile Include="..\..\xbmc\addons\AddonCallbacksPVR.cpp" /> <ClCompile Include="..\..\xbmc\addons\AddonDatabase.cpp" /> <ClCompile Include="..\..\xbmc\addons\AddonInstaller.cpp" /> <ClCompile Include="..\..\xbmc\addons\AddonVersion.cpp" /> @@ -316,7 +320,9 @@ <ClCompile Include="..\..\xbmc\cores\AudioEngine\Utils\AEWAVLoader.cpp" /> <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Audio\DVDAudioCodecPassthrough.cpp" /> <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Video\CrystalHD.cpp" /> + <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDDemuxers\DVDDemuxPVRClient.cpp" /> <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamBluray.cpp" /> + <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamPVRManager.cpp" /> <ClCompile Include="..\..\xbmc\cores\paplayer\BXAcodec.cpp" /> <ClCompile Include="..\..\xbmc\cores\paplayer\PCMCodec.cpp" /> <ClCompile Include="..\..\xbmc\cores\VideoRenderers\RenderCapture.cpp" /> @@ -333,6 +339,7 @@ <ClCompile Include="..\..\xbmc\dialogs\GUIDialogButtonMenu.cpp" /> <ClCompile Include="..\..\xbmc\dialogs\GUIDialogCache.cpp" /> <ClCompile Include="..\..\xbmc\dialogs\GUIDialogContextMenu.cpp" /> + <ClCompile Include="..\..\xbmc\dialogs\GUIDialogExtendedProgressBar.cpp" /> <ClCompile Include="..\..\xbmc\dialogs\GUIDialogFavourites.cpp" /> <ClCompile Include="..\..\xbmc\dialogs\GUIDialogFileBrowser.cpp" /> <ClCompile Include="..\..\xbmc\dialogs\GUIDialogGamepad.cpp" /> @@ -355,6 +362,12 @@ <ClCompile Include="..\..\xbmc\dialogs\GUIDialogVolumeBar.cpp" /> <ClCompile Include="..\..\xbmc\dialogs\GUIDialogYesNo.cpp" /> <ClCompile Include="..\..\xbmc\DynamicDll.cpp" /> + <ClCompile Include="..\..\xbmc\epg\Epg.cpp" /> + <ClCompile Include="..\..\xbmc\epg\EpgContainer.cpp" /> + <ClCompile Include="..\..\xbmc\epg\EpgDatabase.cpp" /> + <ClCompile Include="..\..\xbmc\epg\EpgInfoTag.cpp" /> + <ClCompile Include="..\..\xbmc\epg\EpgSearchFilter.cpp" /> + <ClCompile Include="..\..\xbmc\epg\GUIEPGGridContainer.cpp" /> <ClCompile Include="..\..\xbmc\Favourites.cpp" /> <ClCompile Include="..\..\xbmc\FileItem.cpp" /> <ClCompile Include="..\..\xbmc\filesystem\AddonsDirectory.cpp" /> @@ -414,6 +427,8 @@ <ClCompile Include="..\..\xbmc\filesystem\NSFFileDirectory.cpp" /> <ClCompile Include="..\..\xbmc\filesystem\OGGFileDirectory.cpp" /> <ClCompile Include="..\..\xbmc\filesystem\PipeFile.cpp" /> + <ClCompile Include="..\..\xbmc\filesystem\PVRDirectory.cpp" /> + <ClCompile Include="..\..\xbmc\filesystem\PVRFile.cpp" /> <ClCompile Include="..\..\xbmc\filesystem\PipesManager.cpp" /> <ClCompile Include="..\..\xbmc\filesystem\PlaylistDirectory.cpp" /> <ClCompile Include="..\..\xbmc\filesystem\PlaylistFileDirectory.cpp" /> @@ -857,6 +872,38 @@ <ClCompile Include="..\..\xbmc\powermanagement\windows\Win32PowerSyscall.cpp" /> <ClCompile Include="..\..\xbmc\programs\GUIViewStatePrograms.cpp" /> <ClCompile Include="..\..\xbmc\programs\GUIWindowPrograms.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\addons\PVRClient.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\addons\PVRClients.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannel.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannelGroup.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannelGroupInternal.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannelGroups.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannelGroupsContainer.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRChannelManager.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRChannelsOSD.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRCutterOSD.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRDirectorOSD.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGroupManager.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideInfo.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideOSD.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideSearch.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRRecordingInfo.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRTimerSettings.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\PVRDatabase.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\PVRGUIInfo.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\PVRManager.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\recordings\PVRRecording.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\recordings\PVRRecordings.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\timers\PVRTimerInfoTag.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\timers\PVRTimers.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\windows\GUIViewStatePVR.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVR.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRChannels.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRCommon.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRGuide.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRRecordings.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRSearch.cpp" /> + <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRTimers.cpp" /> <ClCompile Include="..\..\xbmc\rendering\dx\GUIWindowTestPatternDX.cpp" /> <ClCompile Include="..\..\xbmc\rendering\dx\RenderSystemDX.cpp" /> <ClCompile Include="..\..\xbmc\rendering\gl\GUIWindowTestPatternGL.cpp"> @@ -1104,6 +1151,7 @@ <ClCompile Include="..\..\xbmc\utils\LCD.cpp" /> <ClCompile Include="..\..\xbmc\utils\log.cpp" /> <ClCompile Include="..\..\xbmc\utils\md5.cpp" /> + <ClCompile Include="..\..\xbmc\utils\Observer.cpp" /> <ClCompile Include="..\..\xbmc\utils\Mime.cpp" /> <ClCompile Include="..\..\xbmc\utils\PerformanceSample.cpp" /> <ClCompile Include="..\..\xbmc\utils\PerformanceStats.cpp" /> @@ -1122,6 +1170,7 @@ <ClCompile Include="..\..\xbmc\utils\StreamUtils.cpp" /> <ClCompile Include="..\..\xbmc\utils\StringUtils.cpp" /> <ClCompile Include="..\..\xbmc\utils\SystemInfo.cpp" /> + <ClCompile Include="..\..\xbmc\utils\TextSearch.cpp" /> <ClCompile Include="..\..\xbmc\utils\TimeSmoother.cpp" /> <ClCompile Include="..\..\xbmc\utils\TimeUtils.cpp" /> <ClCompile Include="..\..\xbmc\utils\TuxBoxUtil.cpp" /> @@ -1439,10 +1488,15 @@ <ClInclude Include="..\..\lib\DllAvFilter.h" /> <ClInclude Include="..\..\lib\ffmpeg\include-xbmc-win32\libavutil\avconfig.h" /> <ClInclude Include="..\..\lib\SlingboxLib\SlingboxLib.h" /> + <ClInclude Include="..\..\xbmc\addons\AddonCallbacks.h" /> + <ClInclude Include="..\..\xbmc\addons\AddonCallbacksAddon.h" /> + <ClInclude Include="..\..\xbmc\addons\AddonCallbacksGUI.h" /> + <ClInclude Include="..\..\xbmc\addons\AddonCallbacksPVR.h" /> <ClInclude Include="..\..\xbmc\addons\AddonDatabase.h" /> <ClInclude Include="..\..\xbmc\addons\AddonInstaller.h" /> <ClInclude Include="..\..\xbmc\addons\AddonVersion.h" /> <ClInclude Include="..\..\xbmc\addons\DllLibCPluff.h" /> + <ClInclude Include="..\..\xbmc\addons\DllPVRClient.h" /> <ClInclude Include="..\..\xbmc\addons\GUIDialogAddonInfo.h" /> <ClInclude Include="..\..\xbmc\addons\GUIDialogAddonSettings.h" /> <ClInclude Include="..\..\xbmc\addons\GUIViewStateAddonBrowser.h" /> @@ -1457,7 +1511,9 @@ <ClInclude Include="..\..\xbmc\AutoSwitch.h" /> <ClInclude Include="..\..\xbmc\BackgroundInfoLoader.h" /> <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Video\CrystalHD.h" /> + <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDDemuxers\DVDDemuxPVRClient.h" /> <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamBluray.h" /> + <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamPVRManager.h" /> <ClInclude Include="..\..\xbmc\cores\paplayer\BXAcodec.h" /> <ClInclude Include="..\..\xbmc\cores\VideoRenderers\RenderCapture.h" /> <ClInclude Include="..\..\xbmc\cores\VideoRenderers\VideoShaders\WinVideoFilter.h" /> @@ -1472,6 +1528,7 @@ <ClInclude Include="..\..\xbmc\dialogs\GUIDialogButtonMenu.h" /> <ClInclude Include="..\..\xbmc\dialogs\GUIDialogCache.h" /> <ClInclude Include="..\..\xbmc\dialogs\GUIDialogContextMenu.h" /> + <ClInclude Include="..\..\xbmc\dialogs\GUIDialogExtendedProgressBar.h" /> <ClInclude Include="..\..\xbmc\dialogs\GUIDialogFavourites.h" /> <ClInclude Include="..\..\xbmc\dialogs\GUIDialogFileBrowser.h" /> <ClInclude Include="..\..\xbmc\dialogs\GUIDialogGamepad.h" /> @@ -1493,8 +1550,16 @@ <ClInclude Include="..\..\xbmc\dialogs\GUIDialogVolumeBar.h" /> <ClInclude Include="..\..\xbmc\dialogs\GUIDialogYesNo.h" /> <ClInclude Include="..\..\xbmc\DynamicDll.h" /> + <ClInclude Include="..\..\xbmc\epg\Epg.h" /> + <ClInclude Include="..\..\xbmc\epg\EpgContainer.h" /> + <ClInclude Include="..\..\xbmc\epg\EpgDatabase.h" /> + <ClInclude Include="..\..\xbmc\epg\EpgInfoTag.h" /> + <ClInclude Include="..\..\xbmc\epg\EpgSearchFilter.h" /> + <ClInclude Include="..\..\xbmc\epg\GUIEPGGridContainer.h" /> <ClInclude Include="..\..\xbmc\Favourites.h" /> <ClInclude Include="..\..\xbmc\FileItem.h" /> + <ClInclude Include="..\..\xbmc\filesystem\PVRDirectory.h" /> + <ClInclude Include="..\..\xbmc\filesystem\PVRFile.h" /> <ClInclude Include="..\..\xbmc\FileSystem\VideoDatabaseDirectory\DirectoryNodeCountry.h" /> <ClInclude Include="..\..\xbmc\GUIInfoManager.h" /> <ClInclude Include="..\..\xbmc\GUILargeTextureManager.h" /> @@ -1811,6 +1876,38 @@ <ClInclude Include="..\..\xbmc\powermanagement\windows\Win32PowerSyscall.h" /> <ClInclude Include="..\..\xbmc\programs\GUIViewStatePrograms.h" /> <ClInclude Include="..\..\xbmc\programs\GUIWindowPrograms.h" /> + <ClInclude Include="..\..\xbmc\pvr\addons\PVRClient.h" /> + <ClInclude Include="..\..\xbmc\pvr\addons\PVRClients.h" /> + <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannel.h" /> + <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannelGroup.h" /> + <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannelGroupInternal.h" /> + <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannelGroups.h" /> + <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannelGroupsContainer.h" /> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRChannelManager.h" /> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRChannelsOSD.h" /> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRCutterOSD.h" /> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRDirectorOSD.h" /> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGroupManager.h" /> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideInfo.h" /> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideOSD.h" /> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideSearch.h" /> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRRecordingInfo.h" /> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRTimerSettings.h" /> + <ClInclude Include="..\..\xbmc\pvr\PVRDatabase.h" /> + <ClInclude Include="..\..\xbmc\pvr\PVRGUIInfo.h" /> + <ClInclude Include="..\..\xbmc\pvr\PVRManager.h" /> + <ClInclude Include="..\..\xbmc\pvr\recordings\PVRRecording.h" /> + <ClInclude Include="..\..\xbmc\pvr\recordings\PVRRecordings.h" /> + <ClInclude Include="..\..\xbmc\pvr\timers\PVRTimerInfoTag.h" /> + <ClInclude Include="..\..\xbmc\pvr\timers\PVRTimers.h" /> + <ClInclude Include="..\..\xbmc\pvr\windows\GUIViewStatePVR.h" /> + <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVR.h" /> + <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRChannels.h" /> + <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRCommon.h" /> + <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRGuide.h" /> + <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRRecordings.h" /> + <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRSearch.h" /> + <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRTimers.h" /> <ClInclude Include="..\..\xbmc\rendering\dx\GUIWindowTestPatternDX.h" /> <ClInclude Include="..\..\xbmc\rendering\dx\RenderSystemDX.h" /> <ClInclude Include="..\..\xbmc\rendering\gl\GUIWindowTestPatternGL.h"> @@ -1898,6 +1995,7 @@ <ClInclude Include="..\..\xbmc\utils\log.h" /> <ClInclude Include="..\..\xbmc\utils\MathUtils.h" /> <ClInclude Include="..\..\xbmc\utils\md5.h" /> + <ClInclude Include="..\..\xbmc\utils\Observer.h" /> <ClInclude Include="..\..\xbmc\utils\Mime.h" /> <ClInclude Include="..\..\xbmc\utils\PerformanceSample.h" /> <ClInclude Include="..\..\xbmc\utils\PerformanceStats.h" /> @@ -1918,6 +2016,7 @@ <ClInclude Include="..\..\xbmc\utils\StreamUtils.h" /> <ClInclude Include="..\..\xbmc\utils\StringUtils.h" /> <ClInclude Include="..\..\xbmc\utils\SystemInfo.h" /> + <ClInclude Include="..\..\xbmc\utils\TextSearch.h" /> <ClInclude Include="..\..\xbmc\utils\TimeSmoother.h" /> <ClInclude Include="..\..\xbmc\utils\TimeUtils.h" /> <ClInclude Include="..\..\xbmc\utils\TuxBoxUtil.h" /> @@ -2291,4 +2390,4 @@ </VisualStudio> </ProjectExtensions> <Import Project="$(SolutionDir)\$(ProjectFileName).targets.user" Condition="Exists('$(SolutionDir)\$(ProjectFileName).targets.user')" /> -</Project>
\ No newline at end of file +</Project> diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters index b322fa4949..738a6173de 100644 --- a/project/VS2010Express/XBMC.vcxproj.filters +++ b/project/VS2010Express/XBMC.vcxproj.filters @@ -190,6 +190,30 @@ <Filter Include="windows"> <UniqueIdentifier>{910f98b6-acfa-4b29-a589-21fd2544d142}</UniqueIdentifier> </Filter> + <Filter Include="pvr"> + <UniqueIdentifier>{5d07f015-2e93-4085-bda3-2be566fb07a8}</UniqueIdentifier> + </Filter> + <Filter Include="pvr\dialogs"> + <UniqueIdentifier>{5fb53298-e501-4f23-bf84-fc61acf9b814}</UniqueIdentifier> + </Filter> + <Filter Include="epg"> + <UniqueIdentifier>{ef8e21c9-b588-4255-ba38-57c6ae82d0aa}</UniqueIdentifier> + </Filter> + <Filter Include="pvr\windows"> + <UniqueIdentifier>{43455925-2158-4eff-97ce-1fa3f6597a3a}</UniqueIdentifier> + </Filter> + <Filter Include="pvr\timers"> + <UniqueIdentifier>{14af7c50-6457-48ec-87b2-4efb3986bdd8}</UniqueIdentifier> + </Filter> + <Filter Include="pvr\recordings"> + <UniqueIdentifier>{eab084ef-b5b5-4a61-b2a5-eac88bbcc73a}</UniqueIdentifier> + </Filter> + <Filter Include="pvr\channels"> + <UniqueIdentifier>{7be58f63-0e53-4a26-9894-e52c2bd78709}</UniqueIdentifier> + </Filter> + <Filter Include="pvr\addons"> + <UniqueIdentifier>{dbfd4898-7df3-4393-8b04-ab0cc1265c33}</UniqueIdentifier> + </Filter> <Filter Include="libs\SlingboxLib"> <UniqueIdentifier>{dfa70c36-927b-4540-b505-35919e64eb3d}</UniqueIdentifier> </Filter> @@ -1912,6 +1936,39 @@ <ClCompile Include="..\..\xbmc\addons\AddonInstaller.cpp"> <Filter>addons</Filter> </ClCompile> + <ClCompile Include="..\..\xbmc\epg\Epg.cpp"> + <Filter>epg</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\epg\EpgContainer.cpp"> + <Filter>epg</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\epg\EpgDatabase.cpp"> + <Filter>epg</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\epg\EpgInfoTag.cpp"> + <Filter>epg</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\epg\EpgSearchFilter.cpp"> + <Filter>epg</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\filesystem\PVRDirectory.cpp"> + <Filter>filesystem</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\filesystem\PVRFile.cpp"> + <Filter>filesystem</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\utils\Observer.cpp"> + <Filter>utils</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamPVRManager.cpp"> + <Filter>cores\dvdplayer\DVDInputStreams</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDDemuxers\DVDDemuxPVRClient.cpp"> + <Filter>cores\dvdplayer\DVDDemuxers</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\utils\TextSearch.cpp"> + <Filter>utils</Filter> + </ClCompile> <ClCompile Include="..\..\xbmc\utils\GLUtils.cpp"> <Filter>utils</Filter> </ClCompile> @@ -1921,18 +1978,132 @@ <ClCompile Include="..\..\xbmc\win32\stat_utf8.cpp"> <Filter>win32</Filter> </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVR.cpp"> + <Filter>pvr\windows</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\timers\PVRTimerInfoTag.cpp"> + <Filter>pvr\timers</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\timers\PVRTimers.cpp"> + <Filter>pvr\timers</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\recordings\PVRRecording.cpp"> + <Filter>pvr\recordings</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\recordings\PVRRecordings.cpp"> + <Filter>pvr\recordings</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRChannelManager.cpp"> + <Filter>pvr\dialogs</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRChannelsOSD.cpp"> + <Filter>pvr\dialogs</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRCutterOSD.cpp"> + <Filter>pvr\dialogs</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRDirectorOSD.cpp"> + <Filter>pvr\dialogs</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGroupManager.cpp"> + <Filter>pvr\dialogs</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideInfo.cpp"> + <Filter>pvr\dialogs</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideOSD.cpp"> + <Filter>pvr\dialogs</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideSearch.cpp"> + <Filter>pvr\dialogs</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRRecordingInfo.cpp"> + <Filter>pvr\dialogs</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRTimerSettings.cpp"> + <Filter>pvr\dialogs</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannel.cpp"> + <Filter>pvr\channels</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannelGroup.cpp"> + <Filter>pvr\channels</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannelGroupInternal.cpp"> + <Filter>pvr\channels</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannelGroups.cpp"> + <Filter>pvr\channels</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\channels\PVRChannelGroupsContainer.cpp"> + <Filter>pvr\channels</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\addons\PVRClient.cpp"> + <Filter>pvr\addons</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\dialogs\GUIDialogExtendedProgressBar.cpp"> + <Filter>dialogs</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\PVRDatabase.cpp"> + <Filter>pvr</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\PVRManager.cpp"> + <Filter>pvr</Filter> + </ClCompile> <ClCompile Include="..\..\xbmc\interfaces\python\xbmcmodule\xbmcvfsmodule.cpp"> <Filter>interfaces\python\xbmcmodule</Filter> </ClCompile> <ClCompile Include="..\..\xbmc\cores\VideoRenderers\RenderCapture.cpp"> <Filter>cores\VideoRenderers</Filter> </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\windows\GUIViewStatePVR.cpp"> + <Filter>pvr\windows</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRChannels.cpp"> + <Filter>pvr\windows</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRCommon.cpp"> + <Filter>pvr\windows</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRGuide.cpp"> + <Filter>pvr\windows</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRRecordings.cpp"> + <Filter>pvr\windows</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRSearch.cpp"> + <Filter>pvr\windows</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\windows\GUIWindowPVRTimers.cpp"> + <Filter>pvr\windows</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\addons\PVRClients.cpp"> + <Filter>pvr\addons</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\addons\AddonCallbacks.cpp"> + <Filter>addons</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\addons\AddonCallbacksAddon.cpp"> + <Filter>addons</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\addons\AddonCallbacksGUI.cpp"> + <Filter>addons</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\addons\AddonCallbacksPVR.cpp"> + <Filter>addons</Filter> + </ClCompile> + <ClCompile Include="..\..\xbmc\filesystem\CacheCircular.cpp"> + <Filter>filesystem</Filter> + </ClCompile> <ClCompile Include="..\..\lib\SlingboxLib\SlingboxLib.cpp"> <Filter>libs\SlingboxLib</Filter> </ClCompile> <ClCompile Include="..\..\xbmc\dialogs\GUIDialogPlayEject.cpp"> <Filter>dialogs</Filter> </ClCompile> + <ClCompile Include="..\..\xbmc\pvr\PVRGUIInfo.cpp"> + <Filter>pvr</Filter> + </ClCompile> <ClCompile Include="..\..\xbmc\interfaces\json-rpc\JSONServiceDescription.cpp"> <Filter>interfaces\json-rpc</Filter> </ClCompile> @@ -1945,6 +2116,9 @@ <ClCompile Include="..\..\xbmc\interfaces\json-rpc\InputOperations.cpp"> <Filter>interfaces\json-rpc</Filter> </ClCompile> + <ClCompile Include="..\..\xbmc\epg\GUIEPGGridContainer.cpp"> + <Filter>epg</Filter> + </ClCompile> <ClCompile Include="..\..\xbmc\input\XBMC_keytable.cpp"> <Filter>input</Filter> </ClCompile> @@ -4488,8 +4662,41 @@ <ClInclude Include="..\..\xbmc\addons\AddonInstaller.h"> <Filter>addons</Filter> </ClInclude> - <ClInclude Include="..\..\lib\ffmpeg\include-xbmc-win32\libavutil\avconfig.h"> - <Filter>cores\dvdplayer\DVDHeaders</Filter> + <ClInclude Include="..\..\xbmc\epg\EpgSearchFilter.h"> + <Filter>epg</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\epg\Epg.h"> + <Filter>epg</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\epg\EpgContainer.h"> + <Filter>epg</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\epg\EpgDatabase.h"> + <Filter>epg</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\epg\EpgInfoTag.h"> + <Filter>epg</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\filesystem\PVRFile.h"> + <Filter>filesystem</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\filesystem\PVRDirectory.h"> + <Filter>filesystem</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\addons\DllPVRClient.h"> + <Filter>addons</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\utils\Observer.h"> + <Filter>utils</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamPVRManager.h"> + <Filter>cores\dvdplayer\DVDInputStreams</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDDemuxers\DVDDemuxPVRClient.h"> + <Filter>cores\dvdplayer\DVDDemuxers</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\utils\TextSearch.h"> + <Filter>utils</Filter> </ClInclude> <ClInclude Include="..\..\xbmc\utils\GLUtils.h"> <Filter>utils</Filter> @@ -4500,18 +4707,130 @@ <ClInclude Include="..\..\xbmc\win32\stat_utf8.h"> <Filter>win32</Filter> </ClInclude> + <ClInclude Include="..\..\lib\ffmpeg\include-xbmc-win32\libavutil\avconfig.h" /> + <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVR.h"> + <Filter>pvr\windows</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\timers\PVRTimers.h"> + <Filter>pvr\timers</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\timers\PVRTimerInfoTag.h"> + <Filter>pvr\timers</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\recordings\PVRRecordings.h"> + <Filter>pvr\recordings</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\recordings\PVRRecording.h"> + <Filter>pvr\recordings</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRTimerSettings.h"> + <Filter>pvr\dialogs</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRChannelManager.h"> + <Filter>pvr\dialogs</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRChannelsOSD.h"> + <Filter>pvr\dialogs</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRCutterOSD.h"> + <Filter>pvr\dialogs</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRDirectorOSD.h"> + <Filter>pvr\dialogs</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGroupManager.h"> + <Filter>pvr\dialogs</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideInfo.h"> + <Filter>pvr\dialogs</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideOSD.h"> + <Filter>pvr\dialogs</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRGuideSearch.h"> + <Filter>pvr\dialogs</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\dialogs\GUIDialogPVRRecordingInfo.h"> + <Filter>pvr\dialogs</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannelGroupsContainer.h"> + <Filter>pvr\channels</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannel.h"> + <Filter>pvr\channels</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannelGroup.h"> + <Filter>pvr\channels</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannelGroupInternal.h"> + <Filter>pvr\channels</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\channels\PVRChannelGroups.h"> + <Filter>pvr\channels</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\addons\PVRClient.h"> + <Filter>pvr\addons</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\dialogs\GUIDialogExtendedProgressBar.h"> + <Filter>dialogs</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\PVRDatabase.h"> + <Filter>pvr</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\PVRManager.h"> + <Filter>pvr</Filter> + </ClInclude> <ClInclude Include="..\..\xbmc\cores\VideoRenderers\RenderCapture.h"> <Filter>cores\VideoRenderers</Filter> </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRTimers.h"> + <Filter>pvr\windows</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\windows\GUIViewStatePVR.h"> + <Filter>pvr\windows</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRChannels.h"> + <Filter>pvr\windows</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRCommon.h"> + <Filter>pvr\windows</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRGuide.h"> + <Filter>pvr\windows</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRRecordings.h"> + <Filter>pvr\windows</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\windows\GUIWindowPVRSearch.h"> + <Filter>pvr\windows</Filter> + </ClInclude> <ClInclude Include="..\..\xbmc\utils\GlobalsHandling.h"> <Filter>utils</Filter> </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\addons\PVRClients.h"> + <Filter>pvr\addons</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\addons\AddonCallbacks.h"> + <Filter>addons</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\addons\AddonCallbacksAddon.h"> + <Filter>addons</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\addons\AddonCallbacksGUI.h"> + <Filter>addons</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\addons\AddonCallbacksPVR.h"> + <Filter>addons</Filter> + </ClInclude> <ClInclude Include="..\..\lib\SlingboxLib\SlingboxLib.h"> <Filter>libs\SlingboxLib</Filter> </ClInclude> <ClInclude Include="..\..\xbmc\dialogs\GUIDialogPlayEject.h"> <Filter>dialogs</Filter> </ClInclude> + <ClInclude Include="..\..\xbmc\pvr\PVRGUIInfo.h"> + <Filter>pvr</Filter> + </ClInclude> <ClInclude Include="..\..\xbmc\interfaces\json-rpc\JSONServiceDescription.h"> <Filter>interfaces\json-rpc</Filter> </ClInclude> @@ -4521,6 +4840,9 @@ <ClInclude Include="..\..\xbmc\interfaces\json-rpc\InputOperations.h"> <Filter>interfaces\json-rpc</Filter> </ClInclude> + <ClInclude Include="..\..\xbmc\epg\GUIEPGGridContainer.h"> + <Filter>epg</Filter> + </ClInclude> <ClInclude Include="..\..\xbmc\input\XBMC_keytable.h"> <Filter>input</Filter> </ClInclude> @@ -5246,4 +5568,4 @@ <Filter>win32</Filter> </CustomBuild> </ItemGroup> -</Project>
\ No newline at end of file +</Project> diff --git a/system/keymaps/keyboard.xml b/system/keymaps/keyboard.xml index 5478a15787..6ddcf12e14 100644 --- a/system/keymaps/keyboard.xml +++ b/system/keymaps/keyboard.xml @@ -94,6 +94,12 @@ <backslash>ToggleFullScreen</backslash> <home>FirstPage</home> <end>LastPage</end> + <!-- PVR windows --> + <e>XBMC.ActivateWindowAndFocus(MyPVR, 31,0, 10,0)</e> + <h>XBMC.ActivateWindowAndFocus(MyPVR, 32,0, 11,0)</h> + <j>XBMC.ActivateWindowAndFocus(MyPVR, 33,0, 12,0)</j> + <k>XBMC.ActivateWindowAndFocus(MyPVR, 34,0, 13,0)</k> + <b>XBMC.ActivateWindowAndFocus(MyPVR, 35,0, 14,0)</b> <!-- Multimedia keyboard keys --> <browser_back>Back</browser_back> <browser_forward/> @@ -159,6 +165,13 @@ <backspace>Backspace</backspace> </keyboard> </VirtualKeyboard> + <MyTV> + <keyboard> + <delete>Delete</delete> + <m>Move</m> + <r>Rename</r> + </keyboard> + </MyTV> <MyFiles> <keyboard> <space>Highlight</space> @@ -220,6 +233,7 @@ <down>BigStepBack</down> <a>AudioDelay</a> <escape>Fullscreen</escape> + <c>Playlist</c> <v>XBMC.ActivateWindow(Teletext)</v> <up mod="ctrl">SubtitleShiftUp</up> <down mod="ctrl">SubtitleShiftDown</down> @@ -269,6 +283,8 @@ <o>CodecInfo</o> <l>LockPreset</l> <escape>FullScreen</escape> + <g>XBMC.ActivateWindow(PVROSDGuide)</g> + <c>XBMC.ActivateWindow(PVROSDChannels)</c> </keyboard> </Visualisation> <MusicOSD> @@ -458,6 +474,46 @@ <v>Back</v> </keyboard> </Teletext> + <Favourites> + <keyboard> + <backspace>Close</backspace> + </keyboard> + </Favourites> + <NumericInput> + <keyboard> + <backspace>Close</backspace> + </keyboard> + </NumericInput> + <PVROSDChannels> + <keyboard> + <backspace>Close</backspace> + <escape>Close</escape> + <c>Close</c> + </keyboard> + </PVROSDChannels> + <PVROSDGuide> + <keyboard> + <backspace>Close</backspace> + <escape>Close</escape> + </keyboard> + </PVROSDGuide> + <PVROSDDirector> + <keyboard> + <backspace>Close</backspace> + <escape>Close</escape> + </keyboard> + </PVROSDDirector> + <PVROSDCutter> + <keyboard> + <backspace>Close</backspace> + <escape>Close</escape> + </keyboard> + </PVROSDCutter> + <MyTVSettings> + <keyboard> + <backspace>PreviousMenu</backspace> + </keyboard> + </MyTVSettings> <FileBrowser> <keyboard> <space>Highlight</space> diff --git a/system/keymaps/remote.xml b/system/keymaps/remote.xml index c0e06a06ba..a5e7eb1e55 100644 --- a/system/keymaps/remote.xml +++ b/system/keymaps/remote.xml @@ -66,8 +66,13 @@ <myvideo>XBMC.ActivateWindow(MyVideos)</myvideo> <mymusic>XBMC.ActivateWindow(MyMusic)</mymusic> <mypictures>XBMC.ActivateWindow(MyPictures)</mypictures> - <mytv>XBMC.ActivateWindow(VideoLibrary,TvShows)</mytv> - <red>XBMC.ActivateWindow(Home)</red> + <mytv>XBMC.ActivateWindow(MyPVR)</mytv> + <guide>XBMC.ActivateWindowAndFocus(MyPVR, 31,0, 10,0)</guide> + <livetv>XBMC.ActivateWindowAndFocus(MyPVR, 32,0, 11,0)</livetv> + <liveradio>XBMC.ActivateWindowAndFocus(MyPVR, 33,0, 12,0)</liveradio> + <recordedtv>XBMC.ActivateWindowAndFocus(MyPVR, 34,0, 13,0)</recordedtv> + <epgsearch>XBMC.ActivateWindowAndFocus(MyPVR, 35,0, 14,0)</epgsearch> + <red>XBMC.ActivateWindow(MyPVR)</red> <green>XBMC.ActivateWindow(MyVideos)</green> <yellow>XBMC.ActivateWindow(MyMusic)</yellow> <blue>XBMC.ActivateWindow(MyPictures)</blue> @@ -90,6 +95,11 @@ <hash>XBMC.ActivateWindow(Settings)</hash> </remote> </Home> + <MyTV> + <remote> + <clear>Delete</clear> + </remote> + </MyTV> <MyFiles> <remote> <clear>Delete</clear> @@ -143,10 +153,13 @@ <select>AspectRatio</select> <title>CodecInfo</title> <info>Info</info> + <guide>XBMC.ActivateWindow(PVROSDGuide)</guide> <teletext>XBMC.ActivateWindow(Teletext)</teletext> <subtitle>NextSubtitle</subtitle> <star>NextSubtitle</star> <language>AudioNextLanguage</language> + <playlist>Playlist</playlist> + <language>Language</language> <hash>AudioNextLanguage</hash> <pageplus>SkipNext</pageplus> <pageminus>SkipPrevious</pageminus> @@ -182,6 +195,8 @@ <menu>XBMC.ActivateWindow(MusicOSD)</menu> <start>XBMC.ActivateWindow(MusicOSD)</start> <info>Info</info> + <guide>XBMC.ActivateWindow(PVROSDGuide)</guide> + <playlist>XBMC.ActivateWindow(PVROSDChannels)</playlist> </remote> </Visualisation> <MusicOSD> @@ -343,6 +358,103 @@ <back>BackSpace</back> </remote> </NumericInput> + <Weather> + <remote> + <back>PreviousMenu</back> + </remote> + </Weather> + <TV> + <remote> + <red>Red</red> + <green>Green</green> + <yellow>Yellow</yellow> + <blue>Blue</blue> + </remote> + </TV> + <Settings> + <remote> + <back>PreviousMenu</back> + </remote> + </Settings> + <AddonBrowser> + <remote> + </remote> + </AddonBrowser> + <AddonInformation> + <remote> + <back>Close</back> + </remote> + </AddonInformation> + <AddonSettings> + <remote> + <back>Close</back> + </remote> + </AddonSettings> + <TextViewer> + <remote> + <back>Close</back> + </remote> + </TextViewer> + <MyPicturesSettings> + <remote> + <back>PreviousMenu</back> + </remote> + </MyPicturesSettings> + <MyProgramsSettings> + <remote> + <back>PreviousMenu</back> + </remote> + </MyProgramsSettings> + <MyWeatherSettings> + <remote> + <back>PreviousMenu</back> + </remote> + </MyWeatherSettings> + <MyMusicSettings> + <remote> + <back>PreviousMenu</back> + </remote> + </MyMusicSettings> + <SystemSettings> + <remote> + <back>PreviousMenu</back> + </remote> + </SystemSettings> + <MyVideosSettings> + <remote> + <back>PreviousMenu</back> + </remote> + </MyVideosSettings> + <NetworkSettings> + <remote> + <back>PreviousMenu</back> + </remote> + </NetworkSettings> + <AppearanceSettings> + <remote> + <back>PreviousMenu</back> + </remote> + </AppearanceSettings> + <Profiles> + <remote> + <back>PreviousMenu</back> + </remote> + </Profiles> + <systeminfo> + <remote> + <back>PreviousMenu</back> + </remote> + </systeminfo> + <shutdownmenu> + <remote> + <back>PreviousMenu</back> + </remote> + </shutdownmenu> + <submenu> + <remote> + <back>PreviousMenu</back> + </remote> + </submenu> <MusicInformation> <remote> <info>Back</info> @@ -392,6 +504,44 @@ <teletext>Back</teletext> </remote> </Teletext> + <Favourites> + <remote> + <back>Close</back> + </remote> + </Favourites> + <PVROSDChannels> + <remote> + <back>Close</back> + <menu>Close</menu> + <start>Close</start> + <playlist>Close</playlist> + </remote> + </PVROSDChannels> + <PVROSDGuide> + <remote> + <back>Close</back> + <menu>Close</menu> + <start>Close</start> + <guide>Close</guide> + </remote> + </PVROSDGuide> + <PVROSDDirector> + <remote> + <back>Close</back> + <menu>Close</menu> + </remote> + </PVROSDDirector> + <PVROSDCutter> + <remote> + <back>Close</back> + <menu>Close</menu> + </remote> + </PVROSDCutter> + <MyTVSettings> + <remote> + <back>PreviousMenu</back> + </remote> + </MyTVSettings> <AddonSettings> <remote> <clear>Delete</clear> diff --git a/system/playercorefactory.xml b/system/playercorefactory.xml index 6e475ef0f5..0267a5c612 100644 --- a/system/playercorefactory.xml +++ b/system/playercorefactory.xml @@ -33,5 +33,8 @@ <!-- Pass these to dvdplayer as we do not know if they are audio or video --> <rule name="nsv" filetypes="nsv" player="DVDPlayer" /> + + <!-- pvr radio channels should be played by dvdplayer because they need buffering --> + <rule name="radio" filetypes="pvr" filename=".*/radio/.*" player="DVDPlayer" /> </rules> </playercorefactory> diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index 861d0d1290..93f76a1b9c 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -237,6 +237,7 @@ #include "dialogs/GUIDialogYesNo.h" #include "dialogs/GUIDialogOK.h" #include "dialogs/GUIDialogProgress.h" +#include "dialogs/GUIDialogExtendedProgressBar.h" #include "dialogs/GUIDialogSelect.h" #include "dialogs/GUIDialogSeekBar.h" #include "dialogs/GUIDialogKaiToast.h" @@ -260,6 +261,24 @@ #ifdef HAS_LINUX_NETWORK #include "network/GUIDialogAccessPoints.h" #endif + +/* PVR related include Files */ +#include "pvr/PVRManager.h" +#include "pvr/timers/PVRTimers.h" +#include "pvr/windows/GUIWindowPVR.h" +#include "pvr/dialogs/GUIDialogPVRChannelManager.h" +#include "pvr/dialogs/GUIDialogPVRChannelsOSD.h" +#include "pvr/dialogs/GUIDialogPVRCutterOSD.h" +#include "pvr/dialogs/GUIDialogPVRDirectorOSD.h" +#include "pvr/dialogs/GUIDialogPVRGroupManager.h" +#include "pvr/dialogs/GUIDialogPVRGuideInfo.h" +#include "pvr/dialogs/GUIDialogPVRGuideOSD.h" +#include "pvr/dialogs/GUIDialogPVRGuideSearch.h" +#include "pvr/dialogs/GUIDialogPVRRecordingInfo.h" +#include "pvr/dialogs/GUIDialogPVRTimerSettings.h" + +#include "epg/EpgContainer.h" + #include "video/dialogs/GUIDialogFullScreenInfo.h" #include "video/dialogs/GUIDialogTeletext.h" #include "dialogs/GUIDialogSlider.h" @@ -340,6 +359,8 @@ using namespace EVENTSERVER; using namespace JSONRPC; #endif using namespace ANNOUNCEMENT; +using namespace PVR; +using namespace EPG; using namespace PERIPHERALS; using namespace XbmcThreads; @@ -378,6 +399,7 @@ CApplication::CApplication(void) { TiXmlBase::SetCondenseWhiteSpace(false); m_iPlaySpeed = 1; + m_bInhibitIdleShutdown = false; m_bScreenSave = false; m_dpms = NULL; m_dpmsIsActive = false; @@ -1228,6 +1250,7 @@ bool CApplication::Initialize() g_windowManager.Add(new CGUIWindowPointer); // window id = 99 g_windowManager.Add(new CGUIDialogYesNo); // window id = 100 g_windowManager.Add(new CGUIDialogProgress); // window id = 101 + g_windowManager.Add(new CGUIDialogExtendedProgressBar); // window id = 148 g_windowManager.Add(new CGUIDialogKeyboardGeneric); // window id = 103 g_windowManager.Add(new CGUIDialogVolumeBar); // window id = 104 g_windowManager.Add(new CGUIDialogSeekBar); // window id = 115 @@ -1281,6 +1304,20 @@ bool CApplication::Initialize() g_windowManager.Add(new CGUIWindowMusicNav); // window id = 502 g_windowManager.Add(new CGUIWindowMusicPlaylistEditor); // window id = 503 + /* Load PVR related Windows and Dialogs */ + g_windowManager.Add(new CGUIWindowPVR); // window id = 600 + g_windowManager.Add(new CGUIDialogPVRGuideInfo); // window id = 601 + g_windowManager.Add(new CGUIDialogPVRRecordingInfo); // window id = 602 + g_windowManager.Add(new CGUIDialogPVRTimerSettings); // window id = 603 + g_windowManager.Add(new CGUIDialogPVRGroupManager); // window id = 604 + g_windowManager.Add(new CGUIDialogPVRChannelManager); // window id = 605 + g_windowManager.Add(new CGUIDialogPVRGuideSearch); // window id = 606 + g_windowManager.Add(new CGUIDialogPVRChannelsOSD); // window id = 609 + g_windowManager.Add(new CGUIDialogPVRGuideOSD); // window id = 610 + g_windowManager.Add(new CGUIDialogPVRDirectorOSD); // window id = 611 + g_windowManager.Add(new CGUIDialogPVRCutterOSD); // window id = 612 + g_windowManager.Add(new CGUIDialogTeletext); // window id = 613 + g_windowManager.Add(new CGUIDialogSelect); // window id = 2000 g_windowManager.Add(new CGUIDialogMusicInfo); // window id = 2001 g_windowManager.Add(new CGUIDialogOK); // window id = 2002 @@ -1310,6 +1347,9 @@ bool CApplication::Initialize() return false; } + StartEPGManager(); + StartPVRManager(); + if (g_advancedSettings.m_splashImage) SAFE_DELETE(m_splash); @@ -1782,6 +1822,30 @@ void CApplication::StopZeroconf() #endif } +void CApplication::StartPVRManager() +{ + if (g_guiSettings.GetBool("pvrmanager.enabled")) + g_PVRManager.Start(); +} + +void CApplication::StartEPGManager(void) +{ + g_EpgContainer.Start(); +} + +void CApplication::StopPVRManager() +{ + CLog::Log(LOGINFO, "stopping PVRManager"); + if (g_PVRManager.IsPlaying()) + StopPlaying(); + g_PVRManager.Stop(); +} + +void CApplication::StopEPGManager(void) +{ + g_EpgContainer.Stop(); +} + void CApplication::DimLCDOnPlayback(bool dim) { #ifdef HAS_LCD @@ -2722,7 +2786,7 @@ bool CApplication::OnAction(const CAction &action) return true; } - if ( IsPlaying()) + if (IsPlaying() && !CurrentFileItem().IsLiveTV()) { // pause : pauses current audio song if (action.GetID() == ACTION_PAUSE && m_iPlaySpeed == 1) @@ -3444,11 +3508,24 @@ bool CApplication::Cleanup() g_windowManager.Delete(WINDOW_DIALOG_ACCESS_POINTS); g_windowManager.Delete(WINDOW_DIALOG_SLIDER); + /* Delete PVR related windows and dialogs */ + g_windowManager.Delete(WINDOW_PVR); + g_windowManager.Delete(WINDOW_DIALOG_PVR_GUIDE_INFO); + g_windowManager.Delete(WINDOW_DIALOG_PVR_RECORDING_INFO); + g_windowManager.Delete(WINDOW_DIALOG_PVR_TIMER_SETTING); + g_windowManager.Delete(WINDOW_DIALOG_PVR_GROUP_MANAGER); + g_windowManager.Delete(WINDOW_DIALOG_PVR_CHANNEL_MANAGER); + g_windowManager.Delete(WINDOW_DIALOG_PVR_GUIDE_SEARCH); + g_windowManager.Delete(WINDOW_DIALOG_PVR_CHANNEL_SCAN); + g_windowManager.Delete(WINDOW_DIALOG_PVR_UPDATE_PROGRESS); + g_windowManager.Delete(WINDOW_DIALOG_PVR_OSD_CHANNELS); + g_windowManager.Delete(WINDOW_DIALOG_PVR_OSD_GUIDE); + g_windowManager.Delete(WINDOW_DIALOG_PVR_OSD_DIRECTOR); + g_windowManager.Delete(WINDOW_DIALOG_PVR_OSD_CUTTER); g_windowManager.Delete(WINDOW_DIALOG_OSD_TELETEXT); - g_windowManager.Delete(WINDOW_DIALOG_TEXT_VIEWER); + g_windowManager.Delete(WINDOW_DIALOG_TEXT_VIEWER); g_windowManager.Delete(WINDOW_DIALOG_PLAY_EJECT); - g_windowManager.Delete(WINDOW_STARTUP_ANIM); g_windowManager.Delete(WINDOW_LOGIN_SCREEN); g_windowManager.Delete(WINDOW_VISUALISATION); @@ -3464,6 +3541,7 @@ bool CApplication::Cleanup() g_windowManager.Delete(WINDOW_DIALOG_MUSIC_OVERLAY); g_windowManager.Delete(WINDOW_DIALOG_VIDEO_OVERLAY); g_windowManager.Delete(WINDOW_SLIDESHOW); + g_windowManager.Delete(WINDOW_ADDON_BROWSER); g_windowManager.Delete(WINDOW_HOME); g_windowManager.Delete(WINDOW_PROGRAMS); @@ -3478,6 +3556,7 @@ bool CApplication::Cleanup() g_windowManager.Remove(WINDOW_SETTINGS_MYVIDEOS); g_windowManager.Remove(WINDOW_SETTINGS_SERVICE); g_windowManager.Remove(WINDOW_SETTINGS_APPEARANCE); + g_windowManager.Remove(WINDOW_SETTINGS_MYPVR); g_windowManager.Remove(WINDOW_DIALOG_KAI_TOAST); g_windowManager.Remove(WINDOW_DIALOG_SEEK_BAR); @@ -3595,6 +3674,8 @@ void CApplication::Stop(int exitCode) CApplicationMessenger::Get().Cleanup(); + StopPVRManager(); + StopEPGManager(); StopServices(); //Sleep(5000); @@ -4189,6 +4270,9 @@ bool CApplication::PlayFile(const CFileItem& item, bool bRestart) #if !defined(TARGET_DARWIN) && !defined(_LINUX) g_audioManager.Enable(false); #endif + + if (item.HasPVRChannelInfoTag()) + g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_NONE); } m_bPlaybackStarting = false; @@ -4483,7 +4567,7 @@ bool CApplication::IsFullScreen() void CApplication::SaveFileState(bool bForeground /* = false */) { - if (!g_settings.GetCurrentProfile().canWriteDatabases()) + if (m_progressTrackingItem->IsPVRChannel() || !g_settings.GetCurrentProfile().canWriteDatabases()) return; if (bForeground) @@ -4580,6 +4664,9 @@ void CApplication::StopPlaying() m_pKaraokeMgr->Stop(); #endif + if (g_PVRManager.IsPlayingTV() || g_PVRManager.IsPlayingRadio()) + g_PVRManager.SaveCurrentChannelSettings(); + if (m_pPlayer) m_pPlayer->CloseFile(); @@ -4801,7 +4888,7 @@ void CApplication::ActivateScreenSaver(bool forceType /*= false */) if (!forceType) { // set to Dim in the case of a dialog on screen or playing video - if (g_windowManager.HasModalDialog() || (IsPlayingVideo() && g_guiSettings.GetBool("screensaver.usedimonpause"))) + if (g_windowManager.HasModalDialog() || (IsPlayingVideo() && g_guiSettings.GetBool("screensaver.usedimonpause")) || g_PVRManager.IsRunningChannelScan()) { if (!CAddonMgr::Get().GetAddon("screensaver.xbmc.builtin.dim", m_screenSaver)) m_screenSaver.reset(new CScreenSaver("")); @@ -4840,7 +4927,8 @@ void CApplication::ActivateScreenSaver(bool forceType /*= false */) void CApplication::CheckShutdown() { // first check if we should reset the timer - bool resetTimer = false; + bool resetTimer = m_bInhibitIdleShutdown; + if (IsPlaying() || IsPaused()) // is something playing? resetTimer = true; @@ -4853,6 +4941,9 @@ void CApplication::CheckShutdown() if (g_windowManager.IsWindowActive(WINDOW_DIALOG_PROGRESS)) // progress dialog is onscreen resetTimer = true; + if (g_guiSettings.GetBool("pvrmanager.enabled") && !g_PVRManager.IsIdle()) + resetTimer = true; + if (resetTimer) { m_shutdownTimer.StartZero(); @@ -4869,6 +4960,16 @@ void CApplication::CheckShutdown() } } +void CApplication::InhibitIdleShutdown(bool inhibit) +{ + m_bInhibitIdleShutdown = inhibit; +} + +bool CApplication::IsIdleShutdownInhibited() const +{ + return m_bInhibitIdleShutdown; +} + bool CApplication::OnMessage(CGUIMessage& message) { switch ( message.GetMessage() ) @@ -5913,7 +6014,8 @@ bool CApplication::ProcessAndStartPlaylist(const CStdString& strPlayList, CPlayL void CApplication::SaveCurrentFileSettings() { - if (m_itemCurrentFile->IsVideo()) + // don't store settings for PVR in video database + if (m_itemCurrentFile->IsVideo() && !m_itemCurrentFile->IsPVRChannel()) { // save video settings if (g_settings.m_currentVideoSettings != g_settings.m_defaultVideoSettings) @@ -5924,6 +6026,10 @@ void CApplication::SaveCurrentFileSettings() dbs.Close(); } } + else if (m_itemCurrentFile->IsPVRChannel()) + { + g_PVRManager.SaveCurrentChannelSettings(); + } } bool CApplication::AlwaysProcess(const CAction& action) diff --git a/xbmc/Application.h b/xbmc/Application.h index 021aa77151..9c5d6ce49d 100644 --- a/xbmc/Application.h +++ b/xbmc/Application.h @@ -150,6 +150,10 @@ public: void StopUPnPRenderer(); void StartUPnPServer(); void StopUPnPServer(); + void StartPVRManager(); + void StartEPGManager(void); + void StopPVRManager(); + void StopEPGManager(void); bool StartEventServer(); bool StopEventServer(bool bWait, bool promptuser); void RefreshEventServer(); @@ -196,6 +200,8 @@ public: bool OnAppCommand(const CAction &action); bool OnAction(const CAction &action); void CheckShutdown(); + void InhibitIdleShutdown(bool inhibit); + bool IsIdleShutdownInhibited() const; // Checks whether the screensaver and / or DPMS should become active. void CheckScreenSaverAndDPMS(); void CheckPlayingProgress(); @@ -397,6 +403,8 @@ protected: CStopWatch m_screenSaverTimer; CStopWatch m_shutdownTimer; + bool m_bInhibitIdleShutdown; + DPMSSupport* m_dpms; bool m_dpmsIsActive; bool m_dpmsIsManual; diff --git a/xbmc/ApplicationMessenger.cpp b/xbmc/ApplicationMessenger.cpp index aa23a65bc7..81a89f5ab9 100644 --- a/xbmc/ApplicationMessenger.cpp +++ b/xbmc/ApplicationMessenger.cpp @@ -52,6 +52,8 @@ #elif defined(TARGET_DARWIN) #include "CocoaInterface.h" #endif +#include "addons/AddonCallbacks.h" +#include "addons/AddonCallbacksGUI.h" #include "storage/MediaManager.h" #include "guilib/LocalizeStrings.h" #include "threads/SingleLock.h" @@ -69,6 +71,9 @@ #include "ThumbLoader.h" +#include "pvr/PVRManager.h" + +using namespace PVR; using namespace std; using namespace MUSIC_INFO; @@ -253,7 +258,7 @@ void CApplicationMessenger::ProcessMessage(ThreadMessage *pMsg) } break; - case TMSG_POWERDOWN: + case TMSG_POWERDOWN: { g_application.Stop(EXITCODE_POWERDOWN); g_powerManager.Powerdown(); @@ -268,12 +273,14 @@ void CApplicationMessenger::ProcessMessage(ThreadMessage *pMsg) case TMSG_HIBERNATE: { + g_PVRManager.SetWakeupCommand(); g_powerManager.Hibernate(); } break; case TMSG_SUSPEND: { + g_PVRManager.SetWakeupCommand(); g_powerManager.Suspend(); } break; @@ -294,6 +301,12 @@ void CApplicationMessenger::ProcessMessage(ThreadMessage *pMsg) } break; + case TMSG_INHIBITIDLESHUTDOWN: + { + g_application.InhibitIdleShutdown((bool)pMsg->dwParam1); + } + break; + case TMSG_MEDIA_PLAY: { // first check if we were called from the PlayFile() function @@ -700,6 +713,15 @@ void CApplicationMessenger::ProcessMessage(ThreadMessage *pMsg) } break; + case TMSG_GUI_ADDON_DIALOG: + { + if (pMsg->lpVoid) + { // TODO: This is ugly - really these python dialogs should just be normal XBMC dialogs + ((ADDON::CGUIAddonWindowDialog *) pMsg->lpVoid)->Show_Internal(pMsg->dwParam2 > 0); + } + } + break; + case TMSG_GUI_PYTHON_DIALOG: { if (pMsg->lpVoid) @@ -921,10 +943,10 @@ void CApplicationMessenger::PlayFile(const CFileItem &item, bool bRestart /*= fa SendMessage(tMsg, false); } -void CApplicationMessenger::MediaStop() +void CApplicationMessenger::MediaStop(bool bWait /* = true */) { ThreadMessage tMsg = {TMSG_MEDIA_STOP}; - SendMessage(tMsg, true); + SendMessage(tMsg, bWait); } void CApplicationMessenger::MediaPause() @@ -1129,6 +1151,12 @@ void CApplicationMessenger::RestartApp() SendMessage(tMsg); } +void CApplicationMessenger::InhibitIdleShutdown(bool inhibit) +{ + ThreadMessage tMsg = {TMSG_INHIBITIDLESHUTDOWN, (DWORD)inhibit}; + SendMessage(tMsg); +} + void CApplicationMessenger::NetworkMessage(DWORD dwMessage, DWORD dwParam) { ThreadMessage tMsg = {TMSG_NETWORKMESSAGE, dwMessage, dwParam}; diff --git a/xbmc/ApplicationMessenger.h b/xbmc/ApplicationMessenger.h index 0a3b6b6de6..fd19687e4f 100644 --- a/xbmc/ApplicationMessenger.h +++ b/xbmc/ApplicationMessenger.h @@ -87,6 +87,7 @@ namespace MUSIC_INFO #define TMSG_TOGGLEFULLSCREEN 310 #define TMSG_SETLANGUAGE 311 #define TMSG_RENDERER_FLUSH 312 +#define TMSG_INHIBITIDLESHUTDOWN 313 #define TMSG_HTTPAPI 400 @@ -100,7 +101,8 @@ namespace MUSIC_INFO #define TMSG_GUI_ACTION 607 #define TMSG_GUI_INFOLABEL 608 #define TMSG_GUI_INFOBOOL 609 -#define TMSG_GUI_MESSAGE 610 +#define TMSG_GUI_ADDON_DIALOG 610 +#define TMSG_GUI_MESSAGE 611 #define TMSG_CALLBACK 800 @@ -159,7 +161,7 @@ public: void MediaPlay(const CFileItem &item); void MediaPlay(const CFileItemList &item, int song = 0); void MediaPlay(int playlistid, int song = -1); - void MediaStop(); + void MediaStop(bool bWait = true); void MediaPause(); void MediaRestart(bool bWait); @@ -191,6 +193,7 @@ public: void Restart(); void RestartApp(); void Reset(); + void InhibitIdleShutdown(bool inhibit); void SwitchToFullscreen(); // void Minimize(bool wait = false); void ExecOS(const CStdString command, bool waitExit = false); diff --git a/xbmc/DatabaseManager.cpp b/xbmc/DatabaseManager.cpp index a23ce08bd3..2f34415592 100644 --- a/xbmc/DatabaseManager.cpp +++ b/xbmc/DatabaseManager.cpp @@ -26,9 +26,13 @@ #include "TextureDatabase.h" #include "music/MusicDatabase.h" #include "video/VideoDatabase.h" +#include "pvr/PVRDatabase.h" +#include "epg/EpgDatabase.h" #include "settings/AdvancedSettings.h" using namespace std; +using namespace EPG; +using namespace PVR; CDatabaseManager &CDatabaseManager::Get() { @@ -55,6 +59,8 @@ void CDatabaseManager::Initialize(bool addonsOnly) { CTextureDatabase db; UpdateDatabase(db); } { CMusicDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseMusic); } { CVideoDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseVideo); } + { CPVRDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseTV); } + { CEpgDatabase db; UpdateDatabase(db, &g_advancedSettings.m_databaseEpg); } CLog::Log(LOGDEBUG, "%s, updating databases... DONE", __FUNCTION__); } diff --git a/xbmc/FileItem.cpp b/xbmc/FileItem.cpp index f35fed45bd..8512e4dea5 100644 --- a/xbmc/FileItem.cpp +++ b/xbmc/FileItem.cpp @@ -37,6 +37,11 @@ #include "video/VideoDatabase.h" #include "music/MusicDatabase.h" #include "utils/TuxBoxUtil.h" +#include "epg/Epg.h" +#include "pvr/channels/PVRChannel.h" +#include "pvr/recordings/PVRRecording.h" +#include "pvr/timers/PVRTimerInfoTag.h" +#include "utils/Observer.h" #include "video/VideoInfoTag.h" #include "threads/SingleLock.h" #include "music/tags/MusicInfoTag.h" @@ -58,11 +63,17 @@ using namespace std; using namespace XFILE; using namespace PLAYLIST; using namespace MUSIC_INFO; +using namespace PVR; +using namespace EPG; CFileItem::CFileItem(const CSong& song) { m_musicInfoTag = NULL; m_videoInfoTag = NULL; + m_epgInfoTag = NULL; + m_pvrChannelInfoTag = NULL; + m_pvrRecordingInfoTag = NULL; + m_pvrTimerInfoTag = NULL; m_pictureInfoTag = NULL; Reset(); SetLabel(song.strTitle); @@ -79,6 +90,10 @@ CFileItem::CFileItem(const CStdString &path, const CAlbum& album) { m_musicInfoTag = NULL; m_videoInfoTag = NULL; + m_epgInfoTag = NULL; + m_pvrChannelInfoTag = NULL; + m_pvrRecordingInfoTag = NULL; + m_pvrTimerInfoTag = NULL; m_pictureInfoTag = NULL; Reset(); SetLabel(album.strAlbum); @@ -95,6 +110,10 @@ CFileItem::CFileItem(const CMusicInfoTag& music) { m_musicInfoTag = NULL; m_videoInfoTag = NULL; + m_epgInfoTag = NULL; + m_pvrChannelInfoTag = NULL; + m_pvrRecordingInfoTag = NULL; + m_pvrTimerInfoTag = NULL; m_pictureInfoTag = NULL; Reset(); SetLabel(music.GetTitle()); @@ -108,6 +127,10 @@ CFileItem::CFileItem(const CVideoInfoTag& movie) { m_musicInfoTag = NULL; m_videoInfoTag = NULL; + m_epgInfoTag = NULL; + m_pvrChannelInfoTag = NULL; + m_pvrRecordingInfoTag = NULL; + m_pvrTimerInfoTag = NULL; m_pictureInfoTag = NULL; Reset(); SetLabel(movie.m_strTitle); @@ -127,10 +150,136 @@ CFileItem::CFileItem(const CVideoInfoTag& movie) FillInDefaultIcon(); } +CFileItem::CFileItem(const CEpgInfoTag& tag) +{ + m_musicInfoTag = NULL; + m_videoInfoTag = NULL; + m_epgInfoTag = NULL; + m_pvrChannelInfoTag = NULL; + m_pvrRecordingInfoTag = NULL; + m_pvrTimerInfoTag = NULL; + m_pictureInfoTag = NULL; + + Reset(); + + m_strPath = tag.Path(); + m_bIsFolder = false; + *GetEPGInfoTag() = tag; + SetLabel(tag.Title()); + m_strLabel2 = tag.Plot(); + m_dateTime = tag.StartAsLocalTime(); + + if (!tag.Icon().IsEmpty()) + { + SetThumbnailImage(tag.Icon()); + SetIconImage(tag.Icon()); + } +} + +CFileItem::CFileItem(const CPVRChannel& channel) +{ + m_musicInfoTag = NULL; + m_videoInfoTag = NULL; + m_epgInfoTag = NULL; + m_pvrChannelInfoTag = NULL; + m_pvrRecordingInfoTag = NULL; + m_pvrTimerInfoTag = NULL; + m_pictureInfoTag = NULL; + + Reset(); + CEpgInfoTag epgNow; + bool bHasEpgNow = channel.GetEPGNow(epgNow); + + m_strPath = channel.Path(); + m_bIsFolder = false; + *GetPVRChannelInfoTag() = channel; + SetLabel(channel.ChannelName()); + m_strLabel2 = bHasEpgNow ? epgNow.Title() : + g_guiSettings.GetBool("epg.hidenoinfoavailable") ? + StringUtils::EmptyString : + g_localizeStrings.Get(19055); // no information available + + if (channel.IsRadio() && bHasEpgNow) + { + CMusicInfoTag* musictag = GetMusicInfoTag(); + if (musictag) + { + musictag->SetURL(channel.Path()); + musictag->SetTitle(m_strLabel2); + musictag->SetArtist(channel.ChannelName()); + musictag->SetAlbumArtist(channel.ChannelName()); + if (bHasEpgNow) + musictag->SetGenre(epgNow.Genre()); + musictag->SetDuration(bHasEpgNow ? epgNow.GetDuration() : 3600); + musictag->SetLoaded(true); + musictag->SetComment(""); + musictag->SetLyrics(""); + } + } + + if (!channel.IconPath().IsEmpty()) + { + SetThumbnailImage(channel.IconPath()); + SetIconImage(channel.IconPath()); + } + + SetProperty("channelid", channel.ChannelID()); + SetProperty("path", channel.Path()); +} + +CFileItem::CFileItem(const CPVRRecording& record) +{ + m_musicInfoTag = NULL; + m_videoInfoTag = NULL; + m_epgInfoTag = NULL; + m_pvrChannelInfoTag = NULL; + m_pvrRecordingInfoTag = NULL; + m_pvrTimerInfoTag = NULL; + m_pictureInfoTag = NULL; + + Reset(); + + m_strPath = record.m_strFileNameAndPath; + m_bIsFolder = false; + *GetPVRRecordingInfoTag() = record; + SetLabel(record.m_strTitle); + m_strLabel2 = record.m_strPlot; +} + +CFileItem::CFileItem(const CPVRTimerInfoTag& timer) +{ + m_musicInfoTag = NULL; + m_videoInfoTag = NULL; + m_epgInfoTag = NULL; + m_pvrChannelInfoTag = NULL; + m_pvrRecordingInfoTag = NULL; + m_pvrTimerInfoTag = NULL; + m_pictureInfoTag = NULL; + + Reset(); + + m_strPath = timer.Path(); + m_bIsFolder = false; + *GetPVRTimerInfoTag() = timer; + SetLabel(timer.Title()); + m_strLabel2 = timer.Summary(); + m_dateTime = timer.StartAsLocalTime(); + + if (!timer.ChannelIcon().IsEmpty()) + { + SetThumbnailImage(timer.ChannelIcon()); + SetIconImage(timer.ChannelIcon()); + } +} + CFileItem::CFileItem(const CArtist& artist) { m_musicInfoTag = NULL; m_videoInfoTag = NULL; + m_epgInfoTag = NULL; + m_pvrChannelInfoTag = NULL; + m_pvrRecordingInfoTag = NULL; + m_pvrTimerInfoTag = NULL; m_pictureInfoTag = NULL; Reset(); SetLabel(artist.strArtist); @@ -144,6 +293,10 @@ CFileItem::CFileItem(const CGenre& genre) { m_musicInfoTag = NULL; m_videoInfoTag = NULL; + m_epgInfoTag = NULL; + m_pvrChannelInfoTag = NULL; + m_pvrRecordingInfoTag = NULL; + m_pvrTimerInfoTag = NULL; m_pictureInfoTag = NULL; Reset(); SetLabel(genre.strGenre); @@ -157,6 +310,10 @@ CFileItem::CFileItem(const CFileItem& item): CGUIListItem() { m_musicInfoTag = NULL; m_videoInfoTag = NULL; + m_epgInfoTag = NULL; + m_pvrChannelInfoTag = NULL; + m_pvrRecordingInfoTag = NULL; + m_pvrTimerInfoTag = NULL; m_pictureInfoTag = NULL; *this = item; } @@ -165,6 +322,10 @@ CFileItem::CFileItem(const CGUIListItem& item) { m_musicInfoTag = NULL; m_videoInfoTag = NULL; + m_epgInfoTag = NULL; + m_pvrChannelInfoTag = NULL; + m_pvrRecordingInfoTag = NULL; + m_pvrTimerInfoTag = NULL; m_pictureInfoTag = NULL; Reset(); // not particularly pretty, but it gets around the issue of Reset() defaulting @@ -176,6 +337,10 @@ CFileItem::CFileItem(void) { m_musicInfoTag = NULL; m_videoInfoTag = NULL; + m_epgInfoTag = NULL; + m_pvrChannelInfoTag = NULL; + m_pvrRecordingInfoTag = NULL; + m_pvrTimerInfoTag = NULL; m_pictureInfoTag = NULL; Reset(); } @@ -185,6 +350,10 @@ CFileItem::CFileItem(const CStdString& strLabel) { m_musicInfoTag = NULL; m_videoInfoTag = NULL; + m_epgInfoTag = NULL; + m_pvrChannelInfoTag = NULL; + m_pvrRecordingInfoTag = NULL; + m_pvrTimerInfoTag = NULL; m_pictureInfoTag = NULL; Reset(); SetLabel(strLabel); @@ -194,6 +363,10 @@ CFileItem::CFileItem(const CStdString& strPath, bool bIsFolder) { m_musicInfoTag = NULL; m_videoInfoTag = NULL; + m_epgInfoTag = NULL; + m_pvrChannelInfoTag = NULL; + m_pvrRecordingInfoTag = NULL; + m_pvrTimerInfoTag = NULL; m_pictureInfoTag = NULL; Reset(); m_strPath = strPath; @@ -207,6 +380,10 @@ CFileItem::CFileItem(const CMediaSource& share) { m_musicInfoTag = NULL; m_videoInfoTag = NULL; + m_epgInfoTag = NULL; + m_pvrChannelInfoTag = NULL; + m_pvrRecordingInfoTag = NULL; + m_pvrTimerInfoTag = NULL; m_pictureInfoTag = NULL; Reset(); m_bIsFolder = true; @@ -232,10 +409,18 @@ CFileItem::~CFileItem(void) { delete m_musicInfoTag; delete m_videoInfoTag; + delete m_epgInfoTag; + delete m_pvrChannelInfoTag; + delete m_pvrRecordingInfoTag; + delete m_pvrTimerInfoTag; delete m_pictureInfoTag; m_musicInfoTag = NULL; m_videoInfoTag = NULL; + m_epgInfoTag = NULL; + m_pvrChannelInfoTag = NULL; + m_pvrRecordingInfoTag = NULL; + m_pvrTimerInfoTag = NULL; m_pictureInfoTag = NULL; } @@ -275,6 +460,62 @@ const CFileItem& CFileItem::operator=(const CFileItem& item) m_videoInfoTag = NULL; } + if (item.HasEPGInfoTag()) + { + m_epgInfoTag = GetEPGInfoTag(); + if (m_epgInfoTag) + *m_epgInfoTag = *item.m_epgInfoTag; + } + else + { + if (m_epgInfoTag) + delete m_epgInfoTag; + + m_epgInfoTag = NULL; + } + + if (item.HasPVRChannelInfoTag()) + { + m_pvrChannelInfoTag = GetPVRChannelInfoTag(); + if (m_pvrChannelInfoTag) + *m_pvrChannelInfoTag = *item.m_pvrChannelInfoTag; + } + else + { + if (m_pvrChannelInfoTag) + delete m_pvrChannelInfoTag; + + m_pvrChannelInfoTag = NULL; + } + + if (item.HasPVRRecordingInfoTag()) + { + m_pvrRecordingInfoTag = GetPVRRecordingInfoTag(); + if (m_pvrRecordingInfoTag) + *m_pvrRecordingInfoTag = *item.m_pvrRecordingInfoTag; + } + else + { + if (m_pvrRecordingInfoTag) + delete m_pvrRecordingInfoTag; + + m_pvrRecordingInfoTag = NULL; + } + + if (item.HasPVRTimerInfoTag()) + { + m_pvrTimerInfoTag = GetPVRTimerInfoTag(); + if (m_pvrTimerInfoTag) + *m_pvrTimerInfoTag = *item.m_pvrTimerInfoTag; + } + else + { + if (m_pvrTimerInfoTag) + delete m_pvrTimerInfoTag; + + m_pvrTimerInfoTag = NULL; + } + if (item.HasPictureInfoTag()) { m_pictureInfoTag = GetPictureInfoTag(); @@ -339,6 +580,14 @@ void CFileItem::Reset() m_musicInfoTag=NULL; delete m_videoInfoTag; m_videoInfoTag=NULL; + delete m_epgInfoTag; + m_epgInfoTag=NULL; + delete m_pvrChannelInfoTag; + m_pvrChannelInfoTag=NULL; + delete m_pvrRecordingInfoTag; + m_pvrRecordingInfoTag=NULL; + delete m_pvrTimerInfoTag; + m_pvrTimerInfoTag=NULL; delete m_pictureInfoTag; m_pictureInfoTag=NULL; m_extrainfo.Empty(); @@ -488,6 +737,9 @@ void CFileItem::ToSortable(SortItem &sortable) if (HasPictureInfoTag()) GetPictureInfoTag()->ToSortable(sortable); + + if (HasPVRChannelInfoTag()) + GetPVRChannelInfoTag()->ToSortable(sortable); } bool CFileItem::Exists(bool bUseCache /* = true */) const @@ -531,6 +783,7 @@ bool CFileItem::IsVideo() const if (HasVideoInfoTag()) return true; if (HasMusicInfoTag()) return false; if (HasPictureInfoTag()) return false; + if (IsPVRRecording()) return true; if (IsHDHomeRun() || IsTuxBox() || URIUtils::IsDVD(m_strPath) || IsSlingbox()) return true; @@ -555,6 +808,30 @@ bool CFileItem::IsVideo() const return (g_settings.m_videoExtensions.Find(extension) != -1); } +bool CFileItem::IsEPG() const +{ + if (HasEPGInfoTag()) return true; /// is this enough? + return false; +} + +bool CFileItem::IsPVRChannel() const +{ + if (HasPVRChannelInfoTag()) return true; /// is this enough? + return false; +} + +bool CFileItem::IsPVRRecording() const +{ + if (HasPVRRecordingInfoTag()) return true; /// is this enough? + return false; +} + +bool CFileItem::IsPVRTimer() const +{ + if (HasPVRTimerInfoTag()) return true; /// is this enough? + return false; +} + bool CFileItem::IsDiscStub() const { if (IsVideoDb() && HasVideoInfoTag()) @@ -882,6 +1159,11 @@ bool CFileItem::IsVTP() const return URIUtils::IsVTP(m_strPath); } +bool CFileItem::IsPVR() const +{ + return CUtil::IsPVR(m_strPath); +} + bool CFileItem::IsLiveTV() const { return URIUtils::IsLiveTV(m_strPath); @@ -946,7 +1228,19 @@ void CFileItem::FillInDefaultIcon() * in mind the complexity of the code behind the check in the * case of IsWhatater() returns false. */ - if ( IsAudio() ) + if (IsPVRChannel()) + { + if (GetPVRChannelInfoTag()->IsRadio()) + SetIconImage("DefaultAudio.png"); + else + SetIconImage("DefaultVideo.png"); + } + else if ( IsLiveTV() ) + { + // Live TV Channel + SetIconImage("DefaultVideo.png"); + } + else if ( IsAudio() ) { // audio SetIconImage("DefaultAudio.png"); @@ -956,6 +1250,14 @@ void CFileItem::FillInDefaultIcon() // video SetIconImage("DefaultVideo.png"); } + else if (IsPVRRecording()) + { + SetIconImage("DefaultVideo.png"); + } + else if (IsPVRTimer()) + { + SetIconImage("DefaultVideo.png"); + } else if ( IsPicture() ) { // picture @@ -1033,6 +1335,12 @@ void CFileItem::SetLabel(const CStdString &strLabel) CGUIListItem::SetLabel(strLabel); } +void CFileItem::SetLabel2(const CStdString &strLabel) +{ + m_strLabel2 = strLabel; +} + + void CFileItem::SetFileSizeLabel() { if( m_bIsFolder && m_dwSize == 0 ) @@ -1598,6 +1906,9 @@ void CFileItemList::Sort(SORT_METHOD sortMethod, SortOrder sortOrder) case SORT_METHOD_LISTENERS: sortBy = SortByListeners; break; + case SORT_METHOD_CHANNEL: + sortBy = SortByChannel; + break; default: CLog::Log(LOGWARNING, "Unknown sort method %d", sortMethod); return; @@ -2380,6 +2691,8 @@ bool CFileItemList::AlwaysCache() const return CMusicDatabaseDirectory::CanCache(GetPath()); if (IsVideoDb()) return CVideoDatabaseDirectory::CanCache(GetPath()); + if (IsEPG()) + return true; // always cache return false; } @@ -2848,6 +3161,38 @@ CVideoInfoTag* CFileItem::GetVideoInfoTag() return m_videoInfoTag; } +CEpgInfoTag* CFileItem::GetEPGInfoTag() +{ + if (!m_epgInfoTag) + m_epgInfoTag = new CEpgInfoTag; + + return m_epgInfoTag; +} + +CPVRChannel* CFileItem::GetPVRChannelInfoTag() +{ + if (!m_pvrChannelInfoTag) + m_pvrChannelInfoTag = new CPVRChannel; + + return m_pvrChannelInfoTag; +} + +CPVRRecording* CFileItem::GetPVRRecordingInfoTag() +{ + if (!m_pvrRecordingInfoTag) + m_pvrRecordingInfoTag = new CPVRRecording; + + return m_pvrRecordingInfoTag; +} + +CPVRTimerInfoTag* CFileItem::GetPVRTimerInfoTag() +{ + if (!m_pvrTimerInfoTag) + m_pvrTimerInfoTag = new CPVRTimerInfoTag; + + return m_pvrTimerInfoTag; +} + CPictureInfoTag* CFileItem::GetPictureInfoTag() { if (!m_pictureInfoTag) diff --git a/xbmc/FileItem.h b/xbmc/FileItem.h index f32429ff49..2c77d6751d 100644 --- a/xbmc/FileItem.h +++ b/xbmc/FileItem.h @@ -43,6 +43,16 @@ namespace MUSIC_INFO class CMusicInfoTag; } class CVideoInfoTag; +namespace EPG +{ + class CEpgInfoTag; +} +namespace PVR +{ + class CPVRChannel; + class CPVRRecording; + class CPVRTimerInfoTag; +} class CPictureInfoTag; class CAlbum; @@ -76,6 +86,10 @@ public: CFileItem(const CGenre& genre); CFileItem(const MUSIC_INFO::CMusicInfoTag& music); CFileItem(const CVideoInfoTag& movie); + CFileItem(const EPG::CEpgInfoTag& tag); + CFileItem(const PVR::CPVRChannel& channel); + CFileItem(const PVR::CPVRRecording& record); + CFileItem(const PVR::CPVRTimerInfoTag& timer); CFileItem(const CMediaSource& share); virtual ~CFileItem(void); virtual CGUIListItem *Clone() const { return new CFileItem(*this); }; @@ -133,6 +147,10 @@ public: bool IsMultiPath() const; bool IsMusicDb() const; bool IsVideoDb() const; + bool IsEPG() const; + bool IsPVRChannel() const; + bool IsPVRRecording() const; + bool IsPVRTimer() const; bool IsType(const char *ext) const; bool IsVirtualDirectoryRoot() const; bool IsReadOnly() const; @@ -146,6 +164,7 @@ public: bool IsHDHomeRun() const; bool IsSlingbox() const; bool IsVTP() const; + bool IsPVR() const; bool IsLiveTV() const; bool IsRSS() const; bool IsAndroidApp() const; @@ -155,6 +174,7 @@ public: void FillInDefaultIcon(); void SetFileSizeLabel(); virtual void SetLabel(const CStdString &strLabel); + virtual void SetLabel2(const CStdString &strLabel); CURL GetAsUrl() const; int GetVideoContentType() const; /* return VIDEODB_CONTENT_TYPE, but don't want to include videodb in this header */ bool IsLabelPreformated() const { return m_bLabelPreformated; } @@ -187,6 +207,54 @@ public: return m_videoInfoTag; } + inline bool HasEPGInfoTag() const + { + return m_epgInfoTag != NULL; + } + + EPG::CEpgInfoTag* GetEPGInfoTag(); + + inline const EPG::CEpgInfoTag* GetEPGInfoTag() const + { + return m_epgInfoTag; + } + + inline bool HasPVRChannelInfoTag() const + { + return m_pvrChannelInfoTag != NULL; + } + + PVR::CPVRChannel* GetPVRChannelInfoTag(); + + inline const PVR::CPVRChannel* GetPVRChannelInfoTag() const + { + return m_pvrChannelInfoTag; + } + + inline bool HasPVRRecordingInfoTag() const + { + return m_pvrRecordingInfoTag != NULL; + } + + PVR::CPVRRecording* GetPVRRecordingInfoTag(); + + inline const PVR::CPVRRecording* GetPVRRecordingInfoTag() const + { + return m_pvrRecordingInfoTag; + } + + inline bool HasPVRTimerInfoTag() const + { + return m_pvrTimerInfoTag != NULL; + } + + PVR::CPVRTimerInfoTag* GetPVRTimerInfoTag(); + + inline const PVR::CPVRTimerInfoTag* GetPVRTimerInfoTag() const + { + return m_pvrTimerInfoTag; + } + inline bool HasPictureInfoTag() const { return m_pictureInfoTag != NULL; @@ -300,6 +368,10 @@ private: CStdString m_extrainfo; MUSIC_INFO::CMusicInfoTag* m_musicInfoTag; CVideoInfoTag* m_videoInfoTag; + EPG::CEpgInfoTag* m_epgInfoTag; + PVR::CPVRChannel* m_pvrChannelInfoTag; + PVR::CPVRRecording* m_pvrRecordingInfoTag; + PVR::CPVRTimerInfoTag * m_pvrTimerInfoTag; CPictureInfoTag* m_pictureInfoTag; bool m_bIsAlbum; }; diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp index 947847151d..542d76dc01 100644 --- a/xbmc/GUIInfoManager.cpp +++ b/xbmc/GUIInfoManager.cpp @@ -67,6 +67,12 @@ #include "threads/SingleLock.h" #include "utils/log.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "epg/EpgInfoTag.h" +#include "pvr/timers/PVRTimers.h" +#include "pvr/recordings/PVRRecording.h" + #include "addons/AddonManager.h" #include "interfaces/info/InfoBool.h" #include "ThumbLoader.h" @@ -78,9 +84,12 @@ using namespace std; using namespace XFILE; using namespace MUSIC_INFO; using namespace ADDON; +using namespace PVR; using namespace INFO; +using namespace EPG; -CGUIInfoManager::CGUIInfoManager(void) +CGUIInfoManager::CGUIInfoManager(void) : + Observable() { m_lastSysHeatInfoTime = -SYSHEATUPDATEINTERVAL; // make sure we grab CPU temp on the first pass m_lastMusicBitrateTime = 0; @@ -257,6 +266,8 @@ const infomap system_labels[] = {{ "hasnetwork", SYSTEM_ETHERNET_LINK_ACT { "batterylevel", SYSTEM_BATTERY_LEVEL }, { "friendlyname", SYSTEM_FRIENDLY_NAME }, { "alarmpos", SYSTEM_ALARM_POS }, + { "isinhibit", SYSTEM_ISINHIBIT }, + { "hasshutdown", SYSTEM_HAS_SHUTDOWN }, { "haspvr", SYSTEM_HAS_PVR }}; const infomap system_param[] = {{ "hasalarm", SYSTEM_HAS_ALARM }, @@ -336,7 +347,11 @@ const infomap musicplayer[] = {{ "title", MUSICPLAYER_TITLE }, { "hasprevious", MUSICPLAYER_HASPREVIOUS }, { "hasnext", MUSICPLAYER_HASNEXT }, { "playcount", MUSICPLAYER_PLAYCOUNT }, - { "lastplayed", MUSICPLAYER_LASTPLAYED }}; + { "lastplayed", MUSICPLAYER_LASTPLAYED }, + { "channelname", MUSICPLAYER_CHANNEL_NAME }, + { "channelnumber", MUSICPLAYER_CHANNEL_NUMBER }, + { "channelgroup", MUSICPLAYER_CHANNEL_GROUP } +}; const infomap videoplayer[] = {{ "title", VIDEOPLAYER_TITLE }, { "genre", VIDEOPLAYER_GENRE }, @@ -378,7 +393,20 @@ const infomap videoplayer[] = {{ "title", VIDEOPLAYER_TITLE }, { "lastplayed", VIDEOPLAYER_LASTPLAYED }, { "playcount", VIDEOPLAYER_PLAYCOUNT }, { "hassubtitles", VIDEOPLAYER_HASSUBTITLES }, - { "subtitlesenabled", VIDEOPLAYER_SUBTITLESENABLED }}; + { "subtitlesenabled", VIDEOPLAYER_SUBTITLESENABLED }, + { "endtime", VIDEOPLAYER_ENDTIME }, + { "nexttitle", VIDEOPLAYER_NEXT_TITLE }, + { "nextgenre", VIDEOPLAYER_NEXT_GENRE }, + { "nextplot", VIDEOPLAYER_NEXT_PLOT }, + { "nextplotoutline", VIDEOPLAYER_NEXT_PLOT_OUTLINE }, + { "nextstarttime", VIDEOPLAYER_NEXT_STARTTIME }, + { "nextendtime", VIDEOPLAYER_NEXT_ENDTIME }, + { "nextduration", VIDEOPLAYER_NEXT_DURATION }, + { "channelname", VIDEOPLAYER_CHANNEL_NAME }, + { "channelnumber", VIDEOPLAYER_CHANNEL_NUMBER }, + { "channelgroup", VIDEOPLAYER_CHANNEL_GROUP }, + { "hasepg", VIDEOPLAYER_HAS_EPG }, + { "parentalrating", VIDEOPLAYER_PARENTAL_RATING }}; const infomap mediacontainer[] = {{ "hasfiles", CONTAINER_HASFILES }, { "hasfolders", CONTAINER_HASFOLDERS }, @@ -493,6 +521,26 @@ const infomap listitem_labels[]= {{ "thumb", LISTITEM_THUMB }, { "lastplayed", LISTITEM_LASTPLAYED }, { "playcount", LISTITEM_PLAYCOUNT }, { "discnumber", LISTITEM_DISC_NUMBER }, + { "starttime", LISTITEM_STARTTIME }, + { "endtime", LISTITEM_ENDTIME }, + { "startdate", LISTITEM_STARTDATE }, + { "enddate", LISTITEM_ENDDATE }, + { "nexttitle", LISTITEM_NEXT_TITLE }, + { "nextgenre", LISTITEM_NEXT_GENRE }, + { "nextplot", LISTITEM_NEXT_PLOT }, + { "nextplotoutline", LISTITEM_NEXT_PLOT_OUTLINE }, + { "nextstarttime", LISTITEM_NEXT_STARTTIME }, + { "nextendtime", LISTITEM_NEXT_ENDTIME }, + { "nextstartdate", LISTITEM_NEXT_STARTDATE }, + { "nextenddate", LISTITEM_NEXT_ENDDATE }, + { "channelname", LISTITEM_CHANNEL_NAME }, + { "channelnumber", LISTITEM_CHANNEL_NUMBER }, + { "channelgroup", LISTITEM_CHANNEL_GROUP }, + { "hasepg", LISTITEM_HAS_EPG }, + { "hastimer", LISTITEM_HASTIMER }, + { "isrecording", LISTITEM_ISRECORDING }, + { "isencrypted", LISTITEM_ISENCRYPTED }, + { "progress", LISTITEM_PROGRESS }, { "dateadded", LISTITEM_DATE_ADDED }, { "dbtype", LISTITEM_DBTYPE }, { "dbid", LISTITEM_DBID }}; @@ -532,6 +580,51 @@ const infomap playlist[] = {{ "length", PLAYLIST_LENGTH }, { "isrepeat", PLAYLIST_ISREPEAT }, { "isrepeatone", PLAYLIST_ISREPEATONE }}; +const infomap pvr[] = {{ "isrecording", PVR_IS_RECORDING }, + { "hastimer", PVR_HAS_TIMER }, + { "hasnonrecordingtimer", PVR_HAS_NONRECORDING_TIMER }, + { "nowrecordingtitle", PVR_NOW_RECORDING_TITLE }, + { "nowrecordingdatetime", PVR_NOW_RECORDING_DATETIME }, + { "nowrecordingchannel", PVR_NOW_RECORDING_CHANNEL }, + { "nowrecordingchannelicon", PVR_NOW_RECORDING_CHAN_ICO }, + { "nextrecordingtitle", PVR_NEXT_RECORDING_TITLE }, + { "nextrecordingdatetime", PVR_NEXT_RECORDING_DATETIME }, + { "nextrecordingchannel", PVR_NEXT_RECORDING_CHANNEL }, + { "nextrecordingchannelicon", PVR_NEXT_RECORDING_CHAN_ICO }, + { "backendname", PVR_BACKEND_NAME }, + { "backendversion", PVR_BACKEND_VERSION }, + { "backendhost", PVR_BACKEND_HOST }, + { "backenddiskspace", PVR_BACKEND_DISKSPACE }, + { "backendchannels", PVR_BACKEND_CHANNELS }, + { "backendtimers", PVR_BACKEND_TIMERS }, + { "backendrecordings", PVR_BACKEND_RECORDINGS }, + { "backendnumber", PVR_BACKEND_NUMBER }, + { "hasepg", PVR_HAS_EPG }, + { "hastxt", PVR_HAS_TXT }, + { "hasdirector", PVR_HAS_DIRECTOR }, + { "totaldiscspace", PVR_TOTAL_DISKSPACE }, + { "nexttimer", PVR_NEXT_TIMER }, + { "isplayingtv", PVR_IS_PLAYING_TV }, + { "isplayingradio", PVR_IS_PLAYING_RADIO }, + { "isplayingrecording", PVR_IS_PLAYING_RECORDING }, + { "duration", PVR_PLAYING_DURATION }, + { "time", PVR_PLAYING_TIME }, + { "progress", PVR_PLAYING_PROGRESS }, + { "actstreamclient", PVR_ACTUAL_STREAM_CLIENT }, + { "actstreamdevice", PVR_ACTUAL_STREAM_DEVICE }, + { "actstreamstatus", PVR_ACTUAL_STREAM_STATUS }, + { "actstreamsignal", PVR_ACTUAL_STREAM_SIG }, + { "actstreamsnr", PVR_ACTUAL_STREAM_SNR }, + { "actstreamber", PVR_ACTUAL_STREAM_BER }, + { "actstreamunc", PVR_ACTUAL_STREAM_UNC }, + { "actstreamvideobitrate", PVR_ACTUAL_STREAM_VIDEO_BR }, + { "actstreamaudiobitrate", PVR_ACTUAL_STREAM_AUDIO_BR }, + { "actstreamdolbybitrate", PVR_ACTUAL_STREAM_DOLBY_BR }, + { "actstreamprogrsignal", PVR_ACTUAL_STREAM_SIG_PROGR }, + { "actstreamprogrsnr", PVR_ACTUAL_STREAM_SNR_PROGR }, + { "actstreamisencrypted", PVR_ACTUAL_STREAM_ENCRYPTED }, + { "actstreamencryptionname", PVR_ACTUAL_STREAM_CRYPTION }}; + const infomap slideshow[] = {{ "ispaused", SLIDESHOW_ISPAUSED }, { "isactive", SLIDESHOW_ISACTIVE }, { "israndom", SLIDESHOW_ISRANDOM }}; @@ -1016,6 +1109,14 @@ int CGUIInfoManager::TranslateSingleString(const CStdString &strCondition) return playlist[i].val; } } + else if (cat.name == "pvr") + { + for (size_t i = 0; i < sizeof(pvr) / sizeof(infomap); i++) + { + if (prop.name == pvr[i].str) + return pvr[i].val; + } + } } else if (info.size() == 3) { @@ -1134,6 +1235,42 @@ CStdString CGUIInfoManager::GetLabel(int info, int contextWindow, CStdString *fa switch (info) { + case PVR_NEXT_RECORDING_CHANNEL: + case PVR_NEXT_RECORDING_CHAN_ICO: + case PVR_NEXT_RECORDING_DATETIME: + case PVR_NEXT_RECORDING_TITLE: + case PVR_NOW_RECORDING_CHANNEL: + case PVR_NOW_RECORDING_CHAN_ICO: + case PVR_NOW_RECORDING_DATETIME: + case PVR_NOW_RECORDING_TITLE: + case PVR_BACKEND_NAME: + case PVR_BACKEND_VERSION: + case PVR_BACKEND_HOST: + case PVR_BACKEND_DISKSPACE: + case PVR_BACKEND_CHANNELS: + case PVR_BACKEND_TIMERS: + case PVR_BACKEND_RECORDINGS: + case PVR_BACKEND_NUMBER: + case PVR_TOTAL_DISKSPACE: + case PVR_NEXT_TIMER: + case PVR_PLAYING_DURATION: + case PVR_PLAYING_TIME: + case PVR_PLAYING_PROGRESS: + case PVR_ACTUAL_STREAM_CLIENT: + case PVR_ACTUAL_STREAM_DEVICE: + case PVR_ACTUAL_STREAM_STATUS: + case PVR_ACTUAL_STREAM_SIG: + case PVR_ACTUAL_STREAM_SNR: + case PVR_ACTUAL_STREAM_SIG_PROGR: + case PVR_ACTUAL_STREAM_SNR_PROGR: + case PVR_ACTUAL_STREAM_BER: + case PVR_ACTUAL_STREAM_UNC: + case PVR_ACTUAL_STREAM_VIDEO_BR: + case PVR_ACTUAL_STREAM_AUDIO_BR: + case PVR_ACTUAL_STREAM_DOLBY_BR: + case PVR_ACTUAL_STREAM_CRYPTION: + g_PVRManager.TranslateCharInfo(info, strLabel); + break; case WEATHER_CONDITIONS: strLabel = g_weatherManager.GetInfo(WEATHER_LABEL_CURRENT_COND); strLabel = strLabel.Trim(); @@ -1234,6 +1371,9 @@ CStdString CGUIInfoManager::GetLabel(int info, int contextWindow, CStdString *fa case MUSICPLAYER_RATING: case MUSICPLAYER_COMMENT: case MUSICPLAYER_LYRICS: + case MUSICPLAYER_CHANNEL_NAME: + case MUSICPLAYER_CHANNEL_NUMBER: + case MUSICPLAYER_CHANNEL_GROUP: case MUSICPLAYER_PLAYCOUNT: case MUSICPLAYER_LASTPLAYED: strLabel = GetMusicLabel(info); @@ -1264,6 +1404,19 @@ CStdString CGUIInfoManager::GetLabel(int info, int contextWindow, CStdString *fa case VIDEOPLAYER_WRITER: case VIDEOPLAYER_TAGLINE: case VIDEOPLAYER_TRAILER: + case VIDEOPLAYER_STARTTIME: + case VIDEOPLAYER_ENDTIME: + case VIDEOPLAYER_NEXT_TITLE: + case VIDEOPLAYER_NEXT_GENRE: + case VIDEOPLAYER_NEXT_PLOT: + case VIDEOPLAYER_NEXT_PLOT_OUTLINE: + case VIDEOPLAYER_NEXT_STARTTIME: + case VIDEOPLAYER_NEXT_ENDTIME: + case VIDEOPLAYER_NEXT_DURATION: + case VIDEOPLAYER_CHANNEL_NAME: + case VIDEOPLAYER_CHANNEL_NUMBER: + case VIDEOPLAYER_CHANNEL_GROUP: + case VIDEOPLAYER_PARENTAL_RATING: case VIDEOPLAYER_PLAYCOUNT: case VIDEOPLAYER_LASTPLAYED: strLabel = GetVideoLabel(info); @@ -1776,6 +1929,11 @@ bool CGUIInfoManager::GetInt(int &value, int info, int contextWindow, const CGUI case SYSTEM_CPU_USAGE: value = g_cpuInfo.getUsedPercentage(); return true; + case PVR_PLAYING_PROGRESS: + case PVR_ACTUAL_STREAM_SIG_PROGR: + case PVR_ACTUAL_STREAM_SNR_PROGR: + value = g_PVRManager.TranslateIntInfo(info); + return true; case SYSTEM_BATTERY_LEVEL: value = g_powerManager.BatteryLevel(); return true; @@ -1957,13 +2115,17 @@ bool CGUIInfoManager::GetBool(int condition1, int contextWindow, const CGUIListI else if (condition == SYSTEM_HASLOCKS) bReturn = g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE; else if (condition == SYSTEM_HAS_PVR) - bReturn = false; + bReturn = true; else if (condition == SYSTEM_ISMASTER) bReturn = g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && g_passwordManager.bMasterUser; else if (condition == SYSTEM_ISFULLSCREEN) bReturn = g_Windowing.IsFullScreen(); else if (condition == SYSTEM_ISSTANDALONE) bReturn = g_application.IsStandAlone(); + else if (condition == SYSTEM_ISINHIBIT) + bReturn = g_application.IsIdleShutdownInhibited(); + else if (condition == SYSTEM_HAS_SHUTDOWN) + bReturn = (g_guiSettings.GetInt("powermanagement.shutdowntime") > 0); else if (condition == SYSTEM_LOGGEDON) bReturn = !(g_windowManager.GetActiveWindow() == WINDOW_LOGIN_SCREEN); else if (condition == SYSTEM_SHOW_EXIT_BUTTON) @@ -1972,6 +2134,9 @@ bool CGUIInfoManager::GetBool(int condition1, int contextWindow, const CGUIListI bReturn = g_settings.UsingLoginScreen(); else if (condition == WEATHER_IS_FETCHED) bReturn = g_weatherManager.IsFetched(); + else if (condition >= PVR_CONDITIONS_START && condition <= PVR_CONDITIONS_END) + bReturn = g_PVRManager.TranslateBoolInfo(condition); + else if (condition == SYSTEM_INTERNET_STATE) { g_sysinfo.GetInfo(condition); @@ -2030,7 +2195,8 @@ bool CGUIInfoManager::GetBool(int condition1, int contextWindow, const CGUIListI } } else if (condition == VIDEOPLAYER_HAS_INFO) - bReturn = (m_currentFile->HasVideoInfoTag() && !m_currentFile->GetVideoInfoTag()->IsEmpty()); + bReturn = ((m_currentFile->HasVideoInfoTag() && !m_currentFile->GetVideoInfoTag()->IsEmpty()) || + (m_currentFile->HasPVRChannelInfoTag() && !m_currentFile->GetPVRChannelInfoTag()->IsEmpty())); else if (condition >= CONTAINER_SCROLL_PREVIOUS && condition <= CONTAINER_SCROLL_NEXT) { // no parameters, so we assume it's just requested for a media window. It therefore @@ -2228,6 +2394,13 @@ bool CGUIInfoManager::GetBool(int condition1, int contextWindow, const CGUIListI case VISUALISATION_ENABLED: bReturn = !g_guiSettings.GetString("musicplayer.visualisation").IsEmpty(); break; + case VIDEOPLAYER_HAS_EPG: + if (m_currentFile->HasPVRChannelInfoTag()) + { + CEpgInfoTag epgTag; + bReturn = m_currentFile->GetPVRChannelInfoTag()->GetEPGNow(epgTag); + } + break; default: // default, use integer value different from 0 as true { int val; @@ -2568,6 +2741,8 @@ bool CGUIInfoManager::GetMultiInfoBool(const GUIInfo &info, int contextWindow, c strContent = "musicvideos"; if (m_currentFile->HasVideoInfoTag() && m_currentFile->GetVideoInfoTag()->m_strStatus == "livetv") strContent = "livetv"; + if (m_currentFile->HasPVRChannelInfoTag()) + strContent = "livetv"; bReturn = m_stringParameters[info.GetData1()].Equals(strContent); } break; @@ -3310,6 +3485,31 @@ CStdString CGUIInfoManager::GetMusicTagLabel(int info, const CFileItem *item) return GetItemLabel(item, LISTITEM_COMMENT); case MUSICPLAYER_DURATION: return GetItemLabel(item, LISTITEM_DURATION); + case MUSICPLAYER_CHANNEL_NAME: + { + CPVRChannel* channeltag = m_currentFile->GetPVRChannelInfoTag(); + if (channeltag) + return channeltag->ChannelName(); + } + break; + case MUSICPLAYER_CHANNEL_NUMBER: + { + CPVRChannel* channeltag = m_currentFile->GetPVRChannelInfoTag(); + if (channeltag) + { + CStdString strNumber; + strNumber.Format("%i", channeltag->ChannelNumber()); + return strNumber; + } + } + break; + case MUSICPLAYER_CHANNEL_GROUP: + { + CPVRChannel* channeltag = m_currentFile->GetPVRChannelInfoTag(); + if (channeltag && channeltag->IsRadio()) + return g_PVRManager.GetPlayingGroup(true)->GroupName(); + } + break; case MUSICPLAYER_PLAYCOUNT: return GetItemLabel(item, LISTITEM_PLAYCOUNT); case MUSICPLAYER_LASTPLAYED: @@ -3325,6 +3525,15 @@ CStdString CGUIInfoManager::GetVideoLabel(int item) if (item == VIDEOPLAYER_TITLE) { + if (m_currentFile->HasPVRChannelInfoTag()) + { + CEpgInfoTag tag; + return m_currentFile->GetPVRChannelInfoTag()->GetEPGNow(tag) ? + tag.Title() : + g_guiSettings.GetBool("epg.hidenoinfoavailable") ? + StringUtils::EmptyString : + g_localizeStrings.Get(19055); // no information available + } if (m_currentFile->HasVideoInfoTag() && !m_currentFile->GetVideoInfoTag()->m_strTitle.IsEmpty()) return m_currentFile->GetVideoInfoTag()->m_strTitle; // don't have the title, so use dvdplayer, label, or drop down to title from path @@ -3344,6 +3553,81 @@ CStdString CGUIInfoManager::GetVideoLabel(int item) if (g_playlistPlayer.GetCurrentPlaylist() == PLAYLIST_VIDEO) return GetPlaylistLabel(PLAYLIST_POSITION); } + else if (m_currentFile->HasPVRChannelInfoTag()) + { + CPVRChannel* tag = m_currentFile->GetPVRChannelInfoTag(); + CEpgInfoTag epgTag; + + switch (item) + { + /* Now playing infos */ + case VIDEOPLAYER_ORIGINALTITLE: + return tag->GetEPGNow(epgTag) ? + epgTag.Title() : + g_guiSettings.GetBool("epg.hidenoinfoavailable") ? + StringUtils::EmptyString : + g_localizeStrings.Get(19055); // no information available + case VIDEOPLAYER_GENRE: + return tag->GetEPGNow(epgTag) ? StringUtils::Join(epgTag.Genre(), g_advancedSettings.m_videoItemSeparator) : StringUtils::EmptyString; + case VIDEOPLAYER_PLOT: + return tag->GetEPGNow(epgTag) ? epgTag.Plot() : StringUtils::EmptyString; + case VIDEOPLAYER_PLOT_OUTLINE: + return tag->GetEPGNow(epgTag) ? epgTag.PlotOutline() : StringUtils::EmptyString; + case VIDEOPLAYER_STARTTIME: + return tag->GetEPGNow(epgTag) ? epgTag.StartAsLocalTime().GetAsLocalizedTime("", false) : CDateTime::GetCurrentDateTime().GetAsLocalizedTime("", false); + case VIDEOPLAYER_ENDTIME: + return tag->GetEPGNow(epgTag) ? epgTag.EndAsLocalTime().GetAsLocalizedTime("", false) : CDateTime::GetCurrentDateTime().GetAsLocalizedTime("", false); + + /* Next playing infos */ + case VIDEOPLAYER_NEXT_TITLE: + return tag->GetEPGNext(epgTag) ? + epgTag.Title() : + g_guiSettings.GetBool("epg.hidenoinfoavailable") ? + StringUtils::EmptyString : + g_localizeStrings.Get(19055); // no information available + case VIDEOPLAYER_NEXT_GENRE: + return tag->GetEPGNext(epgTag) ? StringUtils::Join(epgTag.Genre(), g_advancedSettings.m_videoItemSeparator) : StringUtils::EmptyString; + case VIDEOPLAYER_NEXT_PLOT: + return tag->GetEPGNext(epgTag) ? epgTag.Plot() : StringUtils::EmptyString; + case VIDEOPLAYER_NEXT_PLOT_OUTLINE: + return tag->GetEPGNext(epgTag) ? epgTag.PlotOutline() : StringUtils::EmptyString; + case VIDEOPLAYER_NEXT_STARTTIME: + return tag->GetEPGNext(epgTag) ? epgTag.StartAsLocalTime().GetAsLocalizedTime("", false) : CDateTime::GetCurrentDateTime().GetAsLocalizedTime("", false); + case VIDEOPLAYER_NEXT_ENDTIME: + return tag->GetEPGNext(epgTag) ? epgTag.EndAsLocalTime().GetAsLocalizedTime("", false) : CDateTime::GetCurrentDateTime().GetAsLocalizedTime("", false); + case VIDEOPLAYER_NEXT_DURATION: + { + CStdString duration; + if (tag->GetEPGNext(epgTag) && epgTag.GetDuration() > 0) + duration = StringUtils::SecondsToTimeString(epgTag.GetDuration()); + return duration; + } + + case VIDEOPLAYER_PARENTAL_RATING: + { + CStdString rating; + if (tag->GetEPGNow(epgTag) && epgTag.ParentalRating() > 0) + rating.Format("%i", epgTag.ParentalRating()); + return rating; + } + break; + + /* General channel infos */ + case VIDEOPLAYER_CHANNEL_NAME: + return tag->ChannelName(); + case VIDEOPLAYER_CHANNEL_NUMBER: + { + CStdString strNumber; + strNumber.Format("%i", tag->ChannelNumber()); + return strNumber; + } + case VIDEOPLAYER_CHANNEL_GROUP: + { + if (tag && !tag->IsRadio()) + return g_PVRManager.GetPlayingGroup(false)->GroupName(); + } + } + } else if (m_currentFile->HasVideoInfoTag()) { switch (item) @@ -3537,6 +3821,9 @@ void CGUIInfoManager::SetCurrentItem(CFileItem &item) SetCurrentSong(item); else SetCurrentMovie(item); + + SetChanged(); + NotifyObservers(ObservableMessageCurrentItem, true); } void CGUIInfoManager::SetCurrentAlbumThumb(const CStdString thumbFileName) @@ -3590,11 +3877,15 @@ void CGUIInfoManager::SetCurrentMovie(CFileItem &item) CLog::Log(LOGDEBUG,"CGUIInfoManager::SetCurrentMovie(%s)",item.GetPath().c_str()); *m_currentFile = item; - CVideoDatabase dbs; - if (dbs.Open()) + /* also call GetMovieInfo() when a VideoInfoTag is already present or additional info won't be present in the tag */ + if (!m_currentFile->HasPVRChannelInfoTag()) { - dbs.LoadVideoInfo(item.GetPath(), *m_currentFile->GetVideoInfoTag()); - dbs.Close(); + CVideoDatabase dbs; + if (dbs.Open()) + { + dbs.LoadVideoInfo(item.GetPath(), *m_currentFile->GetVideoInfoTag()); + dbs.Close(); + } } // Find a thumb for this file. @@ -3834,6 +4125,27 @@ bool CGUIInfoManager::GetItemInt(int &value, const CGUIListItem *item, int info) switch (info) { + case LISTITEM_PROGRESS: + { + value = 0; + if (item->IsFileItem()) + { + const CFileItem *pItem = (const CFileItem *)item; + if (pItem && pItem->HasPVRChannelInfoTag()) + { + CEpgInfoTag epgNow; + if (pItem->GetPVRChannelInfoTag()->GetEPGNow(epgNow)) + value = (int) epgNow.ProgressPercentage(); + } + else if (pItem && pItem->HasEPGInfoTag()) + { + value = (int) pItem->GetEPGInfoTag()->ProgressPercentage(); + } + } + + return true; + } + break; case LISTITEM_PERCENT_PLAYED: if (item->IsFileItem() && ((const CFileItem *)item)->HasVideoInfoTag() && ((const CFileItem *)item)->GetVideoInfoTag()->m_resumePoint.IsPartWay()) value = (int)(100 * ((const CFileItem *)item)->GetVideoInfoTag()->m_resumePoint.timeInSeconds / ((const CFileItem *)item)->GetVideoInfoTag()->m_resumePoint.totalTimeInSeconds); @@ -3869,6 +4181,21 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info, CStdSt case LISTITEM_LABEL2: return item->GetLabel2(); case LISTITEM_TITLE: + if (item->HasPVRChannelInfoTag()) + { + CEpgInfoTag epgTag; + return item->GetPVRChannelInfoTag()->GetEPGNow(epgTag) ? + epgTag.Title() : + g_guiSettings.GetBool("epg.hidenoinfoavailable") ? + StringUtils::EmptyString : + g_localizeStrings.Get(19055); // no information available + } + if (item->HasPVRRecordingInfoTag()) + return item->GetPVRRecordingInfoTag()->m_strTitle; + if (item->HasEPGInfoTag()) + return item->GetEPGInfoTag()->Title(); + if (item->HasPVRTimerInfoTag()) + return item->GetPVRTimerInfoTag()->Title(); if (item->HasVideoInfoTag()) return item->GetVideoInfoTag()->m_strTitle; if (item->HasMusicInfoTag()) @@ -3964,6 +4291,15 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info, CStdSt return StringUtils::Join(item->GetVideoInfoTag()->m_genre, g_advancedSettings.m_videoItemSeparator); if (item->HasMusicInfoTag()) return StringUtils::Join(item->GetMusicInfoTag()->GetGenre(), g_advancedSettings.m_musicItemSeparator); + if (item->HasPVRChannelInfoTag()) + { + CEpgInfoTag epgTag; + return item->GetPVRChannelInfoTag()->GetEPGNow(epgTag) ? StringUtils::Join(epgTag.Genre(), g_advancedSettings.m_videoItemSeparator) : StringUtils::EmptyString; + } + if (item->HasPVRRecordingInfoTag()) + return StringUtils::Join(item->GetPVRRecordingInfoTag()->m_genre, g_advancedSettings.m_videoItemSeparator); + if (item->HasEPGInfoTag()) + return StringUtils::Join(item->GetEPGInfoTag()->Genre(), g_advancedSettings.m_videoItemSeparator); break; case LISTITEM_FILENAME: case LISTITEM_FILE_EXTENSION: @@ -3985,6 +4321,17 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info, CStdSt } break; case LISTITEM_DATE: + if (item->HasEPGInfoTag()) + return item->GetEPGInfoTag()->StartAsLocalTime().GetAsLocalizedDateTime(false, false); + if (item->HasPVRChannelInfoTag()) + { + CEpgInfoTag epgTag; + return item->GetPVRChannelInfoTag()->GetEPGNow(epgTag) ? epgTag.StartAsLocalTime().GetAsLocalizedDateTime(false, false) : CDateTime::GetCurrentDateTime().GetAsLocalizedDateTime(false, false); + } + if (item->HasPVRRecordingInfoTag()) + return item->GetPVRRecordingInfoTag()->RecordingTimeAsLocalTime().GetAsLocalizedDateTime(false, false); + if (item->HasPVRTimerInfoTag()) + return item->GetPVRTimerInfoTag()->Summary(); if (item->m_dateTime.IsValid()) return item->m_dateTime.GetAsLocalizedDate(); break; @@ -4025,12 +4372,30 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info, CStdSt case LISTITEM_DURATION: { CStdString duration; - if (item->HasVideoInfoTag()) + if (item->HasPVRChannelInfoTag()) + { + const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL; + CEpgInfoTag tag; + if (channel && channel->GetEPGNow(tag)) + return StringUtils::SecondsToTimeString(tag.GetDuration()); + return StringUtils::EmptyString; + } + else if (item->HasPVRRecordingInfoTag()) + { + if (item->GetPVRRecordingInfoTag()->GetDuration() > 0) + duration = StringUtils::SecondsToTimeString(item->GetPVRRecordingInfoTag()->GetDuration()); + } + else if (item->HasEPGInfoTag()) + { + if (item->GetEPGInfoTag()->GetDuration() > 0) + duration = StringUtils::SecondsToTimeString(item->GetEPGInfoTag()->GetDuration()); + } + else if (item->HasVideoInfoTag()) { if (!item->GetVideoInfoTag()->m_strRuntime.IsEmpty()) duration = item->GetVideoInfoTag()->m_strRuntime; } - if (item->HasMusicInfoTag()) + else if (item->HasMusicInfoTag()) { if (item->GetMusicInfoTag()->GetDuration() > 0) duration = StringUtils::SecondsToTimeString(item->GetMusicInfoTag()->GetDuration()); @@ -4038,6 +4403,18 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info, CStdSt return duration; } case LISTITEM_PLOT: + if (item->HasPVRChannelInfoTag()) + { + const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL; + CEpgInfoTag tag; + if (channel && channel->GetEPGNow(tag)) + return tag.Plot(); + return StringUtils::EmptyString; + } + if (item->HasEPGInfoTag()) + return item->GetEPGInfoTag()->Plot(); + if (item->HasPVRRecordingInfoTag()) + return item->GetPVRRecordingInfoTag()->m_strPlot; if (item->HasVideoInfoTag()) { if (!(!item->GetVideoInfoTag()->m_strShowTitle.IsEmpty() && item->GetVideoInfoTag()->m_iSeason == -1)) // dont apply to tvshows @@ -4048,6 +4425,18 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info, CStdSt } break; case LISTITEM_PLOT_OUTLINE: + if (item->HasPVRChannelInfoTag()) + { + const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL; + CEpgInfoTag tag; + if (channel && channel->GetEPGNow(tag)) + return tag.PlotOutline(); + return StringUtils::EmptyString; + } + if (item->HasEPGInfoTag()) + return item->GetEPGInfoTag()->PlotOutline(); + if (item->HasPVRRecordingInfoTag()) + return item->GetPVRRecordingInfoTag()->m_strPlotOutline; if (item->HasVideoInfoTag()) return item->GetVideoInfoTag()->m_strPlotOutline; break; @@ -4078,6 +4467,8 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info, CStdSt return item->GetVideoInfoTag()->m_strShowTitle; break; case LISTITEM_COMMENT: + if (item->HasPVRTimerInfoTag()) + return item->GetPVRTimerInfoTag()->GetStatus(); if (item->HasMusicInfoTag()) return item->GetMusicInfoTag()->GetComment(); break; @@ -4223,6 +4614,173 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info, CStdSt if (item->HasVideoInfoTag()) return item->GetVideoInfoTag()->m_streamDetails.GetSubtitleLanguage(); break; + case LISTITEM_STARTTIME: + if (item->HasPVRChannelInfoTag()) + { + const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL; + CEpgInfoTag tag; + if (channel && channel->GetEPGNow(tag)) + return tag.StartAsLocalTime().GetAsLocalizedTime("", false); + return CDateTime::GetCurrentDateTime().GetAsLocalizedTime("", false); + } + if (item->HasEPGInfoTag()) + return item->GetEPGInfoTag()->StartAsLocalTime().GetAsLocalizedTime("", false); + if (item->HasPVRTimerInfoTag()) + return item->GetPVRTimerInfoTag()->StartAsLocalTime().GetAsLocalizedTime("", false); + if (item->HasPVRRecordingInfoTag()) + return item->GetPVRRecordingInfoTag()->RecordingTimeAsLocalTime().GetAsLocalizedTime("", false); + if (item->m_dateTime.IsValid()) + return item->m_dateTime.GetAsLocalizedTime("", false); + break; + case LISTITEM_ENDTIME: + if (item->HasPVRChannelInfoTag()) + { + const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL; + CEpgInfoTag tag; + if (channel && channel->GetEPGNow(tag)) + return tag.EndAsLocalTime().GetAsLocalizedTime("", false); + return CDateTime::GetCurrentDateTime().GetAsLocalizedTime("", false); + } + if (item->HasEPGInfoTag()) + return item->GetEPGInfoTag()->EndAsLocalTime().GetAsLocalizedTime("", false); + if (item->HasPVRTimerInfoTag()) + return item->GetPVRTimerInfoTag()->EndAsLocalTime().GetAsLocalizedTime("", false); + break; + case LISTITEM_STARTDATE: + if (item->HasPVRChannelInfoTag()) + { + const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL; + CEpgInfoTag tag; + if (channel && channel->GetEPGNow(tag)) + return tag.StartAsLocalTime().GetAsLocalizedDate(true); + return CDateTime::GetCurrentDateTime().GetAsLocalizedDate(true); + } + if (item->HasEPGInfoTag()) + return item->GetEPGInfoTag()->StartAsLocalTime().GetAsLocalizedDate(true); + if (item->HasPVRTimerInfoTag()) + return item->GetPVRTimerInfoTag()->StartAsLocalTime().GetAsLocalizedDate(true); + if (item->HasPVRRecordingInfoTag()) + return item->GetPVRRecordingInfoTag()->RecordingTimeAsLocalTime().GetAsLocalizedDate(true); + if (item->m_dateTime.IsValid()) + return item->m_dateTime.GetAsLocalizedDate(true); + break; + case LISTITEM_ENDDATE: + if (item->HasPVRChannelInfoTag()) + { + const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL; + CEpgInfoTag tag; + if (channel && channel->GetEPGNow(tag)) + return tag.EndAsLocalTime().GetAsLocalizedDate(true); + return CDateTime::GetCurrentDateTime().GetAsLocalizedDate(true); + } + if (item->HasEPGInfoTag()) + return item->GetEPGInfoTag()->EndAsLocalTime().GetAsLocalizedDate(true); + if (item->HasPVRTimerInfoTag()) + return item->GetPVRTimerInfoTag()->EndAsLocalTime().GetAsLocalizedDate(true); + break; + case LISTITEM_CHANNEL_NUMBER: + { + CStdString number; + if (item->HasPVRChannelInfoTag()) + number.Format("%i", item->GetPVRChannelInfoTag()->ChannelNumber()); + if (item->HasEPGInfoTag() && item->GetEPGInfoTag()->HasPVRChannel()) + number.Format("%i", item->GetEPGInfoTag()->PVRChannelNumber()); + if (item->HasPVRTimerInfoTag()) + number.Format("%i", item->GetPVRTimerInfoTag()->ChannelNumber()); + + return number; + } + break; + case LISTITEM_CHANNEL_NAME: + if (item->HasPVRChannelInfoTag()) + return item->GetPVRChannelInfoTag()->ChannelName(); + if (item->HasEPGInfoTag() && item->GetEPGInfoTag()->HasPVRChannel()) + return item->GetEPGInfoTag()->PVRChannelName(); + if (item->HasPVRRecordingInfoTag()) + return item->GetPVRRecordingInfoTag()->m_strChannelName; + if (item->HasPVRTimerInfoTag()) + return item->GetPVRTimerInfoTag()->ChannelName(); + break; + case LISTITEM_NEXT_STARTTIME: + { + const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL; + CEpgInfoTag tag; + if (channel && channel->GetEPGNext(tag)) + return tag.StartAsLocalTime().GetAsLocalizedTime("", false); + } + return CDateTime::GetCurrentDateTime().GetAsLocalizedTime("", false); + case LISTITEM_NEXT_ENDTIME: + { + const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL; + CEpgInfoTag tag; + if (channel && channel->GetEPGNext(tag)) + return tag.EndAsLocalTime().GetAsLocalizedTime("", false); + } + return CDateTime::GetCurrentDateTime().GetAsLocalizedTime("", false); + case LISTITEM_NEXT_STARTDATE: + { + const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL; + CEpgInfoTag tag; + if (channel && channel->GetEPGNext(tag)) + return tag.StartAsLocalTime().GetAsLocalizedDate(true); + } + return CDateTime::GetCurrentDateTime().GetAsLocalizedDate(true); + case LISTITEM_NEXT_ENDDATE: + { + const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL; + CEpgInfoTag tag; + if (channel && channel->GetEPGNext(tag)) + return tag.EndAsLocalTime().GetAsLocalizedDate(true); + } + return CDateTime::GetCurrentDateTime().GetAsLocalizedDate(true); + case LISTITEM_NEXT_PLOT: + { + const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL; + CEpgInfoTag tag; + if (channel && channel->GetEPGNext(tag)) + return tag.Plot(); + } + return StringUtils::EmptyString; + case LISTITEM_NEXT_PLOT_OUTLINE: + { + const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL; + CEpgInfoTag tag; + if (channel && channel->GetEPGNext(tag)) + return tag.PlotOutline(); + } + return StringUtils::EmptyString; + case LISTITEM_NEXT_DURATION: + { + const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL; + CEpgInfoTag tag; + if (channel && channel->GetEPGNext(tag)) + return StringUtils::SecondsToTimeString(tag.GetDuration()); + } + return StringUtils::EmptyString; + case LISTITEM_NEXT_GENRE: + { + const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL; + CEpgInfoTag tag; + if (channel && channel->GetEPGNext(tag)) + return StringUtils::Join(tag.Genre(), g_advancedSettings.m_videoItemSeparator); + } + return StringUtils::EmptyString; + case LISTITEM_NEXT_TITLE: + { + const CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL; + CEpgInfoTag tag; + if (channel && channel->GetEPGNext(tag)) + return tag.Title(); + } + return StringUtils::EmptyString; + case LISTITEM_PARENTALRATING: + { + CStdString rating; + if (item->HasEPGInfoTag() && item->GetEPGInfoTag()->ParentalRating() > 0) + rating.Format("%i", item->GetEPGInfoTag()->ParentalRating()); + return rating; + } + break; case LISTITEM_PERCENT_PLAYED: { int val; @@ -4324,6 +4882,65 @@ bool CGUIInfoManager::GetItemBool(const CGUIListItem *item, int condition) const return item->m_bIsFolder; else if (condition == LISTITEM_IS_RESUMABLE) return (item->IsFileItem() && ((const CFileItem *)item)->HasVideoInfoTag() && ((const CFileItem *)item)->GetVideoInfoTag()->m_resumePoint.timeInSeconds > 0); + else if (item->IsFileItem()) + { + const CFileItem *pItem = (const CFileItem *)item; + if (condition == LISTITEM_ISRECORDING) + { + if (!g_PVRManager.IsStarted()) + return false; + + if (pItem->HasPVRChannelInfoTag()) + { + return pItem->GetPVRChannelInfoTag()->IsRecording(); + } + else if (pItem->HasPVRTimerInfoTag()) + { + const CPVRTimerInfoTag *timer = pItem->GetPVRTimerInfoTag(); + if (timer) + return timer->IsRecording(); + } + else if (pItem->HasEPGInfoTag()) + { + CFileItemPtr timer = g_PVRTimers->GetTimerForEpgTag(pItem); + if (timer && timer->HasPVRTimerInfoTag()) + return timer->GetPVRTimerInfoTag()->IsRecording(); + } + } + else if (condition == LISTITEM_HASTIMER) + { + if (pItem->HasEPGInfoTag()) + { + CFileItemPtr timer = g_PVRTimers->GetTimerForEpgTag(pItem); + if (timer && timer->HasPVRTimerInfoTag()) + return timer->GetPVRTimerInfoTag()->IsActive(); + } + } + else if (condition == LISTITEM_HAS_EPG) + { + if (pItem->HasPVRChannelInfoTag()) + { + CEpgInfoTag epgTag; + return pItem->GetPVRChannelInfoTag()->GetEPGNow(epgTag); + } + else + { + return pItem->HasEPGInfoTag(); + } + } + else if (condition == LISTITEM_ISENCRYPTED) + { + if (pItem->HasPVRChannelInfoTag()) + { + return pItem->GetPVRChannelInfoTag()->IsEncrypted(); + } + else if (pItem->HasEPGInfoTag() && pItem->GetEPGInfoTag()->HasPVRChannel()) + { + return pItem->GetEPGInfoTag()->ChannelTag()->IsEncrypted(); + } + } + } + return false; } diff --git a/xbmc/GUIInfoManager.h b/xbmc/GUIInfoManager.h index 968c76d42b..47b8dea976 100644 --- a/xbmc/GUIInfoManager.h +++ b/xbmc/GUIInfoManager.h @@ -32,6 +32,7 @@ #include "guilib/IMsgTargetCallback.h" #include "inttypes.h" #include "XBDateTime.h" +#include "utils/Observer.h" #include "interfaces/info/SkinVariable.h" #include <list> @@ -177,7 +178,9 @@ namespace INFO #define SYSTEM_PROFILECOUNT 181 #define SYSTEM_ISFULLSCREEN 182 #define SYSTEM_ISSTANDALONE 183 -#define SYSTEM_HAS_PVR 184 +#define SYSTEM_ISINHIBIT 184 +#define SYSTEM_HAS_SHUTDOWN 185 +#define SYSTEM_HAS_PVR 186 #define NETWORK_IP_ADDRESS 190 #define NETWORK_MAC_ADDRESS 191 @@ -215,6 +218,9 @@ namespace INFO #define MUSICPLAYER_ALBUM_ARTIST 226 #define MUSICPLAYER_PLAYCOUNT 227 #define MUSICPLAYER_LASTPLAYED 228 +#define MUSICPLAYER_CHANNEL_NAME 229 +#define MUSICPLAYER_CHANNEL_NUMBER 230 +#define MUSICPLAYER_CHANNEL_GROUP 231 #define VIDEOPLAYER_TITLE 250 #define VIDEOPLAYER_GENRE 251 @@ -260,14 +266,29 @@ namespace INFO #define VIDEOPLAYER_PLAYCOUNT 293 #define VIDEOPLAYER_LASTPLAYED 294 -#define AUDIOSCROBBLER_ENABLED 300 -#define AUDIOSCROBBLER_CONN_STATE 301 -#define AUDIOSCROBBLER_SUBMIT_INT 302 -#define AUDIOSCROBBLER_FILES_CACHED 303 -#define AUDIOSCROBBLER_SUBMIT_STATE 304 -#define LASTFM_RADIOPLAYING 305 -#define LASTFM_CANLOVE 306 -#define LASTFM_CANBAN 307 +#define VIDEOPLAYER_STARTTIME 295 +#define VIDEOPLAYER_ENDTIME 296 +#define VIDEOPLAYER_NEXT_TITLE 297 +#define VIDEOPLAYER_NEXT_GENRE 298 +#define VIDEOPLAYER_NEXT_PLOT 299 +#define VIDEOPLAYER_NEXT_PLOT_OUTLINE 300 +#define VIDEOPLAYER_NEXT_STARTTIME 301 +#define VIDEOPLAYER_NEXT_ENDTIME 302 +#define VIDEOPLAYER_NEXT_DURATION 303 +#define VIDEOPLAYER_CHANNEL_NAME 304 +#define VIDEOPLAYER_CHANNEL_NUMBER 305 +#define VIDEOPLAYER_CHANNEL_GROUP 306 +#define VIDEOPLAYER_PARENTAL_RATING 307 +#define VIDEOPLAYER_HAS_EPG 308 + +#define AUDIOSCROBBLER_ENABLED 325 +#define AUDIOSCROBBLER_CONN_STATE 326 +#define AUDIOSCROBBLER_SUBMIT_INT 327 +#define AUDIOSCROBBLER_FILES_CACHED 328 +#define AUDIOSCROBBLER_SUBMIT_STATE 329 +#define LASTFM_RADIOPLAYING 330 +#define LASTFM_CANLOVE 331 +#define LASTFM_CANBAN 332 #define CONTAINER_SCROLL_PREVIOUS 345 // NOTE: These 5 must be kept in this consecutive order #define CONTAINER_MOVE_PREVIOUS 346 @@ -409,6 +430,56 @@ namespace INFO #define FANART_COLOR3 1002 #define FANART_IMAGE 1003 +#define PVR_CONDITIONS_START 1100 +#define PVR_IS_RECORDING (PVR_CONDITIONS_START) +#define PVR_HAS_TIMER (PVR_CONDITIONS_START + 1) +#define PVR_HAS_NONRECORDING_TIMER (PVR_CONDITIONS_START + 2) +#define PVR_HAS_EPG (PVR_CONDITIONS_START + 3) +#define PVR_HAS_TXT (PVR_CONDITIONS_START + 4) +#define PVR_HAS_DIRECTOR (PVR_CONDITIONS_START + 5) +#define PVR_IS_PLAYING_TV (PVR_CONDITIONS_START + 6) +#define PVR_IS_PLAYING_RADIO (PVR_CONDITIONS_START + 7) +#define PVR_IS_PLAYING_RECORDING (PVR_CONDITIONS_START + 8) +#define PVR_ACTUAL_STREAM_ENCRYPTED (PVR_CONDITIONS_START + 9) +#define PVR_CONDITIONS_END PVR_ACTUAL_STREAM_ENCRYPTED + +#define PVR_STRINGS_START 1200 +#define PVR_NEXT_RECORDING_CHANNEL (PVR_STRINGS_START) +#define PVR_NEXT_RECORDING_CHAN_ICO (PVR_STRINGS_START + 1) +#define PVR_NEXT_RECORDING_DATETIME (PVR_STRINGS_START + 2) +#define PVR_NEXT_RECORDING_TITLE (PVR_STRINGS_START + 3) +#define PVR_NOW_RECORDING_CHANNEL (PVR_STRINGS_START + 4) +#define PVR_NOW_RECORDING_CHAN_ICO (PVR_STRINGS_START + 5) +#define PVR_NOW_RECORDING_DATETIME (PVR_STRINGS_START + 6) +#define PVR_NOW_RECORDING_TITLE (PVR_STRINGS_START + 7) +#define PVR_BACKEND_NAME (PVR_STRINGS_START + 8) +#define PVR_BACKEND_VERSION (PVR_STRINGS_START + 9) +#define PVR_BACKEND_HOST (PVR_STRINGS_START + 10) +#define PVR_BACKEND_DISKSPACE (PVR_STRINGS_START + 11) +#define PVR_BACKEND_CHANNELS (PVR_STRINGS_START + 12) +#define PVR_BACKEND_TIMERS (PVR_STRINGS_START + 13) +#define PVR_BACKEND_RECORDINGS (PVR_STRINGS_START + 14) +#define PVR_BACKEND_NUMBER (PVR_STRINGS_START + 15) +#define PVR_TOTAL_DISKSPACE (PVR_STRINGS_START + 16) +#define PVR_NEXT_TIMER (PVR_STRINGS_START + 17) +#define PVR_PLAYING_DURATION (PVR_STRINGS_START + 18) +#define PVR_PLAYING_TIME (PVR_STRINGS_START + 19) +#define PVR_PLAYING_PROGRESS (PVR_STRINGS_START + 20) +#define PVR_ACTUAL_STREAM_CLIENT (PVR_STRINGS_START + 21) +#define PVR_ACTUAL_STREAM_DEVICE (PVR_STRINGS_START + 22) +#define PVR_ACTUAL_STREAM_STATUS (PVR_STRINGS_START + 23) +#define PVR_ACTUAL_STREAM_SIG (PVR_STRINGS_START + 24) +#define PVR_ACTUAL_STREAM_SNR (PVR_STRINGS_START + 25) +#define PVR_ACTUAL_STREAM_SIG_PROGR (PVR_STRINGS_START + 26) +#define PVR_ACTUAL_STREAM_SNR_PROGR (PVR_STRINGS_START + 27) +#define PVR_ACTUAL_STREAM_BER (PVR_STRINGS_START + 28) +#define PVR_ACTUAL_STREAM_UNC (PVR_STRINGS_START + 29) +#define PVR_ACTUAL_STREAM_VIDEO_BR (PVR_STRINGS_START + 30) +#define PVR_ACTUAL_STREAM_AUDIO_BR (PVR_STRINGS_START + 31) +#define PVR_ACTUAL_STREAM_DOLBY_BR (PVR_STRINGS_START + 32) +#define PVR_ACTUAL_STREAM_CRYPTION (PVR_STRINGS_START + 33) +#define PVR_STRINGS_END PVR_ACTUAL_STREAM_CRYPTION + #define WINDOW_PROPERTY 9993 #define WINDOW_IS_TOPMOST 9994 #define WINDOW_IS_VISIBLE 9995 @@ -514,6 +585,29 @@ namespace INFO #define LISTITEM_DBTYPE (LISTITEM_START + 80) #define LISTITEM_DBID (LISTITEM_START + 81) +#define LISTITEM_STARTTIME (LISTITEM_START + 82) +#define LISTITEM_ENDTIME (LISTITEM_START + 83) +#define LISTITEM_STARTDATE (LISTITEM_START + 84) +#define LISTITEM_ENDDATE (LISTITEM_START + 85) +#define LISTITEM_NEXT_TITLE (LISTITEM_START + 86) +#define LISTITEM_NEXT_GENRE (LISTITEM_START + 87) +#define LISTITEM_NEXT_PLOT (LISTITEM_START + 88) +#define LISTITEM_NEXT_PLOT_OUTLINE (LISTITEM_START + 89) +#define LISTITEM_NEXT_STARTTIME (LISTITEM_START + 90) +#define LISTITEM_NEXT_ENDTIME (LISTITEM_START + 91) +#define LISTITEM_NEXT_STARTDATE (LISTITEM_START + 92) +#define LISTITEM_NEXT_ENDDATE (LISTITEM_START + 93) +#define LISTITEM_NEXT_DURATION (LISTITEM_START + 94) +#define LISTITEM_CHANNEL_NAME (LISTITEM_START + 95) +#define LISTITEM_CHANNEL_NUMBER (LISTITEM_START + 96) +#define LISTITEM_CHANNEL_GROUP (LISTITEM_START + 97) +#define LISTITEM_HASTIMER (LISTITEM_START + 98) +#define LISTITEM_ISRECORDING (LISTITEM_START + 99) +#define LISTITEM_ISENCRYPTED (LISTITEM_START + 100) +#define LISTITEM_PARENTALRATING (LISTITEM_START + 101) +#define LISTITEM_PROGRESS (LISTITEM_START + 102) +#define LISTITEM_HAS_EPG (LISTITEM_START + 103) + #define LISTITEM_PROPERTY_START (LISTITEM_START + 200) #define LISTITEM_PROPERTY_END (LISTITEM_PROPERTY_START + 1000) #define LISTITEM_END (LISTITEM_PROPERTY_END) @@ -569,7 +663,7 @@ private: \ingroup strings \brief */ -class CGUIInfoManager : public IMsgTargetCallback +class CGUIInfoManager : public IMsgTargetCallback, public Observable { public: CGUIInfoManager(void); diff --git a/xbmc/GUIViewControl.cpp b/xbmc/GUIViewControl.cpp index ce0b945cbd..b741922bf0 100644 --- a/xbmc/GUIViewControl.cpp +++ b/xbmc/GUIViewControl.cpp @@ -62,7 +62,7 @@ void CGUIViewControl::SetParentWindow(int window) m_parentWindow = window; } -void CGUIViewControl::SetCurrentView(int viewMode) +void CGUIViewControl::SetCurrentView(int viewMode, bool bRefresh /* = false */) { // grab the previous control CGUIControl *previousView = NULL; @@ -99,7 +99,7 @@ void CGUIViewControl::SetCurrentView(int viewMode) (*view)->SetVisible(false); pNewView->SetVisible(true); - if (pNewView == previousView) + if (!bRefresh && pNewView == previousView) return; // no need to actually update anything (other than visibility above) // CLog::Log(LOGDEBUG,"SetCurrentView: Oldview: %i, Newview :%i", m_currentView, viewMode); @@ -124,8 +124,9 @@ void CGUIViewControl::SetCurrentView(int viewMode) g_windowManager.SendMessage(msg); } - // Update our view control - UpdateViewAsControl(((CGUIBaseContainer *)pNewView)->GetLabel()); + // Update our view control only if we are not in the TV Window + if (m_parentWindow != WINDOW_PVR) + UpdateViewAsControl(((CGUIBaseContainer *)pNewView)->GetLabel()); } void CGUIViewControl::SetItems(CFileItemList &items) diff --git a/xbmc/GUIViewControl.h b/xbmc/GUIViewControl.h index 7991f13f90..f9ce011d01 100644 --- a/xbmc/GUIViewControl.h +++ b/xbmc/GUIViewControl.h @@ -36,7 +36,7 @@ public: void AddView(const CGUIControl *control); void SetViewControlID(int control); - void SetCurrentView(int viewMode); + void SetCurrentView(int viewMode, bool bRefresh = false); void SetItems(CFileItemList &items); diff --git a/xbmc/GUIViewState.cpp b/xbmc/GUIViewState.cpp index a4d8203900..35d0964aba 100644 --- a/xbmc/GUIViewState.cpp +++ b/xbmc/GUIViewState.cpp @@ -20,6 +20,7 @@ */ #include "GUIViewState.h" +#include "pvr/windows/GUIViewStatePVR.h" #include "addons/GUIViewStateAddonBrowser.h" #include "music/GUIViewStateMusic.h" #include "video/GUIViewStateVideo.h" @@ -49,6 +50,7 @@ #endif using namespace std; using namespace ADDON; +using namespace PVR; CStdString CGUIViewState::m_strPlaylistDirectory; VECSOURCES CGUIViewState::m_sources; @@ -128,6 +130,9 @@ CGUIViewState* CGUIViewState::GetViewState(int windowId, const CFileItemList& it if (windowId==WINDOW_VIDEO_PLAYLIST) return new CGUIViewStateWindowVideoPlaylist(items); + if (windowId==WINDOW_PVR) + return new CGUIViewStatePVR(items); + if (windowId==WINDOW_PICTURES) return new CGUIViewStateWindowPictures(items); diff --git a/xbmc/SortFileItem.h b/xbmc/SortFileItem.h index 4f017a5171..22659f55c6 100644 --- a/xbmc/SortFileItem.h +++ b/xbmc/SortFileItem.h @@ -62,6 +62,7 @@ typedef enum { SORT_METHOD_PLAYCOUNT, SORT_METHOD_LISTENERS, SORT_METHOD_UNSORTED, + SORT_METHOD_CHANNEL, SORT_METHOD_BITRATE, SORT_METHOD_MAX } SORT_METHOD; diff --git a/xbmc/URL.cpp b/xbmc/URL.cpp index a1e8085c84..fc1a06ab61 100644 --- a/xbmc/URL.cpp +++ b/xbmc/URL.cpp @@ -170,6 +170,7 @@ void CURL::Parse(const CStdString& strURL1) int iEnd = strURL.length(); const char* sep = NULL; + //TODO fix all Addon paths CStdString strProtocol2 = GetTranslatedProtocol(); if(m_strProtocol.Equals("rss") || m_strProtocol.Equals("rar") || @@ -183,6 +184,7 @@ void CURL::Parse(const CStdString& strURL1) if(strProtocol2.Equals("http") || strProtocol2.Equals("https") || strProtocol2.Equals("plugin") + || strProtocol2.Equals("addons") || strProtocol2.Equals("hdhomerun") || strProtocol2.Equals("rtsp") || strProtocol2.Equals("apk") @@ -308,6 +310,7 @@ void CURL::Parse(const CStdString& strURL1) || m_strProtocol.CompareNoCase("videodb") == 0 || m_strProtocol.CompareNoCase("sources") == 0 || m_strProtocol.CompareNoCase("lastfm") == 0 + || m_strProtocol.CompareNoCase("pvr") == 0 || m_strProtocol.Left(3).CompareNoCase("mem") == 0) { if (m_strHostName != "" && m_strFileName != "") diff --git a/xbmc/Util.cpp b/xbmc/Util.cpp index f45b161ed0..00acb43140 100644 --- a/xbmc/Util.cpp +++ b/xbmc/Util.cpp @@ -41,6 +41,7 @@ #include "Application.h" #include "Util.h" #include "addons/Addon.h" +#include "filesystem/PVRDirectory.h" #include "filesystem/Directory.h" #include "filesystem/StackDirectory.h" #include "filesystem/MultiPathDirectory.h" @@ -556,6 +557,39 @@ void CUtil::GetHomePath(CStdString& strPath, const CStdString& strTarget) #endif } +bool CUtil::IsPVR(const CStdString& strFile) +{ + return strFile.Left(4).Equals("pvr:"); +} + +bool CUtil::IsHTSP(const CStdString& strFile) +{ + return strFile.Left(5).Equals("htsp:"); +} + +bool CUtil::IsLiveTV(const CStdString& strFile) +{ + if (strFile.Left(14).Equals("pvr://channels")) + return true; + + if(URIUtils::IsTuxBox(strFile) + || URIUtils::IsVTP(strFile) + || URIUtils::IsHDHomeRun(strFile) + || URIUtils::IsHTSP(strFile) + || strFile.Left(4).Equals("sap:")) + return true; + + if (URIUtils::IsMythTV(strFile) && CMythDirectory::IsLiveTV(strFile)) + return true; + + return false; +} + +bool CUtil::IsTVRecording(const CStdString& strFile) +{ + return strFile.Left(15).Equals("pvr://recording"); +} + bool CUtil::IsPicture(const CStdString& strFile) { CStdString extension = URIUtils::GetExtension(strFile); @@ -1866,6 +1900,8 @@ bool CUtil::SupportsFileOperations(const CStdString& strPath) return true; if (URIUtils::IsSmb(strPath)) return true; + if (CUtil::IsTVRecording(strPath)) + return CPVRDirectory::SupportsFileOperations(strPath); if (URIUtils::IsNfs(strPath)) return true; if (URIUtils::IsAfp(strPath)) diff --git a/xbmc/Util.h b/xbmc/Util.h index b9f764718f..2cb6876d05 100644 --- a/xbmc/Util.h +++ b/xbmc/Util.h @@ -65,6 +65,10 @@ public: static void GetQualifiedFilename(const CStdString &strBasePath, CStdString &strFilename); static void RunShortcut(const char* szPath); static void GetHomePath(CStdString& strPath, const CStdString& strTarget = "XBMC_HOME"); + static bool IsPVR(const CStdString& strFile); + static bool IsHTSP(const CStdString& strFile); + static bool IsLiveTV(const CStdString& strFile); + static bool IsTVRecording(const CStdString& strFile); static bool ExcludeFileOrFolder(const CStdString& strFileOrFolder, const CStdStringArray& regexps); static void GetFileAndProtocol(const CStdString& strURL, CStdString& strDir); static int GetDVDIfoTitle(const CStdString& strPathFile); diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp index fca04e0135..2d7d3bd7f7 100644 --- a/xbmc/XBDateTime.cpp +++ b/xbmc/XBDateTime.cpp @@ -864,6 +864,45 @@ CStdString CDateTime::GetAsDBDateTime() const return date; } +CStdString CDateTime::GetAsSaveString() const +{ + SYSTEMTIME st; + GetAsSystemTime(st); + + CStdString date; + date.Format("%04i%02i%02i_%02i%02i%02i", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); + + return date; +} + +void CDateTime::SetFromUTCDateTime(const CDateTime &dateTime) +{ + TIME_ZONE_INFORMATION tz; + CDateTime tmp(dateTime); + + switch(GetTimeZoneInformation(&tz)) + { + case TIME_ZONE_ID_DAYLIGHT: + tmp -= CDateTimeSpan(0, 0, tz.Bias + tz.DaylightBias, 0); + break; + case TIME_ZONE_ID_STANDARD: + tmp -= CDateTimeSpan(0, 0, tz.Bias + tz.StandardBias, 0); + break; + case TIME_ZONE_ID_UNKNOWN: + tmp -= CDateTimeSpan(0, 0, tz.Bias, 0); + break; + } + + m_time = tmp.m_time; + m_state = tmp.m_state; +} + +void CDateTime::SetFromUTCDateTime(const time_t &dateTime) +{ + CDateTime tmp(dateTime); + SetFromUTCDateTime(tmp); +} + void CDateTime::SetFromW3CDate(const CStdString &dateTime) { CStdString date, time, zone; diff --git a/xbmc/XBDateTime.h b/xbmc/XBDateTime.h index ee48ddd289..653fc542ef 100644 --- a/xbmc/XBDateTime.h +++ b/xbmc/XBDateTime.h @@ -170,6 +170,8 @@ public: void SetFromDBDate(const CStdString &date); void SetFromDBTime(const CStdString &time); void SetFromW3CDate(const CStdString &date); + void SetFromUTCDateTime(const CDateTime &dateTime); + void SetFromUTCDateTime(const time_t &dateTime); /*! \brief set from a database datetime format YYYY-MM-DD HH:MM:SS \sa GetAsDBDateTime() @@ -182,6 +184,7 @@ public: void GetAsTimeStamp(FILETIME& time) const; CDateTime GetAsUTCDateTime() const; + CStdString GetAsSaveString() const; CStdString GetAsDBDateTime() const; CStdString GetAsDBDate() const; CStdString GetAsLocalizedDate(bool longDate=false, bool withShortNames=true) const; diff --git a/xbmc/addons/Addon.cpp b/xbmc/addons/Addon.cpp index ddb28c0e9f..55504cb838 100644 --- a/xbmc/addons/Addon.cpp +++ b/xbmc/addons/Addon.cpp @@ -80,7 +80,7 @@ static const TypeMapping types[] = {"xbmc.gui.skin", ADDON_SKIN, 166, "DefaultAddonSkin.png" }, {"xbmc.gui.webinterface", ADDON_WEB_INTERFACE, 199, "DefaultAddonWebSkin.png" }, {"xbmc.addon.repository", ADDON_REPOSITORY, 24011, "DefaultAddonRepository.png" }, - {"pvrclient", ADDON_PVRDLL, 0, "" }, + {"xbmc.pvrclient", ADDON_PVRDLL, 24019, "" }, {"xbmc.addon.video", ADDON_VIDEO, 1037, "DefaultAddonVideo.png" }, {"xbmc.addon.audio", ADDON_AUDIO, 1038, "DefaultAddonMusic.png" }, {"xbmc.addon.image", ADDON_IMAGE, 1039, "DefaultAddonPicture.png" }, @@ -290,6 +290,9 @@ void CAddon::BuildLibName(const cp_extension_t *extension) case ADDON_VIZ: ext = ADDON_VIS_EXT; break; + case ADDON_PVRDLL: + ext = ADDON_PVRDLL_EXT; + break; case ADDON_SCRIPT: case ADDON_SCRIPT_LIBRARY: case ADDON_SCRIPT_LYRICS: @@ -325,6 +328,7 @@ void CAddon::BuildLibName(const cp_extension_t *extension) case ADDON_SCRAPER_MUSICVIDEOS: case ADDON_SCRAPER_TVSHOWS: case ADDON_SCRAPER_LIBRARY: + case ADDON_PVRDLL: case ADDON_PLUGIN: case ADDON_SERVICE: { diff --git a/xbmc/addons/Addon.h b/xbmc/addons/Addon.h index 553a0c880f..b49f129a80 100644 --- a/xbmc/addons/Addon.h +++ b/xbmc/addons/Addon.h @@ -26,6 +26,7 @@ #include "guilib/LocalizeStrings.h" class TiXmlElement; +class CAddonCallbacksAddon; typedef struct cp_plugin_info_t cp_plugin_info_t; typedef struct cp_extension_t cp_extension_t; @@ -167,7 +168,11 @@ public: bool MeetsVersion(const AddonVersion &version) const; virtual bool ReloadSettings(); + void MarkAsDisabled() { m_enabled = false; } + protected: + friend class CAddonCallbacksAddon; + CAddon(const CAddon&); // protected as all copying is handled by Clone() CAddon(const CAddon&, const AddonPtr&); const AddonPtr Parent() const { return m_parent; } @@ -206,7 +211,7 @@ protected: bool m_userSettingsLoaded; private: - friend class AddonMgr; + friend class CAddonMgr; AddonProps m_props; const AddonPtr m_parent; CStdString m_userSettingsPath; diff --git a/xbmc/addons/AddonCallbacks.cpp b/xbmc/addons/AddonCallbacks.cpp new file mode 100644 index 0000000000..3cbb4162ae --- /dev/null +++ b/xbmc/addons/AddonCallbacks.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "Addon.h" +#include "AddonCallbacks.h" +#include "AddonCallbacksAddon.h" +#include "AddonCallbacksGUI.h" +#include "AddonCallbacksPVR.h" +#include "filesystem/SpecialProtocol.h" +#include "utils/log.h" + +namespace ADDON +{ + +CAddonCallbacks::CAddonCallbacks(CAddon* addon) +{ + m_addon = addon; + m_callbacks = new AddonCB; + m_helperAddon = NULL; + m_helperGUI = NULL; + m_helperPVR = NULL; + + m_callbacks->libBasePath = strdup(CSpecialProtocol::TranslatePath("special://xbmcbin/addons")); + m_callbacks->addonData = this; + m_callbacks->AddOnLib_RegisterMe = CAddonCallbacks::AddOnLib_RegisterMe; + m_callbacks->AddOnLib_UnRegisterMe = CAddonCallbacks::AddOnLib_UnRegisterMe; + m_callbacks->GUILib_RegisterMe = CAddonCallbacks::GUILib_RegisterMe; + m_callbacks->GUILib_UnRegisterMe = CAddonCallbacks::GUILib_UnRegisterMe; + m_callbacks->PVRLib_RegisterMe = CAddonCallbacks::PVRLib_RegisterMe; + m_callbacks->PVRLib_UnRegisterMe = CAddonCallbacks::PVRLib_UnRegisterMe; +} + +CAddonCallbacks::~CAddonCallbacks() +{ + delete m_helperAddon; + m_helperAddon = NULL; + delete m_helperGUI; + m_helperGUI = NULL; + delete m_helperPVR; + m_helperPVR = NULL; + free((char*)m_callbacks->libBasePath); + delete m_callbacks; + m_callbacks = NULL; +} + +CB_AddOnLib* CAddonCallbacks::AddOnLib_RegisterMe(void *addonData) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return NULL; + } + + addon->m_helperAddon = new CAddonCallbacksAddon(addon->m_addon); + return addon->m_helperAddon->GetCallbacks(); +} + +void CAddonCallbacks::AddOnLib_UnRegisterMe(void *addonData, CB_AddOnLib *cbTable) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return; + } + + delete addon->m_helperAddon; + addon->m_helperAddon = NULL; +} + +CB_GUILib* CAddonCallbacks::GUILib_RegisterMe(void *addonData) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return NULL; + } + + addon->m_helperGUI = new CAddonCallbacksGUI(addon->m_addon); + return addon->m_helperGUI->GetCallbacks(); +} + +void CAddonCallbacks::GUILib_UnRegisterMe(void *addonData, CB_GUILib *cbTable) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return; + } + + delete addon->m_helperGUI; + addon->m_helperGUI = NULL; +} + +CB_PVRLib* CAddonCallbacks::PVRLib_RegisterMe(void *addonData) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return NULL; + } + + addon->m_helperPVR = new CAddonCallbacksPVR(addon->m_addon); + return addon->m_helperPVR->GetCallbacks(); +} + +void CAddonCallbacks::PVRLib_UnRegisterMe(void *addonData, CB_PVRLib *cbTable) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return; + } + + delete addon->m_helperPVR; + addon->m_helperPVR = NULL; +} + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacks.h b/xbmc/addons/AddonCallbacks.h new file mode 100644 index 0000000000..d9178fa1f8 --- /dev/null +++ b/xbmc/addons/AddonCallbacks.h @@ -0,0 +1,268 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h" +#include "addons/include/xbmc_pvr_types.h" +#include "../../addons/library.xbmc.addon/libXBMC_addon.h" +#include "../../addons/library.xbmc.gui/libXBMC_gui.h" + +typedef void (*AddOnLogCallback)(void *addonData, const ADDON::addon_log_t loglevel, const char *msg); +typedef void (*AddOnQueueNotification)(void *addonData, const ADDON::queue_msg_t type, const char *msg); +typedef bool (*AddOnGetSetting)(void *addonData, const char *settingName, void *settingValue); +typedef char* (*AddOnUnknownToUTF8)(const char *sourceDest); +typedef const char* (*AddOnGetLocalizedString)(const void* addonData, long dwCode); +typedef const char* (*AddOnGetDVDMenuLanguage)(const void* addonData); + +typedef struct CB_AddOn +{ + AddOnLogCallback Log; + AddOnQueueNotification QueueNotification; + AddOnGetSetting GetSetting; + AddOnUnknownToUTF8 UnknownToUTF8; + AddOnGetLocalizedString GetLocalizedString; + AddOnGetDVDMenuLanguage GetDVDMenuLanguage; +} CB_AddOnLib; + +typedef void (*GUILock)(); +typedef void (*GUIUnlock)(); +typedef int (*GUIGetScreenHeight)(); +typedef int (*GUIGetScreenWidth)(); +typedef int (*GUIGetVideoResolution)(); +typedef GUIHANDLE (*GUIWindow_New)(void *addonData, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog); +typedef void (*GUIWindow_Delete)(void *addonData, GUIHANDLE handle); +typedef void (*GUIWindow_SetCallbacks)(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*)(GUIHANDLE handle), bool (*)(GUIHANDLE handle, int), bool (*)(GUIHANDLE handle, int), bool (*)(GUIHANDLE handle, int)); +typedef bool (*GUIWindow_Show)(void *addonData, GUIHANDLE handle); +typedef bool (*GUIWindow_Close)(void *addonData, GUIHANDLE handle); +typedef bool (*GUIWindow_DoModal)(void *addonData, GUIHANDLE handle); +typedef bool (*GUIWindow_SetFocusId)(void *addonData, GUIHANDLE handle, int iControlId); +typedef int (*GUIWindow_GetFocusId)(void *addonData, GUIHANDLE handle); +typedef bool (*GUIWindow_SetCoordinateResolution)(void *addonData, GUIHANDLE handle, int res); +typedef void (*GUIWindow_SetProperty)(void *addonData, GUIHANDLE handle, const char *key, const char *value); +typedef void (*GUIWindow_SetPropertyInt)(void *addonData, GUIHANDLE handle, const char *key, int value); +typedef void (*GUIWindow_SetPropertyBool)(void *addonData, GUIHANDLE handle, const char *key, bool value); +typedef void (*GUIWindow_SetPropertyDouble)(void *addonData, GUIHANDLE handle, const char *key, double value); +typedef const char* (*GUIWindow_GetProperty)(void *addonData, GUIHANDLE handle, const char *key); +typedef int (*GUIWindow_GetPropertyInt)(void *addonData, GUIHANDLE handle, const char *key); +typedef bool (*GUIWindow_GetPropertyBool)(void *addonData, GUIHANDLE handle, const char *key); +typedef double (*GUIWindow_GetPropertyDouble)(void *addonData, GUIHANDLE handle, const char *key); +typedef void (*GUIWindow_ClearProperties)(void *addonData, GUIHANDLE handle); +typedef int (*GUIWindow_GetListSize)(void *addonData, GUIHANDLE handle); +typedef void (*GUIWindow_ClearList)(void *addonData, GUIHANDLE handle); +typedef GUIHANDLE (*GUIWindow_AddItem)(void *addonData, GUIHANDLE handle, GUIHANDLE item, int itemPosition); +typedef GUIHANDLE (*GUIWindow_AddStringItem)(void *addonData, GUIHANDLE handle, const char *itemName, int itemPosition); +typedef void (*GUIWindow_RemoveItem)(void *addonData, GUIHANDLE handle, int itemPosition); +typedef GUIHANDLE (*GUIWindow_GetListItem)(void *addonData, GUIHANDLE handle, int listPos); +typedef void (*GUIWindow_SetCurrentListPosition)(void *addonData, GUIHANDLE handle, int listPos); +typedef int (*GUIWindow_GetCurrentListPosition)(void *addonData, GUIHANDLE handle); +typedef GUIHANDLE (*GUIWindow_GetControl_Spin)(void *addonData, GUIHANDLE handle, int controlId); +typedef GUIHANDLE (*GUIWindow_GetControl_Button)(void *addonData, GUIHANDLE handle, int controlId); +typedef GUIHANDLE (*GUIWindow_GetControl_RadioButton)(void *addonData, GUIHANDLE handle, int controlId); +typedef GUIHANDLE (*GUIWindow_GetControl_Edit)(void *addonData, GUIHANDLE handle, int controlId); +typedef GUIHANDLE (*GUIWindow_GetControl_Progress)(void *addonData, GUIHANDLE handle, int controlId); +typedef void (*GUIWindow_SetControlLabel)(void *addonData, GUIHANDLE handle, int controlId, const char *label); +typedef void (*GUIControl_Spin_SetVisible)(void *addonData, GUIHANDLE spinhandle, bool yesNo); +typedef void (*GUIControl_Spin_SetText)(void *addonData, GUIHANDLE spinhandle, const char *label); +typedef void (*GUIControl_Spin_Clear)(void *addonData, GUIHANDLE spinhandle); +typedef void (*GUIControl_Spin_AddLabel)(void *addonData, GUIHANDLE spinhandle, const char *label, int iValue); +typedef int (*GUIControl_Spin_GetValue)(void *addonData, GUIHANDLE spinhandle); +typedef void (*GUIControl_Spin_SetValue)(void *addonData, GUIHANDLE spinhandle, int iValue); +typedef void (*GUIControl_RadioButton_SetVisible)(void *addonData, GUIHANDLE handle, bool yesNo); +typedef void (*GUIControl_RadioButton_SetText)(void *addonData, GUIHANDLE handle, const char *label); +typedef void (*GUIControl_RadioButton_SetSelected)(void *addonData, GUIHANDLE handle, bool yesNo); +typedef bool (*GUIControl_RadioButton_IsSelected)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_Progress_SetPercentage)(void *addonData, GUIHANDLE handle, float fPercent); +typedef float (*GUIControl_Progress_GetPercentage)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_Progress_SetInfo)(void *addonData, GUIHANDLE handle, int iInfo); +typedef int (*GUIControl_Progress_GetInfo)(void *addonData, GUIHANDLE handle); +typedef const char* (*GUIControl_Progress_GetDescription)(void *addonData, GUIHANDLE handle); +typedef GUIHANDLE (*GUIListItem_Create)(void *addonData, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path); +typedef const char* (*GUIListItem_GetLabel)(void *addonData, GUIHANDLE handle); +typedef void (*GUIListItem_SetLabel)(void *addonData, GUIHANDLE handle, const char *label); +typedef const char* (*GUIListItem_GetLabel2)(void *addonData, GUIHANDLE handle); +typedef void (*GUIListItem_SetLabel2)(void *addonData, GUIHANDLE handle, const char *label); +typedef void (*GUIListItem_SetIconImage)(void *addonData, GUIHANDLE handle, const char *image); +typedef void (*GUIListItem_SetThumbnailImage)(void *addonData, GUIHANDLE handle, const char *image); +typedef void (*GUIListItem_SetInfo)(void *addonData, GUIHANDLE handle, const char *info); +typedef void (*GUIListItem_SetProperty)(void *addonData, GUIHANDLE handle, const char *key, const char *value); +typedef const char* (*GUIListItem_GetProperty)(void *addonData, GUIHANDLE handle, const char *key); +typedef void (*GUIListItem_SetPath)(void *addonData, GUIHANDLE handle, const char *path); + +typedef struct CB_GUILib +{ + GUILock Lock; + GUIUnlock Unlock; + GUIGetScreenHeight GetScreenHeight; + GUIGetScreenWidth GetScreenWidth; + GUIGetVideoResolution GetVideoResolution; + GUIWindow_New Window_New; + GUIWindow_Delete Window_Delete; + GUIWindow_SetCallbacks Window_SetCallbacks; + GUIWindow_Show Window_Show; + GUIWindow_Close Window_Close; + GUIWindow_DoModal Window_DoModal; + GUIWindow_SetFocusId Window_SetFocusId; + GUIWindow_GetFocusId Window_GetFocusId; + GUIWindow_SetCoordinateResolution Window_SetCoordinateResolution; + GUIWindow_SetProperty Window_SetProperty; + GUIWindow_SetPropertyInt Window_SetPropertyInt; + GUIWindow_SetPropertyBool Window_SetPropertyBool; + GUIWindow_SetPropertyDouble Window_SetPropertyDouble; + GUIWindow_GetProperty Window_GetProperty; + GUIWindow_GetPropertyInt Window_GetPropertyInt; + GUIWindow_GetPropertyBool Window_GetPropertyBool; + GUIWindow_GetPropertyDouble Window_GetPropertyDouble; + GUIWindow_ClearProperties Window_ClearProperties; + GUIWindow_GetListSize Window_GetListSize; + GUIWindow_ClearList Window_ClearList; + GUIWindow_AddItem Window_AddItem; + GUIWindow_AddStringItem Window_AddStringItem; + GUIWindow_RemoveItem Window_RemoveItem; + GUIWindow_GetListItem Window_GetListItem; + GUIWindow_SetCurrentListPosition Window_SetCurrentListPosition; + GUIWindow_GetCurrentListPosition Window_GetCurrentListPosition; + GUIWindow_GetControl_Spin Window_GetControl_Spin; + GUIWindow_GetControl_Button Window_GetControl_Button; + GUIWindow_GetControl_RadioButton Window_GetControl_RadioButton; + GUIWindow_GetControl_Edit Window_GetControl_Edit; + GUIWindow_GetControl_Progress Window_GetControl_Progress; + GUIWindow_SetControlLabel Window_SetControlLabel; + GUIControl_Spin_SetVisible Control_Spin_SetVisible; + GUIControl_Spin_SetText Control_Spin_SetText; + GUIControl_Spin_Clear Control_Spin_Clear; + GUIControl_Spin_AddLabel Control_Spin_AddLabel; + GUIControl_Spin_GetValue Control_Spin_GetValue; + GUIControl_Spin_SetValue Control_Spin_SetValue; + GUIControl_RadioButton_SetVisible Control_RadioButton_SetVisible; + GUIControl_RadioButton_SetText Control_RadioButton_SetText; + GUIControl_RadioButton_SetSelected Control_RadioButton_SetSelected; + GUIControl_RadioButton_IsSelected Control_RadioButton_IsSelected; + GUIControl_Progress_SetPercentage Control_Progress_SetPercentage; + GUIControl_Progress_GetPercentage Control_Progress_GetPercentage; + GUIControl_Progress_SetInfo Control_Progress_SetInfo; + GUIControl_Progress_GetInfo Control_Progress_GetInfo; + GUIControl_Progress_GetDescription Control_Progress_GetDescription; + GUIListItem_Create ListItem_Create; + GUIListItem_GetLabel ListItem_GetLabel; + GUIListItem_SetLabel ListItem_SetLabel; + GUIListItem_GetLabel2 ListItem_GetLabel2; + GUIListItem_SetLabel2 ListItem_SetLabel2; + GUIListItem_SetIconImage ListItem_SetIconImage; + GUIListItem_SetThumbnailImage ListItem_SetThumbnailImage; + GUIListItem_SetInfo ListItem_SetInfo; + GUIListItem_SetProperty ListItem_SetProperty; + GUIListItem_GetProperty ListItem_GetProperty; + GUIListItem_SetPath ListItem_SetPath; + +} CB_GUILib; + +typedef void (*PVRTransferEpgEntry)(void *userData, const ADDON_HANDLE handle, const EPG_TAG *epgentry); +typedef void (*PVRTransferChannelEntry)(void *userData, const ADDON_HANDLE handle, const PVR_CHANNEL *chan); +typedef void (*PVRTransferTimerEntry)(void *userData, const ADDON_HANDLE handle, const PVR_TIMER *timer); +typedef void (*PVRTransferRecordingEntry)(void *userData, const ADDON_HANDLE handle, const PVR_RECORDING *recording); +typedef void (*PVRAddMenuHook)(void *addonData, PVR_MENUHOOK *hook); +typedef void (*PVRRecording)(void *addonData, const char *Name, const char *FileName, bool On); +typedef void (*PVRTriggerChannelUpdate)(void *addonData); +typedef void (*PVRTriggerTimerUpdate)(void *addonData); +typedef void (*PVRTriggerRecordingUpdate)(void *addonData); +typedef void (*PVRTriggerChannelGroupsUpdate)(void *addonData); + +typedef void (*PVRTransferChannelGroup)(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP *group); +typedef void (*PVRTransferChannelGroupMember)(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER *member); + +typedef void (*PVRFreeDemuxPacket)(void *addonData, DemuxPacket* pPacket); +typedef DemuxPacket* (*PVRAllocateDemuxPacket)(void *addonData, int iDataSize); + +typedef struct CB_PVRLib +{ + PVRTransferEpgEntry TransferEpgEntry; + PVRTransferChannelEntry TransferChannelEntry; + PVRTransferTimerEntry TransferTimerEntry; + PVRTransferRecordingEntry TransferRecordingEntry; + PVRAddMenuHook AddMenuHook; + PVRRecording Recording; + PVRTriggerChannelUpdate TriggerChannelUpdate; + PVRTriggerTimerUpdate TriggerTimerUpdate; + PVRTriggerRecordingUpdate TriggerRecordingUpdate; + PVRTriggerChannelGroupsUpdate TriggerChannelGroupsUpdate; + PVRFreeDemuxPacket FreeDemuxPacket; + PVRAllocateDemuxPacket AllocateDemuxPacket; + PVRTransferChannelGroup TransferChannelGroup; + PVRTransferChannelGroupMember TransferChannelGroupMember; + +} CB_PVRLib; + + +typedef CB_AddOnLib* (*XBMCAddOnLib_RegisterMe)(void *addonData); +typedef void (*XBMCAddOnLib_UnRegisterMe)(void *addonData, CB_AddOnLib *cbTable); +typedef CB_GUILib* (*XBMCGUILib_RegisterMe)(void *addonData); +typedef void (*XBMCGUILib_UnRegisterMe)(void *addonData, CB_GUILib *cbTable); +typedef CB_PVRLib* (*XBMCPVRLib_RegisterMe)(void *addonData); +typedef void (*XBMCPVRLib_UnRegisterMe)(void *addonData, CB_PVRLib *cbTable); + +typedef struct AddonCB +{ + const char *libBasePath; ///> Never, never change this!!! + void *addonData; + XBMCAddOnLib_RegisterMe AddOnLib_RegisterMe; + XBMCAddOnLib_UnRegisterMe AddOnLib_UnRegisterMe; + XBMCGUILib_RegisterMe GUILib_RegisterMe; + XBMCGUILib_UnRegisterMe GUILib_UnRegisterMe; + XBMCPVRLib_RegisterMe PVRLib_RegisterMe; + XBMCPVRLib_UnRegisterMe PVRLib_UnRegisterMe; +} AddonCB; + + +namespace ADDON +{ + +class CAddon; +class CAddonCallbacksAddon; +class CAddonCallbacksGUI; +class CAddonCallbacksPVR; + +class CAddonCallbacks +{ +public: + CAddonCallbacks(CAddon* addon); + ~CAddonCallbacks(); + AddonCB *GetCallbacks() { return m_callbacks; } + + static CB_AddOnLib* AddOnLib_RegisterMe(void *addonData); + static void AddOnLib_UnRegisterMe(void *addonData, CB_AddOnLib *cbTable); + static CB_GUILib* GUILib_RegisterMe(void *addonData); + static void GUILib_UnRegisterMe(void *addonData, CB_GUILib *cbTable); + static CB_PVRLib* PVRLib_RegisterMe(void *addonData); + static void PVRLib_UnRegisterMe(void *addonData, CB_PVRLib *cbTable); + + CAddonCallbacksAddon *GetHelperAddon() { return m_helperAddon; } + CAddonCallbacksGUI *GetHelperGUI() { return m_helperGUI; } + CAddonCallbacksPVR *GetHelperPVR() { return m_helperPVR; } + +private: + AddonCB *m_callbacks; + CAddon *m_addon; + CAddonCallbacksAddon *m_helperAddon; + CAddonCallbacksGUI *m_helperGUI; + CAddonCallbacksPVR *m_helperPVR; +}; + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksAddon.cpp b/xbmc/addons/AddonCallbacksAddon.cpp new file mode 100644 index 0000000000..bb80156520 --- /dev/null +++ b/xbmc/addons/AddonCallbacksAddon.cpp @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "Application.h" +#include "Addon.h" +#include "AddonCallbacksAddon.h" +#include "utils/log.h" +#include "LangInfo.h" +#include "dialogs/GUIDialogKaiToast.h" + +namespace ADDON +{ + +CAddonCallbacksAddon::CAddonCallbacksAddon(CAddon* addon) +{ + m_addon = addon; + m_callbacks = new CB_AddOnLib; + + /* write XBMC addon-on specific add-on function addresses to the callback table */ + m_callbacks->Log = AddOnLog; + m_callbacks->QueueNotification = QueueNotification; + m_callbacks->GetSetting = GetAddonSetting; + m_callbacks->UnknownToUTF8 = UnknownToUTF8; + m_callbacks->GetLocalizedString = GetLocalizedString; + m_callbacks->GetDVDMenuLanguage = GetDVDMenuLanguage; +} + +CAddonCallbacksAddon::~CAddonCallbacksAddon() +{ + /* delete the callback table */ + delete m_callbacks; +} + +void CAddonCallbacksAddon::AddOnLog(void *addonData, const addon_log_t addonLogLevel, const char *strMessage) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL || strMessage == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - called with a null pointer", __FUNCTION__); + return; + } + + CAddonCallbacksAddon* addonHelper = addon->GetHelperAddon(); + + try + { + int xbmcLogLevel = LOGNONE; + switch (addonLogLevel) + { + case LOG_ERROR: + xbmcLogLevel = LOGERROR; + break; + case LOG_INFO: + xbmcLogLevel = LOGINFO; + break; + case LOG_NOTICE: + xbmcLogLevel = LOGNOTICE; + break; + case LOG_DEBUG: + default: + xbmcLogLevel = LOGDEBUG; + break; + } + + CStdString strXbmcMessage; + strXbmcMessage.Format("AddOnLog: %s: %s", addonHelper->m_addon->Name().c_str(), strMessage); + CLog::Log(xbmcLogLevel, "%s", strXbmcMessage.c_str()); + } + catch (std::exception &e) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - exception '%s' caught in call in add-on '%s'. please contact the developer of this addon: %s", + __FUNCTION__, e.what(), addonHelper->m_addon->Name().c_str(), addonHelper->m_addon->Author().c_str()); + } +} + +void CAddonCallbacksAddon::QueueNotification(void *addonData, const queue_msg_t type, const char *strMessage) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL || strMessage == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - called with a null pointer", __FUNCTION__); + return; + } + + CAddonCallbacksAddon* addonHelper = addon->GetHelperAddon(); + + try + { + switch (type) + { + case QUEUE_WARNING: + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, addonHelper->m_addon->Name(), strMessage, 3000, true); + CLog::Log(LOGDEBUG, "CAddonCallbacksAddon - %s - %s - Warning Message: '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str(), strMessage); + break; + + case QUEUE_ERROR: + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, addonHelper->m_addon->Name(), strMessage, 3000, true); + CLog::Log(LOGDEBUG, "CAddonCallbacksAddon - %s - %s - Error Message : '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str(), strMessage); + break; + + case QUEUE_INFO: + default: + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, addonHelper->m_addon->Name(), strMessage, 3000, false); + CLog::Log(LOGDEBUG, "CAddonCallbacksAddon - %s - %s - Info Message : '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str(), strMessage); + break; + } + } + catch (std::exception &e) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - exception '%s' caught in call in add-on '%s'. please contact the developer of this addon: %s", + __FUNCTION__, e.what(), addonHelper->m_addon->Name().c_str(), addonHelper->m_addon->Author().c_str()); + } +} + +bool CAddonCallbacksAddon::GetAddonSetting(void *addonData, const char *strSettingName, void *settingValue) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL || strSettingName == NULL || settingValue == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - called with a null pointer", __FUNCTION__); + return false; + } + + CAddonCallbacksAddon* addonHelper = addon->GetHelperAddon(); + + try + { + CLog::Log(LOGDEBUG, "CAddonCallbacksAddon - %s - add-on '%s' requests setting '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str(), strSettingName); + + if (!addonHelper->m_addon->ReloadSettings()) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - could't get settings for add-on '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str()); + return false; + } + + const TiXmlElement *category = addonHelper->m_addon->GetSettingsXML()->FirstChildElement("category"); + if (!category) // add a default one... + category = addonHelper->m_addon->GetSettingsXML(); + + while (category) + { + const TiXmlElement *setting = category->FirstChildElement("setting"); + while (setting) + { + const char *id = setting->Attribute("id"); + const char *type = setting->Attribute("type"); + + if (strcmpi(id, strSettingName) == 0 && type) + { + if (strcmpi(type, "text") == 0 || strcmpi(type, "ipaddress") == 0 || + strcmpi(type, "folder") == 0 || strcmpi(type, "action") == 0 || + strcmpi(type, "music") == 0 || strcmpi(type, "pictures") == 0 || + strcmpi(type, "folder") == 0 || strcmpi(type, "programs") == 0 || + strcmpi(type, "file") == 0 || strcmpi(type, "fileenum") == 0) + { + strcpy((char*) settingValue, addonHelper->m_addon->GetSetting(id).c_str()); + return true; + } + else if (strcmpi(type, "number") == 0 || strcmpi(type, "enum") == 0 || + strcmpi(type, "labelenum") == 0) + { + *(int*) settingValue = (int) atoi(addonHelper->m_addon->GetSetting(id)); + return true; + } + else if (strcmpi(type, "bool") == 0) + { + *(bool*) settingValue = (bool) (addonHelper->m_addon->GetSetting(id) == "true" ? true : false); + return true; + } + } + setting = setting->NextSiblingElement("setting"); + } + category = category->NextSiblingElement("category"); + } + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - can't find setting '%s' in '%s'", __FUNCTION__, strSettingName, addonHelper->m_addon->Name().c_str()); + } + catch (std::exception &e) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - exception '%s' caught in call in add-on '%s'. please contact the developer of this addon: %s", + __FUNCTION__, e.what(), addonHelper->m_addon->Name().c_str(), addonHelper->m_addon->Author().c_str()); + } + + return false; +} + +char* CAddonCallbacksAddon::UnknownToUTF8(const char *strSource) +{ + CStdString string; + if (strSource != NULL) + g_charsetConverter.unknownToUTF8(strSource, string); + else + string = ""; + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +const char* CAddonCallbacksAddon::GetLocalizedString(const void* addonData, long dwCode) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || g_application.m_bStop) + return NULL; + + CAddonCallbacksAddon* addonHelper = helper->GetHelperAddon(); + + CStdString string; + if (dwCode >= 30000 && dwCode <= 30999) + string = addonHelper->m_addon->GetString(dwCode).c_str(); + else if (dwCode >= 32000 && dwCode <= 32999) + string = addonHelper->m_addon->GetString(dwCode).c_str(); + else + string = g_localizeStrings.Get(dwCode).c_str(); + + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +const char* CAddonCallbacksAddon::GetDVDMenuLanguage(const void* addonData) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return NULL; + + CStdString string = g_langInfo.GetDVDMenuLanguage(); + + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksAddon.h b/xbmc/addons/AddonCallbacksAddon.h new file mode 100644 index 0000000000..2f6436bb00 --- /dev/null +++ b/xbmc/addons/AddonCallbacksAddon.h @@ -0,0 +1,91 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "AddonCallbacks.h" + +namespace ADDON +{ + +class CAddonCallbacksAddon +{ +public: + CAddonCallbacksAddon(CAddon* addon); + ~CAddonCallbacksAddon(); + + /*! + * @return The callback table. + */ + CB_AddOnLib *GetCallbacks() { return m_callbacks; } + + /*! + * @brief Add a message to XBMC's log. + * @param addonData A pointer to the add-on. + * @param addonLogLevel The log level of the message. + * @param strMessage The message itself. + */ + static void AddOnLog(void *addonData, const addon_log_t addonLogLevel, const char *strMessage); + + /*! + * @brief Queue a notification in the GUI. + * @param addonData A pointer to the add-on. + * @param type The message type. + * @param strMessage The message to display. + */ + static void QueueNotification(void *addonData, const queue_msg_t type, const char *strMessage); + + /*! + * @brief Get a settings value for this add-on. + * @param addonData A pointer to the add-on. + * @param settingName The name of the setting to get. + * @param settingValue The value. + * @return True if the settings was fetched successfully, false otherwise. + */ + static bool GetAddonSetting(void *addonData, const char *strSettingName, void *settingValue); + + /*! + * @brief Translate a string with an unknown encoding to UTF8. + * @param sourceDest The source string. + * @return The converted string. + */ + static char *UnknownToUTF8(const char *strSource); + + /*! + * @brief Get a localised message. + * @param addonData A pointer to the add-on. + * @param dwCode The code of the message to get. + * @return The message. + */ + static const char *GetLocalizedString(const void* addonData, long dwCode); + + /*! + * @brief Get the DVD menu language. + * @param addonData A pointer to the add-on. + * @return The language. + */ + static const char *GetDVDMenuLanguage(const void* addonData); + +private: + CB_AddOnLib *m_callbacks; /*!< callback addresses */ + CAddon *m_addon; /*!< the add-on */ +}; + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksGUI.cpp b/xbmc/addons/AddonCallbacksGUI.cpp new file mode 100644 index 0000000000..742d8770ef --- /dev/null +++ b/xbmc/addons/AddonCallbacksGUI.cpp @@ -0,0 +1,1542 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "Application.h" +#include "ApplicationMessenger.h" +#include "Addon.h" +#include "AddonCallbacksGUI.h" +#include "utils/log.h" +#include "Skin.h" +#include "FileItem.h" +#include "filesystem/File.h" +#include "utils/URIUtils.h" +#include "utils/TimeUtils.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/TextureManager.h" +#include "settings/GUISettings.h" +#include "guilib/GUISpinControlEx.h" +#include "guilib/GUIRadioButtonControl.h" +#include "guilib/GUISettingsSliderControl.h" +#include "guilib/GUIEditControl.h" +#include "guilib/GUIProgressControl.h" + +#define CONTROL_BTNVIEWASICONS 2 +#define CONTROL_BTNSORTBY 3 +#define CONTROL_BTNSORTASC 4 +#define CONTROL_LABELFILES 12 + +using namespace std; + +namespace ADDON +{ + +static int iXBMCGUILockRef = 0; + +CAddonCallbacksGUI::CAddonCallbacksGUI(CAddon* addon) +{ + m_addon = addon; + m_callbacks = new CB_GUILib; + + /* GUI Helper functions */ + m_callbacks->Lock = CAddonCallbacksGUI::Lock; + m_callbacks->Unlock = CAddonCallbacksGUI::Unlock; + m_callbacks->GetScreenHeight = CAddonCallbacksGUI::GetScreenHeight; + m_callbacks->GetScreenWidth = CAddonCallbacksGUI::GetScreenWidth; + m_callbacks->GetVideoResolution = CAddonCallbacksGUI::GetVideoResolution; + m_callbacks->Window_New = CAddonCallbacksGUI::Window_New; + m_callbacks->Window_Delete = CAddonCallbacksGUI::Window_Delete; + m_callbacks->Window_SetCallbacks = CAddonCallbacksGUI::Window_SetCallbacks; + m_callbacks->Window_Show = CAddonCallbacksGUI::Window_Show; + m_callbacks->Window_Close = CAddonCallbacksGUI::Window_Close; + m_callbacks->Window_DoModal = CAddonCallbacksGUI::Window_DoModal; + m_callbacks->Window_SetFocusId = CAddonCallbacksGUI::Window_SetFocusId; + m_callbacks->Window_GetFocusId = CAddonCallbacksGUI::Window_GetFocusId; + m_callbacks->Window_SetCoordinateResolution = CAddonCallbacksGUI::Window_SetCoordinateResolution; + m_callbacks->Window_SetProperty = CAddonCallbacksGUI::Window_SetProperty; + m_callbacks->Window_SetPropertyInt = CAddonCallbacksGUI::Window_SetPropertyInt; + m_callbacks->Window_SetPropertyBool = CAddonCallbacksGUI::Window_SetPropertyBool; + m_callbacks->Window_SetPropertyDouble = CAddonCallbacksGUI::Window_SetPropertyDouble; + m_callbacks->Window_GetProperty = CAddonCallbacksGUI::Window_GetProperty; + m_callbacks->Window_GetPropertyInt = CAddonCallbacksGUI::Window_GetPropertyInt; + m_callbacks->Window_GetPropertyBool = CAddonCallbacksGUI::Window_GetPropertyBool; + m_callbacks->Window_GetPropertyDouble = CAddonCallbacksGUI::Window_GetPropertyDouble; + m_callbacks->Window_ClearProperties = CAddonCallbacksGUI::Window_ClearProperties; + + m_callbacks->Window_GetListSize = CAddonCallbacksGUI::Window_GetListSize; + m_callbacks->Window_ClearList = CAddonCallbacksGUI::Window_ClearList; + m_callbacks->Window_AddItem = CAddonCallbacksGUI::Window_AddItem; + m_callbacks->Window_AddStringItem = CAddonCallbacksGUI::Window_AddStringItem; + m_callbacks->Window_RemoveItem = CAddonCallbacksGUI::Window_RemoveItem; + m_callbacks->Window_GetListItem = CAddonCallbacksGUI::Window_GetListItem; + m_callbacks->Window_SetCurrentListPosition = CAddonCallbacksGUI::Window_SetCurrentListPosition; + m_callbacks->Window_GetCurrentListPosition = CAddonCallbacksGUI::Window_GetCurrentListPosition; + + m_callbacks->Window_GetControl_Spin = CAddonCallbacksGUI::Window_GetControl_Spin; + m_callbacks->Window_GetControl_Button = CAddonCallbacksGUI::Window_GetControl_Button; + m_callbacks->Window_GetControl_RadioButton = CAddonCallbacksGUI::Window_GetControl_RadioButton; + m_callbacks->Window_GetControl_Edit = CAddonCallbacksGUI::Window_GetControl_Edit; + m_callbacks->Window_GetControl_Progress = CAddonCallbacksGUI::Window_GetControl_Progress; + + m_callbacks->Window_SetControlLabel = CAddonCallbacksGUI::Window_SetControlLabel; + + m_callbacks->Control_Spin_SetVisible = CAddonCallbacksGUI::Control_Spin_SetVisible; + m_callbacks->Control_Spin_SetText = CAddonCallbacksGUI::Control_Spin_SetText; + m_callbacks->Control_Spin_Clear = CAddonCallbacksGUI::Control_Spin_Clear; + m_callbacks->Control_Spin_AddLabel = CAddonCallbacksGUI::Control_Spin_AddLabel; + m_callbacks->Control_Spin_GetValue = CAddonCallbacksGUI::Control_Spin_GetValue; + m_callbacks->Control_Spin_SetValue = CAddonCallbacksGUI::Control_Spin_SetValue; + + m_callbacks->Control_RadioButton_SetVisible = CAddonCallbacksGUI::Control_RadioButton_SetVisible; + m_callbacks->Control_RadioButton_SetText = CAddonCallbacksGUI::Control_RadioButton_SetText; + m_callbacks->Control_RadioButton_SetSelected= CAddonCallbacksGUI::Control_RadioButton_SetSelected; + m_callbacks->Control_RadioButton_IsSelected = CAddonCallbacksGUI::Control_RadioButton_IsSelected; + + m_callbacks->Control_Progress_SetPercentage = CAddonCallbacksGUI::Control_Progress_SetPercentage; + m_callbacks->Control_Progress_GetPercentage = CAddonCallbacksGUI::Control_Progress_GetPercentage; + m_callbacks->Control_Progress_SetInfo = CAddonCallbacksGUI::Control_Progress_SetInfo; + m_callbacks->Control_Progress_GetInfo = CAddonCallbacksGUI::Control_Progress_GetInfo; + m_callbacks->Control_Progress_GetDescription= CAddonCallbacksGUI::Control_Progress_GetDescription; + + m_callbacks->ListItem_Create = CAddonCallbacksGUI::ListItem_Create; + m_callbacks->ListItem_GetLabel = CAddonCallbacksGUI::ListItem_GetLabel; + m_callbacks->ListItem_SetLabel = CAddonCallbacksGUI::ListItem_SetLabel; + m_callbacks->ListItem_GetLabel2 = CAddonCallbacksGUI::ListItem_GetLabel2; + m_callbacks->ListItem_SetLabel2 = CAddonCallbacksGUI::ListItem_SetLabel2; + m_callbacks->ListItem_SetIconImage = CAddonCallbacksGUI::ListItem_SetIconImage; + m_callbacks->ListItem_SetThumbnailImage = CAddonCallbacksGUI::ListItem_SetThumbnailImage; + m_callbacks->ListItem_SetInfo = CAddonCallbacksGUI::ListItem_SetInfo; + m_callbacks->ListItem_SetProperty = CAddonCallbacksGUI::ListItem_SetProperty; + m_callbacks->ListItem_GetProperty = CAddonCallbacksGUI::ListItem_GetProperty; + m_callbacks->ListItem_SetPath = CAddonCallbacksGUI::ListItem_SetPath; +} + +CAddonCallbacksGUI::~CAddonCallbacksGUI() +{ + delete m_callbacks; +} + +void CAddonCallbacksGUI::Lock() +{ + if (iXBMCGUILockRef == 0) g_graphicsContext.Lock(); + iXBMCGUILockRef++; +} + +void CAddonCallbacksGUI::Unlock() +{ + if (iXBMCGUILockRef > 0) + { + iXBMCGUILockRef--; + if (iXBMCGUILockRef == 0) g_graphicsContext.Unlock(); + } +} + +int CAddonCallbacksGUI::GetScreenHeight() +{ + return g_graphicsContext.GetHeight(); +} + +int CAddonCallbacksGUI::GetScreenWidth() +{ + return g_graphicsContext.GetWidth(); +} + +int CAddonCallbacksGUI::GetVideoResolution() +{ + return (int)g_graphicsContext.GetVideoResolution(); +} + +GUIHANDLE CAddonCallbacksGUI::Window_New(void *addonData, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return NULL; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + RESOLUTION_INFO res; + CStdString strSkinPath; + if (!forceFallback) + { + /* Check to see if the XML file exists in current skin. If not use + fallback path to find a skin for the addon */ + strSkinPath = g_SkinInfo->GetSkinPath(xmlFilename, &res); + + if (!XFILE::CFile::Exists(strSkinPath)) + { + /* Check for the matching folder for the skin in the fallback skins folder */ + CStdString basePath; + URIUtils::AddFileToFolder(guiHelper->m_addon->Path(), "resources", basePath); + URIUtils::AddFileToFolder(basePath, "skins", basePath); + URIUtils::AddFileToFolder(basePath, URIUtils::GetFileName(g_SkinInfo->Path()), basePath); + strSkinPath = g_SkinInfo->GetSkinPath(xmlFilename, &res, basePath); + if (!XFILE::CFile::Exists(strSkinPath)) + { + /* Finally fallback to the DefaultSkin as it didn't exist in either the + XBMC Skin folder or the fallback skin folder */ + forceFallback = true; + } + } + } + + if (forceFallback) + { + //FIXME make this static method of current skin? + CStdString str("none"); + AddonProps props(str, ADDON_SKIN, str, str); + CSkinInfo skinInfo(props); + CStdString basePath; + URIUtils::AddFileToFolder(guiHelper->m_addon->Path(), "resources", basePath); + URIUtils::AddFileToFolder(basePath, "skins", basePath); + URIUtils::AddFileToFolder(basePath, defaultSkin, basePath); + props.path = basePath; + + skinInfo.Start(); + strSkinPath = skinInfo.GetSkinPath(xmlFilename, &res, basePath); + + if (!XFILE::CFile::Exists(strSkinPath)) + { + CLog::Log(LOGERROR, "Window_New: %s/%s - XML File '%s' for Window is missing, contact Developer '%s' of this AddOn", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str(), strSkinPath.c_str(), guiHelper->m_addon->Author().c_str()); + return NULL; + } + } + // window id's 14000 - 14100 are reserved for addons + // get first window id that is not in use + int id = WINDOW_ADDON_START; + // if window 14099 is in use it means addon can't create more windows + Lock(); + if (g_windowManager.GetWindow(WINDOW_ADDON_END)) + { + Unlock(); + CLog::Log(LOGERROR, "Window_New: %s/%s - maximum number of windows reached", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return NULL; + } + while(id < WINDOW_ADDON_END && g_windowManager.GetWindow(id) != NULL) id++; + Unlock(); + + CGUIWindow *window; + if (!asDialog) + window = new CGUIAddonWindow(id, strSkinPath, guiHelper->m_addon); + else + window = new CGUIAddonWindowDialog(id, strSkinPath, guiHelper->m_addon); + + Lock(); + g_windowManager.Add(window); + Unlock(); + + window->SetCoordsRes(res); + + return window; +} + +void CAddonCallbacksGUI::Window_Delete(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_Show: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + Lock(); + // first change to an existing window + if (g_windowManager.GetActiveWindow() == pAddonWindow->m_iWindowId && !g_application.m_bStop) + { + if(g_windowManager.GetWindow(pAddonWindow->m_iOldWindowId)) + g_windowManager.ActivateWindow(pAddonWindow->m_iOldWindowId); + else // old window does not exist anymore, switch to home + g_windowManager.ActivateWindow(WINDOW_HOME); + } + // Free any window properties + pAddonWindow->ClearProperties(); + // free the window's resources and unload it (free all guicontrols) + pAddonWindow->FreeResources(true); + + g_windowManager.Remove(pAddonWindow->GetID()); + delete pAddonWindow; + Unlock(); +} + +void CAddonCallbacksGUI::Window_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*initCB)(GUIHANDLE), bool (*clickCB)(GUIHANDLE, int), bool (*focusCB)(GUIHANDLE, int), bool (*onActionCB)(GUIHANDLE handle, int)) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + pAddonWindow->m_clientHandle = clienthandle; + pAddonWindow->CBOnInit = initCB; + pAddonWindow->CBOnClick = clickCB; + pAddonWindow->CBOnFocus = focusCB; + pAddonWindow->CBOnAction = onActionCB; + Unlock(); +} + +bool CAddonCallbacksGUI::Window_Show(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_Show: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + if (pAddonWindow->m_iOldWindowId != pAddonWindow->m_iWindowId && pAddonWindow->m_iWindowId != g_windowManager.GetActiveWindow()) + pAddonWindow->m_iOldWindowId = g_windowManager.GetActiveWindow(); + + Lock(); + if (pAddonWindow->IsDialog()) + ((CGUIAddonWindowDialog*)pAddonWindow)->Show(); + else + g_windowManager.ActivateWindow(pAddonWindow->m_iWindowId); + Unlock(); + + return true; +} + +bool CAddonCallbacksGUI::Window_Close(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_Close: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + pAddonWindow->m_bModal = false; + if (pAddonWindow->IsDialog()) + ((CGUIAddonWindowDialog*)pAddonWindow)->PulseActionEvent(); + else + ((CGUIAddonWindow*)pAddonWindow)->PulseActionEvent(); + + Lock(); + // if it's a dialog, we have to close it a bit different + if (pAddonWindow->IsDialog()) + ((CGUIAddonWindowDialog*)pAddonWindow)->Show(false); + else + g_windowManager.ActivateWindow(pAddonWindow->m_iOldWindowId); + pAddonWindow->m_iOldWindowId = 0; + + Unlock(); + + return true; +} + +bool CAddonCallbacksGUI::Window_DoModal(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_DoModal: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + pAddonWindow->m_bModal = true; + + if (pAddonWindow->m_iWindowId != g_windowManager.GetActiveWindow()) + Window_Show(addonData, handle); + + return true; +} + +bool CAddonCallbacksGUI::Window_SetFocusId(void *addonData, GUIHANDLE handle, int iControlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_SetFocusId: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + if(!pWindow->GetControl(iControlId)) + { + CLog::Log(LOGERROR, "Window_SetFocusId: %s/%s - Control does not exist in window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + Lock(); + CGUIMessage msg = CGUIMessage(GUI_MSG_SETFOCUS, pAddonWindow->m_iWindowId, iControlId); + pWindow->OnMessage(msg); + Unlock(); + + return true; +} + +int CAddonCallbacksGUI::Window_GetFocusId(void *addonData, GUIHANDLE handle) +{ + int iControlId = -1; + + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return iControlId; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_GetFocusId: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return iControlId; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return iControlId; + + Lock(); + iControlId = pWindow->GetFocusedControlID(); + Unlock(); + + if (iControlId == -1) + { + CLog::Log(LOGERROR, "Window_GetFocusId: %s/%s - No control in this window has focus", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return iControlId; + } + + return iControlId; +} + +bool CAddonCallbacksGUI::Window_SetCoordinateResolution(void *addonData, GUIHANDLE handle, int res) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "SetCoordinateResolution: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + if (res < RES_HDTV_1080i || res > RES_AUTORES) + { + CLog::Log(LOGERROR, "SetCoordinateResolution: %s/%s - Invalid resolution", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + pWindow->SetCoordsRes((RESOLUTION)res); + + return true; +} + +void CAddonCallbacksGUI::Window_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_SetProperty: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + CStdString lowerKey = key; + + Lock(); + pWindow->SetProperty(lowerKey.ToLower(), value); + Unlock(); +} + +void CAddonCallbacksGUI::Window_SetPropertyInt(void *addonData, GUIHANDLE handle, const char *key, int value) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_SetPropertyInt: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + CStdString lowerKey = key; + + Lock(); + pWindow->SetProperty(lowerKey.ToLower(), value); + Unlock(); +} + +void CAddonCallbacksGUI::Window_SetPropertyBool(void *addonData, GUIHANDLE handle, const char *key, bool value) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_SetPropertyBool: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + CStdString lowerKey = key; + + Lock(); + pWindow->SetProperty(lowerKey.ToLower(), value); + Unlock(); +} + +void CAddonCallbacksGUI::Window_SetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key, double value) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_SetPropertyDouble: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + CStdString lowerKey = key; + + Lock(); + pWindow->SetProperty(lowerKey.ToLower(), value); + Unlock(); +} + +const char* CAddonCallbacksGUI::Window_GetProperty(void *addonData, GUIHANDLE handle, const char *key) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return NULL; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_GetProperty: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return NULL; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return NULL; + + Lock(); + CStdString lowerKey = key; + string value = pWindow->GetProperty(lowerKey.ToLower()).asString(); + Unlock(); + + return value.c_str(); +} + +int CAddonCallbacksGUI::Window_GetPropertyInt(void *addonData, GUIHANDLE handle, const char *key) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return -1; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_GetPropertyInt: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return -1; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return -1; + + Lock(); + CStdString lowerKey = key; + int value = pWindow->GetProperty(lowerKey.ToLower()).asInteger(); + Unlock(); + + return value; +} + +bool CAddonCallbacksGUI::Window_GetPropertyBool(void *addonData, GUIHANDLE handle, const char *key) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_GetPropertyBool: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + Lock(); + CStdString lowerKey = key; + bool value = pWindow->GetProperty(lowerKey.ToLower()).asBoolean(); + Unlock(); + + return value; +} + +double CAddonCallbacksGUI::Window_GetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return 0.0; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_GetPropertyDouble: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return 0.0; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return 0.0; + + Lock(); + CStdString lowerKey = key; + double value = pWindow->GetProperty(lowerKey.ToLower()).asDouble(); + Unlock(); + + return value; +} + +void CAddonCallbacksGUI::Window_ClearProperties(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_ClearProperties: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + Lock(); + pWindow->ClearProperties(); + Unlock(); +} + +int CAddonCallbacksGUI::Window_GetListSize(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return -1; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + int listSize = pAddonWindow->GetListSize(); + Unlock(); + + return listSize; +} + +void CAddonCallbacksGUI::Window_ClearList(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + pAddonWindow->ClearList(); + Unlock(); + + return; +} + +GUIHANDLE CAddonCallbacksGUI::Window_AddItem(void *addonData, GUIHANDLE handle, GUIHANDLE item, int itemPosition) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle || !item) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CFileItemPtr pItem((CFileItem*)item); + Lock(); + pAddonWindow->AddItem(pItem, itemPosition); + Unlock(); + + return item; +} + +GUIHANDLE CAddonCallbacksGUI::Window_AddStringItem(void *addonData, GUIHANDLE handle, const char *itemName, int itemPosition) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle || !itemName) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CFileItemPtr item(new CFileItem(itemName)); + Lock(); + pAddonWindow->AddItem(item, itemPosition); + Unlock(); + + return item.get(); +} + +void CAddonCallbacksGUI::Window_RemoveItem(void *addonData, GUIHANDLE handle, int itemPosition) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + pAddonWindow->RemoveItem(itemPosition); + Unlock(); + + return; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetListItem(void *addonData, GUIHANDLE handle, int listPos) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + CFileItemPtr fi = pAddonWindow->GetListItem(listPos); + if (fi == NULL) + { + Unlock(); + CLog::Log(LOGERROR, "Window_GetListItem: %s/%s - Index out of range", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return NULL; + } + Unlock(); + + return fi.get(); +} + +void CAddonCallbacksGUI::Window_SetCurrentListPosition(void *addonData, GUIHANDLE handle, int listPos) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + pAddonWindow->SetCurrentListPosition(listPos); + Unlock(); + + return; +} + +int CAddonCallbacksGUI::Window_GetCurrentListPosition(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return -1; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + int listPos = pAddonWindow->GetCurrentListPosition(); + Unlock(); + + return listPos; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Spin(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_SPINEX) + return NULL; + + return pGUIControl; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Button(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_BUTTON) + return NULL; + + return pGUIControl; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_RadioButton(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_RADIO) + return NULL; + + return pGUIControl; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Edit(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_EDIT) + return NULL; + + return pGUIControl; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Progress(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_PROGRESS) + return NULL; + + return pGUIControl; +} + +void CAddonCallbacksGUI::Window_SetControlLabel(void *addonData, GUIHANDLE handle, int controlId, const char *label) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + CGUIMessage msg(GUI_MSG_LABEL_SET, pAddonWindow->m_iWindowId, controlId); + msg.SetLabel(label); + pAddonWindow->OnMessage(msg); +} + +void CAddonCallbacksGUI::Control_Spin_SetVisible(void *addonData, GUIHANDLE spinhandle, bool yesNo) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + pSpin->SetVisible(yesNo); +} + +void CAddonCallbacksGUI::Control_Spin_SetText(void *addonData, GUIHANDLE spinhandle, const char *label) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + pSpin->SetText(label); +} + +void CAddonCallbacksGUI::Control_Spin_Clear(void *addonData, GUIHANDLE spinhandle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + pSpin->Clear(); +} + +void CAddonCallbacksGUI::Control_Spin_AddLabel(void *addonData, GUIHANDLE spinhandle, const char *label, int iValue) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + pSpin->AddLabel(label, iValue); +} + +int CAddonCallbacksGUI::Control_Spin_GetValue(void *addonData, GUIHANDLE spinhandle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return -1; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + return pSpin->GetValue(); +} + +void CAddonCallbacksGUI::Control_Spin_SetValue(void *addonData, GUIHANDLE spinhandle, int iValue) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + pSpin->SetValue(iValue); +} + +void CAddonCallbacksGUI::Control_RadioButton_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle; + pRadioButton->SetVisible(yesNo); +} + +void CAddonCallbacksGUI::Control_RadioButton_SetText(void *addonData, GUIHANDLE handle, const char *label) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle; + pRadioButton->SetLabel(label); +} + +void CAddonCallbacksGUI::Control_RadioButton_SetSelected(void *addonData, GUIHANDLE handle, bool yesNo) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle; + pRadioButton->SetSelected(yesNo); +} + +bool CAddonCallbacksGUI::Control_RadioButton_IsSelected(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return false; + + CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle; + return pRadioButton->IsSelected(); +} + +void CAddonCallbacksGUI::Control_Progress_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIProgressControl *pControl = (CGUIProgressControl*)handle; + pControl->SetPercentage(fPercent); +} + +float CAddonCallbacksGUI::Control_Progress_GetPercentage(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return 0.0; + + CGUIProgressControl *pControl = (CGUIProgressControl*)handle; + return pControl->GetPercentage(); +} + +void CAddonCallbacksGUI::Control_Progress_SetInfo(void *addonData, GUIHANDLE handle, int iInfo) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIProgressControl *pControl = (CGUIProgressControl*)handle; + pControl->SetInfo(iInfo); +} + +int CAddonCallbacksGUI::Control_Progress_GetInfo(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return -1; + + CGUIProgressControl *pControl = (CGUIProgressControl*)handle; + return pControl->GetInfo(); +} + +const char* CAddonCallbacksGUI::Control_Progress_GetDescription(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIProgressControl *pControl = (CGUIProgressControl*)handle; + CStdString string = pControl->GetDescription(); + + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +GUIHANDLE CAddonCallbacksGUI::ListItem_Create(void *addonData, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return NULL; + + // create CFileItem + CFileItem *pItem = new CFileItem(); + if (!pItem) + return NULL; + + if (label) + pItem->SetLabel(label); + if (label2) + pItem->SetLabel2(label2); + if (iconImage) + pItem->SetIconImage(iconImage); + if (thumbnailImage) + pItem->SetThumbnailImage(thumbnailImage); + if (path) + pItem->SetPath(path); + + return pItem; +} + +const char* CAddonCallbacksGUI::ListItem_GetLabel(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CStdString string = ((CFileItem*)handle)->GetLabel(); + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +void CAddonCallbacksGUI::ListItem_SetLabel(void *addonData, GUIHANDLE handle, const char *label) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetLabel(label); +} + +const char* CAddonCallbacksGUI::ListItem_GetLabel2(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CStdString string = ((CFileItem*)handle)->GetLabel2(); + + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +void CAddonCallbacksGUI::ListItem_SetLabel2(void *addonData, GUIHANDLE handle, const char *label) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetLabel2(label); +} + +void CAddonCallbacksGUI::ListItem_SetIconImage(void *addonData, GUIHANDLE handle, const char *image) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetIconImage(image); +} + +void CAddonCallbacksGUI::ListItem_SetThumbnailImage(void *addonData, GUIHANDLE handle, const char *image) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetThumbnailImage(image); +} + +void CAddonCallbacksGUI::ListItem_SetInfo(void *addonData, GUIHANDLE handle, const char *info) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + +} + +void CAddonCallbacksGUI::ListItem_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetProperty(key, value); +} + +const char* CAddonCallbacksGUI::ListItem_GetProperty(void *addonData, GUIHANDLE handle, const char *key) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + string string = ((CFileItem*)handle)->GetProperty(key).asString(); + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +void CAddonCallbacksGUI::ListItem_SetPath(void *addonData, GUIHANDLE handle, const char *path) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetPath(path); +} + + + + + + + +CGUIAddonWindow::CGUIAddonWindow(int id, CStdString strXML, CAddon* addon) + : CGUIMediaWindow(id, strXML) + , m_iWindowId(id) + , m_iOldWindowId(0) + , m_bModal(false) + , m_bIsDialog(false) + , m_actionEvent(true) + , m_addon(addon) +{ + m_loadOnDemand = false; + CBOnInit = NULL; + CBOnFocus = NULL; + CBOnClick = NULL; + CBOnAction = NULL; +} + +CGUIAddonWindow::~CGUIAddonWindow(void) +{ +} + +bool CGUIAddonWindow::OnAction(const CAction &action) +{ + // do the base class window first, and the call to python after this + bool ret = CGUIWindow::OnAction(action); // we don't currently want the mediawindow actions here + if (CBOnAction) + { + CBOnAction(m_clientHandle, action.GetID()); + } + return ret; +} + +bool CGUIAddonWindow::OnMessage(CGUIMessage& message) +{ + // TODO: We shouldn't be dropping down to CGUIWindow in any of this ideally. + // We have to make up our minds about what python should be doing and + // what this side of things should be doing + switch (message.GetMessage()) + { + case GUI_MSG_WINDOW_DEINIT: + { + return CGUIMediaWindow::OnMessage(message); + } + break; + + case GUI_MSG_WINDOW_INIT: + { + CGUIMediaWindow::OnMessage(message); + if (CBOnInit) + CBOnInit(m_clientHandle); + + return true; + } + break; + + case GUI_MSG_SETFOCUS: + { + if (m_viewControl.HasControl(message.GetControlId()) && m_viewControl.GetCurrentControl() != (int)message.GetControlId()) + { + m_viewControl.SetFocused(); + return true; + } + // check if our focused control is one of our category buttons + int iControl = message.GetControlId(); + if (CBOnFocus) + { + CBOnFocus(m_clientHandle, iControl); + } + } + break; + case GUI_MSG_CLICKED: + { + int iControl=message.GetSenderId(); + // Handle Sort/View internally. Scripters shouldn't use ID 2, 3 or 4. + if (iControl == CONTROL_BTNSORTASC) // sort asc + { + CLog::Log(LOGINFO, "WindowXML: Internal asc/dsc button not implemented"); + /*if (m_guiState.get()) + m_guiState->SetNextSortOrder(); + UpdateFileList();*/ + return true; + } + else if (iControl == CONTROL_BTNSORTBY) // sort by + { + CLog::Log(LOGINFO, "WindowXML: Internal sort button not implemented"); + /*if (m_guiState.get()) + m_guiState->SetNextSortMethod(); + UpdateFileList();*/ + return true; + } + + if (CBOnClick && iControl && iControl != (int)this->GetID()) + { + CGUIControl* controlClicked = (CGUIControl*)this->GetControl(iControl); + + // The old python way used to check list AND SELECITEM method or if its a button, checkmark. + // Its done this way for now to allow other controls without a python version like togglebutton to still raise a onAction event + if (controlClicked) // Will get problems if we the id is not on the window and we try to do GetControlType on it. So check to make sure it exists + { + if ((controlClicked->IsContainer() && (message.GetParam1() == ACTION_SELECT_ITEM || + message.GetParam1() == ACTION_MOUSE_LEFT_CLICK)) || + !controlClicked->IsContainer()) + { + CBOnClick(m_clientHandle, iControl); + } + else if (controlClicked->IsContainer() && message.GetParam1() == ACTION_MOUSE_RIGHT_CLICK) + { +// PyXBMCAction* inf = new PyXBMCAction; +// inf->pObject = Action_FromAction(CAction(ACTION_CONTEXT_MENU)); +// inf->pCallbackWindow = pCallbackWindow; +// +// // aquire lock? +// PyXBMC_AddPendingCall(Py_XBMC_Event_OnAction, inf); +// PulseActionEvent(); + } + return true; + } + } + } + break; + } + + return CGUIMediaWindow::OnMessage(message); +} + +void CGUIAddonWindow::AllocResources(bool forceLoad /*= FALSE */) +{ + CStdString tmpDir; + URIUtils::GetDirectory(GetProperty("xmlfile").asString(), tmpDir); + CStdString fallbackMediaPath; + URIUtils::GetParentPath(tmpDir, fallbackMediaPath); + URIUtils::RemoveSlashAtEnd(fallbackMediaPath); + m_mediaDir = fallbackMediaPath; + + //CLog::Log(LOGDEBUG, "CGUIPythonWindowXML::AllocResources called: %s", fallbackMediaPath.c_str()); + g_TextureManager.AddTexturePath(m_mediaDir); + CGUIMediaWindow::AllocResources(forceLoad); + g_TextureManager.RemoveTexturePath(m_mediaDir); +} + +void CGUIAddonWindow::FreeResources(bool forceUnLoad /*= FALSE */) +{ + // Unload temporary language strings + ClearAddonStrings(); + + CGUIMediaWindow::FreeResources(forceUnLoad); +} + +void CGUIAddonWindow::Render() +{ + g_TextureManager.AddTexturePath(m_mediaDir); + CGUIMediaWindow::Render(); + g_TextureManager.RemoveTexturePath(m_mediaDir); +} + +void CGUIAddonWindow::Update() +{ +} + +void CGUIAddonWindow::AddItem(CFileItemPtr fileItem, int itemPosition) +{ + if (itemPosition == -1 || itemPosition > m_vecItems->Size()) + { + m_vecItems->Add(fileItem); + } + else if (itemPosition < -1 && !(itemPosition-1 < m_vecItems->Size())) + { + m_vecItems->AddFront(fileItem,0); + } + else + { + m_vecItems->AddFront(fileItem,itemPosition); + } + m_viewControl.SetItems(*m_vecItems); + UpdateButtons(); +} + +void CGUIAddonWindow::RemoveItem(int itemPosition) +{ + m_vecItems->Remove(itemPosition); + m_viewControl.SetItems(*m_vecItems); + UpdateButtons(); +} + +int CGUIAddonWindow::GetCurrentListPosition() +{ + return m_viewControl.GetSelectedItem(); +} + +void CGUIAddonWindow::SetCurrentListPosition(int item) +{ + m_viewControl.SetSelectedItem(item); +} + +int CGUIAddonWindow::GetListSize() +{ + return m_vecItems->Size(); +} + +CFileItemPtr CGUIAddonWindow::GetListItem(int position) +{ + if (position < 0 || position >= m_vecItems->Size()) return CFileItemPtr(); + return m_vecItems->Get(position); +} + +void CGUIAddonWindow::ClearList() +{ + ClearFileItems(); + + m_viewControl.SetItems(*m_vecItems); + UpdateButtons(); +} + +void CGUIAddonWindow::GetContextButtons(int itemNumber, CContextButtons &buttons) +{ + // maybe on day we can make an easy way to do this context menu + // with out this method overriding the MediaWindow version, it will display 'Add to Favorites' +} + +void CGUIAddonWindow::WaitForActionEvent(unsigned int timeout) +{ + m_actionEvent.WaitMSec(timeout); + m_actionEvent.Reset(); +} + +void CGUIAddonWindow::PulseActionEvent() +{ + m_actionEvent.Set(); +} + +void CGUIAddonWindow::ClearAddonStrings() +{ + // Unload temporary language strings + g_localizeStrings.ClearBlock(m_addon->Path()); +} + +bool CGUIAddonWindow::OnClick(int iItem) +{ + // Hook Over calling CGUIMediaWindow::OnClick(iItem) results in it trying to PLAY the file item + // which if its not media is BAD and 99 out of 100 times undesireable. + return false; +} + +// SetupShares(); +/* + CGUIMediaWindow::OnWindowLoaded() calls SetupShares() so override it +and just call UpdateButtons(); +*/ +void CGUIAddonWindow::SetupShares() +{ + UpdateButtons(); +} + + +CGUIAddonWindowDialog::CGUIAddonWindowDialog(int id, CStdString strXML, CAddon* addon) +: CGUIAddonWindow(id,strXML,addon) +{ + m_bRunning = false; + m_loadOnDemand = false; + m_bIsDialog = true; +} + +CGUIAddonWindowDialog::~CGUIAddonWindowDialog(void) +{ +} + +bool CGUIAddonWindowDialog::OnMessage(CGUIMessage &message) +{ + if (message.GetMessage() == GUI_MSG_WINDOW_DEINIT) + { + CGUIWindow *pWindow = g_windowManager.GetWindow(g_windowManager.GetActiveWindow()); + if (pWindow) + g_windowManager.ShowOverlay(pWindow->GetOverlayState()); + return CGUIWindow::OnMessage(message); + } + return CGUIAddonWindow::OnMessage(message); +} + +void CGUIAddonWindowDialog::Show(bool show /* = true */) +{ + unsigned int iCount = g_graphicsContext.exit(); + ThreadMessage tMsg = {TMSG_GUI_ADDON_DIALOG, 1, show ? 1 : 0}; + tMsg.lpVoid = this; + CApplicationMessenger::Get().SendMessage(tMsg, true); + g_graphicsContext.restore(iCount); +} + +void CGUIAddonWindowDialog::Show_Internal(bool show /* = true */) +{ + if (show) + { + m_bModal = true; + m_bRunning = true; + g_windowManager.RouteToWindow(this); + + // active this window... + CGUIMessage msg(GUI_MSG_WINDOW_INIT, 0, 0, WINDOW_INVALID, m_iWindowId); + OnMessage(msg); + + while (m_bRunning && !g_application.m_bStop) + { + g_windowManager.Process(CTimeUtils::GetFrameTime()); + } + } + else // hide + { + m_bRunning = false; + + CGUIMessage msg(GUI_MSG_WINDOW_DEINIT,0,0); + OnMessage(msg); + + g_windowManager.RemoveDialog(GetID()); + } +} + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksGUI.h b/xbmc/addons/AddonCallbacksGUI.h new file mode 100644 index 0000000000..05d55a559f --- /dev/null +++ b/xbmc/addons/AddonCallbacksGUI.h @@ -0,0 +1,182 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + + +#include "AddonCallbacks.h" +#include "windows/GUIMediaWindow.h" +#include "threads/Event.h" + +class CGUISpinControlEx; +class CGUIButtonControl; +class CGUIRadioButtonControl; +class CGUISettingsSliderControl; +class CGUIEditControl; + +namespace ADDON +{ + +class CAddonCallbacksGUI +{ +public: + CAddonCallbacksGUI(CAddon* addon); + ~CAddonCallbacksGUI(); + + /**! \name General Functions */ + CB_GUILib *GetCallbacks() { return m_callbacks; } + + static void Lock(); + static void Unlock(); + static int GetScreenHeight(); + static int GetScreenWidth(); + static int GetVideoResolution(); + + static GUIHANDLE Window_New(void *addonData, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog); + static void Window_Delete(void *addonData, GUIHANDLE handle); + static void Window_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*initCB)(GUIHANDLE), bool (*clickCB)(GUIHANDLE, int), bool (*focusCB)(GUIHANDLE, int), bool (*onActionCB)(GUIHANDLE handle, int)); + static bool Window_Show(void *addonData, GUIHANDLE handle); + static bool Window_Close(void *addonData, GUIHANDLE handle); + static bool Window_DoModal(void *addonData, GUIHANDLE handle); + static bool Window_SetFocusId(void *addonData, GUIHANDLE handle, int iControlId); + static int Window_GetFocusId(void *addonData, GUIHANDLE handle); + static bool Window_SetCoordinateResolution(void *addonData, GUIHANDLE handle, int res); + static void Window_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value); + static void Window_SetPropertyInt(void *addonData, GUIHANDLE handle, const char *key, int value); + static void Window_SetPropertyBool(void *addonData, GUIHANDLE handle, const char *key, bool value); + static void Window_SetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key, double value); + static const char * Window_GetProperty(void *addonData, GUIHANDLE handle, const char *key); + static int Window_GetPropertyInt(void *addonData, GUIHANDLE handle, const char *key); + static bool Window_GetPropertyBool(void *addonData, GUIHANDLE handle, const char *key); + static double Window_GetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key); + static void Window_ClearProperties(void *addonData, GUIHANDLE handle); + static int Window_GetListSize(void *addonData, GUIHANDLE handle); + static void Window_ClearList(void *addonData, GUIHANDLE handle); + static GUIHANDLE Window_AddItem(void *addonData, GUIHANDLE handle, GUIHANDLE item, int itemPosition); + static GUIHANDLE Window_AddStringItem(void *addonData, GUIHANDLE handle, const char *itemName, int itemPosition); + static void Window_RemoveItem(void *addonData, GUIHANDLE handle, int itemPosition); + static GUIHANDLE Window_GetListItem(void *addonData, GUIHANDLE handle, int listPos); + static void Window_SetCurrentListPosition(void *addonData, GUIHANDLE handle, int listPos); + static int Window_GetCurrentListPosition(void *addonData, GUIHANDLE handle); + static GUIHANDLE Window_GetControl_Spin(void *addonData, GUIHANDLE handle, int controlId); + static GUIHANDLE Window_GetControl_Button(void *addonData, GUIHANDLE handle, int controlId); + static GUIHANDLE Window_GetControl_RadioButton(void *addonData, GUIHANDLE handle, int controlId); + static GUIHANDLE Window_GetControl_Edit(void *addonData, GUIHANDLE handle, int controlId); + static GUIHANDLE Window_GetControl_Progress(void *addonData, GUIHANDLE handle, int controlId); + static void Window_SetControlLabel(void *addonData, GUIHANDLE handle, int controlId, const char *label); + static void Control_Spin_SetVisible(void *addonData, GUIHANDLE spinhandle, bool yesNo); + static void Control_Spin_SetText(void *addonData, GUIHANDLE spinhandle, const char *label); + static void Control_Spin_Clear(void *addonData, GUIHANDLE spinhandle); + static void Control_Spin_AddLabel(void *addonData, GUIHANDLE spinhandle, const char *label, int iValue); + static int Control_Spin_GetValue(void *addonData, GUIHANDLE spinhandle); + static void Control_Spin_SetValue(void *addonData, GUIHANDLE spinhandle, int iValue); + static void Control_RadioButton_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo); + static void Control_RadioButton_SetText(void *addonData, GUIHANDLE handle, const char *label); + static void Control_RadioButton_SetSelected(void *addonData, GUIHANDLE handle, bool yesNo); + static bool Control_RadioButton_IsSelected(void *addonData, GUIHANDLE handle); + static void Control_Progress_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent); + static float Control_Progress_GetPercentage(void *addonData, GUIHANDLE handle); + static void Control_Progress_SetInfo(void *addonData, GUIHANDLE handle, int iInfo); + static int Control_Progress_GetInfo(void *addonData, GUIHANDLE handle); + static const char * Control_Progress_GetDescription(void *addonData, GUIHANDLE handle); + static GUIHANDLE ListItem_Create(void *addonData, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path); + static const char * ListItem_GetLabel(void *addonData, GUIHANDLE handle); + static void ListItem_SetLabel(void *addonData, GUIHANDLE handle, const char *label); + static const char * ListItem_GetLabel2(void *addonData, GUIHANDLE handle); + static void ListItem_SetLabel2(void *addonData, GUIHANDLE handle, const char *label); + static void ListItem_SetIconImage(void *addonData, GUIHANDLE handle, const char *image); + static void ListItem_SetThumbnailImage(void *addonData, GUIHANDLE handle, const char *image); + static void ListItem_SetInfo(void *addonData, GUIHANDLE handle, const char *info); + static void ListItem_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value); + static const char * ListItem_GetProperty(void *addonData, GUIHANDLE handle, const char *key); + static void ListItem_SetPath(void *addonData, GUIHANDLE handle, const char *path); + +private: + CB_GUILib *m_callbacks; + CAddon *m_addon; +}; + +class CGUIAddonWindow : public CGUIMediaWindow +{ +friend class CAddonCallbacksGUI; + +public: + CGUIAddonWindow(int id, CStdString strXML, CAddon* addon); + virtual ~CGUIAddonWindow(void); + + virtual bool OnMessage(CGUIMessage& message); + virtual bool OnAction(const CAction &action); + virtual void AllocResources(bool forceLoad = false); + virtual void FreeResources(bool forceUnLoad = false); + virtual void Render(); + void WaitForActionEvent(unsigned int timeout); + void PulseActionEvent(); + void AddItem(CFileItemPtr fileItem, int itemPosition); + void RemoveItem(int itemPosition); + void ClearList(); + CFileItemPtr GetListItem(int position); + int GetListSize(); + int GetCurrentListPosition(); + void SetCurrentListPosition(int item); + virtual bool OnClick(int iItem); + +protected: + virtual void Update(); + virtual void GetContextButtons(int itemNumber, CContextButtons &buttons); + void ClearAddonStrings(); + void SetupShares(); + + bool (*CBOnInit)(GUIHANDLE cbhdl); + bool (*CBOnFocus)(GUIHANDLE cbhdl, int controlId); + bool (*CBOnClick)(GUIHANDLE cbhdl, int controlId); + bool (*CBOnAction)(GUIHANDLE cbhdl, int); + + GUIHANDLE m_clientHandle; + const int m_iWindowId; + int m_iOldWindowId; + bool m_bModal; + bool m_bIsDialog; + +private: + CEvent m_actionEvent; + CAddon *m_addon; + CStdString m_mediaDir; +}; + +class CGUIAddonWindowDialog : public CGUIAddonWindow +{ +public: + CGUIAddonWindowDialog(int id, CStdString strXML, CAddon* addon); + virtual ~CGUIAddonWindowDialog(void); + + void Show(bool show = true); + virtual bool OnMessage(CGUIMessage &message); + virtual bool IsDialogRunning() const { return m_bRunning; } + virtual bool IsDialog() const { return true;}; + virtual bool IsModalDialog() const { return true; }; + virtual bool IsMediaWindow() const { return false; }; + + void Show_Internal(bool show = true); + +private: + bool m_bRunning; +}; + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksPVR.cpp b/xbmc/addons/AddonCallbacksPVR.cpp new file mode 100644 index 0000000000..e6f54f10b9 --- /dev/null +++ b/xbmc/addons/AddonCallbacksPVR.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "Application.h" +#include "AddonCallbacksPVR.h" +#include "settings/AdvancedSettings.h" +#include "utils/log.h" +#include "dialogs/GUIDialogKaiToast.h" + +#include "epg/Epg.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/channels/PVRChannelGroupInternal.h" +#include "pvr/addons/PVRClient.h" +#include "pvr/recordings/PVRRecordings.h" +#include "pvr/timers/PVRTimers.h" +#include "pvr/timers/PVRTimerInfoTag.h" + +using namespace PVR; +using namespace EPG; + +namespace ADDON +{ + +CAddonCallbacksPVR::CAddonCallbacksPVR(CAddon* addon) +{ + m_addon = addon; + m_callbacks = new CB_PVRLib; + + /* write XBMC PVR specific add-on function addresses to callback table */ + m_callbacks->TransferEpgEntry = PVRTransferEpgEntry; + m_callbacks->TransferChannelEntry = PVRTransferChannelEntry; + m_callbacks->TransferTimerEntry = PVRTransferTimerEntry; + m_callbacks->TransferRecordingEntry = PVRTransferRecordingEntry; + m_callbacks->AddMenuHook = PVRAddMenuHook; + m_callbacks->Recording = PVRRecording; + m_callbacks->TriggerChannelUpdate = PVRTriggerChannelUpdate; + m_callbacks->TriggerChannelGroupsUpdate = PVRTriggerChannelGroupsUpdate; + m_callbacks->TriggerTimerUpdate = PVRTriggerTimerUpdate; + m_callbacks->TriggerRecordingUpdate = PVRTriggerRecordingUpdate; + m_callbacks->FreeDemuxPacket = PVRFreeDemuxPacket; + m_callbacks->AllocateDemuxPacket = PVRAllocateDemuxPacket; + m_callbacks->TransferChannelGroup = PVRTransferChannelGroup; + m_callbacks->TransferChannelGroupMember = PVRTransferChannelGroupMember; +} + +CAddonCallbacksPVR::~CAddonCallbacksPVR() +{ + /* delete the callback table */ + delete m_callbacks; +} + +CPVRClient *CAddonCallbacksPVR::GetPVRClient(void *addonData) +{ + CAddonCallbacks *addon = static_cast<CAddonCallbacks *>(addonData); + if (!addon || !addon->GetHelperPVR()) + { + CLog::Log(LOGERROR, "PVR - %s - called with a null pointer", __FUNCTION__); + return NULL; + } + + return dynamic_cast<CPVRClient *>(addon->GetHelperPVR()->m_addon); +} + +void CAddonCallbacksPVR::PVRTransferChannelGroup(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP *group) +{ + CPVRChannelGroups *xbmcGroups = static_cast<CPVRChannelGroups *>(handle->dataAddress); + if (!handle || !group || !xbmcGroups) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + if (strlen(group->strGroupName) == 0) + { + CLog::Log(LOGERROR, "PVR - %s - empty group name", __FUNCTION__); + return; + } + + /* transfer this entry to the groups container */ + CPVRChannelGroup transferGroup(*group); + xbmcGroups->UpdateFromClient(transferGroup); +} + +void CAddonCallbacksPVR::PVRTransferChannelGroupMember(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER *member) +{ + CPVRClient *client = GetPVRClient(addonData); + CPVRChannelGroup *group = static_cast<CPVRChannelGroup *>(handle->dataAddress); + if (!handle || !member || !client || !group) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CPVRChannelPtr channel = g_PVRChannelGroups->GetByUniqueID(member->iChannelUniqueId, client->GetID()); + if (!channel) + { + CLog::Log(LOGERROR, "PVR - %s - cannot find group '%s' or channel '%d'", __FUNCTION__, member->strGroupName, member->iChannelUniqueId); + } + else if (group->IsRadio() == channel->IsRadio()) + { + /* transfer this entry to the group */ + group->AddToGroup(*channel, member->iChannelNumber, false); + } +} + +void CAddonCallbacksPVR::PVRTransferEpgEntry(void *addonData, const ADDON_HANDLE handle, const EPG_TAG *epgentry) +{ + CEpg *xbmcEpg = static_cast<CEpg *>(handle->dataAddress); + if (!handle || !xbmcEpg) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + /* transfer this entry to the epg */ + xbmcEpg->UpdateEntry(epgentry, handle->dataIdentifier == 1 /* update db */); +} + +void CAddonCallbacksPVR::PVRTransferChannelEntry(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL *channel) +{ + CPVRClient *client = GetPVRClient(addonData); + CPVRChannelGroupInternal *xbmcChannels = static_cast<CPVRChannelGroupInternal *>(handle->dataAddress); + if (!handle || !channel || !client || !xbmcChannels) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + /* transfer this entry to the internal channels group */ + CPVRChannel transferChannel(*channel, client->GetID()); + xbmcChannels->UpdateFromClient(transferChannel); +} + +void CAddonCallbacksPVR::PVRTransferRecordingEntry(void *addonData, const ADDON_HANDLE handle, const PVR_RECORDING *recording) +{ + CPVRClient *client = GetPVRClient(addonData); + CPVRRecordings *xbmcRecordings = static_cast<CPVRRecordings *>(handle->dataAddress); + if (!handle || !recording || !client || !xbmcRecordings) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + /* transfer this entry to the recordings container */ + CPVRRecording transferRecording(*recording, client->GetID()); + xbmcRecordings->UpdateFromClient(transferRecording); +} + +void CAddonCallbacksPVR::PVRTransferTimerEntry(void *addonData, const ADDON_HANDLE handle, const PVR_TIMER *timer) +{ + CPVRClient *client = GetPVRClient(addonData); + CPVRTimers *xbmcTimers = static_cast<CPVRTimers *>(handle->dataAddress); + if (!handle || !timer || !client || !xbmcTimers) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CPVRChannelPtr channel = g_PVRChannelGroups->GetByUniqueID(timer->iClientChannelUid, client->GetID()); + if (!channel) + { + CLog::Log(LOGERROR, "PVR - %s - cannot find channel %d on client %d", __FUNCTION__, timer->iClientChannelUid, client->GetID()); + return; + } + + /* transfer this entry to the timers container */ + CPVRTimerInfoTag transferTimer(*timer, channel, client->GetID()); + xbmcTimers->UpdateFromClient(transferTimer); +} + +void CAddonCallbacksPVR::PVRAddMenuHook(void *addonData, PVR_MENUHOOK *hook) +{ + CPVRClient *client = GetPVRClient(addonData); + if (!hook || !client) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + PVR_MENUHOOKS *hooks = client->GetMenuHooks(); + if (hooks) + { + PVR_MENUHOOK hookInt; + hookInt.iHookId = hook->iHookId; + hookInt.iLocalizedStringId = hook->iLocalizedStringId; + + /* add this new hook */ + hooks->push_back(hookInt); + } +} + +void CAddonCallbacksPVR::PVRRecording(void *addonData, const char *strName, const char *strFileName, bool bOnOff) +{ + CPVRClient *client = GetPVRClient(addonData); + if (!client || !strFileName) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CStdString strLine1; + if (bOnOff) + strLine1.Format(g_localizeStrings.Get(19197), client->Name()); + else + strLine1.Format(g_localizeStrings.Get(19198), client->Name()); + + CStdString strLine2; + if (strName) + strLine2 = strName; + else if (strFileName) + strLine2 = strFileName; + + /* display a notification for 5 seconds */ + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, strLine1, strLine2, 5000, false); + + CLog::Log(LOGDEBUG, "PVR - %s - recording %s on client '%s'. name='%s' filename='%s'", + __FUNCTION__, bOnOff ? "started" : "finished", client->Name().c_str(), strName, strFileName); +} + +void CAddonCallbacksPVR::PVRTriggerChannelUpdate(void *addonData) +{ + /* update the channels table in the next iteration of the pvrmanager's main loop */ + g_PVRManager.TriggerChannelsUpdate(); +} + +void CAddonCallbacksPVR::PVRTriggerTimerUpdate(void *addonData) +{ + /* update the timers table in the next iteration of the pvrmanager's main loop */ + g_PVRManager.TriggerTimersUpdate(); +} + +void CAddonCallbacksPVR::PVRTriggerRecordingUpdate(void *addonData) +{ + /* update the recordings table in the next iteration of the pvrmanager's main loop */ + g_PVRManager.TriggerRecordingsUpdate(); +} + +void CAddonCallbacksPVR::PVRTriggerChannelGroupsUpdate(void *addonData) +{ + /* update all channel groups in the next iteration of the pvrmanager's main loop */ + g_PVRManager.TriggerChannelGroupsUpdate(); +} + +void CAddonCallbacksPVR::PVRFreeDemuxPacket(void *addonData, DemuxPacket* pPacket) +{ + CDVDDemuxUtils::FreeDemuxPacket(pPacket); +} + +DemuxPacket* CAddonCallbacksPVR::PVRAllocateDemuxPacket(void *addonData, int iDataSize) +{ + return CDVDDemuxUtils::AllocateDemuxPacket(iDataSize); +} + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksPVR.h b/xbmc/addons/AddonCallbacksPVR.h new file mode 100644 index 0000000000..2011be30d4 --- /dev/null +++ b/xbmc/addons/AddonCallbacksPVR.h @@ -0,0 +1,160 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "AddonCallbacks.h" +#include "include/xbmc_pvr_types.h" + +namespace PVR +{ + class CPVRClient; +} + +namespace ADDON +{ + +/*! + * Callbacks for a PVR add-on to XBMC. + * + * Also translates the addon's C structures to XBMC's C++ structures. + */ +class CAddonCallbacksPVR +{ +public: + CAddonCallbacksPVR(CAddon* addon); + ~CAddonCallbacksPVR(void); + + /*! + * @return The callback table. + */ + CB_PVRLib *GetCallbacks() { return m_callbacks; } + + /*! + * @brief Transfer a channel group from the add-on to XBMC. The group will be created if it doesn't exist. + * @param addonData A pointer to the add-on. + * @param handle The handle containing a pointer to the CPVRChannelGroups instance that this group needs to be added to. + * @param group The entry to transfer. + */ + static void PVRTransferChannelGroup(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP *group); + + /*! + * @brief Transfer a channel group member from the add-on to XBMC. The channel will be added to the group if the group can be found. + * @param addonData A pointer to the add-on. + * @param handle The handle that initiated this action. + * @param member The entry to transfer. + */ + static void PVRTransferChannelGroupMember(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER *member); + + /*! + * @brief Transfer an EPG entry from the add-on to XBMC. + * @param addonData A pointer to the add-on. + * @param handle The handle that initiated this action. + * @param epgentry The entry to transfer. + */ + static void PVRTransferEpgEntry(void *addonData, const ADDON_HANDLE handle, const EPG_TAG *epgentry); + + /*! + * @brief Transfer a channel entry from the add-on to XBMC. + * @param addonData A pointer to the add-on. + * @param handle The handle that initiated this action. + * @param channel The entry to transfer. + */ + static void PVRTransferChannelEntry(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL *channel); + + /*! + * @brief Transfer a timer entry from the add-on to XBMC. + * @param addonData A pointer to the add-on. + * @param handle The handle that initiated this action. + * @param timer The entry to transfer. + */ + static void PVRTransferTimerEntry(void *addonData, const ADDON_HANDLE handle, const PVR_TIMER *timer); + + /*! + * @brief Transfer a recording entry from the add-on to XBMC. + * @param addonData A pointer to the add-on. + * @param handle The handle that initiated this action. + * @param recording The entry to transfer. + */ + static void PVRTransferRecordingEntry(void *addonData, const ADDON_HANDLE handle, const PVR_RECORDING *recording); + + /*! + * @brief Add a menu hook to this add-on table. + * @param addonData A pointer to the add-on. + * @param hook The hook to add. + */ + static void PVRAddMenuHook(void *addonData, PVR_MENUHOOK *hook); + + /*! + * @brief Notify XBMC that a recording has started or stoppped. + * @param addonData A pointer to the add-on. + * @param strName The name of the recording. + * @param strFileName The filename of the recording. + * @param bOnOff True if the recording started, false if it stopped. + */ + static void PVRRecording(void *addonData, const char *strName, const char *strFileName, bool bOnOff); + + /*! + * @brief Ask the PVRManager to refresh it's channels list. + * @param addonData A pointer to the add-on. + */ + static void PVRTriggerChannelUpdate(void *addonData); + + /*! + * @brief Ask the PVRManager to refresh it's timers list. + * @param addonData A pointer to the add-on. + */ + static void PVRTriggerTimerUpdate(void *addonData); + + /*! + * @brief Ask the PVRManager to refresh it's recordings list. + * @param addonData A pointer to the add-on. + */ + static void PVRTriggerRecordingUpdate(void *addonData); + + /*! + * @brief Ask the PVRManager to refresh it's channel groups list. + * @param addonData A pointer to the add-on. + */ + static void PVRTriggerChannelGroupsUpdate(void *addonData); + + /*! + * @brief Free an allocated demux packet. + * @param addonData A pointer to the add-on. + * @param pPacket The packet to free. + */ + static void PVRFreeDemuxPacket(void *addonData, DemuxPacket* pPacket); + + /*! + * @brief Allocate a new demux packet. + * @param addonData A pointer to the add-on. + * @param iDataSize The packet size. + * @return The allocated packet. + */ + static DemuxPacket* PVRAllocateDemuxPacket(void *addonData, int iDataSize = 0); + +private: + static PVR::CPVRClient *GetPVRClient(void *addonData); + + CB_PVRLib *m_callbacks; /*!< callback addresses */ + CAddon *m_addon; /*!< the addon */ +}; + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonDatabase.cpp b/xbmc/addons/AddonDatabase.cpp index 743e7ac4bb..5a7bdabeeb 100644 --- a/xbmc/addons/AddonDatabase.cpp +++ b/xbmc/addons/AddonDatabase.cpp @@ -157,6 +157,9 @@ int CAddonDatabase::AddAddon(const AddonPtr& addon, sql = PrepareSQL("insert into dependencies(id, addon, version, optional) values (%i, '%s', '%s', %i)", idAddon, i->first.c_str(), i->second.first.c_str(), i->second.second ? 1 : 0); m_pDS->exec(sql.c_str()); } + // these need to be configured + if (addon->Type() == ADDON_PVRDLL) + DisableAddon(addon->ID(), true); return idAddon; } catch (...) @@ -650,6 +653,14 @@ bool CAddonDatabase::IsAddonDisabled(const CStdString &addonID) return false; } +bool CAddonDatabase::IsSystemPVRAddonEnabled(const CStdString &addonID) +{ + CStdString strWhereClause = PrepareSQL("addonID = '%s'", addonID.c_str()); + CStdString strEnabled = GetSingleValue("pvrenabled", "id", strWhereClause); + + return !strEnabled.IsEmpty(); +} + CStdString CAddonDatabase::IsAddonBroken(const CStdString &addonID) { try diff --git a/xbmc/addons/AddonDatabase.h b/xbmc/addons/AddonDatabase.h index 515fa98d0d..34b8ed3c63 100644 --- a/xbmc/addons/AddonDatabase.h +++ b/xbmc/addons/AddonDatabase.h @@ -79,6 +79,10 @@ public: \sa DisableAddon, IsAddonDisabled */ bool HasDisabledAddons(); + /*! @deprecated only here to allow clean upgrades from earlier pvr versions + */ + bool IsSystemPVRAddonEnabled(const CStdString &addonID); + /*! \brief Mark an addon as broken Sets a flag that this addon has been marked as broken in the repository. \param addonID id of the addon to mark as broken diff --git a/xbmc/addons/AddonDll.h b/xbmc/addons/AddonDll.h index b78bb55e22..2e67505bc2 100644 --- a/xbmc/addons/AddonDll.h +++ b/xbmc/addons/AddonDll.h @@ -23,6 +23,7 @@ #include "DllAddon.h" #include "AddonManager.h" #include "AddonStatusHandler.h" +#include "AddonCallbacks.h" #include "settings/GUIDialogSettings.h" #include "utils/URIUtils.h" #include "filesystem/File.h" @@ -52,6 +53,8 @@ namespace ADDON virtual void Stop(); void Destroy(); + bool DllLoaded(void) const; + protected: void HandleException(std::exception &e, const char* context); bool Initialized() { return m_initialized; } @@ -59,6 +62,7 @@ namespace ADDON virtual bool LoadSettings(); TheStruct* m_pStruct; TheProps* m_pInfo; + CAddonCallbacks* m_pHelpers; private: TheDll* m_pDll; @@ -112,6 +116,7 @@ CAddonDll<TheDll, TheStruct, TheProps>::CAddonDll(const AddonProps &props) m_initialized = false; m_pDll = NULL; m_pInfo = NULL; + m_pHelpers = NULL; m_needsavedsettings = false; } @@ -204,9 +209,15 @@ bool CAddonDll<TheDll, TheStruct, TheProps>::Create() if (!LoadDll()) return false; + /* Allocate the helper function class to allow crosstalk over + helper libraries */ + m_pHelpers = new CAddonCallbacks(this); + + /* Call Create to make connections, initializing data or whatever is + needed to become the AddOn running */ try { - ADDON_STATUS status = m_pDll->Create(NULL, m_pInfo); + ADDON_STATUS status = m_pDll->Create(m_pHelpers->GetCallbacks(), m_pInfo); if (status == ADDON_STATUS_OK) m_initialized = true; else if ((status == ADDON_STATUS_NEED_SETTINGS) || (status == ADDON_STATUS_NEED_SAVEDSETTINGS)) @@ -228,6 +239,9 @@ bool CAddonDll<TheDll, TheStruct, TheProps>::Create() HandleException(e, "m_pDll->Create"); } + if (!m_initialized) + SAFE_DELETE(m_pHelpers); + return m_initialized; } @@ -283,6 +297,8 @@ void CAddonDll<TheDll, TheStruct, TheProps>::Destroy() { HandleException(e, "m_pDll->Unload"); } + delete m_pHelpers; + m_pHelpers = NULL; free(m_pStruct); m_pStruct = NULL; if (m_pDll) @@ -295,6 +311,12 @@ void CAddonDll<TheDll, TheStruct, TheProps>::Destroy() } template<class TheDll, typename TheStruct, typename TheProps> +bool CAddonDll<TheDll, TheStruct, TheProps>::DllLoaded(void) const +{ + return m_pDll != NULL; +} + +template<class TheDll, typename TheStruct, typename TheProps> ADDON_STATUS CAddonDll<TheDll, TheStruct, TheProps>::GetStatus() { try @@ -445,7 +467,8 @@ ADDON_STATUS CAddonDll<TheDll, TheStruct, TheProps>::TransferSettings() { status = m_pDll->SetSetting(id, (const char*) GetSetting(id).c_str()); } - else if ((strcmpi(type, "enum") == 0 || strcmpi(type,"integer") == 0)) + else if ((strcmpi(type, "enum") == 0 || strcmpi(type,"integer") == 0) || + strcmpi(type, "labelenum") == 0 || strcmpi(type, "rangeofnum") == 0) { int tmp = atoi(GetSetting(id)); status = m_pDll->SetSetting(id, (int*) &tmp); diff --git a/xbmc/addons/AddonManager.cpp b/xbmc/addons/AddonManager.cpp index 3ab9e9f2e4..95390115e8 100644 --- a/xbmc/addons/AddonManager.cpp +++ b/xbmc/addons/AddonManager.cpp @@ -37,6 +37,10 @@ #ifdef HAS_SCREENSAVER #include "ScreenSaver.h" #endif +#ifdef HAS_PVRCLIENTS +#include "DllPVRClient.h" +#include "pvr/addons/PVRClient.h" +#endif //#ifdef HAS_SCRAPERS #include "Scraper.h" //#endif @@ -44,9 +48,12 @@ #include "Repository.h" #include "Skin.h" #include "Service.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClients.h" #include "Util.h" using namespace std; +using namespace PVR; namespace ADDON { @@ -108,6 +115,7 @@ AddonPtr CAddonMgr::Factory(const cp_extension_t *props) return AddonPtr(new CScraper(props)); case ADDON_VIZ: case ADDON_SCREENSAVER: + case ADDON_PVRDLL: { // begin temporary platform handling for Dlls // ideally platforms issues will be handled by C-Pluff // this is not an attempt at a solution @@ -144,6 +152,12 @@ AddonPtr CAddonMgr::Factory(const cp_extension_t *props) return AddonPtr(new CVisualisation(props)); #endif } + else if (type == ADDON_PVRDLL) + { +#ifdef HAS_PVRCLIENTS + return AddonPtr(new CPVRClient(props)); +#endif + } else return AddonPtr(new CScreenSaver(props)); } @@ -390,9 +404,26 @@ bool CAddonMgr::GetAddons(const TYPE &type, VECADDONS &addons, bool enabled /* = cp_extension_t **exts = m_cpluff->get_extensions_info(m_cp_context, ext_point.c_str(), &status, &num); for(int i=0; i <num; i++) { - AddonPtr addon(Factory(exts[i])); - if (addon && m_database.IsAddonDisabled(addon->ID()) != enabled) - addons.push_back(addon); + const cp_extension_t *props = exts[i]; + if (m_database.IsAddonDisabled(props->plugin->identifier) != enabled) + { + // get a pointer to a running pvrclient if it's already started, or we won't be able to change settings + if (TranslateType(props->ext_point_id) == ADDON_PVRDLL && + enabled && + g_PVRManager.IsStarted()) + { + AddonPtr pvrAddon; + if (g_PVRClients->GetClient(props->plugin->identifier, pvrAddon)) + { + addons.push_back(pvrAddon); + continue; + } + } + + AddonPtr addon(Factory(props)); + if (addon) + addons.push_back(addon); + } } m_cpluff->release_info(m_cp_context, exts); return addons.size() > 0; @@ -408,8 +439,19 @@ bool CAddonMgr::GetAddon(const CStdString &str, AddonPtr &addon, const TYPE &typ { addon = GetAddonFromDescriptor(cpaddon); m_cpluff->release_info(m_cp_context, cpaddon); - if (addon.get() && enabledOnly && m_database.IsAddonDisabled(addon->ID())) - return false; + + if (addon && addon.get()) + { + if (enabledOnly && m_database.IsAddonDisabled(addon->ID())) + return false; + + if (addon->Type() == ADDON_PVRDLL && g_PVRManager.IsStarted()) + { + AddonPtr pvrAddon; + if (g_PVRClients->GetClient(addon->ID(), pvrAddon)) + addon = pvrAddon; + } + } return NULL != addon.get(); } if (cpaddon) @@ -497,15 +539,25 @@ CStdString CAddonMgr::GetString(const CStdString &id, const int number) void CAddonMgr::FindAddons() { - CSingleLock lock(m_critSection); - if (m_cpluff && m_cp_context) - m_cpluff->scan_plugins(m_cp_context, CP_SP_UPGRADE); + { + CSingleLock lock(m_critSection); + if (m_cpluff && m_cp_context) + { + m_cpluff->scan_plugins(m_cp_context, CP_SP_UPGRADE); + SetChanged(); + } + } + NotifyObservers(ObservableMessageAddons); } void CAddonMgr::RemoveAddon(const CStdString& ID) { if (m_cpluff && m_cp_context) + { m_cpluff->uninstall_plugin(m_cp_context,ID.c_str()); + SetChanged(); + NotifyObservers(ObservableMessageAddons); + } } const char *CAddonMgr::GetTranslatedString(const cp_cfg_element_t *root, const char *tag) @@ -562,6 +614,8 @@ AddonPtr CAddonMgr::AddonFromProps(AddonProps& addonProps) return AddonPtr(new CScreenSaver(addonProps)); case ADDON_VIZ_LIBRARY: return AddonPtr(new CAddonLibrary(addonProps)); + case ADDON_PVRDLL: + return AddonPtr(new CPVRClient(addonProps)); case ADDON_REPOSITORY: return AddonPtr(new CRepository(addonProps)); default: diff --git a/xbmc/addons/AddonManager.h b/xbmc/addons/AddonManager.h index 8166974786..f5edc7a788 100644 --- a/xbmc/addons/AddonManager.h +++ b/xbmc/addons/AddonManager.h @@ -22,6 +22,7 @@ #include "Addon.h" #include "threads/CriticalSection.h" #include "utils/StdString.h" +#include "utils/Observer.h" #include <vector> #include <map> #include <deque> @@ -44,6 +45,7 @@ namespace ADDON const CStdString ADDON_PYTHON_EXT = "*.py"; const CStdString ADDON_SCRAPER_EXT = "*.xml"; const CStdString ADDON_SCREENSAVER_EXT = "*.xbs"; + const CStdString ADDON_PVRDLL_EXT = "*.pvr"; const CStdString ADDON_DSP_AUDIO_EXT = "*.adsp"; const CStdString ADDON_VERSION_RE = "(?<Major>\\d*)\\.?(?<Minor>\\d*)?\\.?(?<Build>\\d*)?\\.?(?<Revision>\\d*)?"; @@ -67,7 +69,7 @@ namespace ADDON * otherwise. Services the generic callbacks available * to all addon variants. */ - class CAddonMgr + class CAddonMgr : public Observable { public: static CAddonMgr &Get(); diff --git a/xbmc/addons/AddonStatusHandler.cpp b/xbmc/addons/AddonStatusHandler.cpp index 032d620ed4..0d990b3f43 100644 --- a/xbmc/addons/AddonStatusHandler.cpp +++ b/xbmc/addons/AddonStatusHandler.cpp @@ -26,6 +26,8 @@ #include "GUIDialogAddonSettings.h" #include "dialogs/GUIDialogYesNo.h" #include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "settings/GUISettings.h" #include "utils/log.h" namespace ADDON @@ -86,19 +88,28 @@ void CAddonStatusHandler::Process() /* AddOn lost connection to his backend (for ones that use Network) */ if (m_status == ADDON_STATUS_LOST_CONNECTION) { - CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); - if (!pDialog) return; + if (m_addon->Type() == ADDON_PVRDLL) + { + if (!g_guiSettings.GetBool("pvrmanager.hideconnectionlostwarning")) + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, m_addon->Name().c_str(), g_localizeStrings.Get(36030)); // connection lost + // TODO handle disconnects after the add-on's been initialised + } + else + { + CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + if (!pDialog) return; - pDialog->SetHeading(heading); - pDialog->SetLine(1, 24070); - pDialog->SetLine(2, 24073); + pDialog->SetHeading(heading); + pDialog->SetLine(1, 24070); + pDialog->SetLine(2, 24073); - //send message and wait for user input - ThreadMessage tMsg = {TMSG_DIALOG_DOMODAL, WINDOW_DIALOG_YES_NO, g_windowManager.GetActiveWindow()}; - CApplicationMessenger::Get().SendMessage(tMsg, true); + //send message and wait for user input + ThreadMessage tMsg = {TMSG_DIALOG_DOMODAL, WINDOW_DIALOG_YES_NO, g_windowManager.GetActiveWindow()}; + CApplicationMessenger::Get().SendMessage(tMsg, true); - if (pDialog->IsConfirmed()) - CAddonMgr::Get().GetCallbackForType(m_addon->Type())->RequestRestart(m_addon, false); + if (pDialog->IsConfirmed()) + CAddonMgr::Get().GetCallbackForType(m_addon->Type())->RequestRestart(m_addon, false); + } } /* Request to restart the AddOn and data structures need updated */ else if (m_status == ADDON_STATUS_NEED_RESTART) diff --git a/xbmc/addons/DllPVRClient.h b/xbmc/addons/DllPVRClient.h new file mode 100644 index 0000000000..688302b8fb --- /dev/null +++ b/xbmc/addons/DllPVRClient.h @@ -0,0 +1,30 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "DllAddon.h" +#include "include/xbmc_pvr_types.h" + +class DllPVRClient : public DllAddon<PVRClient, PVR_PROPERTIES> +{ + // this is populated via Macro calls in DllAddon.h +}; + diff --git a/xbmc/addons/GUIDialogAddonInfo.cpp b/xbmc/addons/GUIDialogAddonInfo.cpp index c3bf5a3c06..2db656a8bf 100644 --- a/xbmc/addons/GUIDialogAddonInfo.cpp +++ b/xbmc/addons/GUIDialogAddonInfo.cpp @@ -37,6 +37,7 @@ #include "utils/StringUtils.h" #include "utils/URIUtils.h" #include "addons/AddonInstaller.h" +#include "Application.h" #define CONTROL_BTN_INSTALL 6 #define CONTROL_BTN_ENABLE 7 @@ -138,11 +139,13 @@ void CGUIDialogAddonInfo::UpdateControls() GrabRollbackVersions(); // TODO: System addons should be able to be disabled - bool canDisable = isInstalled && !isSystem && !m_localAddon->IsInUse(); + // TODO: the following line will have to be changed later, when the PVR add-ons are no longer part of our source tree + bool isPVR = isInstalled && m_localAddon->Type() == ADDON_PVRDLL; + bool canDisable = isInstalled && (!isSystem || isPVR) && !m_localAddon->IsInUse(); bool canInstall = !isInstalled && m_item->GetProperty("Addon.Broken").empty(); bool isRepo = (isInstalled && m_localAddon->Type() == ADDON_REPOSITORY) || (m_addon && m_addon->Type() == ADDON_REPOSITORY); - CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_INSTALL, canDisable || canInstall); + CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_INSTALL, (canDisable || canInstall) && !isPVR); SET_CONTROL_LABEL(CONTROL_BTN_INSTALL, isInstalled ? 24037 : 24038); CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_ENABLE, canDisable); @@ -208,9 +211,15 @@ void CGUIDialogAddonInfo::OnEnable(bool enable) if (!m_localAddon.get()) return; + CStdString xbmcPath = CSpecialProtocol::TranslatePath("special://xbmc/addons"); CAddonDatabase database; database.Open(); database.DisableAddon(m_localAddon->ID(), !enable); + database.Close(); + + if (m_localAddon->Type() == ADDON_PVRDLL && enable) + g_application.StartPVRManager(); + SetItem(m_item); UpdateControls(); g_windowManager.SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE); diff --git a/xbmc/addons/Makefile b/xbmc/addons/Makefile index 8848788531..952a267189 100644 --- a/xbmc/addons/Makefile +++ b/xbmc/addons/Makefile @@ -1,4 +1,8 @@ SRCS=Addon.cpp \ + AddonCallbacks.cpp \ + AddonCallbacksAddon.cpp \ + AddonCallbacksGUI.cpp \ + AddonCallbacksPVR.cpp \ AddonDatabase.cpp \ AddonInstaller.cpp \ AddonManager.cpp \ diff --git a/xbmc/addons/Skin.cpp b/xbmc/addons/Skin.cpp index 4c9b3061ee..cb893096c2 100644 --- a/xbmc/addons/Skin.cpp +++ b/xbmc/addons/Skin.cpp @@ -202,6 +202,7 @@ bool CSkinInfo::LoadStartupWindows(const cp_extension_t *ext) { m_startupWindows.clear(); m_startupWindows.push_back(CStartupWindow(WINDOW_HOME, "513")); + m_startupWindows.push_back(CStartupWindow(WINDOW_PVR, "19180")); m_startupWindows.push_back(CStartupWindow(WINDOW_PROGRAMS, "0")); m_startupWindows.push_back(CStartupWindow(WINDOW_PICTURES, "1")); m_startupWindows.push_back(CStartupWindow(WINDOW_MUSIC, "2")); diff --git a/xbmc/addons/include/NOTE b/xbmc/addons/include/NOTE index a38d4328c3..dbcc329fe7 100644 --- a/xbmc/addons/include/NOTE +++ b/xbmc/addons/include/NOTE @@ -7,3 +7,6 @@ XBMC related classes or functions. Also this headers are never changed without a API Version change. + +The current PVR API version can be found in xbmc_pvr_types.h: +XBMC_PVR_API_VERSION diff --git a/xbmc/addons/include/xbmc_addon_types.h b/xbmc/addons/include/xbmc_addon_types.h index e149a21012..a16fa8b3ba 100644 --- a/xbmc/addons/include/xbmc_addon_types.h +++ b/xbmc/addons/include/xbmc_addon_types.h @@ -46,6 +46,17 @@ typedef struct unsigned int entry_elements; } ADDON_StructSetting; +/*! + * @brief Handle used to return data from the PVR add-on to CPVRClient + */ +struct ADDON_HANDLE_STRUCT +{ + void *callerAddress; /*!< address of the caller */ + void *dataAddress; /*!< address to store data in */ + int dataIdentifier; /*!< parameter to pass back when calling the callback */ +}; +typedef ADDON_HANDLE_STRUCT *ADDON_HANDLE; + #ifdef __cplusplus }; #endif diff --git a/xbmc/addons/include/xbmc_epg_types.h b/xbmc/addons/include/xbmc_epg_types.h new file mode 100644 index 0000000000..0a7674252b --- /dev/null +++ b/xbmc/addons/include/xbmc_epg_types.h @@ -0,0 +1,96 @@ +#pragma once +/* + * Copyright (C) 2005-2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include <string.h> +#include <time.h> + +#undef ATTRIBUTE_PACKED +#undef PRAGMA_PACK_BEGIN +#undef PRAGMA_PACK_END + +#if defined(__GNUC__) +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) +#define ATTRIBUTE_PACKED __attribute__ ((packed)) +#define PRAGMA_PACK 0 +#endif +#endif + +#if !defined(ATTRIBUTE_PACKED) +#define ATTRIBUTE_PACKED +#define PRAGMA_PACK 1 +#endif + +/*! @name EPG entry content event types */ +//@{ +/* These IDs come from the DVB-SI EIT table "content descriptor" + * Also known under the name "E-book genre assignments" + */ +#define EPG_EVENT_CONTENTMASK_UNDEFINED 0x00 +#define EPG_EVENT_CONTENTMASK_MOVIEDRAMA 0x10 +#define EPG_EVENT_CONTENTMASK_NEWSCURRENTAFFAIRS 0x20 +#define EPG_EVENT_CONTENTMASK_SHOW 0x30 +#define EPG_EVENT_CONTENTMASK_SPORTS 0x40 +#define EPG_EVENT_CONTENTMASK_CHILDRENYOUTH 0x50 +#define EPG_EVENT_CONTENTMASK_MUSICBALLETDANCE 0x60 +#define EPG_EVENT_CONTENTMASK_ARTSCULTURE 0x70 +#define EPG_EVENT_CONTENTMASK_SOCIALPOLITICALECONOMICS 0x80 +#define EPG_EVENT_CONTENTMASK_EDUCATIONALSCIENCE 0x90 +#define EPG_EVENT_CONTENTMASK_LEISUREHOBBIES 0xA0 +#define EPG_EVENT_CONTENTMASK_SPECIAL 0xB0 +#define EPG_EVENT_CONTENTMASK_USERDEFINED 0xF0 +//@} + +/* Set EPGTAG.iGenreType to EPG_GENRE_USE_STRING to transfer genre strings to XBMC */ +#define EPG_GENRE_USE_STRING 0x100 + +#ifdef __cplusplus +extern "C" { +#endif + + /*! + * @brief Representation of an EPG event. + */ + typedef struct EPG_TAG { + unsigned int iUniqueBroadcastId; /*!< @brief (required) identifier for this event */ + const char * strTitle; /*!< @brief (required) this event's title */ + unsigned int iChannelNumber; /*!< @brief (required) the number of the channel this event occurs on */ + time_t startTime; /*!< @brief (required) start time in UTC */ + time_t endTime; /*!< @brief (required) end time in UTC */ + const char * strPlotOutline; /*!< @brief (optional) plot outline */ + const char * strPlot; /*!< @brief (optional) plot */ + const char * strIconPath; /*!< @brief (optional) icon path */ + int iGenreType; /*!< @brief (optional) genre type */ + int iGenreSubType; /*!< @brief (optional) genre sub type */ + const char * strGenreDescription; /*!< @brief (optional) genre. Will be used only when iGenreType = EPG_GENRE_USE_STRING */ + time_t firstAired; /*!< @brief (optional) first aired in UTC */ + int iParentalRating; /*!< @brief (optional) parental rating */ + int iStarRating; /*!< @brief (optional) star rating */ + bool bNotify; /*!< @brief (optional) notify the user when this event starts */ + int iSeriesNumber; /*!< @brief (optional) series number */ + int iEpisodeNumber; /*!< @brief (optional) episode number */ + int iEpisodePartNumber; /*!< @brief (optional) episode part number */ + const char * strEpisodeName; /*!< @brief (optional) episode name */ + } ATTRIBUTE_PACKED EPG_TAG; + +#ifdef __cplusplus +} +#endif diff --git a/xbmc/addons/include/xbmc_pvr_dll.h b/xbmc/addons/include/xbmc_pvr_dll.h new file mode 100644 index 0000000000..a7132caf52 --- /dev/null +++ b/xbmc/addons/include/xbmc_pvr_dll.h @@ -0,0 +1,587 @@ +/* + * Copyright (C) 2005-2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#ifndef __XBMC_PVR_H__ +#define __XBMC_PVR_H__ + +#include "xbmc_addon_dll.h" +#include "xbmc_pvr_types.h" + +/*! + * Functions that the PVR client add-on must implement, but some can be empty. + * + * The 'remarks' field indicates which methods should be implemented, and which ones are optional. + */ + +extern "C" +{ + /*! @name PVR add-on methods */ + //@{ + /*! + * Get the XBMC_PVR_API_VERSION that was used to compile this add-on. + * Used to check if this add-on is compatible with XBMC. + * @return The XBMC_PVR_API_VERSION that was used to compile this add-on. + * @remarks Valid implementation required. + */ + const char* GetPVRAPIVersion(void); + + /*! + * Get the XBMC_PVR_MIN_API_VERSION that was used to compile this add-on. + * Used to check if this add-on is compatible with XBMC. + * @return The XBMC_PVR_MIN_API_VERSION that was used to compile this add-on. + * @remarks Valid implementation required. + */ + const char* GetMininumPVRAPIVersion(void); + + /*! + * Get the list of features that this add-on provides. + * Called by XBMC to query the add-on's capabilities. + * Used to check which options should be presented in the UI, which methods to call, etc. + * All capabilities that the add-on supports should be set to true. + * @param pCapabilities The add-on's capabilities. + * @return PVR_ERROR_NO_ERROR if the properties were fetched successfully. + * @remarks Valid implementation required. + */ + PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES *pCapabilities); + + /*! + * @return The name reported by the backend that will be displayed in the UI. + * @remarks Valid implementation required. + */ + const char* GetBackendName(void); + + /*! + * @return The version string reported by the backend that will be displayed in the UI. + * @remarks Valid implementation required. + */ + const char* GetBackendVersion(void); + + /*! + * @return The connection string reported by the backend that will be displayed in the UI. + * @remarks Valid implementation required. + */ + const char* GetConnectionString(void); + + /*! + * Get the disk space reported by the backend (if supported). + * @param iTotal The total disk space in bytes. + * @param iUsed The used disk space in bytes. + * @return PVR_ERROR_NO_ERROR if the drive space has been fetched successfully. + * @remarks Optional. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetDriveSpace(long long* iTotal, long long* iUsed); + + /*! + * Call one of the menu hooks (if supported). + * Supported PVR_MENUHOOK instances have to be added in ADDON_Create(), by calling AddMenuHook() on the callback. + * @param menuhook The hook to call. + * @return PVR_ERROR_NO_ERROR if the hook was called successfully. + * @remarks Optional. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR CallMenuHook(const PVR_MENUHOOK& menuhook); + //@} + + /*! @name PVR EPG methods + * @remarks Only used by XBMC if bSupportsEPG is set to true. + */ + //@{ + /*! + * Request the EPG for a channel from the backend. + * EPG entries are added to XBMC by calling TransferEpgEntry() on the callback. + * @param handle Handle to pass to the callback method. + * @param channel The channel to get the EPG table for. + * @param iStart Get events after this time (UTC). + * @param iEnd Get events before this time (UTC). + * @return PVR_ERROR_NO_ERROR if the table has been fetched successfully. + * @remarks Required if bSupportsEPG is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, const PVR_CHANNEL& channel, time_t iStart, time_t iEnd); + //@} + + /*! @name PVR channel group methods + * @remarks Only used by XBMC is bSupportsChannelGroups is set to true. + * If a group or one of the group members changes after the initial import, or if a new one was added, then the add-on + * should call TriggerChannelGroupsUpdate() + */ + //@{ + /*! + * Get the total amount of channel groups on the backend if it supports channel groups. + * @return The amount of channels, or -1 on error. + * @remarks Required if bSupportsChannelGroups is set to true. Return -1 if this add-on won't provide this function. + */ + int GetChannelGroupsAmount(void); + + /*! + * Request the list of all channel groups from the backend if it supports channel groups. + * Channel group entries are added to XBMC by calling TransferChannelGroup() on the callback. + * @param handle Handle to pass to the callback method. + * @param bRadio True to get the radio channel groups, false to get the TV channel groups. + * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully. + * @remarks Required if bSupportsChannelGroups is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio); + + /*! + * Request the list of all group members of a group from the backend if it supports channel groups. + * Member entries are added to XBMC by calling TransferChannelGroupMember() on the callback. + * @param handle Handle to pass to the callback method. + * @param group The group to get the members for. + * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully. + * @remarks Required if bSupportsChannelGroups is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP& group); + //@} + + /** @name PVR channel methods + * @remarks Either bSupportsTV or bSupportsRadio is required to be set to true. + * If a channel changes after the initial import, or if a new one was added, then the add-on + * should call TriggerChannelUpdate() + */ + //@{ + /*! + * Show the channel scan dialog if this backend supports it. + * @return PVR_ERROR_NO_ERROR if the dialog was displayed successfully. + * @remarks Required if bSupportsChannelScan is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR DialogChannelScan(void); + + /*! + * @return The total amount of channels on the backend, or -1 on error. + * @remarks Valid implementation required. + */ + int GetChannelsAmount(void); + + /*! + * Request the list of all channels from the backend. + * Channel entries are added to XBMC by calling TransferChannelEntry() on the callback. + * @param handle Handle to pass to the callback method. + * @param bRadio True to get the radio channels, false to get the TV channels. + * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully. + * @remarks If bSupportsTV is set to true, a valid result set needs to be provided for bRadio = false. + * If bSupportsRadio is set to true, a valid result set needs to be provided for bRadio = true. + * At least one of these two must provide a valid result set. + */ + PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio); + + /*! + * Delete a channel from the backend. + * @param channel The channel to delete. + * @return PVR_ERROR_NO_ERROR if the channel has been deleted successfully. + * @remarks Optional. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR DeleteChannel(const PVR_CHANNEL& channel); + + /*! + * Rename a channel on the backend. + * @param channel The channel to rename, containing the new channel name. + * @return PVR_ERROR_NO_ERROR if the channel has been renamed successfully. + * @remarks Optional. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR RenameChannel(const PVR_CHANNEL& channel); + + /*! + * Move a channel to another channel number on the backend. + * @param channel The channel to move, containing the new channel number. + * @return PVR_ERROR_NO_ERROR if the channel has been moved successfully. + * @remarks Optional. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR MoveChannel(const PVR_CHANNEL& channel); + + /*! + * Show the channel settings dialog, if supported by the backend. + * @param channel The channel to show the dialog for. + * @return PVR_ERROR_NO_ERROR if the dialog has been displayed successfully. + * @remarks Optional. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR DialogChannelSettings(const PVR_CHANNEL& channel); + + /*! + * Show the dialog to add a channel on the backend, if supported by the backend. + * @param channel The channel to add. + * @return PVR_ERROR_NO_ERROR if the channel has been added successfully. + * @remarks Optional. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR DialogAddChannel(const PVR_CHANNEL& channel); + //@} + + /** @name PVR recording methods + * @remarks Only used by XBMC is bSupportsRecordings is set to true. + * If a recording changes after the initial import, or if a new one was added, + * then the add-on should call TriggerRecordingUpdate() + */ + //@{ + /*! + * @return The total amount of channels on the backend or -1 on error. + * @remarks Required if bSupportsRecordings is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + int GetRecordingsAmount(void); + + /*! + * Request the list of all recordings from the backend, if supported. + * Recording entries are added to XBMC by calling TransferRecordingEntry() on the callback. + * @param handle Handle to pass to the callback method. + * @return PVR_ERROR_NO_ERROR if the recordings have been fetched successfully. + * @remarks Required if bSupportsRecordings is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetRecordings(ADDON_HANDLE handle); + + /*! + * Delete a recording on the backend. + * @param recording The recording to delete. + * @return PVR_ERROR_NO_ERROR if the recording has been deleted successfully. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR DeleteRecording(const PVR_RECORDING& recording); + + /*! + * Rename a recording on the backend. + * @param recording The recording to rename, containing the new name. + * @return PVR_ERROR_NO_ERROR if the recording has been renamed successfully. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR RenameRecording(const PVR_RECORDING& recording); + + /*! + * Set the play count of a recording on the backend. + * @param recording The recording to change the play count. + * @param count Play count. + * @return PVR_ERROR_NO_ERROR if the recording's play count has been set successfully. + * @remarks Required if bSupportsRecordingPlayCount is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR SetRecordingPlayCount(const PVR_RECORDING& recording, int count); + + /*! + * Set the last watched position of a recording on the backend. + * @param recording The recording. + * @param position The last watched position in seconds + * @return PVR_ERROR_NO_ERROR if the position has been stored successfully. + * @remarks Required if bSupportsLastPlayedPosition is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR SetRecordingLastPlayedPosition(const PVR_RECORDING& recording, int lastplayedposition); + + /*! + * Retrieve the last watched position of a recording on the backend. + * @param recording The recording. + * @return The last watched position in seconds or -1 on error + * @remarks Required if bSupportsRecordingPlayCount is set to true. Return -1 if this add-on won't provide this function. + */ + int GetRecordingLastPlayedPosition(const PVR_RECORDING& recording); + + //@} + /** @name PVR timer methods + * @remarks Only used by XBMC is bSupportsTimers is set to true. + * If a timer changes after the initial import, or if a new one was added, + * then the add-on should call TriggerTimerUpdate() + */ + //@{ + /*! + * @return The total amount of timers on the backend or -1 on error. + * @remarks Required if bSupportsTimers is set to true. Return -1 if this add-on won't provide this function. + */ + int GetTimersAmount(void); + + /*! + * Request the list of all timers from the backend if supported. + * Timer entries are added to XBMC by calling TransferTimerEntry() on the callback. + * @param handle Handle to pass to the callback method. + * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully. + * @remarks Required if bSupportsTimers is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetTimers(ADDON_HANDLE handle); + + /*! + * Add a timer on the backend. + * @param timer The timer to add. + * @return PVR_ERROR_NO_ERROR if the timer has been added successfully. + * @remarks Required if bSupportsTimers is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR AddTimer(const PVR_TIMER& timer); + + /*! + * Delete a timer on the backend. + * @param timer The timer to delete. + * @param bForceDelete Set to true to delete a timer that is currently recording a program. + * @return PVR_ERROR_NO_ERROR if the timer has been deleted successfully. + * @remarks Required if bSupportsTimers is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR DeleteTimer(const PVR_TIMER& timer, bool bForceDelete); + + /*! + * Update the timer information on the backend. + * @param timer The timer to update. + * @return PVR_ERROR_NO_ERROR if the timer has been updated successfully. + * @remarks Required if bSupportsTimers is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR UpdateTimer(const PVR_TIMER& timer); + + //@} + + /** @name PVR live stream methods, used to open and close a stream to a channel, and optionally perform read operations on the stream */ + //@{ + /*! + * Open a live stream on the backend. + * @param channel The channel to stream. + * @return True if the stream has been opened successfully, false otherwise. + * @remarks Required if bHandlesInputStream or bHandlesDemuxing is set to true. Return false if this add-on won't provide this function. + */ + bool OpenLiveStream(const PVR_CHANNEL& channel); + + /*! + * Close an open live stream. + * @remarks Required if bHandlesInputStream or bHandlesDemuxing is set to true. + */ + void CloseLiveStream(void); + + /*! + * Read from an open live stream. + * @param pBuffer The buffer to store the data in. + * @param iBufferSize The amount of bytes to read. + * @return The amount of bytes that were actually read from the stream. + * @remarks Required if bHandlesInputStream is set to true. Return -1 if this add-on won't provide this function. + */ + int ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize); + + /*! + * Seek in a live stream on a backend that supports timeshifting. + * @param iPosition The position to seek to. + * @param iWhence ? + * @return The new position. + * @remarks Optional, and only used if bHandlesInputStream is set to true. Return -1 if this add-on won't provide this function. + */ + long long SeekLiveStream(long long iPosition, int iWhence = SEEK_SET); + + /*! + * @return The position in the stream that's currently being read. + * @remarks Optional, and only used if bHandlesInputStream is set to true. Return -1 if this add-on won't provide this function. + */ + long long PositionLiveStream(void); + + /*! + * @return The total length of the stream that's currently being read. + * @remarks Optional, and only used if bHandlesInputStream is set to true. Return -1 if this add-on won't provide this function. + */ + long long LengthLiveStream(void); + + /*! + * @return The channel number on the backend of the live stream that's currently being read. + * @remarks Required if bHandlesInputStream or bHandlesDemuxing is set to true. Return -1 if this add-on won't provide this function. + */ + int GetCurrentClientChannel(void); + + /*! + * Switch to another channel. Only to be called when a live stream has already been opened. + * @param channel The channel to switch to. + * @return True if the switch was successful, false otherwise. + * @remarks Required if bHandlesInputStream or bHandlesDemuxing is set to true. Return false if this add-on won't provide this function. + */ + bool SwitchChannel(const PVR_CHANNEL& channel); + + /*! + * Get the signal status of the stream that's currently open. + * @param signalStatus The signal status. + * @return True if the signal status has been read successfully, false otherwise. + * @remarks Optional, and only used if bHandlesInputStream or bHandlesDemuxing is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS& signalStatus); + + /*! + * Get the stream URL for a channel from the backend. Used by the MediaPortal add-on. + * @param channel The channel to get the stream URL for. + * @return The requested URL. + * @remarks Optional, and only used if bHandlesInputStream is set to true. Return NULL if this add-on won't provide this function. + */ + const char* GetLiveStreamURL(const PVR_CHANNEL& channel); + + /*! + * Get the stream properties of the stream that's currently being read. + * @param pProperties The properties of the currently playing stream. + * @return PVR_ERROR_NO_ERROR if the properties have been fetched successfully. + * @remarks Required if bHandlesInputStream or bHandlesDemuxing is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties); + //@} + + /** @name PVR recording stream methods, used to open and close a stream to a recording, and perform read operations on the stream. + * @remarks This will only be used if the backend doesn't provide a direct URL in the recording tag. + */ + //@{ + /*! + * Open a stream to a recording on the backend. + * @param recording The recording to open. + * @return True if the stream has been opened successfully, false otherwise. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return false if this add-on won't provide this function. + */ + bool OpenRecordedStream(const PVR_RECORDING& recording); + + /*! + * Close an open stream from a recording. + * @remarks Optional, and only used if bSupportsRecordings is set to true. + */ + void CloseRecordedStream(void); + + /*! + * Read from a recording. + * @param pBuffer The buffer to store the data in. + * @param iBufferSize The amount of bytes to read. + * @return The amount of bytes that were actually read from the stream. + * @remarks Optional, and only used if bSupportsRecordings is set to true, but required if OpenRecordedStream() is implemented. Return -1 if this add-on won't provide this function. + */ + int ReadRecordedStream(unsigned char* pBuffer, unsigned int iBufferSize); + + /*! + * Seek in a recorded stream. + * @param iPosition The position to seek to. + * @param iWhence ? + * @return The new position. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return -1 if this add-on won't provide this function. + */ + long long SeekRecordedStream(long long iPosition, int iWhence = SEEK_SET); + + /*! + * @return The position in the stream that's currently being read. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return -1 if this add-on won't provide this function. + */ + long long PositionRecordedStream(void); + + /*! + * @return The total length of the stream that's currently being read. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return -1 if this add-on won't provide this function. + */ + long long LengthRecordedStream(void); + //@} + + /** @name PVR demultiplexer methods + * @remarks Only used by XBMC is bHandlesDemuxing is set to true. + */ + //@{ + /*! + * Reset the demultiplexer in the add-on. + * @remarks Required if bHandlesDemuxing is set to true. + */ + void DemuxReset(void); + + /*! + * Abort the demultiplexer thread in the add-on. + * @remarks Required if bHandlesDemuxing is set to true. + */ + void DemuxAbort(void); + + /*! + * Flush all data that's currently in the demultiplexer buffer in the add-on. + * @remarks Required if bHandlesDemuxing is set to true. + */ + void DemuxFlush(void); + + /*! + * Read the next packet from the demultiplexer, if there is one. + * @return The next packet. + * If there is no next packet, then the add-on should return the + * packet created by calling AllocateDemuxPacket(0) on the callback. + * If the stream changed and XBMC's player needs to be reinitialised, + * then, the add-on should call AllocateDemuxPacket(0) on the + * callback, and set the streamid to DMX_SPECIALID_STREAMCHANGE and + * return the value. + * The add-on should return NULL if an error occured. + * @remarks Required if bHandlesDemuxing is set to true. Return NULL if this add-on won't provide this function. + */ + DemuxPacket* DemuxRead(void); + //@} + + /*! + * Delay to use when using switching channels for add-ons not providing an input stream. + * If the add-on does provide an input stream, then this method will not be called. + * Those add-ons can do that in OpenLiveStream() if needed. + * @return The delay in milliseconds. + */ + unsigned int GetChannelSwitchDelay(void); + + /*! + * Called by XBMC to assign the function pointers of this add-on to pClient. + * @param pClient The struct to assign the function pointers to. + */ + void __declspec(dllexport) get_addon(struct PVRClient* pClient) + { + pClient->GetPVRAPIVersion = GetPVRAPIVersion; + pClient->GetMininumPVRAPIVersion = GetMininumPVRAPIVersion; + pClient->GetAddonCapabilities = GetAddonCapabilities; + pClient->GetStreamProperties = GetStreamProperties; + pClient->GetConnectionString = GetConnectionString; + pClient->GetBackendName = GetBackendName; + pClient->GetBackendVersion = GetBackendVersion; + pClient->GetDriveSpace = GetDriveSpace; + pClient->DialogChannelScan = DialogChannelScan; + pClient->MenuHook = CallMenuHook; + + pClient->GetEpg = GetEPGForChannel; + + pClient->GetChannelGroupsAmount = GetChannelGroupsAmount; + pClient->GetChannelGroups = GetChannelGroups; + pClient->GetChannelGroupMembers = GetChannelGroupMembers; + + pClient->GetChannelsAmount = GetChannelsAmount; + pClient->GetChannels = GetChannels; + pClient->DeleteChannel = DeleteChannel; + pClient->RenameChannel = RenameChannel; + pClient->MoveChannel = MoveChannel; + pClient->DialogChannelSettings = DialogChannelSettings; + pClient->DialogAddChannel = DialogAddChannel; + + pClient->GetRecordingsAmount = GetRecordingsAmount; + pClient->GetRecordings = GetRecordings; + pClient->DeleteRecording = DeleteRecording; + pClient->RenameRecording = RenameRecording; + pClient->SetRecordingPlayCount = SetRecordingPlayCount; + pClient->SetRecordingLastPlayedPosition = SetRecordingLastPlayedPosition; + pClient->GetRecordingLastPlayedPosition = GetRecordingLastPlayedPosition; + + pClient->GetTimersAmount = GetTimersAmount; + pClient->GetTimers = GetTimers; + pClient->AddTimer = AddTimer; + pClient->DeleteTimer = DeleteTimer; + pClient->UpdateTimer = UpdateTimer; + + pClient->OpenLiveStream = OpenLiveStream; + pClient->CloseLiveStream = CloseLiveStream; + pClient->ReadLiveStream = ReadLiveStream; + pClient->SeekLiveStream = SeekLiveStream; + pClient->PositionLiveStream = PositionLiveStream; + pClient->LengthLiveStream = LengthLiveStream; + pClient->GetCurrentClientChannel = GetCurrentClientChannel; + pClient->SwitchChannel = SwitchChannel; + pClient->SignalStatus = SignalStatus; + pClient->GetLiveStreamURL = GetLiveStreamURL; + pClient->GetChannelSwitchDelay = GetChannelSwitchDelay; + + pClient->OpenRecordedStream = OpenRecordedStream; + pClient->CloseRecordedStream = CloseRecordedStream; + pClient->ReadRecordedStream = ReadRecordedStream; + pClient->SeekRecordedStream = SeekRecordedStream; + pClient->PositionRecordedStream = PositionRecordedStream; + pClient->LengthRecordedStream = LengthRecordedStream; + + pClient->DemuxReset = DemuxReset; + pClient->DemuxAbort = DemuxAbort; + pClient->DemuxFlush = DemuxFlush; + pClient->DemuxRead = DemuxRead; + }; +}; + +#endif diff --git a/xbmc/addons/include/xbmc_pvr_types.h b/xbmc/addons/include/xbmc_pvr_types.h new file mode 100644 index 0000000000..bb2c3c162c --- /dev/null +++ b/xbmc/addons/include/xbmc_pvr_types.h @@ -0,0 +1,336 @@ +#pragma once +/* + * Copyright (C) 2005-2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#ifndef __PVRCLIENT_TYPES_H__ +#define __PVRCLIENT_TYPES_H__ + +#ifdef _WIN32 +#include <windows.h> +#else +#ifndef __cdecl +#define __cdecl +#endif +#ifndef __declspec +#define __declspec(X) +#endif +#endif +#include <string.h> + +#include "xbmc_addon_types.h" +#include "xbmc_epg_types.h" + +/*! @note Define "USE_DEMUX" at compile time if demuxing in the PVR add-on is used. + * Also XBMC's "DVDDemuxPacket.h" file must be in the include path of the add-on, + * and the add-on should set bHandlesDemuxing to true. + */ +#ifdef USE_DEMUX +#include "DVDDemuxPacket.h" +#else +struct DemuxPacket; +#endif + +#undef ATTRIBUTE_PACKED +#undef PRAGMA_PACK_BEGIN +#undef PRAGMA_PACK_END + +#if defined(__GNUC__) +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) +#define ATTRIBUTE_PACKED __attribute__ ((packed)) +#define PRAGMA_PACK 0 +#endif +#endif + +#if !defined(ATTRIBUTE_PACKED) +#define ATTRIBUTE_PACKED +#define PRAGMA_PACK 1 +#endif + +#define PVR_ADDON_NAME_STRING_LENGTH 1024 +#define PVR_ADDON_URL_STRING_LENGTH 1024 +#define PVR_ADDON_DESC_STRING_LENGTH 1024 +#define PVR_ADDON_INPUT_FORMAT_STRING_LENGTH 32 + +/* using the default avformat's MAX_STREAMS value to be safe */ +#define PVR_STREAM_MAX_STREAMS 20 + +/* current PVR API version */ +#define XBMC_PVR_API_VERSION "1.2.0" + +/* min. PVR API version */ +#define XBMC_PVR_MIN_API_VERSION "1.2.0" + +#ifdef __cplusplus +extern "C" { +#endif + + /*! + * @brief PVR add-on error codes + */ + typedef enum + { + PVR_ERROR_NO_ERROR = 0, /*!< @brief no error occurred */ + PVR_ERROR_UNKNOWN = -1, /*!< @brief an unknown error occurred */ + PVR_ERROR_NOT_IMPLEMENTED = -2, /*!< @brief the method that XBMC called is not implemented by the add-on */ + PVR_ERROR_SERVER_ERROR = -3, /*!< @brief the backend reported an error, or the add-on isn't connected */ + PVR_ERROR_SERVER_TIMEOUT = -4, /*!< @brief the command was sent to the backend, but the response timed out */ + PVR_ERROR_REJECTED = -5, /*!< @brief the command was rejected by the backend */ + PVR_ERROR_ALREADY_PRESENT = -6, /*!< @brief the requested item can not be added, because it's already present */ + PVR_ERROR_INVALID_PARAMETERS = -7, /*!< @brief the parameters of the method that was called are invalid for this operation */ + PVR_ERROR_RECORDING_RUNNING = -8, /*!< @brief a recording is running, so the timer can't be deleted without doing a forced delete */ + PVR_ERROR_FAILED = -9, /*!< @brief the command failed */ + } PVR_ERROR; + + /*! + * @brief PVR timer states + */ + typedef enum + { + PVR_TIMER_STATE_NEW = 0, /*!< @brief a new, unsaved timer */ + PVR_TIMER_STATE_SCHEDULED = 1, /*!< @brief the timer is scheduled for recording */ + PVR_TIMER_STATE_RECORDING = 2, /*!< @brief the timer is currently recordings */ + PVR_TIMER_STATE_COMPLETED = 3, /*!< @brief the recording completed successfully */ + PVR_TIMER_STATE_ABORTED = 4, /*!< @brief recording started, but was aborted */ + PVR_TIMER_STATE_CANCELLED = 5 /*!< @brief the timer was scheduled, but was canceled */ + } PVR_TIMER_STATE; + + /*! + * @brief Properties passed to the Create() method of an add-on. + */ + typedef struct PVR_PROPERTIES + { + const char* strUserPath; /*!< @brief path to the user profile */ + const char* strClientPath; /*!< @brief path to this add-on */ + } PVR_PROPERTIES; + + /*! + * @brief PVR add-on capabilities. All capabilities are set to "false" as default. + * If a capabilty is set to true, then the corresponding methods from xbmc_pvr_dll.h need to be implemented. + */ + typedef struct PVR_ADDON_CAPABILITIES + { + bool bSupportsEPG; /*!< @brief true if the add-on provides EPG information */ + bool bSupportsTV; /*!< @brief true if this add-on provides TV channels */ + bool bSupportsRadio; /*!< @brief true if this add-on supports radio channels */ + bool bSupportsRecordings; /*!< @brief true if this add-on supports playback of recordings stored on the backend */ + bool bSupportsTimers; /*!< @brief true if this add-on supports the creation and editing of timers */ + bool bSupportsChannelGroups; /*!< @brief true if this add-on supports channel groups */ + bool bSupportsChannelScan; /*!< @brief true if this add-on support scanning for new channels on the backend */ + bool bHandlesInputStream; /*!< @brief true if this add-on provides an input stream. false if XBMC handles the stream. */ + bool bHandlesDemuxing; /*!< @brief true if this add-on demultiplexes packets. */ + bool bSupportsRecordingFolders; /*!< @brief true if the backend supports timers / recordings in folders. */ + bool bSupportsRecordingPlayCount; /*!< @brief true if the backend supports play count for recordings. */ + bool bSupportsLastPlayedPosition; /*!< @brief true if the backend supports store/retrieve of last played position for recordings. */ + } ATTRIBUTE_PACKED PVR_ADDON_CAPABILITIES; + + /*! + * @brief PVR stream properties + */ + typedef struct PVR_STREAM_PROPERTIES + { + unsigned int iStreamCount; + struct PVR_STREAM + { + unsigned int iStreamIndex; /*!< @brief (required) stream index */ + unsigned int iPhysicalId; /*!< @brief (required) physical index */ + unsigned int iCodecType; /*!< @brief (required) codec type id */ + unsigned int iCodecId; /*!< @brief (required) codec id */ + char strLanguage[4]; /*!< @brief (required) language id */ + int iIdentifier; /*!< @brief (required) stream id */ + int iFPSScale; /*!< @brief (required) scale of 1000 and a rate of 29970 will result in 29.97 fps */ + int iFPSRate; /*!< @brief (required) FPS rate */ + int iHeight; /*!< @brief (required) height of the stream reported by the demuxer */ + int iWidth; /*!< @brief (required) width of the stream reported by the demuxer */ + float fAspect; /*!< @brief (required) display aspect ratio of the stream */ + int iChannels; /*!< @brief (required) amount of channels */ + int iSampleRate; /*!< @brief (required) sample rate */ + int iBlockAlign; /*!< @brief (required) block alignment */ + int iBitRate; /*!< @brief (required) bit rate */ + int iBitsPerSample; /*!< @brief (required) bits per sample */ + } stream[PVR_STREAM_MAX_STREAMS]; /*!< @brief (required) the streams */ + } ATTRIBUTE_PACKED PVR_STREAM_PROPERTIES; + + /*! + * @brief Signal status information + */ + typedef struct PVR_SIGNAL_STATUS + { + char strAdapterName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) name of the adapter that's being used */ + char strAdapterStatus[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) status of the adapter that's being used */ + int iSNR; /*!< @brief (optional) signal/noise ratio */ + int iSignal; /*!< @brief (optional) signal strength */ + long iBER; /*!< @brief (optional) bit error rate */ + long iUNC; /*!< @brief (optional) uncorrected blocks */ + double dVideoBitrate; /*!< @brief (optional) video bitrate */ + double dAudioBitrate; /*!< @brief (optional) audio bitrate */ + double dDolbyBitrate; /*!< @brief (optional) dolby bitrate */ + } ATTRIBUTE_PACKED PVR_SIGNAL_STATUS; + + /*! + * @brief Menu hooks that are available in the context menus while playing a stream via this add-on. + */ + typedef struct PVR_MENUHOOK + { + unsigned int iHookId; /*!< @brief (required) this hook's identifier */ + unsigned int iLocalizedStringId; /*!< @brief (required) the id of the label for this hook in g_localizeStrings */ + } ATTRIBUTE_PACKED PVR_MENUHOOK; + + /*! + * @brief Representation of a TV or radio channel. + */ + typedef struct PVR_CHANNEL + { + unsigned int iUniqueId; /*!< @brief (required) unique identifier for this channel */ + bool bIsRadio; /*!< @brief (required) true if this is a radio channel, false if it's a TV channel */ + unsigned int iChannelNumber; /*!< @brief (optional) channel number of this channel on the backend */ + char strChannelName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) channel name given to this channel */ + char strInputFormat[PVR_ADDON_INPUT_FORMAT_STRING_LENGTH]; /*!< @brief (optional) input format type. types can be found in ffmpeg/libavformat/allformats.c + leave empty if unknown */ + char strStreamURL[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) the URL to use to access this channel. + leave empty to use this add-on to access the stream. + set to a path that's supported by XBMC otherwise. */ + unsigned int iEncryptionSystem; /*!< @brief (optional) the encryption ID or CaID of this channel */ + char strIconPath[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) path to the channel icon (if present) */ + bool bIsHidden; /*!< @brief (optional) true if this channel is marked as hidden */ + } ATTRIBUTE_PACKED PVR_CHANNEL; + + typedef struct PVR_CHANNEL_GROUP + { + char strGroupName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (required) name of this channel group */ + bool bIsRadio; /*!< @brief (required) true if this is a radio channel group, false otherwise. */ + } ATTRIBUTE_PACKED PVR_CHANNEL_GROUP; + + typedef struct PVR_CHANNEL_GROUP_MEMBER + { + char strGroupName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (required) name of the channel group to add the channel to */ + unsigned int iChannelUniqueId; /*!< @brief (required) unique id of the member */ + unsigned int iChannelNumber; /*!< @brief (optional) channel number within the group */ + } ATTRIBUTE_PACKED PVR_CHANNEL_GROUP_MEMBER; + + /*! + * @brief Representation of a timer event. + */ + typedef struct PVR_TIMER { + unsigned int iClientIndex; /*!< @brief (required) the index of this timer given by the client */ + int iClientChannelUid; /*!< @brief (required) unique identifier of the channel to record on */ + time_t startTime; /*!< @brief (required) start time of the recording in UTC. instant timers that are sent to the add-on by xbmc will have this value set to 0 */ + time_t endTime; /*!< @brief (required) end time of the recording in UTC */ + PVR_TIMER_STATE state; /*!< @brief (required) the state of this timer */ + char strTitle[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) title of this timer */ + char strDirectory[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) the directory where the recording will be stored in */ + char strSummary[PVR_ADDON_DESC_STRING_LENGTH]; /*!< @brief (optional) the summary for this timer */ + int iPriority; /*!< @brief (optional) the priority of this timer */ + int iLifetime; /*!< @brief (optional) lifetimer of this timer in days */ + bool bIsRepeating; /*!< @brief (optional) true if this is a recurring timer */ + time_t firstDay; /*!< @brief (optional) the first day this recording is active in case of a repeating event */ + int iWeekdays; /*!< @brief (optional) weekday mask */ + int iEpgUid; /*!< @brief (optional) epg event id */ + unsigned int iMarginStart; /*!< @brief (optional) if set, the backend starts the recording iMarginStart minutes before startTime. */ + unsigned int iMarginEnd; /*!< @brief (optional) if set, the backend ends the recording iMarginEnd minutes after endTime. */ + int iGenreType; /*!< @brief (optional) genre type */ + int iGenreSubType; /*!< @brief (optional) genre sub type */ + } ATTRIBUTE_PACKED PVR_TIMER; + /*! + * @brief Representation of a recording. + */ + typedef struct PVR_RECORDING { + char strRecordingId[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (required) unique id of the recording on the client. */ + char strTitle[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (required) the title of this recording */ + char strStreamURL[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (required) stream URL to access this recording */ + char strDirectory[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) directory of this recording on the client */ + char strPlotOutline[PVR_ADDON_DESC_STRING_LENGTH]; /*!< @brief (optional) plot outline */ + char strPlot[PVR_ADDON_DESC_STRING_LENGTH]; /*!< @brief (optional) plot */ + char strChannelName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) channel name */ + time_t recordingTime; /*!< @brief (optional) start time of the recording */ + int iDuration; /*!< @brief (optional) duration of the recording in seconds */ + int iPriority; /*!< @brief (optional) priority of this recording (from 0 - 100) */ + int iLifetime; /*!< @brief (optional) life time in days of this recording */ + int iGenreType; /*!< @brief (optional) genre type */ + int iGenreSubType; /*!< @brief (optional) genre sub type */ + int iPlayCount; /*!< @brief (optional) play count of this recording on the client */ + } ATTRIBUTE_PACKED PVR_RECORDING; + + /*! + * @brief Structure to transfer the methods from xbmc_pvr_dll.h to XBMC + */ + typedef struct PVRClient + { + const char* (__cdecl* GetPVRAPIVersion)(void); + const char* (__cdecl* GetMininumPVRAPIVersion)(void); + PVR_ERROR (__cdecl* GetAddonCapabilities)(PVR_ADDON_CAPABILITIES*); + PVR_ERROR (__cdecl* GetStreamProperties)(PVR_STREAM_PROPERTIES*); + const char* (__cdecl* GetBackendName)(void); + const char* (__cdecl* GetBackendVersion)(void); + const char* (__cdecl* GetConnectionString)(void); + PVR_ERROR (__cdecl* GetDriveSpace)(long long*, long long*); + PVR_ERROR (__cdecl* MenuHook)(const PVR_MENUHOOK&); + PVR_ERROR (__cdecl* GetEpg)(ADDON_HANDLE, const PVR_CHANNEL&, time_t, time_t); + int (__cdecl* GetChannelGroupsAmount)(void); + PVR_ERROR (__cdecl* GetChannelGroups)(ADDON_HANDLE, bool); + PVR_ERROR (__cdecl* GetChannelGroupMembers)(ADDON_HANDLE, const PVR_CHANNEL_GROUP&); + PVR_ERROR (__cdecl* DialogChannelScan)(void); + int (__cdecl* GetChannelsAmount)(void); + PVR_ERROR (__cdecl* GetChannels)(ADDON_HANDLE, bool); + PVR_ERROR (__cdecl* DeleteChannel)(const PVR_CHANNEL&); + PVR_ERROR (__cdecl* RenameChannel)(const PVR_CHANNEL&); + PVR_ERROR (__cdecl* MoveChannel)(const PVR_CHANNEL&); + PVR_ERROR (__cdecl* DialogChannelSettings)(const PVR_CHANNEL&); + PVR_ERROR (__cdecl* DialogAddChannel)(const PVR_CHANNEL&); + int (__cdecl* GetRecordingsAmount)(void); + PVR_ERROR (__cdecl* GetRecordings)(ADDON_HANDLE); + PVR_ERROR (__cdecl* DeleteRecording)(const PVR_RECORDING&); + PVR_ERROR (__cdecl* RenameRecording)(const PVR_RECORDING&); + PVR_ERROR (__cdecl* SetRecordingPlayCount)(const PVR_RECORDING&, int); + PVR_ERROR (__cdecl* SetRecordingLastPlayedPosition)(const PVR_RECORDING&, int); + int (__cdecl* GetRecordingLastPlayedPosition)(const PVR_RECORDING&); + int (__cdecl* GetTimersAmount)(void); + PVR_ERROR (__cdecl* GetTimers)(ADDON_HANDLE); + PVR_ERROR (__cdecl* AddTimer)(const PVR_TIMER&); + PVR_ERROR (__cdecl* DeleteTimer)(const PVR_TIMER&, bool); + PVR_ERROR (__cdecl* UpdateTimer)(const PVR_TIMER&); + bool (__cdecl* OpenLiveStream)(const PVR_CHANNEL&); + void (__cdecl* CloseLiveStream)(void); + int (__cdecl* ReadLiveStream)(unsigned char*, unsigned int); + long long (__cdecl* SeekLiveStream)(long long, int); + long long (__cdecl* PositionLiveStream)(void); + long long (__cdecl* LengthLiveStream)(void); + int (__cdecl* GetCurrentClientChannel)(void); + bool (__cdecl* SwitchChannel)(const PVR_CHANNEL&); + PVR_ERROR (__cdecl* SignalStatus)(PVR_SIGNAL_STATUS&); + const char* (__cdecl* GetLiveStreamURL)(const PVR_CHANNEL&); + bool (__cdecl* OpenRecordedStream)(const PVR_RECORDING&); + void (__cdecl* CloseRecordedStream)(void); + int (__cdecl* ReadRecordedStream)(unsigned char*, unsigned int); + long long (__cdecl* SeekRecordedStream)(long long, int); + long long (__cdecl* PositionRecordedStream)(void); + long long (__cdecl* LengthRecordedStream)(void); + void (__cdecl* DemuxReset)(void); + void (__cdecl* DemuxAbort)(void); + void (__cdecl* DemuxFlush)(void); + DemuxPacket* (__cdecl* DemuxRead)(void); + unsigned int (__cdecl* GetChannelSwitchDelay)(void); + } PVRClient; + +#ifdef __cplusplus +} +#endif + +#endif //__PVRCLIENT_TYPES_H__ diff --git a/xbmc/cores/DllLoader/DllLoaderContainer.cpp b/xbmc/cores/DllLoader/DllLoaderContainer.cpp index 88111fdcf4..a0eeea1f3d 100644 --- a/xbmc/cores/DllLoader/DllLoaderContainer.cpp +++ b/xbmc/cores/DllLoader/DllLoaderContainer.cpp @@ -241,7 +241,7 @@ LibraryLoader* DllLoaderContainer::LoadDll(const char* sName, bool bLoadSymbols) LibraryLoader* pLoader; #ifdef _LINUX if (strstr(sName, ".so") != NULL || strstr(sName, ".vis") != NULL || strstr(sName, ".xbs") != NULL - || strstr(sName, ".mvis") != NULL || strstr(sName, ".dylib") != NULL || strstr(sName, ".framework") != NULL) + || strstr(sName, ".mvis") != NULL || strstr(sName, ".dylib") != NULL || strstr(sName, ".framework") != NULL || strstr(sName, ".pvr") != NULL) pLoader = new SoLoader(sName, bLoadSymbols); else #elif defined(_WIN32) diff --git a/xbmc/cores/DllLoader/Win32DllLoader.cpp b/xbmc/cores/DllLoader/Win32DllLoader.cpp index a7e17a97b7..b66cdbb15a 100644 --- a/xbmc/cores/DllLoader/Win32DllLoader.cpp +++ b/xbmc/cores/DllLoader/Win32DllLoader.cpp @@ -331,6 +331,9 @@ bool Win32DllLoader::NeedsHooking(const char *dllName) CStdStringW strdllNameW; g_charsetConverter.utf8ToW(CSpecialProtocol::TranslatePath(dllName), strdllNameW, false); HMODULE hModule = GetModuleHandleW(strdllNameW.c_str()); + if (hModule == NULL) + return false; + wchar_t filepathW[MAX_PATH]; GetModuleFileNameW(hModule, filepathW, MAX_PATH); CStdString dllPath; diff --git a/xbmc/cores/IPlayer.h b/xbmc/cores/IPlayer.h index 7339c64791..162fa366ce 100644 --- a/xbmc/cores/IPlayer.h +++ b/xbmc/cores/IPlayer.h @@ -30,6 +30,11 @@ class TiXmlElement; class CStreamDetails; class CAction; +namespace PVR +{ + class CPVRChannel; +} + class IPlayerCallback { public: @@ -176,6 +181,7 @@ public: virtual CStdString GetPlayingTitle() { return ""; }; + virtual bool SwitchChannel(const PVR::CPVRChannel &channel) { return false; } protected: IPlayerCallback& m_callback; }; diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp index 4780802334..b925b91ab3 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp @@ -560,7 +560,8 @@ bool CVDPAU::Supports(VdpVideoMixerFeature feature) bool CVDPAU::Supports(EINTERLACEMETHOD method) { if(method == VS_INTERLACEMETHOD_VDPAU_BOB - || method == VS_INTERLACEMETHOD_AUTO) + || method == VS_INTERLACEMETHOD_AUTO + || method == VS_INTERLACEMETHOD_AUTO_ION) return true; for(SInterlaceMapping* p = g_interlace_mapping; p->method != VS_INTERLACEMETHOD_NONE; p++) @@ -686,8 +687,21 @@ void CVDPAU::SetDeinterlacing() } else { - if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL - || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF) + if (method == VS_INTERLACEMETHOD_AUTO_ION) + { + if (vid_height <= 576) + { + VdpBool enabled[]={1,1,0}; + vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled); + } + else if (vid_height > 576) + { + VdpBool enabled[]={1,0,0}; + vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled); + } + } + else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL + || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF) { VdpBool enabled[]={1,0,0}; vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled); @@ -1416,14 +1430,16 @@ int CVDPAU::Decode(AVCodecContext *avctx, AVFrame *pFrame) if (mode == VS_DEINTERLACEMODE_FORCE || (mode == VS_DEINTERLACEMODE_AUTO && m_DVDVideoPics.front().iFlags & DVP_FLAG_INTERLACED)) { - if((method == VS_INTERLACEMETHOD_VDPAU_BOB + if((method == VS_INTERLACEMETHOD_AUTO_ION + || method == VS_INTERLACEMETHOD_VDPAU_BOB || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF || method == VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE )) { - if(method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF + if((method == VS_INTERLACEMETHOD_AUTO_ION && vid_height > 576) + || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF || avctx->skip_frame == AVDISCARD_NONREF) m_mixerstep = 0; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h index 052f581cd8..8c8d50c590 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h @@ -23,6 +23,7 @@ #include "utils/StdString.h" #include "system.h" +#include "DVDDemuxPacket.h" class CDVDInputStream; @@ -221,19 +222,6 @@ public: virtual void GetStreamInfo(std::string& strInfo); }; -typedef struct DemuxPacket -{ - BYTE* pData; // data - int iSize; // data size - int iStreamId; // integer representing the stream index - int iGroupId; // the group this data belongs to, used to group data from different streams together - - double pts; // pts in DVD_TIME_BASE - double dts; // dts in DVD_TIME_BASE - double duration; // duration in DVD_TIME_BASE if available -} DemuxPacket; - - class CDVDDemux { public: diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp index 8e50008d36..8c4165b443 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp @@ -35,6 +35,7 @@ #ifdef HAVE_LIBBLURAY #include "DVDInputStreams/DVDInputStreamBluray.h" #endif +#include "DVDInputStreams/DVDInputStreamPVRManager.h" #include "DVDDemuxUtils.h" #include "DVDClock.h" // for DVD_TIME_BASE #include "commons/Exception.h" diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp index 90566f598c..36ed0a51b3 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp @@ -284,6 +284,7 @@ void CDVDDemuxHTSP::SubscriptionStart (htsmsg_t *m) CDemuxStreamAudio* a; CDemuxStreamVideo* v; CDemuxStreamSubtitle* s; + CDemuxStreamTeletext* t; } st; CLog::Log(LOGDEBUG, "CDVDDemuxHTSP::SubscriptionStart - id: %d, type: %s", index, type); @@ -320,6 +321,9 @@ void CDVDDemuxHTSP::SubscriptionStart (htsmsg_t *m) } else if(!strcmp(type, "TEXTSUB")) { st.s = new CDemuxStreamSubtitle(); st.s->codec = CODEC_ID_TEXT; + } else if(!strcmp(type, "TELETEXT")) { + st.t = new CDemuxStreamTeletext(); + st.t->codec = CODEC_ID_DVB_TELETEXT; } else { continue; } diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp new file mode 100644 index 0000000000..a8d2f54f38 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "DVDInputStreams/DVDInputStream.h" +#include "DVDDemuxPVRClient.h" +#include "DVDDemuxUtils.h" +#include "utils/log.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClients.h" + +using namespace PVR; + +void CDemuxStreamVideoPVRClient::GetStreamInfo(std::string& strInfo) +{ + switch (codec) + { + case CODEC_ID_MPEG2VIDEO: + strInfo = "mpeg2video"; + break; + case CODEC_ID_H264: + strInfo = "h264"; + break; + default: + break; + } +} + +void CDemuxStreamAudioPVRClient::GetStreamInfo(std::string& strInfo) +{ + switch (codec) + { + case CODEC_ID_AC3: + strInfo = "ac3"; + break; + case CODEC_ID_EAC3: + strInfo = "eac3"; + break; + case CODEC_ID_MP2: + strInfo = "mpeg2audio"; + break; + case CODEC_ID_AAC: + strInfo = "aac"; + break; + case CODEC_ID_DTS: + strInfo = "dts"; + break; + default: + break; + } +} + +void CDemuxStreamSubtitlePVRClient::GetStreamInfo(std::string& strInfo) +{ +} + +CDVDDemuxPVRClient::CDVDDemuxPVRClient() : CDVDDemux() +{ + m_pInput = NULL; + for (int i = 0; i < MAX_STREAMS; i++) m_streams[i] = NULL; +} + +CDVDDemuxPVRClient::~CDVDDemuxPVRClient() +{ + Dispose(); +} + +bool CDVDDemuxPVRClient::Open(CDVDInputStream* pInput) +{ + Abort(); + m_pInput = pInput; + if (!g_PVRClients->GetPlayingClient(m_pvrClient)) + return false; + + RequestStreams(); + return true; +} + +void CDVDDemuxPVRClient::Dispose() +{ + for (int i = 0; i < MAX_STREAMS; i++) + { + if (m_streams[i]) + { + if (m_streams[i]->ExtraData) + delete[] (BYTE*)(m_streams[i]->ExtraData); + delete m_streams[i]; + } + m_streams[i] = NULL; + } + m_pInput = NULL; +} + +void CDVDDemuxPVRClient::Reset() +{ + if(m_pInput && g_PVRManager.IsStarted()) + m_pvrClient->DemuxReset(); + + CDVDInputStream* pInputStream = m_pInput; + Dispose(); + Open(pInputStream); +} + +void CDVDDemuxPVRClient::Abort() +{ + if(m_pInput) + m_pvrClient->DemuxAbort(); +} + +void CDVDDemuxPVRClient::Flush() +{ + if(m_pInput && g_PVRManager.IsStarted()) + m_pvrClient->DemuxFlush(); +} + +DemuxPacket* CDVDDemuxPVRClient::Read() +{ + if (!g_PVRManager.IsStarted()) + return CDVDDemuxUtils::AllocateDemuxPacket(0); + + DemuxPacket* pPacket = m_pvrClient->DemuxRead(); + if (!pPacket) + { + if (m_pInput) + m_pInput->Close(); + return NULL; + } + + if (pPacket->iStreamId == DMX_SPECIALID_STREAMINFO) + { + UpdateStreams((PVR_STREAM_PROPERTIES*)pPacket->pData); + CDVDDemuxUtils::FreeDemuxPacket(pPacket); + return CDVDDemuxUtils::AllocateDemuxPacket(0); + } + else if (pPacket->iStreamId == DMX_SPECIALID_STREAMCHANGE) + { + Reset(); + } + + return pPacket; +} + +CDemuxStream* CDVDDemuxPVRClient::GetStream(int iStreamId) +{ + if (iStreamId < 0 || iStreamId >= MAX_STREAMS) return NULL; + return m_streams[iStreamId]; +} + +void CDVDDemuxPVRClient::RequestStreams() +{ + if (!g_PVRManager.IsStarted()) + return; + + PVR_STREAM_PROPERTIES props; + m_pvrClient->GetStreamProperties(&props); + + for (unsigned int i = 0; i < props.iStreamCount; ++i) + { + if (props.stream[i].iCodecType == AVMEDIA_TYPE_AUDIO) + { + CDemuxStreamAudioPVRClient* st = new CDemuxStreamAudioPVRClient(this); + st->iChannels = props.stream[i].iChannels; + st->iSampleRate = props.stream[i].iSampleRate; + st->iBlockAlign = props.stream[i].iBlockAlign; + st->iBitRate = props.stream[i].iBitRate; + st->iBitsPerSample = props.stream[i].iBitsPerSample; + m_streams[props.stream[i].iStreamIndex] = st; + } + else if (props.stream[i].iCodecType == AVMEDIA_TYPE_VIDEO) + { + CDemuxStreamVideoPVRClient* st = new CDemuxStreamVideoPVRClient(this); + st->iFpsScale = props.stream[i].iFPSScale; + st->iFpsRate = props.stream[i].iFPSRate; + st->iHeight = props.stream[i].iHeight; + st->iWidth = props.stream[i].iWidth; + st->fAspect = props.stream[i].fAspect; + m_streams[props.stream[i].iStreamIndex] = st; + } + else if (props.stream[i].iCodecId == CODEC_ID_DVB_TELETEXT) + { + m_streams[props.stream[i].iStreamIndex] = new CDemuxStreamTeletext(); + } + else if (props.stream[i].iCodecType == AVMEDIA_TYPE_SUBTITLE) + { + CDemuxStreamSubtitlePVRClient* st = new CDemuxStreamSubtitlePVRClient(this); + st->identifier = props.stream[i].iIdentifier; + m_streams[props.stream[i].iStreamIndex] = st; + } + else + m_streams[props.stream[i].iStreamIndex] = new CDemuxStream(); + + m_streams[props.stream[i].iStreamIndex]->codec = (CodecID)props.stream[i].iCodecId; + m_streams[props.stream[i].iStreamIndex]->iId = props.stream[i].iStreamIndex; + m_streams[props.stream[i].iStreamIndex]->iPhysicalId = props.stream[i].iPhysicalId; + m_streams[props.stream[i].iStreamIndex]->language[0] = props.stream[i].strLanguage[0]; + m_streams[props.stream[i].iStreamIndex]->language[1] = props.stream[i].strLanguage[1]; + m_streams[props.stream[i].iStreamIndex]->language[2] = props.stream[i].strLanguage[2]; + m_streams[props.stream[i].iStreamIndex]->language[3] = props.stream[i].strLanguage[3]; + + CLog::Log(LOGDEBUG,"CDVDDemuxPVRClient::RequestStreams(): added stream %d:%d with codec_id %d", + m_streams[props.stream[i].iStreamIndex]->iId, + m_streams[props.stream[i].iStreamIndex]->iPhysicalId, + m_streams[props.stream[i].iStreamIndex]->codec); + } +} + +void CDVDDemuxPVRClient::UpdateStreams(PVR_STREAM_PROPERTIES *props) +{ + bool bGotVideoStream(false); + + for (unsigned int i = 0; i < props->iStreamCount; ++i) + { + if (m_streams[props->stream[i].iStreamIndex] == NULL || + m_streams[props->stream[i].iStreamIndex]->codec != (CodecID)props->stream[i].iCodecId) + { + CLog::Log(LOGERROR,"Invalid stream inside UpdateStreams"); + continue; + } + + if (m_streams[props->stream[i].iStreamIndex]->type == STREAM_AUDIO) + { + CDemuxStreamAudioPVRClient* st = (CDemuxStreamAudioPVRClient*) m_streams[props->stream[i].iStreamIndex]; + st->iChannels = props->stream[i].iChannels; + st->iSampleRate = props->stream[i].iSampleRate; + st->iBlockAlign = props->stream[i].iBlockAlign; + st->iBitRate = props->stream[i].iBitRate; + st->iBitsPerSample = props->stream[i].iBitsPerSample; + } + else if (m_streams[props->stream[i].iStreamIndex]->type == STREAM_VIDEO) + { + if (bGotVideoStream) + { + CLog::Log(LOGDEBUG, "CDVDDemuxPVRClient - %s - skip video stream", __FUNCTION__); + continue; + } + + CDemuxStreamVideoPVRClient* st = (CDemuxStreamVideoPVRClient*) m_streams[props->stream[i].iStreamIndex]; + if (st->iWidth <= 0 || st->iHeight <= 0) + { + CLog::Log(LOGWARNING, "CDVDDemuxPVRClient - %s - invalid stream data", __FUNCTION__); + continue; + } + + st->iFpsScale = props->stream[i].iFPSScale; + st->iFpsRate = props->stream[i].iFPSRate; + st->iHeight = props->stream[i].iHeight; + st->iWidth = props->stream[i].iWidth; + st->fAspect = props->stream[i].fAspect; + bGotVideoStream = true; + } + else if (m_streams[props->stream[i].iStreamIndex]->type == STREAM_SUBTITLE) + { + CDemuxStreamSubtitlePVRClient* st = (CDemuxStreamSubtitlePVRClient*) m_streams[props->stream[i].iStreamIndex]; + st->identifier = props->stream[i].iIdentifier; + } + + m_streams[props->stream[i].iStreamIndex]->language[0] = props->stream[i].strLanguage[0]; + m_streams[props->stream[i].iStreamIndex]->language[1] = props->stream[i].strLanguage[1]; + m_streams[props->stream[i].iStreamIndex]->language[2] = props->stream[i].strLanguage[2]; + m_streams[props->stream[i].iStreamIndex]->language[3] = props->stream[i].strLanguage[3]; + + CLog::Log(LOGDEBUG,"CDVDDemuxPVRClient::UpdateStreams(): update stream %d:%d with codec_id %d", + m_streams[props->stream[i].iStreamIndex]->iId, + m_streams[props->stream[i].iStreamIndex]->iPhysicalId, + m_streams[props->stream[i].iStreamIndex]->codec); + } +} + +int CDVDDemuxPVRClient::GetNrOfStreams() +{ + int i = 0; + while (i < MAX_STREAMS && m_streams[i]) i++; + return i; +} + +std::string CDVDDemuxPVRClient::GetFileName() +{ + if(m_pInput) + return m_pInput->GetFileName(); + else + return ""; +} + +void CDVDDemuxPVRClient::GetStreamCodecName(int iStreamId, CStdString &strName) +{ + CDemuxStream *stream = GetStream(iStreamId); + if (stream) + { + if (stream->codec == CODEC_ID_AC3) + strName = "ac3"; + else if (stream->codec == CODEC_ID_MP2) + strName = "mp2"; + else if (stream->codec == CODEC_ID_AAC) + strName = "aac"; + else if (stream->codec == CODEC_ID_DTS) + strName = "dca"; + else if (stream->codec == CODEC_ID_MPEG2VIDEO) + strName = "mpeg2video"; + else if (stream->codec == CODEC_ID_H264) + strName = "h264"; + else if (stream->codec == CODEC_ID_EAC3) + strName = "eac3"; + } +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h new file mode 100644 index 0000000000..3128e2db70 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h @@ -0,0 +1,111 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "DVDDemux.h" +#include <map> + +#ifndef _LINUX +#include <libavformat/avformat.h> +#else +extern "C" { +#if (defined USE_EXTERNAL_FFMPEG) + #if (defined HAVE_LIBAVFORMAT_AVFORMAT_H) + #include <libavformat/avformat.h> + #elif (defined HAVE_FFMPEG_AVFORMAT_H) + #include <ffmpeg/avformat.h> + #endif +#else + #include "libavformat/avformat.h" +#endif +} +#endif + +#include "pvr/addons/PVRClient.h" + +class CDVDDemuxPVRClient; +struct PVR_STREAM_PROPERTIES; + +class CDemuxStreamVideoPVRClient : public CDemuxStreamVideo +{ + CDVDDemuxPVRClient *m_parent; +public: + CDemuxStreamVideoPVRClient(CDVDDemuxPVRClient *parent) + : m_parent(parent) + {} + virtual void GetStreamInfo(std::string& strInfo); +}; + +class CDemuxStreamAudioPVRClient : public CDemuxStreamAudio +{ + CDVDDemuxPVRClient *m_parent; +public: + CDemuxStreamAudioPVRClient(CDVDDemuxPVRClient *parent) + : m_parent(parent) + {} + virtual void GetStreamInfo(std::string& strInfo); +}; + +class CDemuxStreamSubtitlePVRClient : public CDemuxStreamSubtitle +{ + CDVDDemuxPVRClient *m_parent; +public: + CDemuxStreamSubtitlePVRClient(CDVDDemuxPVRClient *parent) + : m_parent(parent) + {} + virtual void GetStreamInfo(std::string& strInfo); +}; + + +class CDVDDemuxPVRClient : public CDVDDemux +{ +public: + + CDVDDemuxPVRClient(); + ~CDVDDemuxPVRClient(); + + bool Open(CDVDInputStream* pInput); + void Dispose(); + void Reset(); + void Abort(); + void Flush(); + DemuxPacket* Read(); + bool SeekTime(int time, bool backwords = false, double* startpts = NULL) { return false; } + void SetSpeed(int iSpeed) {}; + int GetStreamLength() { return 0; } + CDemuxStream* GetStream(int iStreamId); + int GetNrOfStreams(); + std::string GetFileName(); + virtual void GetStreamCodecName(int iStreamId, CStdString &strName); + +protected: + CDVDInputStream* m_pInput; +#ifndef MAX_STREAMS + #define MAX_STREAMS 100 +#endif + CDemuxStream* m_streams[MAX_STREAMS]; // maximum number of streams that ffmpeg can handle + boost::shared_ptr<PVR::CPVRClient> m_pvrClient; + +private: + void RequestStreams(); + void UpdateStreams(PVR_STREAM_PROPERTIES *props); +}; + diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h new file mode 100644 index 0000000000..e5b8b87a59 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h @@ -0,0 +1,37 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#define DMX_SPECIALID_STREAMINFO -10 +#define DMX_SPECIALID_STREAMCHANGE -11 + + typedef struct DemuxPacket +{ + unsigned char* pData; // data + int iSize; // data size + int iStreamId; // integer representing the stream index + int iGroupId; // the group this data belongs to, used to group data from different streams together + + double pts; // pts in DVD_TIME_BASE + double dts; // dts in DVD_TIME_BASE + double duration; // duration in DVD_TIME_BASE if available +} DemuxPacket; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h index e7d5f416a2..5d288107ea 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h @@ -21,7 +21,7 @@ * */ -#include "DVDDemux.h" +#include "DVDDemuxPacket.h" class CDVDDemuxUtils { diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp index 7021661cd3..47537eb2cd 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp @@ -24,14 +24,19 @@ #include "DVDInputStreams/DVDInputStream.h" #include "DVDInputStreams/DVDInputStreamHttp.h" +#include "DVDInputStreams/DVDInputStreamPVRManager.h" #include "DVDDemuxFFmpeg.h" #include "DVDDemuxShoutcast.h" #ifdef HAS_FILESYSTEM_HTSP #include "DVDDemuxHTSP.h" #endif +#include "DVDDemuxPVRClient.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClients.h" using namespace std; +using namespace PVR; CDVDDemux* CDVDFactoryDemuxer::CreateDemuxer(CDVDInputStream* pInputStream) { @@ -62,6 +67,40 @@ CDVDDemux* CDVDFactoryDemuxer::CreateDemuxer(CDVDInputStream* pInputStream) } #endif + if (pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)) + { + CDVDInputStreamPVRManager* pInputStreamPVR = (CDVDInputStreamPVRManager*)pInputStream; + CDVDInputStream* pOtherStream = pInputStreamPVR->GetOtherStream(); + if(pOtherStream) + { + /* Used for MediaPortal PVR addon (uses PVR otherstream for playback of rtsp streams) */ + if (pOtherStream->IsStreamType(DVDSTREAM_TYPE_FFMPEG)) + { + auto_ptr<CDVDDemuxFFmpeg> demuxer(new CDVDDemuxFFmpeg()); + if(demuxer->Open(pOtherStream)) + return demuxer.release(); + else + return NULL; + } + } + + std::string filename = pInputStream->GetFileName(); + /* Use PVR demuxer only for live streams */ + if (filename.substr(0, 14) == "pvr://channels") + { + boost::shared_ptr<CPVRClient> client; + if (g_PVRClients->GetPlayingClient(client) && + client->HandlesDemuxing()) + { + auto_ptr<CDVDDemuxPVRClient> demuxer(new CDVDDemuxPVRClient()); + if(demuxer->Open(pInputStream)) + return demuxer.release(); + else + return NULL; + } + } + } + auto_ptr<CDVDDemuxFFmpeg> demuxer(new CDVDDemuxFFmpeg()); if(demuxer->Open(pInputStream)) return demuxer.release(); diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in b/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in index dc6f959bce..be2e392070 100644 --- a/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in +++ b/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in @@ -3,6 +3,7 @@ INCLUDES+=-I@abs_top_srcdir@/xbmc/cores/dvdplayer SRCS= DVDDemux.cpp \ DVDDemuxFFmpeg.cpp \ DVDDemuxHTSP.cpp \ + DVDDemuxPVRClient.cpp \ DVDDemuxShoutcast.cpp \ DVDDemuxUtils.cpp \ DVDDemuxVobsub.cpp \ diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDFactoryInputStream.cpp b/xbmc/cores/dvdplayer/DVDInputStreams/DVDFactoryInputStream.cpp index e9fce7fb61..22b0864b2c 100644 --- a/xbmc/cores/dvdplayer/DVDInputStreams/DVDFactoryInputStream.cpp +++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDFactoryInputStream.cpp @@ -26,6 +26,7 @@ #include "DVDInputStreamNavigator.h" #include "DVDInputStreamHttp.h" #include "DVDInputStreamFFmpeg.h" +#include "DVDInputStreamPVRManager.h" #include "DVDInputStreamTV.h" #include "DVDInputStreamRTMP.h" #ifdef HAVE_LIBBLURAY @@ -52,6 +53,8 @@ CDVDInputStream* CDVDFactoryInputStream::CreateInputStream(IDVDPlayer* pPlayer, { return (new CDVDInputStreamNavigator(pPlayer)); } + else if(file.substr(0, 6) == "pvr://") + return new CDVDInputStreamPVRManager(pPlayer); #ifdef HAVE_LIBBLURAY else if (item.IsType(".bdmv") || item.IsType(".mpls") || content == "bluray/iso" || file.substr(0, 7) == "bluray:") return new CDVDInputStreamBluray(pPlayer); diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStream.h b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStream.h index 3631387ed2..35130f2efa 100644 --- a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStream.h +++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStream.h @@ -40,6 +40,7 @@ enum DVDStreamType DVDSTREAM_TYPE_HTSP = 8, DVDSTREAM_TYPE_MPLS = 10, DVDSTREAM_TYPE_BLURAY = 11, + DVDSTREAM_TYPE_PVRMANAGER = 12, }; #define SEEK_POSSIBLE 0x10 // flag used to check if protocol allows seeks @@ -49,6 +50,12 @@ enum DVDStreamType class CPoint; +namespace PVR +{ + class CPVRChannel; + typedef boost::shared_ptr<PVR::CPVRChannel> CPVRChannelPtr; +} + class CDVDInputStream { public: @@ -56,10 +63,15 @@ public: { public: virtual ~IChannel() {}; - virtual bool NextChannel() = 0; - virtual bool PrevChannel() = 0; - virtual bool SelectChannel(unsigned int channel) = 0; + virtual bool NextChannel(bool preview = false) = 0; + virtual bool PrevChannel(bool preview = false) = 0; + virtual bool SelectChannelByNumber(unsigned int channel) = 0; + virtual bool SelectChannel(const PVR::CPVRChannel &channel) { return false; }; + virtual bool GetSelectedChannel(PVR::CPVRChannelPtr&) { return false; }; virtual bool UpdateItem(CFileItem& item) = 0; + virtual bool CanRecord() = 0; + virtual bool IsRecording() = 0; + virtual bool Record(bool bOnOff) = 0; }; class IDisplayTime @@ -129,6 +141,7 @@ public: virtual ENextStream NextStream() { return NEXTSTREAM_NONE; } virtual void Abort() {} virtual int GetBlockSize() { return 0; } + virtual void ResetScanTimeout(unsigned int iTimeoutMs) { } /*! \brief Indicate expected read rate in bytes per second. * This could be used to throttle caching rate. Should diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.cpp b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.cpp index 33ea8846cd..a72c89f118 100644 --- a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.cpp +++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.cpp @@ -195,7 +195,7 @@ bool CDVDInputStreamHTSP::GetChannels(SChannelV &channels, SChannelV::iterator & return false; } -bool CDVDInputStreamHTSP::NextChannel() +bool CDVDInputStreamHTSP::NextChannel(bool preview/* = false*/) { SChannelV channels; SChannelV::iterator it; @@ -209,7 +209,7 @@ bool CDVDInputStreamHTSP::NextChannel() return SetChannel(circ->id); } -bool CDVDInputStreamHTSP::PrevChannel() +bool CDVDInputStreamHTSP::PrevChannel(bool preview/* = false*/) { SChannelV channels; SChannelV::iterator it; @@ -223,7 +223,7 @@ bool CDVDInputStreamHTSP::PrevChannel() return SetChannel(circ->id); } -bool CDVDInputStreamHTSP::SelectChannel(unsigned int channel) +bool CDVDInputStreamHTSP::SelectChannelByNumber(unsigned int channel) { return SetChannel(channel); } @@ -253,9 +253,9 @@ bool CDVDInputStreamHTSP::UpdateItem(CFileItem& item) int CDVDInputStreamHTSP::GetTotalTime() { - if(m_event.id == 0) - return 0; - return (m_event.stop - m_event.start) * 1000; + if(m_event.id == 0) + return 0; + return (m_event.stop - m_event.start) * 1000; } int CDVDInputStreamHTSP::GetTime() diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.h b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.h index 795454405d..f2e2de00c2 100644 --- a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.h +++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.h @@ -43,11 +43,17 @@ public: virtual void Abort(); - bool NextChannel(); - bool PrevChannel(); - bool SelectChannel(unsigned int channel); + bool NextChannel(bool preview = false); + bool PrevChannel(bool preview = false); + bool SelectChannelByNumber(unsigned int channel); + bool SelectChannel(const PVR::CPVRChannel &channel) { return false; } + bool GetSelectedChannel(PVR::CPVRChannel *channel) {return false; } bool UpdateItem(CFileItem& item); + bool CanRecord() { return false; } + bool IsRecording() { return false; } + bool Record(bool bOnOff) { return false; } + int GetTotalTime(); int GetTime(); diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.cpp b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.cpp new file mode 100644 index 0000000000..19adc44f5c --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.cpp @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "DVDFactoryInputStream.h" +#include "DVDInputStreamPVRManager.h" +#include "filesystem/PVRFile.h" +#include "URL.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannel.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "pvr/addons/PVRClients.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "settings/GUISettings.h" + +using namespace XFILE; +using namespace PVR; + +/************************************************************************ + * Description: Class constructor, initialize member variables + * public class is CDVDInputStream + */ +CDVDInputStreamPVRManager::CDVDInputStreamPVRManager(IDVDPlayer* pPlayer) : CDVDInputStream(DVDSTREAM_TYPE_PVRMANAGER) +{ + m_pPlayer = pPlayer; + m_pFile = NULL; + m_pRecordable = NULL; + m_pLiveTV = NULL; + m_pOtherStream = NULL; + m_eof = true; + m_bReopened = false; + m_iScanTimeout = 0; +} + +/************************************************************************ + * Description: Class destructor + */ +CDVDInputStreamPVRManager::~CDVDInputStreamPVRManager() +{ + Close(); +} + +void CDVDInputStreamPVRManager::ResetScanTimeout(unsigned int iTimeoutMs) +{ + m_iScanTimeout = iTimeoutMs > 0 ? + XbmcThreads::SystemClockMillis() + iTimeoutMs : + 0; +} + +bool CDVDInputStreamPVRManager::IsEOF() +{ + // don't mark as eof while within the scan timeout + if (m_iScanTimeout && XbmcThreads::SystemClockMillis() < m_iScanTimeout) + return false; + + if (m_pOtherStream) + return m_pOtherStream->IsEOF(); + else + return !m_pFile || m_eof; +} + +bool CDVDInputStreamPVRManager::Open(const char* strFile, const std::string& content) +{ + /* Open PVR File for both cases, to have access to ILiveTVInterface and + * IRecordable + */ + m_pFile = new CPVRFile; + m_pLiveTV = ((CPVRFile*)m_pFile)->GetLiveTV(); + m_pRecordable = ((CPVRFile*)m_pFile)->GetRecordable(); + + CURL url(strFile); + if (!CDVDInputStream::Open(strFile, content)) return false; + if (!m_pFile->Open(url)) + { + delete m_pFile; + m_pFile = NULL; + m_pLiveTV = NULL; + m_pRecordable = NULL; + return false; + } + m_eof = false; + + /* + * Translate the "pvr://....." entry. + * The PVR Client can use http or whatever else is supported by DVDPlayer. + * to access streams. + * If after translation the file protocol is still "pvr://" use this class + * to read the stream data over the CPVRFile class and the PVR Library itself. + * Otherwise call CreateInputStream again with the translated filename and looks again + * for the right protocol stream handler and swap every call to this input stream + * handler. + */ + std::string transFile = XFILE::CPVRFile::TranslatePVRFilename(strFile); + if(transFile.substr(0, 6) != "pvr://") + { + m_pOtherStream = CDVDFactoryInputStream::CreateInputStream(m_pPlayer, transFile, content); + if (!m_pOtherStream) + { + CLog::Log(LOGERROR, "CDVDInputStreamPVRManager::Open - unable to create input stream for [%s]", transFile.c_str()); + return false; + } + else + m_pOtherStream->SetFileItem(m_item); + + if (!m_pOtherStream->Open(transFile.c_str(), content)) + { + CLog::Log(LOGERROR, "CDVDInputStreamPVRManager::Open - error opening [%s]", transFile.c_str()); + delete m_pFile; + m_pFile = NULL; + m_pLiveTV = NULL; + m_pRecordable = NULL; + delete m_pOtherStream; + m_pOtherStream = NULL; + return false; + } + } + + ResetScanTimeout((unsigned int) g_guiSettings.GetInt("pvrplayback.scantime") * 1000); + m_content = content; + CLog::Log(LOGDEBUG, "CDVDInputStreamPVRManager::Open - stream opened: %s", transFile.c_str()); + + return true; +} + +// close file and reset everyting +void CDVDInputStreamPVRManager::Close() +{ + if (m_pOtherStream) + { + m_pOtherStream->Close(); + delete m_pOtherStream; + } + + if (m_pFile) + { + m_pFile->Close(); + delete m_pFile; + } + + CDVDInputStream::Close(); + + m_pPlayer = NULL; + m_pFile = NULL; + m_pLiveTV = NULL; + m_pRecordable = NULL; + m_pOtherStream = NULL; + m_eof = true; + + CLog::Log(LOGDEBUG, "CDVDInputStreamPVRManager::Close - stream closed"); +} + +int CDVDInputStreamPVRManager::Read(BYTE* buf, int buf_size) +{ + if(!m_pFile) return -1; + + if (m_pOtherStream) + { + return m_pOtherStream->Read(buf, buf_size); + } + else + { + unsigned int ret = m_pFile->Read(buf, buf_size); + + /* we currently don't support non completing reads */ + if( ret <= 0 ) m_eof = true; + + return (int)(ret & 0xFFFFFFFF); + } +} + +int64_t CDVDInputStreamPVRManager::Seek(int64_t offset, int whence) +{ + if (!m_pFile) + return -1; + + if (whence == SEEK_POSSIBLE) + return m_pFile->IoControl(IOCTRL_SEEK_POSSIBLE, NULL); + + if (m_pOtherStream) + { + return m_pOtherStream->Seek(offset, whence); + } + else + { + int64_t ret = m_pFile->Seek(offset, whence); + + /* if we succeed, we are not eof anymore */ + if( ret >= 0 ) m_eof = false; + + return ret; + } +} + +int64_t CDVDInputStreamPVRManager::GetLength() +{ + if(!m_pFile) return -1; + + if (m_pOtherStream) + return m_pOtherStream->GetLength(); + else + return m_pFile->GetLength(); +} + +int CDVDInputStreamPVRManager::GetTotalTime() +{ + if (m_pLiveTV) + return m_pLiveTV->GetTotalTime(); + return 0; +} + +int CDVDInputStreamPVRManager::GetTime() +{ + if (m_pLiveTV) + return m_pLiveTV->GetStartTime(); + return 0; +} + +bool CDVDInputStreamPVRManager::NextChannel(bool preview/* = false*/) +{ + PVR_CLIENT client; + if (!preview && !SupportsChannelSwitch()) + { + CPVRChannelPtr channel; + g_PVRManager.GetCurrentChannel(channel); + CFileItemPtr item = g_PVRChannelGroups->Get(channel->IsRadio())->GetSelectedGroup()->GetByChannelUp(*channel); + if (item.get()) + return CloseAndOpen(item->GetPath().c_str()); + } + else if (m_pLiveTV) + return m_pLiveTV->NextChannel(preview); + return false; +} + +bool CDVDInputStreamPVRManager::PrevChannel(bool preview/* = false*/) +{ + PVR_CLIENT client; + if (!preview && !SupportsChannelSwitch()) + { + CPVRChannelPtr channel; + g_PVRManager.GetCurrentChannel(channel); + CFileItemPtr item = g_PVRChannelGroups->Get(channel->IsRadio())->GetSelectedGroup()->GetByChannelDown(*channel); + if (item.get()) + return CloseAndOpen(item->GetPath().c_str()); + } + else if (m_pLiveTV) + return m_pLiveTV->PrevChannel(preview); + return false; +} + +bool CDVDInputStreamPVRManager::SelectChannelByNumber(unsigned int iChannelNumber) +{ + PVR_CLIENT client; + if (!SupportsChannelSwitch()) + { + CPVRChannelPtr channel; + g_PVRManager.GetCurrentChannel(channel); + CFileItemPtr item = g_PVRChannelGroups->Get(channel->IsRadio())->GetSelectedGroup()->GetByChannelNumber(iChannelNumber); + if (item.get()) + return CloseAndOpen(item->GetPath().c_str()); + } + else if (m_pLiveTV) + return m_pLiveTV->SelectChannel(iChannelNumber); + + return false; +} + +bool CDVDInputStreamPVRManager::SelectChannel(const CPVRChannel &channel) +{ + PVR_CLIENT client; + if (!SupportsChannelSwitch()) + { + CFileItem item(channel); + return CloseAndOpen(item.GetPath().c_str()); + } + else if (m_pLiveTV) + { + return m_pLiveTV->SelectChannel(channel.ChannelNumber()); + } + + return false; +} + +bool CDVDInputStreamPVRManager::GetSelectedChannel(CPVRChannelPtr& channel) const +{ + return g_PVRManager.GetCurrentChannel(channel); +} + +bool CDVDInputStreamPVRManager::UpdateItem(CFileItem& item) +{ + if (m_pLiveTV) + return m_pLiveTV->UpdateItem(item); + return false; +} + +CDVDInputStream::ENextStream CDVDInputStreamPVRManager::NextStream() +{ + if(!m_pFile) return NEXTSTREAM_NONE; + + if(m_bReopened) + { + m_bReopened = false; + m_eof = false; + return NEXTSTREAM_RETRY; + } + + if (m_pOtherStream) + return m_pOtherStream->NextStream(); + else if(m_pFile->SkipNext()) + { + m_eof = false; + return NEXTSTREAM_OPEN; + } + + return NEXTSTREAM_NONE; +} + +bool CDVDInputStreamPVRManager::CanRecord() +{ + if (m_pRecordable) + return m_pRecordable->CanRecord(); + return false; +} + +bool CDVDInputStreamPVRManager::IsRecording() +{ + if (m_pRecordable) + return m_pRecordable->IsRecording(); + return false; +} + +bool CDVDInputStreamPVRManager::Record(bool bOnOff) +{ + if (m_pRecordable) + return m_pRecordable->Record(bOnOff); + return false; +} + +CStdString CDVDInputStreamPVRManager::GetInputFormat() +{ + if (!m_pOtherStream && g_PVRManager.IsStarted()) + return g_PVRClients->GetCurrentInputFormat(); + return StringUtils::EmptyString; +} + +bool CDVDInputStreamPVRManager::CloseAndOpen(const char* strFile) +{ + Close(); + + if (Open(strFile, m_content)) + { + m_bReopened = true; + return true; + } + + return false; +} + +bool CDVDInputStreamPVRManager::SupportsChannelSwitch(void) const +{ + PVR_CLIENT client; + return g_PVRClients->GetPlayingClient(client) && + client->HandlesInputStream(); +} diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.h b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.h new file mode 100644 index 0000000000..cab2971b88 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.h @@ -0,0 +1,115 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +/* +* for DESCRIPTION see 'DVDInputStreamPVRManager.cpp' +*/ + +#include "DVDInputStream.h" +#include "FileItem.h" + +namespace XFILE { +class IFile; +class ILiveTVInterface; +class IRecordable; +} + +class IDVDPlayer; + +class CDVDInputStreamPVRManager + : public CDVDInputStream + , public CDVDInputStream::IChannel + , public CDVDInputStream::IDisplayTime +{ +public: + CDVDInputStreamPVRManager(IDVDPlayer* pPlayer); + virtual ~CDVDInputStreamPVRManager(); + virtual bool Open(const char* strFile, const std::string &content); + virtual void Close(); + virtual int Read(BYTE* buf, int buf_size); + virtual int64_t Seek(int64_t offset, int whence); + virtual bool Pause(double dTime) { return false; } + virtual bool IsEOF(); + virtual int64_t GetLength(); + + virtual ENextStream NextStream(); + + bool SelectChannelByNumber(unsigned int iChannel); + bool SelectChannel(const PVR::CPVRChannel &channel); + bool NextChannel(bool preview = false); + bool PrevChannel(bool preview = false); + bool GetSelectedChannel(PVR::CPVRChannelPtr& channel) const; + + int GetTotalTime(); + int GetTime(); + + bool CanRecord(); + bool IsRecording(); + bool Record(bool bOnOff); + + bool UpdateItem(CFileItem& item); + + /* overloaded is streamtype to support m_pOtherStream */ + bool IsStreamType(DVDStreamType type) const; + + /*! \brief Get the input format from the Backend + If it is empty ffmpeg scanning the stream to find the right input format. + See "xbmc/cores/dvdplayer/Codecs/ffmpeg/libavformat/allformats.c" for a + list of the input formats. + \return The name of the input format + */ + CStdString GetInputFormat(); + + /* returns m_pOtherStream */ + CDVDInputStream* GetOtherStream(); + + void ResetScanTimeout(unsigned int iTimeoutMs); +protected: + bool CloseAndOpen(const char* strFile); + bool SupportsChannelSwitch(void) const; + + IDVDPlayer* m_pPlayer; + CDVDInputStream* m_pOtherStream; + XFILE::IFile* m_pFile; + XFILE::ILiveTVInterface* m_pLiveTV; + XFILE::IRecordable* m_pRecordable; + bool m_eof; + std::string m_strContent; + bool m_bReopened; + unsigned int m_iScanTimeout; +}; + + +inline bool CDVDInputStreamPVRManager::IsStreamType(DVDStreamType type) const +{ + if (m_pOtherStream) + return m_pOtherStream->IsStreamType(type); + + return m_streamType == type; +} + +inline CDVDInputStream* CDVDInputStreamPVRManager::GetOtherStream() +{ + return m_pOtherStream; +}; + diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.cpp b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.cpp index 5183dbbb33..773de5156f 100644 --- a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.cpp +++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.cpp @@ -22,6 +22,8 @@ #include "DVDInputStreamTV.h" #include "filesystem/MythFile.h" #include "filesystem/VTPFile.h" +#include "pvr/channels/PVRChannel.h" +#include "filesystem/VTPFile.h" #include "filesystem/SlingboxFile.h" #include "URL.h" @@ -137,19 +139,19 @@ int CDVDInputStreamTV::GetStartTime() return m_pLiveTV->GetStartTime(); } -bool CDVDInputStreamTV::NextChannel() +bool CDVDInputStreamTV::NextChannel(bool preview/* = false*/) { if(!m_pLiveTV) return false; return m_pLiveTV->NextChannel(); } -bool CDVDInputStreamTV::PrevChannel() +bool CDVDInputStreamTV::PrevChannel(bool preview/* = false*/) { if(!m_pLiveTV) return false; return m_pLiveTV->PrevChannel(); } -bool CDVDInputStreamTV::SelectChannel(unsigned int channel) +bool CDVDInputStreamTV::SelectChannelByNumber(unsigned int channel) { if(!m_pLiveTV) return false; return m_pLiveTV->SelectChannel(channel); diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.h b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.h index 4d99323917..ce2be09972 100644 --- a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.h +++ b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.h @@ -47,10 +47,9 @@ public: virtual ENextStream NextStream(); virtual int GetBlockSize(); - - bool NextChannel(); - bool PrevChannel(); - bool SelectChannel(unsigned int channel); + bool NextChannel(bool preview = false); + bool PrevChannel(bool preview = false); + bool SelectChannelByNumber(unsigned int channel); int GetTotalTime(); int GetStartTime(); diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/Makefile b/xbmc/cores/dvdplayer/DVDInputStreams/Makefile index b522e055af..4607ed3842 100644 --- a/xbmc/cores/dvdplayer/DVDInputStreams/Makefile +++ b/xbmc/cores/dvdplayer/DVDInputStreams/Makefile @@ -11,6 +11,7 @@ SRCS= DVDFactoryInputStream.cpp \ DVDInputStreamMemory.cpp \ DVDInputStreamNavigator.cpp \ DVDInputStreamRTMP.cpp \ + DVDInputStreamPVRManager.cpp \ DVDInputStreamStack.cpp \ DVDInputStreamTV.cpp \ DVDStateSerializer.cpp \ diff --git a/xbmc/cores/dvdplayer/DVDMessage.h b/xbmc/cores/dvdplayer/DVDMessage.h index b30a005e38..2bd43632cc 100644 --- a/xbmc/cores/dvdplayer/DVDMessage.h +++ b/xbmc/cores/dvdplayer/DVDMessage.h @@ -70,7 +70,8 @@ public: PLAYER_CHANNEL_NEXT, // switches to next playback channel PLAYER_CHANNEL_PREV, // switches to previous playback channel - PLAYER_CHANNEL_SELECT, // switches to given playback channel + PLAYER_CHANNEL_SELECT_NUMBER, // switches to the channel with the provided channel number + PLAYER_CHANNEL_SELECT, // switches to the provided channel PLAYER_STARTED, // sent whenever a sub player has finished it's first frame after open // demuxer related messages diff --git a/xbmc/cores/dvdplayer/DVDPlayer.cpp b/xbmc/cores/dvdplayer/DVDPlayer.cpp index eaad1eb38c..0fc6c24805 100644 --- a/xbmc/cores/dvdplayer/DVDPlayer.cpp +++ b/xbmc/cores/dvdplayer/DVDPlayer.cpp @@ -28,6 +28,7 @@ #include "DVDInputStreams/DVDFactoryInputStream.h" #include "DVDInputStreams/DVDInputStreamNavigator.h" #include "DVDInputStreams/DVDInputStreamTV.h" +#include "DVDInputStreams/DVDInputStreamPVRManager.h" #include "DVDDemuxers/DVDDemux.h" #include "DVDDemuxers/DVDDemuxUtils.h" @@ -68,6 +69,11 @@ #include "utils/log.h" #include "utils/TimeUtils.h" #include "utils/StreamDetails.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannel.h" +#include "pvr/windows/GUIWindowPVR.h" +#include "filesystem/PVRFile.h" +#include "video/dialogs/GUIDialogFullScreenInfo.h" #include "utils/StreamUtils.h" #include "utils/Variant.h" #include "storage/MediaManager.h" @@ -77,8 +83,10 @@ #include "utils/StringUtils.h" #include "Util.h" #include "LangInfo.h" +#include "ApplicationMessenger.h" using namespace std; +using namespace PVR; void CSelectionStreams::Clear(StreamType type, StreamSource source) { @@ -593,6 +601,7 @@ retry: // find any available external subtitles for non dvd files if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) + && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV) && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP)) { @@ -631,6 +640,7 @@ retry: m_clock.Reset(); m_dvd.Clear(); m_errorCount = 0; + m_iChannelEntryTimeOut = 0; return true; } @@ -648,7 +658,11 @@ bool CDVDPlayer::OpenDemuxStream() while(!m_bStop && attempts-- > 0) { m_pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(m_pInputStream); - if(!m_pDemuxer && m_pInputStream->NextStream() != CDVDInputStream::NEXTSTREAM_NONE) + if(!m_pDemuxer && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)) + { + continue; + } + else if(!m_pDemuxer && m_pInputStream->NextStream() != CDVDInputStream::NEXTSTREAM_NONE) { CLog::Log(LOGDEBUG, "%s - New stream available from input, retry open", __FUNCTION__); continue; @@ -757,6 +771,15 @@ bool CDVDPlayer::ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream) if(packet) { + if(packet->iStreamId == DMX_SPECIALID_STREAMCHANGE) + { + // reset the caching state for pvr streams + if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)) + SetCaching(CACHESTATE_PVR); + CDVDDemuxUtils::FreeDemuxPacket(packet); + return true; + } + UpdateCorrection(packet, m_offset_pts); if(packet->iStreamId < 0) return true; @@ -879,6 +902,30 @@ bool CDVDPlayer::IsBetterStream(CCurrentStream& current, CDemuxStream* stream) if(current.type == STREAM_VIDEO && current.id < 0) return true; } + else if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)) + { + if(stream->source == current.source && + stream->iId == current.id) + return false; + + if(stream->disabled) + return false; + + if(stream->type != current.type) + return false; + + if(current.type == STREAM_AUDIO && stream->iPhysicalId == m_dvd.iSelectedAudioStream) + return true; + + if(current.type == STREAM_SUBTITLE && stream->iPhysicalId == m_dvd.iSelectedSPUStream) + return true; + + if(current.type == STREAM_TELETEXT) + return true; + + if(current.id < 0) + return true; + } else { if(stream->source == current.source @@ -1002,6 +1049,10 @@ void CDVDPlayer::Process() } } + // make sure all selected stream have data on startup + if (CachePVRStream()) + SetCaching(CACHESTATE_PVR); + // make sure application know our info UpdateApplication(0); UpdatePlayState(0); @@ -1012,7 +1063,8 @@ void CDVDPlayer::Process() // we are done initializing now, set the readyevent m_ready.Set(); - SetCaching(CACHESTATE_FLUSH); + if (!CachePVRStream()) + SetCaching(CACHESTATE_FLUSH); while (!m_bAbortRequest) { @@ -1048,6 +1100,10 @@ void CDVDPlayer::Process() } OpenDefaultStreams(); + + if (CachePVRStream()) + SetCaching(CACHESTATE_PVR); + UpdateApplication(0); UpdatePlayState(0); } @@ -1061,9 +1117,12 @@ void CDVDPlayer::Process() // update application with our state UpdateApplication(1000); + if (CheckDelayedChannelEntry()) + continue; + // if the queues are full, no need to read more - if ((!m_dvdPlayerAudio.AcceptsData() && m_CurrentAudio.id >= 0) - || (!m_dvdPlayerVideo.AcceptsData() && m_CurrentVideo.id >= 0)) + if ((!m_dvdPlayerAudio.AcceptsData() && m_CurrentAudio.id >= 0) || + (!m_dvdPlayerVideo.AcceptsData() && m_CurrentVideo.id >= 0)) { Sleep(10); continue; @@ -1129,6 +1188,15 @@ void CDVDPlayer::Process() Sleep(100); continue; } + else if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)) + { + CDVDInputStreamPVRManager* pStream = static_cast<CDVDInputStreamPVRManager*>(m_pInputStream); + if (pStream->IsEOF()) + break; + + Sleep(100); + continue; + } // make sure we tell all players to finish it's data if(m_CurrentAudio.inited) @@ -1188,6 +1256,23 @@ void CDVDPlayer::Process() } } +bool CDVDPlayer::CheckDelayedChannelEntry(void) +{ + bool bReturn(false); + + if (m_iChannelEntryTimeOut > 0 && XbmcThreads::SystemClockMillis() >= m_iChannelEntryTimeOut) + { + CFileItem currentFile(g_application.CurrentFileItem()); + CPVRChannel *currentChannel = currentFile.GetPVRChannelInfoTag(); + SwitchChannel(*currentChannel); + + bReturn = true; + m_iChannelEntryTimeOut = 0; + } + + return bReturn; +} + void CDVDPlayer::ProcessPacket(CDemuxStream* pStream, DemuxPacket* pPacket) { /* process packet if it belongs to selected stream. for dvd's don't allow automatic opening of streams*/ @@ -1438,6 +1523,40 @@ void CDVDPlayer::HandlePlaySpeed() } } + if (caching == CACHESTATE_PVR) + { + bool bGotAudio(m_pDemuxer->GetNrOfAudioStreams() > 0); + bool bGotVideo(m_pDemuxer->GetNrOfVideoStreams() > 0); + bool bAudioLevelOk(m_dvdPlayerAudio.GetLevel() > g_advancedSettings.m_iPVRMinAudioCacheLevel); + bool bVideoLevelOk(m_dvdPlayerVideo.GetLevel() > g_advancedSettings.m_iPVRMinVideoCacheLevel); + bool bAudioFull(!m_dvdPlayerAudio.AcceptsData()); + bool bVideoFull(!m_dvdPlayerVideo.AcceptsData()); + + if (/* if all streams got at least g_advancedSettings.m_iPVRMinCacheLevel in their buffers, we're done */ + ((bGotVideo || bGotAudio) && (!bGotAudio || bAudioLevelOk) && (!bGotVideo || bVideoLevelOk)) || + /* or if one of the buffers is full */ + (bAudioFull || bVideoFull)) + { + CLog::Log(LOGDEBUG, "set caching from pvr to done. audio (%d) = %d. video (%d) = %d", + bGotAudio, m_dvdPlayerAudio.GetLevel(), + bGotVideo, m_dvdPlayerVideo.GetLevel()); + + CFileItem currentItem(g_application.CurrentFileItem()); + if (currentItem.HasPVRChannelInfoTag()) + g_PVRManager.LoadCurrentChannelSettings(); + + caching = CACHESTATE_DONE; + } + else + { + /* ensure that automatically started players are stopped while caching */ + if (m_CurrentAudio.started) + m_dvdPlayerAudio.SetSpeed(DVD_PLAYSPEED_PAUSE); + if (m_CurrentVideo.started) + m_dvdPlayerVideo.SetSpeed(DVD_PLAYSPEED_PAUSE); + } + } + if(caching == CACHESTATE_PLAY) { // if all enabled streams have started playing we are done @@ -1501,21 +1620,26 @@ bool CDVDPlayer::CheckStartCaching(CCurrentStream& current) if((current.type == STREAM_AUDIO && m_dvdPlayerAudio.IsStalled()) || (current.type == STREAM_VIDEO && m_dvdPlayerVideo.IsStalled())) { + if (CachePVRStream()) + { + if ((current.type == STREAM_AUDIO && current.started && m_dvdPlayerAudio.GetLevel() == 0) || + (current.type == STREAM_VIDEO && current.started && m_dvdPlayerVideo.GetLevel() == 0)) + { + CLog::Log(LOGDEBUG, "%s stream stalled. start buffering", current.type == STREAM_AUDIO ? "audio" : "video"); + SetCaching(CACHESTATE_PVR); + } + return true; + } + // don't start caching if it's only a single stream that has run dry if(m_dvdPlayerAudio.GetLevel() > 50 || m_dvdPlayerVideo.GetLevel() > 50) return false; - if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP) - || m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)) - SetCaching(CACHESTATE_INIT); + if(current.inited) + SetCaching(CACHESTATE_FULL); else - { - if(current.inited) - SetCaching(CACHESTATE_FULL); - else - SetCaching(CACHESTATE_INIT); - } + SetCaching(CACHESTATE_INIT); return true; } return false; @@ -1862,6 +1986,11 @@ void CDVDPlayer::OnExit() } m_pSubtitleDemuxer = NULL; + if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) && g_PVRManager.IsPlayingRecording()) + { + g_PVRManager.UpdateCurrentLastPlayedPosition(m_State.time / 1000); + } + // destroy the inputstream if (m_pInputStream) { @@ -2054,8 +2183,9 @@ void CDVDPlayer::HandleMessages() } else if (pMsg->IsType(CDVDMsg::PLAYER_SET_RECORD)) { - if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)) - static_cast<CDVDInputStreamTV*>(m_pInputStream)->Record(*(CDVDMsgBool*)pMsg); + CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream); + if(input) + input->Record(*(CDVDMsgBool*)pMsg); } else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) { @@ -2097,30 +2227,71 @@ void CDVDPlayer::HandleMessages() if(m_pDemuxer) m_pDemuxer->SetSpeed(speed); } - else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT) || - pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_PREV) || - (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_CHANNEL_SELECT) == 0)) + else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT_NUMBER) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_CHANNEL_SELECT_NUMBER) == 0) + { + FlushBuffers(false); + CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream); + if(input && input->SelectChannelByNumber(static_cast<CDVDMsgInt*>(pMsg)->m_value)) + { + SAFE_DELETE(m_pDemuxer); + }else + { + CLog::Log(LOGWARNING, "%s - failed to switch channel. playback stopped", __FUNCTION__); + CApplicationMessenger::Get().MediaStop(false); + } + } + else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_CHANNEL_SELECT) == 0) + { + FlushBuffers(false); + CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream); + if(input && input->SelectChannel(static_cast<CDVDMsgType <CPVRChannel> *>(pMsg)->m_value)) + { + SAFE_DELETE(m_pDemuxer); + }else + { + CLog::Log(LOGWARNING, "%s - failed to switch channel. playback stopped", __FUNCTION__); + CApplicationMessenger::Get().MediaStop(false); + } + } + else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT) || pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_PREV)) { CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream); if(input) { - g_infoManager.SetDisplayAfterSeek(100000); - - bool result; - if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT)) - result = input->SelectChannel(static_cast<CDVDMsgInt*>(pMsg)->m_value); - else if(pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT)) - result = input->NextChannel(); - else - result = input->PrevChannel(); + bool bSwitchSuccessful(false); + bool bShowPreview(g_guiSettings.GetInt("pvrplayback.channelentrytimeout") > 0); - if(result) + if (!bShowPreview) { + g_infoManager.SetDisplayAfterSeek(100000); FlushBuffers(false); - SAFE_DELETE(m_pDemuxer); } - g_infoManager.SetDisplayAfterSeek(); + if(pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT)) + bSwitchSuccessful = input->NextChannel(bShowPreview); + else + bSwitchSuccessful = input->PrevChannel(bShowPreview); + + if(bSwitchSuccessful) + { + if (bShowPreview) + { + UpdateApplication(0); + m_iChannelEntryTimeOut = XbmcThreads::SystemClockMillis() + g_guiSettings.GetInt("pvrplayback.channelentrytimeout"); + } + else + { + m_iChannelEntryTimeOut = 0; + SAFE_DELETE(m_pDemuxer); + + g_infoManager.SetDisplayAfterSeek(); + } + } + else + { + CLog::Log(LOGWARNING, "%s - failed to switch channel. playback stopped", __FUNCTION__); + CApplicationMessenger::Get().MediaStop(false); + } } } else if (pMsg->IsType(CDVDMsg::GENERAL_GUI_ACTION)) @@ -2161,13 +2332,17 @@ void CDVDPlayer::SetCaching(ECacheState state) CLog::Log(LOGDEBUG, "CDVDPlayer::SetCaching - caching state %d", state); if(state == CACHESTATE_FULL - || state == CACHESTATE_INIT) + || state == CACHESTATE_INIT + || state == CACHESTATE_PVR) { m_clock.SetSpeed(DVD_PLAYSPEED_PAUSE); m_dvdPlayerAudio.SetSpeed(DVD_PLAYSPEED_PAUSE); m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1); m_dvdPlayerVideo.SetSpeed(DVD_PLAYSPEED_PAUSE); m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::PLAYER_STARTED), 1); + + if (state == CACHESTATE_PVR) + m_pInputStream->ResetScanTimeout((unsigned int) g_guiSettings.GetInt("pvrplayback.scantime") * 1000); } if(state == CACHESTATE_PLAY @@ -2176,6 +2351,7 @@ void CDVDPlayer::SetCaching(ECacheState state) m_clock.SetSpeed(m_playSpeed); m_dvdPlayerAudio.SetSpeed(m_playSpeed); m_dvdPlayerVideo.SetSpeed(m_playSpeed); + m_pInputStream->ResetScanTimeout(0); } m_caching = state; } @@ -2190,7 +2366,7 @@ void CDVDPlayer::SetPlaySpeed(int speed) void CDVDPlayer::Pause() { - if(m_playSpeed != DVD_PLAYSPEED_PAUSE && m_caching == CACHESTATE_FULL) + if(m_playSpeed != DVD_PLAYSPEED_PAUSE && (m_caching == CACHESTATE_FULL || m_caching == CACHESTATE_PVR)) { SetCaching(CACHESTATE_DONE); return; @@ -2211,7 +2387,7 @@ void CDVDPlayer::Pause() bool CDVDPlayer::IsPaused() const { - return (m_playSpeed == DVD_PLAYSPEED_PAUSE) || m_caching == CACHESTATE_FULL; + return m_playSpeed == DVD_PLAYSPEED_PAUSE || m_caching == CACHESTATE_FULL || m_caching == CACHESTATE_PVR; } bool CDVDPlayer::HasVideo() const @@ -3202,6 +3378,21 @@ int CDVDPlayer::OnDVDNavResult(void* pData, int iMessage) return NAVRESULT_NOP; } +bool CDVDPlayer::ShowPVRChannelInfo(void) +{ + bool bReturn(false); + + if (g_guiSettings.GetBool("pvrmenu.infoswitch")) + { + int iTimeout = g_guiSettings.GetBool("pvrmenu.infotimeout") ? g_guiSettings.GetInt("pvrmenu.infotime") : 0; + g_PVRManager.ShowPlayerInfo(iTimeout); + + bReturn = true; + } + + return bReturn; +} + bool CDVDPlayer::OnAction(const CAction &action) { #define THREAD_ACTION(action) \ @@ -3384,15 +3575,19 @@ bool CDVDPlayer::OnAction(const CAction &action) { switch (action.GetID()) { + case ACTION_MOVE_UP: case ACTION_NEXT_ITEM: m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_NEXT)); g_infoManager.SetDisplayAfterSeek(); + ShowPVRChannelInfo(); return true; break; + case ACTION_MOVE_DOWN: case ACTION_PREV_ITEM: m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_PREV)); g_infoManager.SetDisplayAfterSeek(); + ShowPVRChannelInfo(); return true; break; @@ -3400,8 +3595,9 @@ bool CDVDPlayer::OnAction(const CAction &action) { // Offset from key codes back to button number int channel = action.GetAmount(); - m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_CHANNEL_SELECT, channel)); + m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_CHANNEL_SELECT_NUMBER, channel)); g_infoManager.SetDisplayAfterSeek(); + ShowPVRChannelInfo(); return true; } break; @@ -3634,9 +3830,14 @@ void CDVDPlayer::UpdatePlayState(double timeout) state.canrecord = static_cast<CDVDInputStreamTV*>(m_pInputStream)->CanRecord(); state.recording = static_cast<CDVDInputStreamTV*>(m_pInputStream)->IsRecording(); } + else if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)) + { + state.canrecord = static_cast<CDVDInputStreamPVRManager*>(m_pInputStream)->CanRecord(); + state.recording = static_cast<CDVDInputStreamPVRManager*>(m_pInputStream)->IsRecording(); + } CDVDInputStream::IDisplayTime* pDisplayTime = dynamic_cast<CDVDInputStream::IDisplayTime*>(m_pInputStream); - if (pDisplayTime) + if (pDisplayTime && pDisplayTime->GetTotalTime() > 0) { state.time = pDisplayTime->GetTime(); state.time_total = pDisplayTime->GetTotalTime(); @@ -3758,7 +3959,8 @@ bool CDVDPlayer::IsRecording() bool CDVDPlayer::Record(bool bOnOff) { - if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)) + if (m_pInputStream && (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV) || + m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)) ) { m_messenger.Put(new CDVDMsgBool(CDVDMsg::PLAYER_SET_RECORD, bOnOff)); return true; @@ -3840,3 +4042,33 @@ CStdString CDVDPlayer::GetPlayingTitle() return ""; } + +bool CDVDPlayer::SwitchChannel(const CPVRChannel &channel) +{ + if (!g_PVRManager.CheckParentalLock(channel)) + return false; + + /* set GUI info */ + if (!g_PVRManager.PerformChannelSwitch(channel, true)) + return false; + + UpdateApplication(0); + UpdatePlayState(0); + + /* make sure the pvr window is updated */ + CGUIWindowPVR *pWindow = (CGUIWindowPVR *) g_windowManager.GetWindow(WINDOW_PVR); + if (pWindow) + pWindow->SetInvalid(); + + /* select the new channel */ + m_messenger.Put(new CDVDMsgType<CPVRChannel>(CDVDMsg::PLAYER_CHANNEL_SELECT, channel)); + + return true; +} + +bool CDVDPlayer::CachePVRStream(void) const +{ + return m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER) && + !g_PVRManager.IsPlayingRecording() && + g_advancedSettings.m_bPVRCacheInDvdPlayer; +} diff --git a/xbmc/cores/dvdplayer/DVDPlayer.h b/xbmc/cores/dvdplayer/DVDPlayer.h index d63219477b..1eb97f863e 100644 --- a/xbmc/cores/dvdplayer/DVDPlayer.h +++ b/xbmc/cores/dvdplayer/DVDPlayer.h @@ -49,6 +49,11 @@ class CDemuxStreamVideo; class CDemuxStreamAudio; class CStreamInfo; +namespace PVR +{ + class CPVRChannel; +} + #define DVDSTATE_NORMAL 0x00000001 // normal dvd state #define DVDSTATE_STILL 0x00000002 // currently displaying a still frame #define DVDSTATE_WAIT 0x00000003 // waiting for demuxer read error @@ -242,15 +247,19 @@ public: virtual CStdString GetPlayingTitle(); + virtual bool SwitchChannel(const PVR::CPVRChannel &channel); + virtual bool CachePVRStream(void) const; + enum ECacheState { CACHESTATE_DONE = 0 , CACHESTATE_FULL // player is filling up the demux queue + , CACHESTATE_PVR // player is waiting for some data in each buffer , CACHESTATE_INIT // player is waiting for first packet of each stream , CACHESTATE_PLAY // player is waiting for players to not be stalled , CACHESTATE_FLUSH // temporary state player will choose startup between init or full }; - virtual bool IsCaching() const { return m_caching == CACHESTATE_FULL; } + virtual bool IsCaching() const { return m_caching == CACHESTATE_FULL || m_caching == CACHESTATE_PVR; } virtual int GetCacheLevel() const ; virtual int OnDVDNavResult(void* pData, int iMessage); @@ -282,6 +291,8 @@ protected: void ProcessSubData(CDemuxStream* pStream, DemuxPacket* pPacket); void ProcessTeletextData(CDemuxStream* pStream, DemuxPacket* pPacket); + bool ShowPVRChannelInfo(); + int AddSubtitleFile(const std::string& filename, const std::string& subfilename = "", CDemuxStream::EFlags flags = CDemuxStream::FLAG_NONE); /** @@ -317,6 +328,7 @@ protected: bool ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream); bool IsValidStream(CCurrentStream& stream); bool IsBetterStream(CCurrentStream& current, CDemuxStream* stream); + bool CheckDelayedChannelEntry(void); bool OpenInputStream(); bool OpenDemuxStream(); @@ -328,10 +340,11 @@ protected: bool m_bAbortRequest; - std::string m_filename; // holds the actual filename - std::string m_mimetype; // hold a hint to what content file contains (mime type) - ECacheState m_caching; - CFileItem m_item; + std::string m_filename; // holds the actual filename + std::string m_mimetype; // hold a hint to what content file contains (mime type) + ECacheState m_caching; + CFileItem m_item; + unsigned int m_iChannelEntryTimeOut; CCurrentStream m_CurrentAudio; diff --git a/xbmc/cores/dvdplayer/Edl.cpp b/xbmc/cores/dvdplayer/Edl.cpp index f7b1326731..dd368ba88e 100644 --- a/xbmc/cores/dvdplayer/Edl.cpp +++ b/xbmc/cores/dvdplayer/Edl.cpp @@ -113,8 +113,9 @@ bool CEdl::ReadEditDecisionLists(const CStdString& strMovie, const float fFrameR * Only check for edit decision lists if the movie is on the local hard drive, or accessed over a * network share. */ - if (URIUtils::IsHD(strMovie) - || URIUtils::IsSmb(strMovie)) + if ((URIUtils::IsHD(strMovie) || URIUtils::IsSmb(strMovie)) && + !URIUtils::IsPVRRecording(strMovie) && + !URIUtils::IsInternetStream(strMovie)) { CLog::Log(LOGDEBUG, "%s - Checking for edit decision lists (EDL) on local drive or remote share for: %s", __FUNCTION__, strMovie.c_str()); diff --git a/xbmc/cores/paplayer/CodecFactory.cpp b/xbmc/cores/paplayer/CodecFactory.cpp index bc30c63883..52d8d37e2e 100644 --- a/xbmc/cores/paplayer/CodecFactory.cpp +++ b/xbmc/cores/paplayer/CodecFactory.cpp @@ -65,7 +65,8 @@ ICodec* CodecFactory::CreateCodec(const CStdString& strFileType) else if (strFileType.Equals("wav")) return new DVDPlayerCodec(); else if (strFileType.Equals("dts") || strFileType.Equals("ac3") || - strFileType.Equals("m4a") || strFileType.Equals("aac")) + strFileType.Equals("m4a") || strFileType.Equals("aac") || + strFileType.Equals("pvr")) return new DVDPlayerCodec(); else if (strFileType.Equals("wv")) return new DVDPlayerCodec(); diff --git a/xbmc/dialogs/GUIDialogContextMenu.h b/xbmc/dialogs/GUIDialogContextMenu.h index 0771c7d50f..ab2edf5b7d 100644 --- a/xbmc/dialogs/GUIDialogContextMenu.h +++ b/xbmc/dialogs/GUIDialogContextMenu.h @@ -100,10 +100,30 @@ enum CONTEXT_BUTTON { CONTEXT_BUTTON_CANCELLED = 0, CONTEXT_BUTTON_SCRIPT_SETTINGS, CONTEXT_BUTTON_LASTFM_UNLOVE_ITEM, CONTEXT_BUTTON_LASTFM_UNBAN_ITEM, + CONTEXT_BUTTON_HIDE, + CONTEXT_BUTTON_SHOW_HIDDEN, + CONTEXT_BUTTON_ADD, + CONTEXT_BUTTON_ACTIVATE, + CONTEXT_BUTTON_START_RECORD, + CONTEXT_BUTTON_STOP_RECORD, + CONTEXT_BUTTON_GROUP_MANAGER, + CONTEXT_BUTTON_CHANNEL_MANAGER, + CONTEXT_BUTTON_FILTER, CONTEXT_BUTTON_SET_MOVIESET_THUMB, + CONTEXT_BUTTON_BEGIN, + CONTEXT_BUTTON_END, + CONTEXT_BUTTON_FIND, CONTEXT_BUTTON_SET_MOVIESET_FANART, CONTEXT_BUTTON_DELETE_PLUGIN, + CONTEXT_BUTTON_SORTASC, + CONTEXT_BUTTON_SORTBY, + CONTEXT_BUTTON_SORTBY_CHANNEL, + CONTEXT_BUTTON_SORTBY_NAME, + CONTEXT_BUTTON_SORTBY_DATE, + CONTEXT_BUTTON_MENU_HOOKS, CONTEXT_BUTTON_PLAY_AND_QUEUE, + CONTEXT_BUTTON_UPDATE_EPG, + CONTEXT_BUTTON_RECORD_ITEM, CONTEXT_BUTTON_TAGS_ADD_ITEMS, CONTEXT_BUTTON_TAGS_REMOVE_ITEMS, CONTEXT_BUTTON_USER1, diff --git a/xbmc/dialogs/GUIDialogExtendedProgressBar.cpp b/xbmc/dialogs/GUIDialogExtendedProgressBar.cpp new file mode 100644 index 0000000000..8a8c0bf0ce --- /dev/null +++ b/xbmc/dialogs/GUIDialogExtendedProgressBar.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIDialogExtendedProgressBar.h" +#include "guilib/GUIProgressControl.h" +#include "guilib/GUISliderControl.h" +#include "threads/SingleLock.h" +#include "threads/SystemClock.h" + +#define CONTROL_LABELHEADER 30 +#define CONTROL_LABELTITLE 31 +#define CONTROL_PROGRESS 32 + +#define ITEM_SWITCH_TIME_MS 2000 + +using namespace std; + +string CGUIDialogProgressBarHandle::Text(void) const +{ + CSingleLock lock(m_critSection); + string retVal(m_strText); + return retVal; +} + +void CGUIDialogProgressBarHandle::SetText(const string &strText) +{ + CSingleLock lock(m_critSection); + m_strText = strText; +} + +void CGUIDialogProgressBarHandle::SetProgress(int currentItem, int itemCount) +{ + float fPercentage = (float)((currentItem*100)/itemCount); + if (fPercentage > 100.0F) + fPercentage = 100.0F; + + m_fPercentage = fPercentage; +} + +CGUIDialogExtendedProgressBar::CGUIDialogExtendedProgressBar(void) + : CGUIDialog(WINDOW_DIALOG_EXT_PROGRESS, "DialogExtendedProgressBar.xml") +{ + m_loadOnDemand = false; + m_iLastSwitchTime = 0; + m_iCurrentItem = 0; +} + +CGUIDialogProgressBarHandle *CGUIDialogExtendedProgressBar::GetHandle(const string &strTitle) +{ + CGUIDialogProgressBarHandle *handle = new CGUIDialogProgressBarHandle(strTitle); + { + CSingleLock lock(m_critSection); + m_handles.push_back(handle); + } + + Show(); + + return handle; +} + +bool CGUIDialogExtendedProgressBar::OnMessage(CGUIMessage& message) +{ + switch (message.GetMessage()) + { + case GUI_MSG_WINDOW_INIT: + { + m_iLastSwitchTime = XbmcThreads::SystemClockMillis(); + CGUIDialog::OnMessage(message); + + UpdateState(0); + return true; + } + break; + } + + return CGUIDialog::OnMessage(message); +} + +void CGUIDialogExtendedProgressBar::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions) +{ + if (m_active) + UpdateState(currentTime); + + CGUIDialog::Process(currentTime, dirtyregions); +} + +void CGUIDialogExtendedProgressBar::UpdateState(unsigned int currentTime) +{ + bool bNoItemsLeft(false); + string strHeader; + string strTitle; + float fProgress(-1.0f); + + { + CSingleLock lock(m_critSection); + + // delete finished items + for (int iPtr = m_handles.size() - 1; iPtr >= 0; iPtr--) + { + if (m_handles.at(iPtr)->IsFinished()) + { + delete m_handles.at(iPtr); + m_handles.erase(m_handles.begin() + iPtr); + + // current item deleted, back one + if ((int)m_iCurrentItem == iPtr && m_iCurrentItem > 0) + m_iCurrentItem--; + } + } + + // update the current item ptr + if (currentTime > m_iLastSwitchTime && + currentTime - m_iLastSwitchTime >= ITEM_SWITCH_TIME_MS) + { + m_iLastSwitchTime = currentTime; + + // select next item + if (++m_iCurrentItem > m_handles.size() - 1) + m_iCurrentItem = 0; + } + + if (m_iCurrentItem < m_handles.size()) + { + CGUIDialogProgressBarHandle *handle = m_handles.at(m_iCurrentItem); + if (handle) + { + strTitle = handle->Text(); + strHeader = handle->Title(); + fProgress = handle->Percentage(); + } + } + else + { + bNoItemsLeft = true; + } + } + + if (bNoItemsLeft) + { + Close(true, 0, true, false); + } + else + { + SET_CONTROL_LABEL(CONTROL_LABELHEADER, strHeader); + SET_CONTROL_LABEL(CONTROL_LABELTITLE, strTitle); + + if (fProgress > -1.0f) + { + SET_CONTROL_VISIBLE(CONTROL_PROGRESS); + CGUIProgressControl* pProgressCtrl=(CGUIProgressControl*)GetControl(CONTROL_PROGRESS); + if (pProgressCtrl) pProgressCtrl->SetPercentage(fProgress); + } + } +} diff --git a/xbmc/dialogs/GUIDialogExtendedProgressBar.h b/xbmc/dialogs/GUIDialogExtendedProgressBar.h new file mode 100644 index 0000000000..2c7584da94 --- /dev/null +++ b/xbmc/dialogs/GUIDialogExtendedProgressBar.h @@ -0,0 +1,71 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "guilib/GUIDialog.h" + +class CGUIDialogProgressBarHandle +{ +public: + CGUIDialogProgressBarHandle(const std::string &strTitle) : + m_fPercentage(0), + m_strTitle(strTitle), + m_bFinished(false) {} + virtual ~CGUIDialogProgressBarHandle(void) {} + + const std::string &Title(void) { return m_strTitle; } + + std::string Text(void) const; + void SetText(const std::string &strText); + + bool IsFinished(void) const { return m_bFinished; } + void MarkFinished(void) { m_bFinished = true; } + + float Percentage(void) const { return m_fPercentage;} + void SetPercentage(float fPercentage) { m_fPercentage = fPercentage; } + void SetProgress(int currentItem, int itemCount); + +private: + CCriticalSection m_critSection; + float m_fPercentage; + std::string m_strTitle; + std::string m_strText; + bool m_bFinished; +}; + +class CGUIDialogExtendedProgressBar : public CGUIDialog +{ +public: + CGUIDialogExtendedProgressBar(void); + virtual ~CGUIDialogExtendedProgressBar(void) {}; + virtual bool OnMessage(CGUIMessage& message); + virtual void Process(unsigned int currentTime, CDirtyRegionList &dirtyregions); + + CGUIDialogProgressBarHandle *GetHandle(const std::string &strTitle); + +protected: + void UpdateState(unsigned int currentTime); + + CCriticalSection m_critSection; + unsigned int m_iCurrentItem; + unsigned int m_iLastSwitchTime; + std::vector<CGUIDialogProgressBarHandle *> m_handles; +}; diff --git a/xbmc/dialogs/GUIDialogMediaSource.cpp b/xbmc/dialogs/GUIDialogMediaSource.cpp index 95345484d8..3272071f50 100644 --- a/xbmc/dialogs/GUIDialogMediaSource.cpp +++ b/xbmc/dialogs/GUIDialogMediaSource.cpp @@ -28,6 +28,8 @@ #include "Util.h" #include "utils/URIUtils.h" #include "filesystem/Directory.h" +#include "filesystem/PluginDirectory.h" +#include "filesystem/PVRDirectory.h" #include "GUIDialogYesNo.h" #include "FileItem.h" #include "settings/Settings.h" @@ -296,6 +298,14 @@ void CGUIDialogMediaSource::OnPathBrowse(int item) share1.strPath = "sap://"; share1.strName = "SAP Streams"; extraShares.push_back(share1); + + // add the recordings dir as needed + if (CPVRDirectory::HasRecordings()) + { + share1.strPath = "pvr://recordings/"; + share1.strName = g_localizeStrings.Get(19017); // TV Recordings + extraShares.push_back(share1); + } } else if (m_type == "pictures") { diff --git a/xbmc/dialogs/GUIDialogNumeric.cpp b/xbmc/dialogs/GUIDialogNumeric.cpp index fa82a0db5a..9d20e9ec84 100644 --- a/xbmc/dialogs/GUIDialogNumeric.cpp +++ b/xbmc/dialogs/GUIDialogNumeric.cpp @@ -338,6 +338,8 @@ void CGUIDialogNumeric::FrameMove() void CGUIDialogNumeric::OnNumber(unsigned int num) { + ResetAutoClose(); + if (m_mode == INPUT_NUMBER || m_mode == INPUT_PASSWORD) { m_number += num + '0'; @@ -676,16 +678,19 @@ bool CGUIDialogNumeric::ShowAndGetIPAddress(CStdString &IPAddress, const CStdStr return true; } -bool CGUIDialogNumeric::ShowAndGetNumber(CStdString& strInput, const CStdString &strHeading) +bool CGUIDialogNumeric::ShowAndGetNumber(CStdString& strInput, const CStdString &strHeading, unsigned int iAutoCloseTimeoutMs /* = 0 */) { // Prompt user for password input CGUIDialogNumeric *pDialog = (CGUIDialogNumeric *)g_windowManager.GetWindow(WINDOW_DIALOG_NUMERIC); pDialog->SetHeading( strHeading ); pDialog->SetMode(INPUT_NUMBER, (void *)&strInput); + if (iAutoCloseTimeoutMs) + pDialog->SetAutoClose(iAutoCloseTimeoutMs); + pDialog->DoModal(); - if (!pDialog->IsConfirmed() || pDialog->IsCanceled()) + if (!pDialog->IsAutoClosed() && (!pDialog->IsConfirmed() || pDialog->IsCanceled())) return false; pDialog->GetOutput(&strInput); return true; diff --git a/xbmc/dialogs/GUIDialogNumeric.h b/xbmc/dialogs/GUIDialogNumeric.h index 5dd8275e3f..22925b95d8 100644 --- a/xbmc/dialogs/GUIDialogNumeric.h +++ b/xbmc/dialogs/GUIDialogNumeric.h @@ -51,7 +51,7 @@ public: static bool ShowAndGetTime(SYSTEMTIME &time, const CStdString &heading); static bool ShowAndGetDate(SYSTEMTIME &date, const CStdString &heading); static bool ShowAndGetIPAddress(CStdString &IPAddress, const CStdString &heading); - static bool ShowAndGetNumber(CStdString& strInput, const CStdString &strHeading); + static bool ShowAndGetNumber(CStdString& strInput, const CStdString &strHeading, unsigned int iAutoCloseTimeoutMs = 0); static bool ShowAndGetSeconds(CStdString& timeString, const CStdString &heading); protected: diff --git a/xbmc/dialogs/GUIDialogSeekBar.cpp b/xbmc/dialogs/GUIDialogSeekBar.cpp index 8fa7e8da55..1f4570f7f9 100644 --- a/xbmc/dialogs/GUIDialogSeekBar.cpp +++ b/xbmc/dialogs/GUIDialogSeekBar.cpp @@ -23,8 +23,14 @@ #include "guilib/GUISliderControl.h" #include "Application.h" #include "GUIInfoManager.h" +#include "utils/TimeUtils.h" +#include "FileItem.h" +#include "settings/GUISettings.h" #include "utils/SeekHandler.h" +#define SEEK_BAR_DISPLAY_TIME 2000L +#define SEEK_BAR_SEEK_TIME 500L + #define POPUP_SEEK_SLIDER 401 #define POPUP_SEEK_LABEL 402 diff --git a/xbmc/dialogs/Makefile b/xbmc/dialogs/Makefile index 85ebf3b319..50a2974f36 100644 --- a/xbmc/dialogs/Makefile +++ b/xbmc/dialogs/Makefile @@ -3,6 +3,7 @@ SRCS=GUIDialogBoxBase.cpp \ GUIDialogButtonMenu.cpp \ GUIDialogCache.cpp \ GUIDialogContextMenu.cpp \ + GUIDialogExtendedProgressBar.cpp \ GUIDialogFavourites.cpp \ GUIDialogFileBrowser.cpp \ GUIDialogGamepad.cpp \ diff --git a/xbmc/epg/Epg.cpp b/xbmc/epg/Epg.cpp new file mode 100644 index 0000000000..5f5dc78b94 --- /dev/null +++ b/xbmc/epg/Epg.cpp @@ -0,0 +1,960 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "guilib/LocalizeStrings.h" +#include "settings/AdvancedSettings.h" +#include "settings/GUISettings.h" +#include "threads/SingleLock.h" +#include "utils/log.h" +#include "utils/TimeUtils.h" + +#include "EpgDatabase.h" +#include "EpgContainer.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClients.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "utils/StringUtils.h" + +#include "../addons/include/xbmc_pvr_types.h" // TODO extract the epg specific stuff + +using namespace PVR; +using namespace EPG; +using namespace std; + +CEpg::CEpg(int iEpgID, const CStdString &strName /* = "" */, const CStdString &strScraperName /* = "" */, bool bLoadedFromDb /* = false */) : + m_bChanged(!bLoadedFromDb), + m_bTagsChanged(false), + m_bLoaded(false), + m_bUpdatePending(false), + m_iEpgID(iEpgID), + m_strName(strName), + m_strScraperName(strScraperName) +{ + CPVRChannelPtr empty; + m_pvrChannel = empty; +} + +CEpg::CEpg(CPVRChannelPtr channel, bool bLoadedFromDb /* = false */) : + m_bChanged(!bLoadedFromDb), + m_bTagsChanged(false), + m_bLoaded(false), + m_bUpdatePending(false), + m_iEpgID(channel->EpgID()), + m_strName(channel->ChannelName()), + m_strScraperName(channel->EPGScraper()), + m_pvrChannel(channel) +{ +} + +CEpg::CEpg(void) : + m_bChanged(false), + m_bTagsChanged(false), + m_bLoaded(false), + m_bUpdatePending(false), + m_iEpgID(0) +{ + CPVRChannelPtr empty; + m_pvrChannel = empty; +} + +CEpg::~CEpg(void) +{ + Clear(); +} + +CEpg &CEpg::operator =(const CEpg &right) +{ + m_bChanged = right.m_bChanged; + m_bTagsChanged = right.m_bTagsChanged; + m_bLoaded = right.m_bLoaded; + m_bUpdatePending = right.m_bUpdatePending; + m_iEpgID = right.m_iEpgID; + m_strName = right.m_strName; + m_strScraperName = right.m_strScraperName; + m_nowActiveStart = right.m_nowActiveStart; + m_lastScanTime = right.m_lastScanTime; + m_pvrChannel = right.m_pvrChannel; + + for (map<CDateTime, CEpgInfoTagPtr>::const_iterator it = right.m_tags.begin(); it != right.m_tags.end(); it++) + m_tags.insert(make_pair(it->first, new CEpgInfoTag(*it->second))); + + return *this; +} + +/** @name Public methods */ +//@{ + +void CEpg::SetName(const CStdString &strName) +{ + CSingleLock lock(m_critSection); + + if (!m_strName.Equals(strName)) + { + m_bChanged = true; + m_strName = strName; + } +} + +void CEpg::SetScraperName(const CStdString &strScraperName) +{ + CSingleLock lock(m_critSection); + + if (!m_strScraperName.Equals(strScraperName)) + { + m_bChanged = true; + m_strScraperName = strScraperName; + } +} + +void CEpg::SetUpdatePending(bool bUpdatePending /* = true */) +{ + { + CSingleLock lock(m_critSection); + m_bUpdatePending = bUpdatePending; + } + + if (bUpdatePending) + g_EpgContainer.SetHasPendingUpdates(true); +} + +void CEpg::ForceUpdate(void) +{ + SetUpdatePending(); +} + +bool CEpg::HasValidEntries(void) const +{ + CSingleLock lock(m_critSection); + + return (m_iEpgID > 0 && /* valid EPG ID */ + m_tags.size() > 0 && /* contains at least 1 tag */ + m_tags.rbegin()->second->EndAsUTC() >= CDateTime::GetCurrentDateTime().GetAsUTCDateTime()); /* the last end time hasn't passed yet */ +} + +void CEpg::Clear(void) +{ + CSingleLock lock(m_critSection); + m_tags.clear(); +} + +void CEpg::Cleanup(void) +{ + CDateTime cleanupTime = CDateTime::GetCurrentDateTime().GetAsUTCDateTime() - + CDateTimeSpan(0, g_advancedSettings.m_iEpgLingerTime / 60, g_advancedSettings.m_iEpgLingerTime % 60, 0); + Cleanup(cleanupTime); +} + +void CEpg::Cleanup(const CDateTime &Time) +{ + CSingleLock lock(m_critSection); + for (map<CDateTime, CEpgInfoTagPtr>::iterator it = m_tags.begin(); it != m_tags.end(); it != m_tags.end() ? it++ : it) + { + if (it->second->EndAsUTC() < Time) + { + if (m_nowActiveStart == it->first) + m_nowActiveStart.SetValid(false); + + it->second->ClearTimer(); + m_tags.erase(it++); + } + } +} + +bool CEpg::InfoTagNow(CEpgInfoTag &tag, bool bUpdateIfNeeded /* = true */) +{ + CSingleLock lock(m_critSection); + if (m_nowActiveStart.IsValid()) + { + map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.find(m_nowActiveStart); + if (it != m_tags.end() && it->second->IsActive()) + { + tag = *it->second; + return true; + } + } + + if (bUpdateIfNeeded) + { + CDateTime lastActiveTag; + + /* one of the first items will always match if the list is sorted */ + for (map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + { + if (it->second->IsActive()) + { + m_nowActiveStart = it->first; + tag = *it->second; + return true; + } + else if (it->second->WasActive()) + lastActiveTag = it->first; + } + + /* there might be a gap between the last and next event. just return the last if found */ + map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.find(lastActiveTag); + if (it != m_tags.end()) + { + tag = *it->second; + return true; + } + } + + return false; +} + +bool CEpg::InfoTagNext(CEpgInfoTag &tag) +{ + CEpgInfoTag nowTag; + if (InfoTagNow(nowTag)) + { + CSingleLock lock(m_critSection); + map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.find(nowTag.StartAsUTC()); + if (it != m_tags.end() && ++it != m_tags.end()) + { + tag = *it->second; + return true; + } + } + else if (Size() > 0) + { + /* return the first event that is in the future */ + for (map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + { + if (it->second->InTheFuture()) + { + tag = *it->second; + return true; + } + } + } + + return false; +} + +bool CEpg::CheckPlayingEvent(void) +{ + bool bReturn(false); + CEpgInfoTag previousTag, newTag; + bool bGotPreviousTag = InfoTagNow(previousTag, false); + bool bGotCurrentTag = InfoTagNow(newTag); + + if (!bGotPreviousTag || (bGotCurrentTag && previousTag != newTag)) + { + NotifyObservers(ObservableMessageEpgActiveItem); + bReturn = true; + } + + return bReturn; +} + +CEpgInfoTagPtr CEpg::GetTag(const CDateTime &StartTime) const +{ + CSingleLock lock(m_critSection); + map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.find(StartTime); + if (it != m_tags.end()) + { + return it->second; + } + + CEpgInfoTagPtr empty; + return empty; +} + +CEpgInfoTagPtr CEpg::GetTagBetween(const CDateTime &beginTime, const CDateTime &endTime) const +{ + CSingleLock lock(m_critSection); + for (map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + { + if (it->second->StartAsUTC() >= beginTime && it->second->EndAsUTC() <= endTime) + return it->second; + } + + CEpgInfoTagPtr retVal; + return retVal; +} + +CEpgInfoTagPtr CEpg::GetTagAround(const CDateTime &time) const +{ + CSingleLock lock(m_critSection); + for (map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + { + if ((it->second->StartAsUTC() <= time) && (it->second->EndAsUTC() >= time)) + return it->second; + } + + CEpgInfoTagPtr retVal; + return retVal; +} + +void CEpg::AddEntry(const CEpgInfoTag &tag) +{ + CEpgInfoTagPtr newTag; + CSingleLock lock(m_critSection); + map<CDateTime, CEpgInfoTagPtr>::iterator itr = m_tags.find(tag.StartAsUTC()); + if (itr != m_tags.end()) + newTag = itr->second; + else + { + newTag = CEpgInfoTagPtr(new CEpgInfoTag(this, m_pvrChannel, m_strName, m_pvrChannel ? m_pvrChannel->IconPath() : StringUtils::EmptyString)); + m_tags.insert(make_pair(tag.StartAsUTC(), newTag)); + } + + if (newTag) + { + newTag->Update(tag); + newTag->SetPVRChannel(m_pvrChannel); + newTag->m_epg = this; + newTag->m_bChanged = false; + } +} + +bool CEpg::UpdateEntry(const CEpgInfoTag &tag, bool bUpdateDatabase /* = false */, bool bSort /* = true */) +{ + CEpgInfoTagPtr infoTag; + bool bReturn(false); + { + CSingleLock lock(m_critSection); + map<CDateTime, CEpgInfoTagPtr>::iterator it = m_tags.find(tag.StartAsUTC()); + bool bNewTag(false); + if (it != m_tags.end()) + { + infoTag = it->second; + } + else + { + /* create a new tag if no tag with this ID exists */ + infoTag = CEpgInfoTagPtr(new CEpgInfoTag(this, m_pvrChannel, m_strName, m_pvrChannel ? m_pvrChannel->IconPath() : StringUtils::EmptyString)); + infoTag->SetUniqueBroadcastID(tag.UniqueBroadcastID()); + m_tags.insert(make_pair(tag.StartAsUTC(), infoTag)); + bNewTag = true; + } + + infoTag->Update(tag, bNewTag); + infoTag->m_epg = this; + infoTag->m_pvrChannel = m_pvrChannel; + } + + if (bUpdateDatabase) + bReturn = infoTag->Persist(); + else + bReturn = true; + + return bReturn; +} + +bool CEpg::Load(void) +{ + bool bReturn(false); + CEpgDatabase *database = g_EpgContainer.GetDatabase(); + + if (!database || !database->IsOpen()) + { + CLog::Log(LOGERROR, "Epg - %s - could not open the database", __FUNCTION__); + return bReturn; + } + + CSingleLock lock(m_critSection); + int iEntriesLoaded = database->Get(*this); + if (iEntriesLoaded <= 0) + { + CLog::Log(LOGNOTICE, "Epg - %s - no database entries found for table '%s'.", + __FUNCTION__, m_strName.c_str()); + } + else + { + m_lastScanTime = GetLastScanTime(); + CLog::Log(LOGDEBUG, "Epg - %s - %d entries loaded for table '%s'.", + __FUNCTION__, (int) m_tags.size(), m_strName.c_str()); + bReturn = true; + } + + m_bLoaded = true; + + return bReturn; +} + +bool CEpg::UpdateEntries(const CEpg &epg, bool bStoreInDb /* = true */) +{ + bool bReturn(false); + CEpgDatabase *database = g_EpgContainer.GetDatabase(); + + if (epg.m_tags.size() > 0) + { + if (bStoreInDb) + { + if (!database || !database->IsOpen()) + { + CLog::Log(LOGERROR, "%s - could not open the database", __FUNCTION__); + return bReturn; + } + database->BeginTransaction(); + } + + { + CSingleLock lock(m_critSection); + CLog::Log(LOGDEBUG, "%s - %u entries in memory before merging", __FUNCTION__, m_tags.size()); + /* copy over tags */ + for (map<CDateTime, CEpgInfoTagPtr>::const_iterator it = epg.m_tags.begin(); it != epg.m_tags.end(); it++) + UpdateEntry(*it->second, bStoreInDb, false); + + CLog::Log(LOGDEBUG, "%s - %u entries in memory after merging and before fixing", __FUNCTION__, m_tags.size()); + FixOverlappingEvents(bStoreInDb); + CLog::Log(LOGDEBUG, "%s - %u entries in memory after fixing", __FUNCTION__, m_tags.size()); + /* update the last scan time of this table */ + m_lastScanTime = CDateTime::GetCurrentDateTime().GetAsUTCDateTime(); + + SetChanged(); + } + /* persist changes */ + if (bStoreInDb) + { + bReturn = database->CommitTransaction(); + if (bReturn) + Persist(true); + } + else + bReturn = true; + } + else + { + if (bStoreInDb) + bReturn = Persist(true); + else + bReturn = true; + } + + NotifyObservers(ObservableMessageEpg); + + return bReturn; +} + +CDateTime CEpg::GetLastScanTime(void) +{ + CDateTime lastScanTime; + { + CSingleLock lock(m_critSection); + + if (!m_lastScanTime.IsValid()) + { + if (!g_guiSettings.GetBool("epg.ignoredbforclient")) + { + CEpgDatabase *database = g_EpgContainer.GetDatabase(); + CDateTime dtReturn; dtReturn.SetValid(false); + + if (database && database->IsOpen()) + database->GetLastEpgScanTime(m_iEpgID, &m_lastScanTime); + } + + if (!m_lastScanTime.IsValid()) + { + m_lastScanTime.SetDateTime(0, 0, 0, 0, 0, 0); + m_lastScanTime.SetValid(true); + } + } + lastScanTime = m_lastScanTime; + } + + return m_lastScanTime; +} + +bool CEpg::Update(const time_t start, const time_t end, int iUpdateTime, bool bForceUpdate /* = false */) +{ + bool bGrabSuccess(true); + bool bUpdate(false); + + /* load the entries from the db first */ + if (!m_bLoaded && !g_EpgContainer.IgnoreDB()) + Load(); + + /* clean up if needed */ + if (m_bLoaded) + Cleanup(); + + /* get the last update time from the database */ + CDateTime lastScanTime = GetLastScanTime(); + + if (!bForceUpdate) + { + /* check if we have to update */ + time_t iNow = 0; + time_t iLastUpdate = 0; + CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow); + lastScanTime.GetAsTime(iLastUpdate); + bUpdate = (iNow > iLastUpdate + iUpdateTime); + } + else + bUpdate = true; + + if (bUpdate) + bGrabSuccess = LoadFromClients(start, end); + + if (bGrabSuccess) + { + CPVRChannelPtr channel; + if (g_PVRManager.GetCurrentChannel(channel) && + channel->EpgID() == m_iEpgID) + g_PVRManager.ResetPlayingTag(); + m_bLoaded = true; + } + else + CLog::Log(LOGERROR, "EPG - %s - failed to update table '%s'", __FUNCTION__, Name().c_str()); + + CSingleLock lock(m_critSection); + m_bUpdatePending = false; + + return bGrabSuccess; +} + +int CEpg::Get(CFileItemList &results) const +{ + int iInitialSize = results.Size(); + + CSingleLock lock(m_critSection); + + for (map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + results.Add(CFileItemPtr(new CFileItem(*it->second))); + + return results.Size() - iInitialSize; +} + +int CEpg::Get(CFileItemList &results, const EpgSearchFilter &filter) const +{ + int iInitialSize = results.Size(); + + if (!HasValidEntries()) + return -1; + + CSingleLock lock(m_critSection); + + for (map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + { + if (filter.FilterEntry(*it->second)) + results.Add(CFileItemPtr(new CFileItem(*it->second))); + } + + return results.Size() - iInitialSize; +} + +bool CEpg::Persist(bool bUpdateLastScanTime /* = false */) +{ + if (g_guiSettings.GetBool("epg.ignoredbforclient")) + return true; + + CEpgDatabase *database = g_EpgContainer.GetDatabase(); + + if (!database || !database->IsOpen()) + { + CLog::Log(LOGERROR, "%s - could not open the database", __FUNCTION__); + return false; + } + + CEpg epgCopy; + { + CSingleLock lock(m_critSection); + epgCopy = *this; + m_bChanged = false; + m_bTagsChanged = false; + } + + database->BeginTransaction(); + + if (epgCopy.m_iEpgID <= 0 || epgCopy.m_bChanged) + { + int iId = database->Persist(epgCopy); + if (iId > 0) + { + epgCopy.m_iEpgID = iId; + epgCopy.m_bChanged = false; + if (m_iEpgID != epgCopy.m_iEpgID) + { + CSingleLock lock(m_critSection); + m_iEpgID = epgCopy.m_iEpgID; + } + } + } + + bool bReturn(true); + + if (bUpdateLastScanTime) + bReturn = database->PersistLastEpgScanTime(epgCopy.m_iEpgID); + + database->CommitTransaction(); + + return bReturn; +} + +CDateTime CEpg::GetFirstDate(void) const +{ + CDateTime first; + + CSingleLock lock(m_critSection); + if (m_tags.size() > 0) + first = m_tags.begin()->second->StartAsUTC(); + + return first; +} + +CDateTime CEpg::GetLastDate(void) const +{ + CDateTime last; + + CSingleLock lock(m_critSection); + if (m_tags.size() > 0) + last = m_tags.rbegin()->second->StartAsUTC(); + + return last; +} + +//@} + +/** @name Protected methods */ +//@{ + +bool CEpg::UpdateMetadata(const CEpg &epg, bool bUpdateDb /* = false */) +{ + bool bReturn = true; + CSingleLock lock(m_critSection); + + m_strName = epg.m_strName; + m_strScraperName = epg.m_strScraperName; + if (epg.m_pvrChannel) + SetChannel(epg.m_pvrChannel); + + if (bUpdateDb) + bReturn = Persist(); + + return bReturn; +} + +//@} + +/** @name Private methods */ +//@{ + +bool CEpg::FixOverlappingEvents(bool bUpdateDb /* = false */) +{ + bool bReturn(true); + CEpgInfoTagPtr previousTag, currentTag; + CEpgDatabase *database(NULL); + if (bUpdateDb) + { + database = g_EpgContainer.GetDatabase(); + if (!database || !database->IsOpen()) + { + CLog::Log(LOGERROR, "%s - could not open the database", __FUNCTION__); + return false; + } + } + + for (map<CDateTime, CEpgInfoTagPtr>::iterator it = m_tags.begin(); it != m_tags.end(); it != m_tags.end() ? it++ : it) + { + if (!previousTag) + { + previousTag = it->second; + continue; + } + currentTag = it->second; + + if (previousTag->EndAsUTC() >= currentTag->EndAsUTC()) + { + // delete the current tag. it's completely overlapped + if (bUpdateDb) + bReturn &= database->Delete(*currentTag); + + if (m_nowActiveStart == it->first) + m_nowActiveStart.SetValid(false); + + it->second->ClearTimer(); + m_tags.erase(it++); + } + else if (previousTag->EndAsUTC() > currentTag->StartAsUTC()) + { + currentTag->SetStartFromUTC(previousTag->EndAsUTC()); + if (bUpdateDb) + bReturn &= currentTag->Persist(); + + previousTag = it->second; + } + else if (previousTag->EndAsUTC() < currentTag->StartAsUTC()) + { + time_t start, end, middle; + previousTag->EndAsUTC().GetAsTime(start); + currentTag->StartAsUTC().GetAsTime(end); + middle = start + ((end - start) / 2); + CDateTime newTime(middle); + + currentTag->SetStartFromUTC(newTime); + previousTag->SetEndFromUTC(newTime); + + if (m_nowActiveStart == it->first) + m_nowActiveStart = currentTag->StartAsUTC(); + + if (bUpdateDb) + { + bReturn &= currentTag->Persist(); + bReturn &= previousTag->Persist(); + } + + previousTag = it->second; + } + else + { + previousTag = it->second; + } + } + + return bReturn; +} + +bool CEpg::UpdateFromScraper(time_t start, time_t end) +{ + bool bGrabSuccess = false; + if (ScraperName() == "client") + { + CPVRChannelPtr channel = Channel(); + if (!channel) + CLog::Log(LOGINFO, "%s - channel not found, can't update", __FUNCTION__); + else if (!channel->EPGEnabled()) + CLog::Log(LOGINFO, "%s - EPG updating disabled in the channel configuration", __FUNCTION__); + else if (!g_PVRClients->SupportsEPG(channel->ClientID())) + CLog::Log(LOGINFO, "%s - the backend for channel '%s' on client '%i' does not support EPGs", __FUNCTION__, channel->ChannelName().c_str(), channel->ClientID()); + else + { + CLog::Log(LOGINFO, "%s - updating EPG for channel '%s' from client '%i'", __FUNCTION__, channel->ChannelName().c_str(), channel->ClientID()); + bGrabSuccess = (g_PVRClients->GetEPGForChannel(*channel, this, start, end) == PVR_ERROR_NO_ERROR); + } + } + else if (m_strScraperName.IsEmpty()) /* no grabber defined */ + CLog::Log(LOGERROR, "EPG - %s - no EPG scraper defined for table '%s'", __FUNCTION__, m_strName.c_str()); + else + { + CLog::Log(LOGINFO, "EPG - %s - updating EPG table '%s' with scraper '%s'", __FUNCTION__, m_strName.c_str(), m_strScraperName.c_str()); + CLog::Log(LOGERROR, "loading the EPG via scraper has not been implemented yet"); + // TODO: Add Support for Web EPG Scrapers here + } + + return bGrabSuccess; +} + +bool CEpg::PersistTags(void) const +{ + bool bReturn = false; + CEpgDatabase *database = g_EpgContainer.GetDatabase(); + + if (!database || !database->IsOpen()) + { + CLog::Log(LOGERROR, "EPG - %s - could not load the database", __FUNCTION__); + return bReturn; + } + + CDateTime first = GetFirstDate(); + CDateTime last = GetLastDate(); + + time_t iStart(0), iEnd(0); + if (first.IsValid()) + first.GetAsTime(iStart); + if (last.IsValid()) + last.GetAsTime(iEnd); + database->Delete(*this, iStart, iEnd); + + if (m_tags.size() > 0) + { + for (map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + { + if (!it->second->Persist()) + { + CLog::Log(LOGERROR, "failed to persist epg tag %d", it->second->UniqueBroadcastID()); + bReturn = false; + } + } + } + else + { + /* Return true if we have no tags, so that no error is logged */ + bReturn = true; + } + + return bReturn; +} + +//@} + +const CStdString &CEpg::ConvertGenreIdToString(int iID, int iSubID) +{ + unsigned int iLabelId = 19499; + switch (iID) + { + case EPG_EVENT_CONTENTMASK_MOVIEDRAMA: + iLabelId = (iSubID <= 8) ? 19500 + iSubID : 19500; + break; + case EPG_EVENT_CONTENTMASK_NEWSCURRENTAFFAIRS: + iLabelId = (iSubID <= 4) ? 19516 + iSubID : 19516; + break; + case EPG_EVENT_CONTENTMASK_SHOW: + iLabelId = (iSubID <= 3) ? 19532 + iSubID : 19532; + break; + case EPG_EVENT_CONTENTMASK_SPORTS: + iLabelId = (iSubID <= 11) ? 19548 + iSubID : 19548; + break; + case EPG_EVENT_CONTENTMASK_CHILDRENYOUTH: + iLabelId = (iSubID <= 5) ? 19564 + iSubID : 19564; + break; + case EPG_EVENT_CONTENTMASK_MUSICBALLETDANCE: + iLabelId = (iSubID <= 6) ? 19580 + iSubID : 19580; + break; + case EPG_EVENT_CONTENTMASK_ARTSCULTURE: + iLabelId = (iSubID <= 11) ? 19596 + iSubID : 19596; + break; + case EPG_EVENT_CONTENTMASK_SOCIALPOLITICALECONOMICS: + iLabelId = (iSubID <= 3) ? 19612 + iSubID : 19612; + break; + case EPG_EVENT_CONTENTMASK_EDUCATIONALSCIENCE: + iLabelId = (iSubID <= 7) ? 19628 + iSubID : 19628; + break; + case EPG_EVENT_CONTENTMASK_LEISUREHOBBIES: + iLabelId = (iSubID <= 7) ? 19644 + iSubID : 19644; + break; + case EPG_EVENT_CONTENTMASK_SPECIAL: + iLabelId = (iSubID <= 3) ? 19660 + iSubID : 19660; + break; + case EPG_EVENT_CONTENTMASK_USERDEFINED: + iLabelId = (iSubID <= 3) ? 19676 + iSubID : 19676; + break; + default: + break; + } + + return g_localizeStrings.Get(iLabelId); +} + +bool CEpg::UpdateEntry(const EPG_TAG *data, bool bUpdateDatabase /* = false */) +{ + if (!data) + return false; + + CEpgInfoTag tag(*data); + return UpdateEntry(tag, bUpdateDatabase); +} + +bool CEpg::IsRadio(void) const +{ + CPVRChannelPtr channel = Channel(); + return channel ? channel->IsRadio() : false; +} + +bool CEpg::IsRemovableTag(const CEpgInfoTag &tag) const +{ + return !tag.HasTimer(); +} + +bool CEpg::LoadFromClients(time_t start, time_t end) +{ + bool bReturn(false); + CPVRChannelPtr channel = Channel(); + if (channel) + { + CEpg tmpEpg(channel); + if (tmpEpg.UpdateFromScraper(start, end)) + bReturn = UpdateEntries(tmpEpg, !g_guiSettings.GetBool("epg.ignoredbforclient")); + } + else + { + CEpg tmpEpg(m_iEpgID, m_strName, m_strScraperName); + if (tmpEpg.UpdateFromScraper(start, end)) + bReturn = UpdateEntries(tmpEpg, !g_guiSettings.GetBool("epg.ignoredbforclient")); + } + + return bReturn; +} + +CEpgInfoTagPtr CEpg::GetNextEvent(const CEpgInfoTag& tag) const +{ + CSingleLock lock(m_critSection); + map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.find(tag.StartAsUTC()); + if (it != m_tags.end() && ++it != m_tags.end()) + return it->second; + + CEpgInfoTagPtr retVal; + return retVal; +} + +CEpgInfoTagPtr CEpg::GetPreviousEvent(const CEpgInfoTag& tag) const +{ + CSingleLock lock(m_critSection); + map<CDateTime, CEpgInfoTagPtr>::const_iterator it = m_tags.find(tag.StartAsUTC()); + if (it != m_tags.end() && it != m_tags.begin()) + { + it--; + return it->second; + } + + CEpgInfoTagPtr retVal; + return retVal; +} + +CPVRChannelPtr CEpg::Channel(void) const +{ + CSingleLock lock(m_critSection); + return m_pvrChannel; +} + +int CEpg::ChannelID(void) const +{ + CSingleLock lock(m_critSection); + return m_pvrChannel ? m_pvrChannel->ChannelID() : -1; +} + +int CEpg::ChannelNumber(void) const +{ + CSingleLock lock(m_critSection); + return m_pvrChannel ? m_pvrChannel->ChannelNumber() : -1; +} + +void CEpg::SetChannel(PVR::CPVRChannelPtr channel) +{ + CSingleLock lock(m_critSection); + if (m_pvrChannel != channel) + { + if (channel) + SetName(channel->ChannelName()); + m_pvrChannel = channel; + for (map<CDateTime, CEpgInfoTagPtr>::iterator it = m_tags.begin(); it != m_tags.end(); it++) + it->second->SetPVRChannel(m_pvrChannel); + } +} + +bool CEpg::HasPVRChannel(void) const +{ + CSingleLock lock(m_critSection); + return m_pvrChannel; +} + +bool CEpg::UpdatePending(void) const +{ + CSingleLock lock(m_critSection); + return m_bUpdatePending; +} + +size_t CEpg::Size(void) const +{ + CSingleLock lock(m_critSection); + return m_tags.size(); +} diff --git a/xbmc/epg/Epg.h b/xbmc/epg/Epg.h new file mode 100644 index 0000000000..20b523cdc8 --- /dev/null +++ b/xbmc/epg/Epg.h @@ -0,0 +1,367 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "FileItem.h" + +#include "threads/CriticalSection.h" + +#include "EpgInfoTag.h" +#include "EpgSearchFilter.h" +#include "utils/Observer.h" +#include "pvr/channels/PVRChannel.h" + +namespace PVR +{ + class CPVRChannel; +} + +/** EPG container for CEpgInfoTag instances */ +namespace EPG +{ + class CEpg : public Observable + { + friend class CEpgDatabase; + friend class CEpgInfoTag; + + public: + /*! + * @brief Create a new EPG instance. + * @param iEpgID The ID of this table or <= 0 to create a new ID. + * @param strName The name of this table. + * @param strScraperName The name of the scraper to use. + * @param bLoadedFromDb True if this table was loaded from the database, false otherwise. + */ + CEpg(int iEpgID, const CStdString &strName = "", const CStdString &strScraperName = "", bool bLoadedFromDb = false); + + /*! + * @brief Create a new EPG instance for a channel. + * @param channel The channel to create the EPG for. + * @param bLoadedFromDb True if this table was loaded from the database, false otherwise. + */ + CEpg(PVR::CPVRChannelPtr channel, bool bLoadedFromDb = false); + + /*! + * @brief Destroy this EPG instance. + */ + virtual ~CEpg(void); + + CEpg &operator =(const CEpg &right); + + /*! + * @brief Update this table's info with the given info. Doesn't change the EpgID. + * @param epg The new info. + * @param bUpdateDb If true, persist the changes. + * @return True if the update was successful, false otherwise. + */ + bool UpdateMetadata(const CEpg &epg, bool bUpdateDb = false); + + /*! + * @brief Load all entries for this table from the database. + * @return True if any entries were loaded, false otherwise. + */ + bool Load(void); + + /*! + * @brief The channel this EPG belongs to. + * @return The channel this EPG belongs to + */ + PVR::CPVRChannelPtr Channel(void) const; + + int ChannelID(void) const; + int ChannelNumber(void) const; + + /*! + * @brief Channel the channel tag linked to this EPG table. + * @param channel The new channel tag. + */ + void SetChannel(PVR::CPVRChannelPtr channel); + + /*! + * @brief Get the name of the scraper to use for this table. + * @return The name of the scraper to use for this table. + */ + const CStdString &ScraperName(void) const { return m_strScraperName; } + + /*! + * @brief Change the name of the scraper to use. + * @param strScraperName The new scraper. + */ + void SetScraperName(const CStdString &strScraperName); + + /*! + * @brief Specify if EPG should be manually updated on the next cycle + * @param bUpdatePending True if EPG should be manually updated + */ + void SetUpdatePending(bool bUpdatePending = true); + + /*! + * @brief Returns if there is a manual update pending for this EPG + * @returns True if there are is a manual update pending, false otherwise + */ + bool UpdatePending(void) const; + + /*! + * @brief Clear the current tag and schedule manual update + */ + void ForceUpdate(void); + + /*! + * @brief Get the name of this table. + * @return The name of this table. + */ + const CStdString &Name(void) const { return m_strName; } + + /*! + * @brief Changed the name of this table. + * @param strName The new name. + */ + void SetName(const CStdString &strName); + + /*! + * @brief Get the database ID of this table. + * @return The database ID of this table. + */ + int EpgID(void) const { return m_iEpgID; } + + /*! + * @brief Check whether this EPG contains valid entries. + * @return True if it has valid entries, false if not. + */ + bool HasValidEntries(void) const; + + /*! + * @return True if this EPG has a PVR channel set, false otherwise. + */ + bool HasPVRChannel(void) const; + + /*! + * @brief Remove all entries from this EPG that finished before the given time + * and that have no timers set. + * @param Time Delete entries with an end time before this time in UTC. + */ + void Cleanup(const CDateTime &Time); + + /*! + * @brief Remove all entries from this EPG that finished before the given time + * and that have no timers set. + */ + void Cleanup(void); + + /*! + * @brief Remove all entries from this EPG. + */ + void Clear(void); + + /*! + * @brief Get the event that is occurring now. + * @return The current event. + */ + bool InfoTagNow(CEpgInfoTag &tag, bool bUpdateIfNeeded = true); + + /*! + * @brief Get the event that will occur next. + * @return The next event. + */ + bool InfoTagNext(CEpgInfoTag &tag); + + /*! + * @brief Get the event that occurs at the given time. + * @param time The time in UTC to find the event for. + * @return The found tag or NULL if it wasn't found. + */ + CEpgInfoTagPtr GetTagAround(const CDateTime &time) const; + + /*! + * Get the event that occurs between the given begin and end time. + * @param beginTime Minimum start time in UTC of the event. + * @param endTime Maximum end time in UTC of the event. + * @return The found tag or NULL if it wasn't found. + */ + CEpgInfoTagPtr GetTagBetween(const CDateTime &beginTime, const CDateTime &endTime) const; + + /*! + * @brief Get the infotag with the given ID. + * + * Get the infotag with the given ID. + * If it wasn't found, try finding the event with the given start time + * + * @param uniqueID The unique ID of the event to find. + * @param beginTime The start time in UTC of the event to find if it wasn't found by it's unique ID. + * @return The found tag or NULL if it wasn't found. + */ + CEpgInfoTagPtr GetTag(const CDateTime &beginTime) const; + + /*! + * @brief Update an entry in this EPG. + * @param tag The tag to update. + * @param bUpdateDatabase If set to true, this event will be persisted in the database. + * @param bSort If set to false, epg entries will not be sorted after updating; used for mass updates + * @return True if it was updated successfully, false otherwise. + */ + bool UpdateEntry(const CEpgInfoTag &tag, bool bUpdateDatabase = false, bool bSort = true); + + /*! + * @brief Update the EPG from 'start' till 'end'. + * @param start The start time. + * @param end The end time. + * @param iUpdateTime Update the table after the given amount of time has passed. + * @param bForceUpdate Force update from client even if it's not the time to + * @return True if the update was successful, false otherwise. + */ + bool Update(const time_t start, const time_t end, int iUpdateTime, bool bForceUpdate = false); + + /*! + * @brief Get all EPG entries. + * @param results The file list to store the results in. + * @return The amount of entries that were added. + */ + int Get(CFileItemList &results) const; + + /*! + * @brief Get all EPG entries that and apply a filter. + * @param results The file list to store the results in. + * @param filter The filter to apply. + * @return The amount of entries that were added. + */ + int Get(CFileItemList &results, const EpgSearchFilter &filter) const; + + /*! + * @brief Persist this table in the database. + * @param bUpdateLastScanTime True to update the last scan time in the db, false otherwise. + * @return True if the table was persisted, false otherwise. + */ + bool Persist(bool bUpdateLastScanTime = false); + + /*! + * @brief Get the start time of the first entry in this table. + * @return The first date in UTC. + */ + CDateTime GetFirstDate(void) const; + + /*! + * @brief Get the end time of the last entry in this table. + * @return The last date in UTC. + */ + CDateTime GetLastDate(void) const; + + /*! + * @return The last time this table was scanned. + */ + CDateTime GetLastScanTime(void); + + /*! + * @brief Notify observers when the currently active tag changed. + */ + bool CheckPlayingEvent(void); + + /*! + * @brief Convert a genre id and subid to a human readable name. + * @param iID The genre ID. + * @param iSubID The genre sub ID. + * @return A human readable name. + */ + static const CStdString &ConvertGenreIdToString(int iID, int iSubID); + + /*! + * @brief Update an entry in this EPG. + * @param data The tag to update. + * @param bUpdateDatabase If set to true, this event will be persisted in the database. + * @return True if it was updated successfully, false otherwise. + */ + bool UpdateEntry(const EPG_TAG *data, bool bUpdateDatabase = false); + + /*! + * @return True if this is an EPG table for a radio channel, false otherwise. + */ + bool IsRadio(void) const; + + CEpgInfoTagPtr GetNextEvent(const CEpgInfoTag& tag) const; + CEpgInfoTagPtr GetPreviousEvent(const CEpgInfoTag& tag) const; + + size_t Size(void) const; + protected: + CEpg(void); + + /*! + * @brief Update the EPG from a scraper set in the channel tag. + * TODO: not implemented yet for non-pvr EPGs + * @param start Get entries with a start date after this time. + * @param end Get entries with an end date before this time. + * @return True if the update was successful, false otherwise. + */ + bool UpdateFromScraper(time_t start, time_t end); + + /*! + * @brief Persist all tags in this container. + * @return True if all tags were persisted, false otherwise. + */ + bool PersistTags(void) const; + + /*! + * @brief Fix overlapping events from the tables. + * @param bUpdateDb If set to yes, any changes to tags during fixing will be persisted to database + * @return True if anything changed, false otherwise. + */ + bool FixOverlappingEvents(bool bUpdateDb = false); + + /*! + * @brief Add an infotag to this container. + * @param tag The tag to add. + */ + void AddEntry(const CEpgInfoTag &tag); + + /*! + * @brief Load all EPG entries from clients into a temporary table and update this table with the contents of that temporary table. + * @param start Only get entries after this start time. Use 0 to get all entries before "end". + * @param end Only get entries before this end time. Use 0 to get all entries after "begin". If both "begin" and "end" are 0, all entries will be updated. + * @return True if the update was successful, false otherwise. + */ + bool LoadFromClients(time_t start, time_t end); + + /*! + * @brief Update the contents of this table with the contents provided in "epg" + * @param epg The updated contents. + * @param bStoreInDb True to store the updated contents in the db, false otherwise. + * @return True if the update was successful, false otherwise. + */ + bool UpdateEntries(const CEpg &epg, bool bStoreInDb = true); + + bool IsRemovableTag(const EPG::CEpgInfoTag &tag) const; + + std::map<CDateTime, CEpgInfoTagPtr> m_tags; + bool m_bChanged; /*!< true if anything changed that needs to be persisted, false otherwise */ + bool m_bTagsChanged; /*!< true when any tags are changed and not persisted, false otherwise */ + bool m_bLoaded; /*!< true when the initial entries have been loaded */ + bool m_bUpdatePending; /*!< true if manual update is pending */ + int m_iEpgID; /*!< the database ID of this table */ + CStdString m_strName; /*!< the name of this table */ + CStdString m_strScraperName; /*!< the name of the scraper to use */ + CDateTime m_nowActiveStart; /*!< the start time of the tag that is currently active */ + + CDateTime m_lastScanTime; /*!< the last time the EPG has been updated */ + + PVR::CPVRChannelPtr m_pvrChannel; /*!< the channel this EPG belongs to */ + + CCriticalSection m_critSection; /*!< critical section for changes in this table */ + }; +} diff --git a/xbmc/epg/EpgContainer.cpp b/xbmc/epg/EpgContainer.cpp new file mode 100644 index 0000000000..ecf32390d3 --- /dev/null +++ b/xbmc/epg/EpgContainer.cpp @@ -0,0 +1,657 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "Application.h" +#include "threads/SingleLock.h" +#include "settings/AdvancedSettings.h" +#include "settings/GUISettings.h" +#include "dialogs/GUIDialogExtendedProgressBar.h" +#include "dialogs/GUIDialogProgress.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "utils/log.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/timers/PVRTimers.h" + +#include "EpgContainer.h" +#include "Epg.h" +#include "EpgInfoTag.h" +#include "EpgSearchFilter.h" + +using namespace std; +using namespace EPG; +using namespace PVR; + +typedef std::map<int, CEpg*>::iterator EPGITR; + +CEpgContainer::CEpgContainer(void) : + CThread("EPG updater") +{ + m_progressHandle = NULL; + m_bStop = true; + m_bIsUpdating = false; + m_bIsInitialising = true; + m_iNextEpgId = 0; + m_bPreventUpdates = false; + m_updateEvent.Reset(); + m_bLoaded = false; + m_bHasPendingUpdates = false; +} + +CEpgContainer::~CEpgContainer(void) +{ + Unload(); +} + +CEpgContainer &CEpgContainer::Get(void) +{ + static CEpgContainer epgInstance; + return epgInstance; +} + +void CEpgContainer::Unload(void) +{ + Stop(); + Clear(false); +} + +unsigned int CEpgContainer::NextEpgId(void) +{ + CSingleLock lock(m_critSection); + return ++m_iNextEpgId; +} + +void CEpgContainer::Clear(bool bClearDb /* = false */) +{ + /* make sure the update thread is stopped */ + bool bThreadRunning = !m_bStop; + if (bThreadRunning && !Stop()) + { + CLog::Log(LOGERROR, "%s - cannot stop the update thread", __FUNCTION__); + return; + } + + { + CSingleLock lock(m_critSection); + /* clear all epg tables and remove pointers to epg tables on channels */ + for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + { + it->second->UnregisterObserver(this); + delete it->second; + } + m_epgs.clear(); + m_iNextEpgUpdate = 0; + m_bIsInitialising = true; + } + + /* clear the database entries */ + if (bClearDb && !m_bIgnoreDbForClient) + { + if (!m_database.IsOpen()) + m_database.Open(); + + if (m_database.IsOpen()) + m_database.DeleteEpg(); + } + + SetChanged(); + NotifyObservers(ObservableMessageEpgContainer, true); + + if (bThreadRunning) + Start(); +} + +void CEpgContainer::Start(void) +{ + CSingleLock lock(m_critSection); + + if (!m_database.IsOpen()) + m_database.Open(); + + m_bIsInitialising = true; + m_bStop = false; + g_guiSettings.RegisterObserver(this); + LoadSettings(); + + m_iNextEpgUpdate = 0; + m_iNextEpgActiveTagCheck = 0; + + Create(); + SetPriority(-1); + CLog::Log(LOGNOTICE, "%s - EPG thread started", __FUNCTION__); +} + +bool CEpgContainer::Stop(void) +{ + StopThread(); + + if (m_database.IsOpen()) + m_database.Close(); + + return true; +} + +void CEpgContainer::Notify(const Observable &obs, const ObservableMessage msg) +{ + /* settings were updated */ + if (msg == ObservableMessageGuiSettings) + LoadSettings(); + else + { + SetChanged(); + NotifyObservers(msg); + } +} + +void CEpgContainer::LoadFromDB(void) +{ + bool bLoaded(true); + unsigned int iCounter(0); + if (!m_bIgnoreDbForClient && m_database.IsOpen()) + { + ShowProgressDialog(false); + + m_database.DeleteOldEpgEntries(); + m_database.Get(*this); + + for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + { + if (InterruptUpdate()) + { + bLoaded = false; + break; + } + UpdateProgressDialog(++iCounter, m_epgs.size(), it->second->Name()); + it->second->Load(); + } + + CloseProgressDialog(); + } + + CSingleLock lock(m_critSection); + m_bLoaded = bLoaded; +} + +bool CEpgContainer::PersistTables(void) +{ + return m_database.Persist(*this); +} + +bool CEpgContainer::PersistAll(void) +{ + bool bReturn(true); + CSingleLock lock(m_critSection); + for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + { + CEpg *epg = it->second; + lock.Leave(); + if (epg) + bReturn &= epg->Persist(false); + lock.Enter(); + } + + return bReturn; +} + +void CEpgContainer::Process(void) +{ + time_t iNow = 0; + + bool bUpdateEpg(true); + bool bHasPendingUpdates(false); + + if (!m_bLoaded) + { + LoadFromDB(); + CheckPlayingEvents(); + } + + while (!m_bStop && !g_application.m_bStop) + { + CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow); + { + CSingleLock lock(m_critSection); + bUpdateEpg = (iNow >= m_iNextEpgUpdate); + } + + /* update the EPG */ + if (!InterruptUpdate() && bUpdateEpg && UpdateEPG()) + m_bIsInitialising = false; + + /* clean up old entries */ + if (!m_bStop && iNow >= m_iLastEpgCleanup) + RemoveOldEntries(); + + /* check for pending manual EPG updates */ + if (!m_bStop) + { + { + CSingleLock lock(m_critSection); + bHasPendingUpdates = m_bHasPendingUpdates; + } + + if (bHasPendingUpdates) + UpdateEPG(true); + } + + /* check for updated active tag */ + if (!m_bStop) + CheckPlayingEvents(); + + Sleep(1000); + } + + g_guiSettings.UnregisterObserver(this); +} + +CEpg *CEpgContainer::GetById(int iEpgId) const +{ + if (iEpgId < 0) + return 0; + + CSingleLock lock(m_critSection); + map<unsigned int, CEpg *>::const_iterator it = m_epgs.find((unsigned int) iEpgId); + return it != m_epgs.end() ? it->second : NULL; +} + +CEpg *CEpgContainer::GetByChannel(const CPVRChannel &channel) const +{ + CSingleLock lock(m_critSection); + for (map<unsigned int, CEpg *>::const_iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + if (channel.ChannelID() == it->second->ChannelID()) + return it->second; + + return NULL; +} + +void CEpgContainer::InsertFromDatabase(int iEpgID, const CStdString &strName, const CStdString &strScraperName) +{ + CEpg *epg = new CEpg(iEpgID, strName, strScraperName, true); + if (epg) + { + m_epgs.insert(make_pair(iEpgID, epg)); + SetChanged(); + epg->RegisterObserver(this); + } +} + +CEpg *CEpgContainer::CreateChannelEpg(CPVRChannelPtr channel) +{ + if (!channel) + return NULL; + + WaitForUpdateFinish(true); + CSingleLock lock(m_critSection); + + CEpg *epg(NULL); + if (channel->EpgID() > 0) + epg = GetById(channel->EpgID()); + + if (!epg) + { + epg = CreateEpg(NextEpgId()); + m_epgs.insert(make_pair((unsigned int)epg->EpgID(), epg)); + SetChanged(); + epg->RegisterObserver(this); + } + + if (epg) + epg->SetChannel(channel); + + m_bPreventUpdates = false; + CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iNextEpgUpdate); + + NotifyObservers(ObservableMessageEpgContainer); + + return epg; +} + +bool CEpgContainer::LoadSettings(void) +{ + m_bIgnoreDbForClient = g_guiSettings.GetBool("epg.ignoredbforclient"); + m_iUpdateTime = g_guiSettings.GetInt ("epg.epgupdate") * 60; + m_iDisplayTime = g_guiSettings.GetInt ("epg.daystodisplay") * 24 * 60 * 60; + + return true; +} + +bool CEpgContainer::RemoveOldEntries(void) +{ + CLog::Log(LOGINFO, "EpgContainer - %s - removing old EPG entries", + __FUNCTION__); + + CDateTime now = CDateTime::GetUTCDateTime() - + CDateTimeSpan(0, g_advancedSettings.m_iEpgLingerTime / 60, g_advancedSettings.m_iEpgLingerTime % 60, 0); + + /* call Cleanup() on all known EPG tables */ + for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + it->second->Cleanup(now); + + /* remove the old entries from the database */ + if (!m_bIgnoreDbForClient && m_database.IsOpen()) + m_database.DeleteOldEpgEntries(); + + CSingleLock lock(m_critSection); + CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iLastEpgCleanup); + m_iLastEpgCleanup += g_advancedSettings.m_iEpgCleanupInterval; + + return true; +} + +CEpg *CEpgContainer::CreateEpg(int iEpgId) +{ + if (g_PVRManager.IsStarted()) + { + CPVRChannelPtr channel = g_PVRChannelGroups->GetChannelByEpgId(iEpgId); + if (channel) + { + CEpg *epg = new CEpg(channel, true); + channel->Persist(); + return epg; + } + } + + return new CEpg(iEpgId); +} + +bool CEpgContainer::DeleteEpg(const CEpg &epg, bool bDeleteFromDatabase /* = false */) +{ + if (epg.EpgID() < 0) + return false; + + CSingleLock lock(m_critSection); + + map<unsigned int, CEpg *>::iterator it = m_epgs.find((unsigned int)epg.EpgID()); + if (it == m_epgs.end()) + return false; + + if (bDeleteFromDatabase && !m_bIgnoreDbForClient && m_database.IsOpen()) + m_database.Delete(*it->second); + + it->second->UnregisterObserver(this); + delete it->second; + m_epgs.erase(it); + + return true; +} + +void CEpgContainer::CloseProgressDialog(void) +{ + if (m_progressHandle) + { + m_progressHandle->MarkFinished(); + m_progressHandle = NULL; + } +} + +void CEpgContainer::ShowProgressDialog(bool bUpdating /* = true */) +{ + if (!m_progressHandle) + { + CGUIDialogExtendedProgressBar *progressDialog = (CGUIDialogExtendedProgressBar *)g_windowManager.GetWindow(WINDOW_DIALOG_EXT_PROGRESS); + if (progressDialog) + m_progressHandle = progressDialog->GetHandle(bUpdating ? g_localizeStrings.Get(19004) : g_localizeStrings.Get(19250)); + } +} + +void CEpgContainer::UpdateProgressDialog(int iCurrent, int iMax, const CStdString &strText) +{ + if (!m_progressHandle) + ShowProgressDialog(); + + if (m_progressHandle) + { + m_progressHandle->SetProgress(iCurrent, iMax); + m_progressHandle->SetText(strText); + } +} + +bool CEpgContainer::InterruptUpdate(void) const +{ + bool bReturn(false); + CSingleLock lock(m_critSection); + bReturn = g_application.m_bStop || m_bStop || m_bPreventUpdates; + lock.Leave(); + + return bReturn || + (g_guiSettings.GetBool("epg.preventupdateswhileplayingtv") && + g_PVRManager.IsStarted() && + g_PVRManager.IsPlaying()); +} + +void CEpgContainer::WaitForUpdateFinish(bool bInterrupt /* = true */) +{ + { + CSingleLock lock(m_critSection); + if (bInterrupt) + m_bPreventUpdates = true; + + if (!m_bIsUpdating) + return; + + m_updateEvent.Reset(); + } + + m_updateEvent.Wait(); +} + +bool CEpgContainer::UpdateEPG(bool bOnlyPending /* = false */) +{ + bool bInterrupted(false); + unsigned int iUpdatedTables(0); + bool bShowProgress(false); + + /* set start and end time */ + time_t start; + time_t end; + CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(start); + end = start + m_iDisplayTime; + start -= g_advancedSettings.m_iEpgLingerTime * 60; + bShowProgress = g_advancedSettings.m_bEpgDisplayUpdatePopup && (m_bIsInitialising || g_advancedSettings.m_bEpgDisplayIncrementalUpdatePopup); + + { + CSingleLock lock(m_critSection); + if (m_bIsUpdating || InterruptUpdate()) + return false; + m_bIsUpdating = true; + } + + if (bShowProgress && !bOnlyPending) + ShowProgressDialog(); + + if (!m_bIgnoreDbForClient && !m_database.IsOpen()) + { + CLog::Log(LOGERROR, "EpgContainer - %s - could not open the database", __FUNCTION__); + + CSingleLock lock(m_critSection); + m_bIsUpdating = false; + m_updateEvent.Set(); + + if (bShowProgress && !bOnlyPending) + CloseProgressDialog(); + + return false; + } + + /* load or update all EPG tables */ + CEpg *epg; + unsigned int iCounter(0); + for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + { + if (InterruptUpdate()) + { + bInterrupted = true; + break; + } + + epg = it->second; + if (!epg) + continue; + + if (bShowProgress && !bOnlyPending) + UpdateProgressDialog(++iCounter, m_epgs.size(), epg->Name()); + + if ((!bOnlyPending || epg->UpdatePending()) && epg->Update(start, end, m_iUpdateTime, bOnlyPending)) + ++iUpdatedTables; + } + + if (!bInterrupted) + { + if (bInterrupted) + { + /* the update has been interrupted. try again later */ + time_t iNow; + CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow); + m_iNextEpgUpdate = iNow + g_advancedSettings.m_iEpgRetryInterruptedUpdateInterval; + } + else + { + CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iNextEpgUpdate); + m_iNextEpgUpdate += g_advancedSettings.m_iEpgUpdateCheckInterval; + m_bHasPendingUpdates = false; + } + } + + if (bShowProgress && !bOnlyPending) + CloseProgressDialog(); + + /* notify observers */ + if (iUpdatedTables > 0) + { + SetChanged(); + NotifyObservers(ObservableMessageEpgContainer, true); + } + + CSingleLock lock(m_critSection); + m_bIsUpdating = false; + m_updateEvent.Set(); + + return !bInterrupted; +} + +int CEpgContainer::GetEPGAll(CFileItemList &results) +{ + int iInitialSize = results.Size(); + + CSingleLock lock(m_critSection); + for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + it->second->Get(results); + + return results.Size() - iInitialSize; +} + +const CDateTime CEpgContainer::GetFirstEPGDate(void) +{ + CDateTime returnValue; + + CSingleLock lock(m_critSection); + for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + { + lock.Leave(); + CDateTime entry = it->second->GetFirstDate(); + if (entry.IsValid() && (!returnValue.IsValid() || entry < returnValue)) + returnValue = entry; + lock.Enter(); + } + + return returnValue; +} + +const CDateTime CEpgContainer::GetLastEPGDate(void) +{ + CDateTime returnValue; + + CSingleLock lock(m_critSection); + for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + { + lock.Leave(); + CDateTime entry = it->second->GetLastDate(); + if (entry.IsValid() && (!returnValue.IsValid() || entry > returnValue)) + returnValue = entry; + lock.Enter(); + } + + return returnValue; +} + +int CEpgContainer::GetEPGSearch(CFileItemList &results, const EpgSearchFilter &filter) +{ + int iInitialSize = results.Size(); + + /* get filtered results from all tables */ + { + CSingleLock lock(m_critSection); + for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + it->second->Get(results, filter); + } + + /* remove duplicate entries */ + if (filter.m_bPreventRepeats) + EpgSearchFilter::RemoveDuplicates(results); + + return results.Size() - iInitialSize; +} + +bool CEpgContainer::CheckPlayingEvents(void) +{ + bool bReturn(false); + time_t iNow; + CSingleLock lock(m_critSection); + + CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow); + if (iNow >= m_iNextEpgActiveTagCheck) + { + bool bFoundChanges(false); + CSingleLock lock(m_critSection); + + for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + bFoundChanges = it->second->CheckPlayingEvent() || bFoundChanges; + CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iNextEpgActiveTagCheck); + m_iNextEpgActiveTagCheck += g_advancedSettings.m_iEpgActiveTagCheckInterval; + + if (bFoundChanges) + { + SetChanged(); + NotifyObservers(ObservableMessageEpgActiveItem, true); + } + + /* pvr tags always start on the full minute */ + if (g_PVRManager.IsStarted()) + m_iNextEpgActiveTagCheck -= m_iNextEpgActiveTagCheck % 60; + + bReturn = true; + } + + return bReturn; +} + +bool CEpgContainer::IsInitialising(void) const +{ + CSingleLock lock(m_critSection); + return m_bIsInitialising; +} + +void CEpgContainer::SetHasPendingUpdates(bool bHasPendingUpdates /* = true */) +{ + CSingleLock lock(m_critSection); + m_bHasPendingUpdates = bHasPendingUpdates; +} diff --git a/xbmc/epg/EpgContainer.h b/xbmc/epg/EpgContainer.h new file mode 100644 index 0000000000..ca4b786f6e --- /dev/null +++ b/xbmc/epg/EpgContainer.h @@ -0,0 +1,292 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "XBDateTime.h" +#include "threads/CriticalSection.h" +#include "threads/Thread.h" +#include "utils/Observer.h" + +#include "Epg.h" +#include "EpgDatabase.h" + +#include <map> + +class CFileItemList; +class CGUIDialogProgressBarHandle; + +namespace EPG +{ + #define g_EpgContainer CEpgContainer::Get() + + class CEpgContainer : public Observer, + public Observable, + private CThread + { + friend class CEpgDatabase; + + public: + /*! + * @brief Create a new EPG table container. + */ + CEpgContainer(void); + + /*! + * @brief Destroy this instance. + */ + virtual ~CEpgContainer(void); + + /*! + * @return An instance of this singleton. + */ + static CEpgContainer &Get(void); + + /*! + * @brief Get a pointer to the database instance. + * @return A pointer to the database instance. + */ + CEpgDatabase *GetDatabase(void) { return &m_database; } + + /*! + * @brief Start the EPG update thread. + */ + virtual void Start(void); + + /*! + * @brief Stop the EPG update thread. + * @return + */ + virtual bool Stop(void); + + /*! + * @brief Clear all EPG entries. + * @param bClearDb Clear the database too if true. + */ + virtual void Clear(bool bClearDb = false); + + /*! + * @brief Stop the update thread and unload all data. + */ + virtual void Unload(void); + + /*! + * @brief Clear the EPG and all it's database entries. + */ + virtual void Reset(void) { Clear(true); } + + /*! + * @brief Delete an EPG table from this container. + * @param epg The table to delete. + * @param bDeleteFromDatabase Delete this table from the database too if true. + * @return + */ + virtual bool DeleteEpg(const CEpg &epg, bool bDeleteFromDatabase = false); + + /*! + * @brief Process a notification from an observable. + * @param obs The observable that sent the update. + * @param msg The update message. + */ + virtual void Notify(const Observable &obs, const ObservableMessage msg); + + CEpg *CreateChannelEpg(PVR::CPVRChannelPtr channel); + + /*! + * @brief Get all EPG tables and apply a filter. + * @param results The fileitem list to store the results in. + * @param filter The filter to apply. + * @return The amount of entries that were added. + */ + virtual int GetEPGSearch(CFileItemList &results, const EpgSearchFilter &filter); + + /*! + * @brief Get all EPG tables. + * @param results The fileitem list to store the results in. + * @return The amount of entries that were added. + */ + virtual int GetEPGAll(CFileItemList &results); + + /*! + * @brief Get the start time of the first entry. + * @return The start time. + */ + virtual const CDateTime GetFirstEPGDate(void); + + /*! + * @brief Get the end time of the last entry. + * @return The end time. + */ + virtual const CDateTime GetLastEPGDate(void); + + /*! + * @brief Get an EPG table given it's ID. + * @param iEpgId The database ID of the table. + * @return The table or NULL if it wasn't found. + */ + virtual CEpg *GetById(int iEpgId) const; + + /*! + * @brief Get an EPG table given a PVR channel. + * @param channel The channel to get the EPG table for. + * @return The table or NULL if it wasn't found. + */ + virtual CEpg *GetByChannel(const PVR::CPVRChannel &channel) const; + + /*! + * @brief Notify EPG table observers when the currently active tag changed. + * @return True if the check was done, false if it was not the right time to check + */ + virtual bool CheckPlayingEvents(void); + + /*! + * @brief The next EPG ID to be given to a table when the db isn't being used. + * @return The next ID. + */ + unsigned int NextEpgId(void); + + /*! + * @brief Close the progress bar if it's visible. + */ + virtual void CloseProgressDialog(void); + + /*! + * @brief Show the progress bar + * @param bUpdating True if updating epg entries, false if just loading them from db + */ + virtual void ShowProgressDialog(bool bUpdating = true); + + /*! + * @brief Update the progress bar. + * @param iCurrent The current position. + * @param iMax The maximum position. + * @param strText The text to display. + */ + virtual void UpdateProgressDialog(int iCurrent, int iMax, const CStdString &strText); + + /*! + * @return True to not to store EPG entries in the database. + */ + virtual bool IgnoreDB(void) const { return m_bIgnoreDbForClient; } + + /*! + * @brief Wait for an EPG update to finish. + * @param bInterrupt True to interrupt a running update. + */ + void WaitForUpdateFinish(bool bInterrupt = true); + + /*! + * @brief Set to true to prevent updates. + * @param bSetTo The new value. + */ + void PreventUpdates(bool bSetTo = true) { m_bPreventUpdates = bSetTo; } + + /*! + * @brief Notify EPG container that there are pending manual EPG updates + * @param bHasPendingUpdates The new value + */ + void SetHasPendingUpdates(bool bHasPendingUpdates = true); + + /*! + * @return True while being initialised. + */ + bool IsInitialising(void) const; + + /*! + * @brief Call Persist() on each table + * @return True when they all were persisted, false otherwise. + */ + bool PersistAll(void); + + bool PersistTables(void); + + protected: + /*! + * @brief Load the EPG settings. + * @return True if the settings were loaded successfully, false otherwise. + */ + virtual bool LoadSettings(void); + + /*! + * @brief Remove old EPG entries. + * @return True if the old entries were removed successfully, false otherwise. + */ + virtual bool RemoveOldEntries(void); + + /*! + * @brief Load and update the EPG data. + * @param bOnlyPending Only check and update EPG tables with pending manual updates + * @return True if the update has not been interrupted, false otherwise. + */ + virtual bool UpdateEPG(bool bOnlyPending = false); + + /*! + * @return True if a running update should be interrupted, false otherwise. + */ + virtual bool InterruptUpdate(void) const; + + /*! + * @brief Create a new EPG table. + * @param iEpgId The table ID or -1 to create a new one. + * @return The new table. + */ + virtual CEpg *CreateEpg(int iEpgId); + + /*! + * @brief EPG update thread + */ + virtual void Process(void); + + /*! + * @brief Load all tables from the database + */ + void LoadFromDB(void); + + void InsertFromDatabase(int iEpgID, const CStdString &strName, const CStdString &strScraperName); + + CEpgDatabase m_database; /*!< the EPG database */ + + /** @name Configuration */ + //@{ + bool m_bIgnoreDbForClient; /*!< don't save the EPG data in the database */ + int m_iDisplayTime; /*!< hours of EPG data to fetch */ + int m_iUpdateTime; /*!< update the full EPG after this period */ + //@} + + /** @name Class state properties */ + //@{ + bool m_bIsUpdating; /*!< true while an update is running */ + bool m_bIsInitialising; /*!< true while the epg manager hasn't loaded all tables */ + bool m_bLoaded; /*!< true after epg data is initially loaded from the database */ + bool m_bPreventUpdates; /*!< true to prevent EPG updates */ + bool m_bHasPendingUpdates; /*!< true if there are manual updates pending */ + time_t m_iLastEpgCleanup; /*!< the time the EPG was cleaned up */ + time_t m_iNextEpgUpdate; /*!< the time the EPG will be updated */ + time_t m_iNextEpgActiveTagCheck; /*!< the time the EPG will be checked for active tag updates */ + unsigned int m_iNextEpgId; /*!< the next epg ID that will be given to a new table when the db isn't being used */ + std::map<unsigned int, CEpg*> m_epgs; /*!< the EPGs in this container */ + //@} + + CGUIDialogProgressBarHandle * m_progressHandle; /*!< the progress dialog that is visible when updating the first time */ + CCriticalSection m_critSection; /*!< a critical section for changes to this container */ + CEvent m_updateEvent; /*!< trigger when an update finishes */ + }; +} diff --git a/xbmc/epg/EpgDatabase.cpp b/xbmc/epg/EpgDatabase.cpp new file mode 100644 index 0000000000..de81face22 --- /dev/null +++ b/xbmc/epg/EpgDatabase.cpp @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "dbwrappers/dataset.h" +#include "settings/AdvancedSettings.h" +#include "settings/VideoSettings.h" +#include "utils/log.h" +#include "threads/SingleLock.h" +#include "addons/include/xbmc_pvr_types.h" + +#include "EpgDatabase.h" +#include "EpgContainer.h" + +using namespace std; +using namespace dbiplus; +using namespace EPG; + +bool CEpgDatabase::Open(void) +{ + CSingleLock lock(m_critSection); + return CDatabase::Open(g_advancedSettings.m_databaseEpg); +} + +bool CEpgDatabase::CreateTables(void) +{ + bool bReturn(false); + CSingleLock lock(m_critSection); + + try + { + CDatabase::CreateTables(); + + BeginTransaction(); + + CLog::Log(LOGINFO, "EpgDB - %s - creating tables", __FUNCTION__); + + CLog::Log(LOGDEBUG, "EpgDB - %s - creating table 'epg'", __FUNCTION__); + m_pDS->exec( + "CREATE TABLE epg (" + "idEpg integer primary key, " + "sName varchar(64)," + "sScraperName varchar(32)" + ")" + ); + + CLog::Log(LOGDEBUG, "EpgDB - %s - creating table 'epgtags'", __FUNCTION__); + m_pDS->exec( + "CREATE TABLE epgtags (" + "idBroadcast integer primary key, " + "iBroadcastUid integer, " + "idEpg integer, " + "sTitle varchar(128), " + "sPlotOutline text, " + "sPlot text, " + "iStartTime integer, " + "iEndTime integer, " + "iGenreType integer, " + "iGenreSubType integer, " + "sGenre varchar(128), " + "iFirstAired integer, " + "iParentalRating integer, " + "iStarRating integer, " + "bNotify bool, " + "iSeriesId integer, " + "iEpisodeId integer, " + "iEpisodePart integer, " + "sEpisodeName varchar(128)" + ")" + ); + m_pDS->exec("CREATE UNIQUE INDEX idx_epg_idEpg_iStartTime on epgtags(idEpg, iStartTime desc);"); + m_pDS->exec("CREATE INDEX idx_epg_iEndTime on epgtags(iEndTime);"); + + CLog::Log(LOGDEBUG, "EpgDB - %s - creating table 'lastepgscan'", __FUNCTION__); + m_pDS->exec("CREATE TABLE lastepgscan (" + "idEpg integer primary key, " + "sLastScan varchar(20)" + ")" + ); + + CommitTransaction(); + + bReturn = true; + } + catch (...) + { + CLog::Log(LOGERROR, "EpgDB - %s - unable to create EPG tables:%i", + __FUNCTION__, (int)GetLastError()); + RollbackTransaction(); + bReturn = false; + } + + return bReturn; +} + +bool CEpgDatabase::UpdateOldVersion(int iVersion) +{ + bool bReturn = true; + + if (iVersion < 4) + { + CLog::Log(LOGERROR, "EpgDB - %s - updating from table versions < 4 not supported. please delete '%s'", __FUNCTION__, GetBaseDBName()); + return false; + } + + BeginTransaction(); + + try + { + if (iVersion < 5) + m_pDS->exec("ALTER TABLE epgtags ADD sGenre varchar(128);"); + if (iVersion < 6) + { + m_pDS->exec("DROP INDEX idx_epg_iBroadcastUid;"); + m_pDS->exec("DROP INDEX idx_epg_idEpg;"); + m_pDS->exec("DROP INDEX idx_epg_iStartTime;"); + m_pDS->exec("DROP INDEX idx_epg_iEndTime;"); + } + if (iVersion < 7) + { + m_pDS->exec("CREATE INDEX idx_epg_iEndTime on epgtags(iEndTime);"); + } + } + catch (...) + { + CLog::Log(LOGERROR, "Error attempting to update the database version!"); + bReturn = false; + } + + if (bReturn) + CommitTransaction(); + else + RollbackTransaction(); + + return bReturn; +} + +bool CEpgDatabase::DeleteEpg(void) +{ + bool bReturn(false); + CSingleLock lock(m_critSection); + CLog::Log(LOGDEBUG, "EpgDB - %s - deleting all EPG data from the database", __FUNCTION__); + + bReturn = DeleteValues("epg") || bReturn; + bReturn = DeleteValues("epgtags") || bReturn; + bReturn = DeleteValues("lastepgscan") || bReturn; + + return bReturn; +} + +bool CEpgDatabase::Delete(const CEpg &table, const time_t start /* = 0 */, const time_t end /* = 0 */) +{ + /* invalid channel */ + if (table.EpgID() <= 0) + { + CLog::Log(LOGERROR, "EpgDB - %s - invalid channel id: %d", + __FUNCTION__, table.EpgID()); + return false; + } + + CStdString strWhereClause; + strWhereClause = FormatSQL("idEpg = %u", table.EpgID()); + + if (start != 0) + strWhereClause.append(FormatSQL(" AND iStartTime >= %u", start).c_str()); + + if (end != 0) + strWhereClause.append(FormatSQL(" AND iEndTime <= %u", end).c_str()); + + CSingleLock lock(m_critSection); + return DeleteValues("epgtags", strWhereClause); +} + +bool CEpgDatabase::DeleteOldEpgEntries(void) +{ + time_t iCleanupTime; + CDateTime cleanupTime = CDateTime::GetCurrentDateTime().GetAsUTCDateTime() - + CDateTimeSpan(0, g_advancedSettings.m_iEpgLingerTime / 60, g_advancedSettings.m_iEpgLingerTime % 60, 0); + cleanupTime.GetAsTime(iCleanupTime); + + CStdString strWhereClause = FormatSQL("iEndTime < %u", iCleanupTime); + + CSingleLock lock(m_critSection); + return DeleteValues("epgtags", strWhereClause); +} + +bool CEpgDatabase::Delete(const CEpgInfoTag &tag) +{ + /* tag without a database ID was not persisted */ + if (tag.BroadcastId() <= 0) + return false; + + CStdString strWhereClause = FormatSQL("idBroadcast = %u", tag.BroadcastId()); + + CSingleLock lock(m_critSection); + return DeleteValues("epgtags", strWhereClause); +} + +int CEpgDatabase::Get(CEpgContainer &container) +{ + int iReturn(-1); + CSingleLock lock(m_critSection); + + CStdString strQuery = FormatSQL("SELECT idEpg, sName, sScraperName FROM epg;"); + if (ResultQuery(strQuery)) + { + iReturn = 0; + + try + { + while (!m_pDS->eof()) + { + int iEpgID = m_pDS->fv("idEpg").get_asInt(); + CStdString strName = m_pDS->fv("sName").get_asString().c_str(); + CStdString strScraperName = m_pDS->fv("sScraperName").get_asString().c_str(); + + container.InsertFromDatabase(iEpgID, strName, strScraperName); + ++iReturn; + m_pDS->next(); + } + m_pDS->close(); + } + catch (...) + { + CLog::Log(LOGERROR, "%s - couldn't load EPG data from the database", __FUNCTION__); + } + } + + return iReturn; +} + +int CEpgDatabase::Get(CEpg &epg) +{ + int iReturn(-1); + CSingleLock lock(m_critSection); + + CStdString strQuery = FormatSQL("SELECT * FROM epgtags WHERE idEpg = %u;", epg.EpgID()); + if (ResultQuery(strQuery)) + { + iReturn = 0; + try + { + while (!m_pDS->eof()) + { + CEpgInfoTag newTag; + + time_t iStartTime, iEndTime, iFirstAired; + iStartTime = (time_t) m_pDS->fv("iStartTime").get_asInt(); + CDateTime startTime(iStartTime); + newTag.m_startTime = startTime; + + iEndTime = (time_t) m_pDS->fv("iEndTime").get_asInt(); + CDateTime endTime(iEndTime); + newTag.m_endTime = endTime; + + iFirstAired = (time_t) m_pDS->fv("iFirstAired").get_asInt(); + CDateTime firstAired(iFirstAired); + newTag.m_firstAired = firstAired; + + newTag.m_iUniqueBroadcastID = m_pDS->fv("iBroadcastUid").get_asInt(); + newTag.m_iBroadcastId = m_pDS->fv("idBroadcast").get_asInt(); + newTag.m_strTitle = m_pDS->fv("sTitle").get_asString().c_str(); + newTag.m_strPlotOutline = m_pDS->fv("sPlotOutline").get_asString().c_str(); + newTag.m_strPlot = m_pDS->fv("sPlot").get_asString().c_str(); + newTag.m_iGenreType = m_pDS->fv("iGenreType").get_asInt(); + newTag.m_iGenreSubType = m_pDS->fv("iGenreSubType").get_asInt(); + newTag.m_genre = StringUtils::Split(m_pDS->fv("sGenre").get_asString().c_str(), g_advancedSettings.m_videoItemSeparator); + newTag.m_iParentalRating = m_pDS->fv("iParentalRating").get_asInt(); + newTag.m_iStarRating = m_pDS->fv("iStarRating").get_asInt(); + newTag.m_bNotify = m_pDS->fv("bNotify").get_asBool(); + newTag.m_iEpisodeNumber = m_pDS->fv("iEpisodeId").get_asInt(); + newTag.m_iEpisodePart = m_pDS->fv("iEpisodePart").get_asInt(); + newTag.m_strEpisodeName = m_pDS->fv("sEpisodeName").get_asString().c_str(); + newTag.m_iSeriesNumber = m_pDS->fv("iSeriesId").get_asInt(); + + epg.AddEntry(newTag); + ++iReturn; + + m_pDS->next(); + } + m_pDS->close(); + } + catch (...) + { + CLog::Log(LOGERROR, "%s - couldn't load EPG data from the database", __FUNCTION__); + } + } + return iReturn; +} + +bool CEpgDatabase::GetLastEpgScanTime(int iEpgId, CDateTime *lastScan) +{ + bool bReturn = false; + CStdString strWhereClause = FormatSQL("idEpg = %u", iEpgId); + CSingleLock lock(m_critSection); + CStdString strValue = GetSingleValue("lastepgscan", "sLastScan", strWhereClause); + + if (!strValue.IsEmpty()) + { + lastScan->SetFromDBDateTime(strValue.c_str()); + bReturn = true; + } + else + { + lastScan->SetValid(false); + } + + return bReturn; +} + +bool CEpgDatabase::PersistLastEpgScanTime(int iEpgId /* = 0 */, bool bQueueWrite /* = false */) +{ + CStdString strQuery = FormatSQL("REPLACE INTO lastepgscan(idEpg, sLastScan) VALUES (%u, '%s');", + iEpgId, CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsDBDateTime().c_str()); + + CSingleLock lock(m_critSection); + return bQueueWrite ? QueueInsertQuery(strQuery) : ExecuteQuery(strQuery); +} + +bool CEpgDatabase::Persist(const CEpgContainer &epg) +{ + for (map<unsigned int, CEpg *>::const_iterator it = epg.m_epgs.begin(); it != epg.m_epgs.end(); it++) + { + CEpg *epg = it->second; + if (epg) + Persist(*epg, true); + } + + return CommitInsertQueries(); +} + +int CEpgDatabase::Persist(const CEpg &epg, bool bQueueWrite /* = false */) +{ + int iReturn(-1); + + CStdString strQuery; + if (epg.EpgID() > 0) + strQuery = FormatSQL("REPLACE INTO epg (idEpg, sName, sScraperName) " + "VALUES (%u, '%s', '%s');", epg.EpgID(), epg.Name().c_str(), epg.ScraperName().c_str()); + else + strQuery = FormatSQL("INSERT INTO epg (sName, sScraperName) " + "VALUES ('%s', '%s');", epg.Name().c_str(), epg.ScraperName().c_str()); + + CSingleLock lock(m_critSection); + if (bQueueWrite) + { + if (QueueInsertQuery(strQuery)) + iReturn = epg.EpgID() <= 0 ? 0 : epg.EpgID(); + } + else + { + if (ExecuteQuery(strQuery)) + iReturn = epg.EpgID() <= 0 ? (int) m_pDS->lastinsertid() : epg.EpgID(); + } + + return iReturn; +} + +int CEpgDatabase::Persist(const CEpgInfoTag &tag, bool bSingleUpdate /* = true */) +{ + int iReturn(-1); + + if (tag.EpgID() <= 0) + { + CLog::Log(LOGERROR, "%s - tag '%s' does not have a valid table", __FUNCTION__, tag.Title(true).c_str()); + return iReturn; + } + + time_t iStartTime, iEndTime, iFirstAired; + tag.StartAsUTC().GetAsTime(iStartTime); + tag.EndAsUTC().GetAsTime(iEndTime); + tag.FirstAiredAsUTC().GetAsTime(iFirstAired); + + int iBroadcastId = tag.BroadcastId(); + CSingleLock lock(m_critSection); + CStdString strQuery; + + /* Only store the genre string when needed */ + CStdString strGenre = (tag.GenreType() == EPG_GENRE_USE_STRING) ? StringUtils::Join(tag.Genre(), g_advancedSettings.m_videoItemSeparator) : ""; + + if (iBroadcastId < 0) + { + strQuery = FormatSQL("INSERT INTO epgtags (idEpg, iStartTime, " + "iEndTime, sTitle, sPlotOutline, sPlot, iGenreType, iGenreSubType, sGenre, " + "iFirstAired, iParentalRating, iStarRating, bNotify, iSeriesId, " + "iEpisodeId, iEpisodePart, sEpisodeName, iBroadcastUid) " + "VALUES (%u, %u, %u, '%s', '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i, %i, %i, '%s', %i);", + tag.EpgID(), iStartTime, iEndTime, + tag.Title(true).c_str(), tag.PlotOutline(true).c_str(), tag.Plot(true).c_str(), tag.GenreType(), tag.GenreSubType(), strGenre.c_str(), + iFirstAired, tag.ParentalRating(), tag.StarRating(), tag.Notify(), + tag.SeriesNum(), tag.EpisodeNum(), tag.EpisodePart(), tag.EpisodeName().c_str(), + tag.UniqueBroadcastID()); + } + else + { + strQuery = FormatSQL("REPLACE INTO epgtags (idEpg, iStartTime, " + "iEndTime, sTitle, sPlotOutline, sPlot, iGenreType, iGenreSubType, sGenre, " + "iFirstAired, iParentalRating, iStarRating, bNotify, iSeriesId, " + "iEpisodeId, iEpisodePart, sEpisodeName, iBroadcastUid, idBroadcast) " + "VALUES (%u, %u, %u, '%s', '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i, %i, %i, '%s', %i, %i);", + tag.EpgID(), iStartTime, iEndTime, + tag.Title(true).c_str(), tag.PlotOutline(true).c_str(), tag.Plot(true).c_str(), tag.GenreType(), tag.GenreSubType(), strGenre.c_str(), + iFirstAired, tag.ParentalRating(), tag.StarRating(), tag.Notify(), + tag.SeriesNum(), tag.EpisodeNum(), tag.EpisodePart(), tag.EpisodeName().c_str(), + tag.UniqueBroadcastID(), iBroadcastId); + } + + if (bSingleUpdate) + { + if (ExecuteQuery(strQuery)) + iReturn = (int) m_pDS->lastinsertid(); + } + else + { + QueueInsertQuery(strQuery); + iReturn = 0; + } + + return iReturn; +} diff --git a/xbmc/epg/EpgDatabase.h b/xbmc/epg/EpgDatabase.h new file mode 100644 index 0000000000..1bde7a84e1 --- /dev/null +++ b/xbmc/epg/EpgDatabase.h @@ -0,0 +1,163 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "dbwrappers/Database.h" +#include "XBDateTime.h" +#include "threads/CriticalSection.h" + +namespace EPG +{ + class CEpg; + class CEpgInfoTag; + class CEpgContainer; + + /** The EPG database */ + + class CEpgDatabase : public CDatabase + { + public: + /*! + * @brief Create a new instance of the EPG database. + */ + CEpgDatabase(void) {}; + + /*! + * @brief Destroy this instance. + */ + virtual ~CEpgDatabase(void) {}; + + /*! + * @brief Open the database. + * @return True if it was opened successfully, false otherwise. + */ + virtual bool Open(void); + + /*! + * @brief Get the minimal database version that is required to operate correctly. + * @return The minimal database version. + */ + virtual int GetMinVersion(void) const { return 7; }; + + /*! + * @brief Get the default sqlite database filename. + * @return The default filename. + */ + const char *GetBaseDBName(void) const { return "Epg"; }; + + /*! @name EPG methods */ + //@{ + + /*! + * @brief Remove all EPG information from the database + * @return True if the EPG information was erased, false otherwise. + */ + virtual bool DeleteEpg(void); + + /*! + * @brief Erase all EPG entries for a table. + * @param table The table to remove the EPG entries for. + * @param start Remove entries after this time if set. + * @param end Remove entries before this time if set. + * @return True if the entries were removed successfully, false otherwise. + */ + virtual bool Delete(const CEpg &table, const time_t start = 0, const time_t end = 0); + + /*! + * @brief Erase all EPG entries older than 1 day. + * @return True if the entries were removed successfully, false otherwise. + */ + virtual bool DeleteOldEpgEntries(void); + + /*! + * @brief Remove a single EPG entry. + * @param tag The entry to remove. + * @return True if it was removed successfully, false otherwise. + */ + virtual bool Delete(const CEpgInfoTag &tag); + + /*! + * @brief Get all EPG tables from the database. Does not get the EPG tables' entries. + * @param container The container to fill. + * @return The amount of entries that was added. + */ + virtual int Get(CEpgContainer &container); + + /*! + * @brief Get all EPG entries for a table. + * @param epg The EPG table to get the entries for. + * @return The amount of entries that was added. + */ + virtual int Get(CEpg &epg); + + /*! + * @brief Get the last stored EPG scan time. + * @param iEpgId The table to update the time for. Use 0 for a global value. + * @param lastScan The last scan time or -1 if it wasn't found. + * @return True if the time was fetched successfully, false otherwise. + */ + virtual bool GetLastEpgScanTime(int iEpgId, CDateTime *lastScan); + + /*! + * @brief Update the last scan time. + * @param iEpgId The table to update the time for. Use 0 for a global value. + * @param bQueueWrite Don't execute the query immediately but queue it if true. + * @return True if it was updated successfully, false otherwise. + */ + virtual bool PersistLastEpgScanTime(int iEpgId = 0, bool bQueueWrite = false); + + bool Persist(const CEpgContainer &epg); + + /*! + * @brief Persist an EPG table. It's entries are not persisted. + * @param epg The table to persist. + * @param bQueueWrite Don't execute the query immediately but queue it if true. + * @return The database ID of this entry or 0 if bSingleUpdate is false and the query was queued. + */ + virtual int Persist(const CEpg &epg, bool bQueueWrite = false); + + /*! + * @brief Persist an infotag. + * @param tag The tag to persist. + * @param bSingleUpdate If true, this is a single update and the query will be executed immediately. + * @return The database ID of this entry or 0 if bSingleUpdate is false and the query was queued. + */ + virtual int Persist(const CEpgInfoTag &tag, bool bSingleUpdate = true); + + //@} + + protected: + /*! + * @brief Create the EPG database tables. + * @return True if the tables were created successfully, false otherwise. + */ + virtual bool CreateTables(void); + + /*! + * @brief Update an old version of the database. + * @param version The version to update the database from. + * @return True if it was updated successfully, false otherwise. + */ + virtual bool UpdateOldVersion(int version); + + CCriticalSection m_critSection; + }; +} diff --git a/xbmc/epg/EpgInfoTag.cpp b/xbmc/epg/EpgInfoTag.cpp new file mode 100644 index 0000000000..f58f8c1e11 --- /dev/null +++ b/xbmc/epg/EpgInfoTag.cpp @@ -0,0 +1,982 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "guilib/LocalizeStrings.h" +#include "Epg.h" +#include "EpgInfoTag.h" +#include "EpgContainer.h" +#include "EpgDatabase.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/timers/PVRTimers.h" +#include "pvr/PVRManager.h" +#include "settings/AdvancedSettings.h" +#include "settings/GUISettings.h" +#include "utils/log.h" +#include "addons/include/xbmc_pvr_types.h" + +using namespace std; +using namespace EPG; +using namespace PVR; + +CEpgInfoTag::CEpgInfoTag(void) : + m_bNotify(false), + m_bChanged(false), + m_iBroadcastId(-1), + m_iGenreType(0), + m_iGenreSubType(0), + m_iParentalRating(0), + m_iStarRating(0), + m_iSeriesNumber(0), + m_iEpisodeNumber(0), + m_iEpisodePart(0), + m_iUniqueBroadcastID(-1), + m_epg(NULL) +{ + CPVRChannelPtr emptyChannel; + m_pvrChannel = emptyChannel; + + CPVRTimerInfoTagPtr emptyTimer; + m_timer = emptyTimer; +} + +CEpgInfoTag::CEpgInfoTag(CEpg *epg, PVR::CPVRChannelPtr pvrChannel, const CStdString &strTableName /* = StringUtils::EmptyString */, const CStdString &strIconPath /* = StringUtils::EmptyString */) : + m_bNotify(false), + m_bChanged(false), + m_iBroadcastId(-1), + m_iGenreType(0), + m_iGenreSubType(0), + m_iParentalRating(0), + m_iStarRating(0), + m_iSeriesNumber(0), + m_iEpisodeNumber(0), + m_iEpisodePart(0), + m_iUniqueBroadcastID(-1), + m_strIconPath(strIconPath), + m_epg(epg), + m_pvrChannel(pvrChannel) +{ + CPVRTimerInfoTagPtr emptyTimer; + m_timer = emptyTimer; +} + +CEpgInfoTag::CEpgInfoTag(const EPG_TAG &data) : + m_bNotify(false), + m_bChanged(false), + m_iBroadcastId(-1), + m_iGenreType(0), + m_iGenreSubType(0), + m_iParentalRating(0), + m_iStarRating(0), + m_iSeriesNumber(0), + m_iEpisodeNumber(0), + m_iEpisodePart(0), + m_iUniqueBroadcastID(-1), + m_epg(NULL) +{ + CPVRChannelPtr emptyChannel; + m_pvrChannel = emptyChannel; + + CPVRTimerInfoTagPtr emptyTimer; + m_timer = emptyTimer; + + Update(data); +} + +CEpgInfoTag::CEpgInfoTag(const CEpgInfoTag &tag) : + m_bNotify(tag.m_bNotify), + m_bChanged(tag.m_bChanged), + m_iBroadcastId(tag.m_iBroadcastId), + m_iGenreType(tag.m_iGenreType), + m_iGenreSubType(tag.m_iGenreSubType), + m_iParentalRating(tag.m_iParentalRating), + m_iStarRating(tag.m_iStarRating), + m_iSeriesNumber(tag.m_iSeriesNumber), + m_iEpisodeNumber(tag.m_iEpisodeNumber), + m_iEpisodePart(tag.m_iEpisodePart), + m_iUniqueBroadcastID(tag.m_iUniqueBroadcastID), + m_strTitle(tag.m_strTitle), + m_strPlotOutline(tag.m_strPlotOutline), + m_strPlot(tag.m_strPlot), + m_genre(tag.m_genre), + m_strEpisodeName(tag.m_strEpisodeName), + m_strIconPath(tag.m_strIconPath), + m_strFileNameAndPath(tag.m_strFileNameAndPath), + m_startTime(tag.m_startTime), + m_endTime(tag.m_endTime), + m_firstAired(tag.m_firstAired), + m_timer(tag.m_timer), + m_epg(tag.m_epg), + m_pvrChannel(tag.m_pvrChannel) +{ +} + +CEpgInfoTag::~CEpgInfoTag() +{ + ClearTimer(); +} + +bool CEpgInfoTag::operator ==(const CEpgInfoTag& right) const +{ + if (this == &right) return true; + + CSingleLock lock(m_critSection); + return (m_bNotify == right.m_bNotify && + m_bChanged == right.m_bChanged && + m_iBroadcastId == right.m_iBroadcastId && + m_iGenreType == right.m_iGenreType && + m_iGenreSubType == right.m_iGenreSubType && + m_iParentalRating == right.m_iParentalRating && + m_firstAired == right.m_firstAired && + m_iStarRating == right.m_iStarRating && + m_iSeriesNumber == right.m_iSeriesNumber && + m_iEpisodeNumber == right.m_iEpisodeNumber && + m_iEpisodePart == right.m_iEpisodePart && + m_iUniqueBroadcastID == right.m_iUniqueBroadcastID && + m_strTitle == right.m_strTitle && + m_strPlotOutline == right.m_strPlotOutline && + m_strPlot == right.m_strPlot && + m_genre == right.m_genre && + m_strEpisodeName == right.m_strEpisodeName && + m_strIconPath == right.m_strIconPath && + m_strFileNameAndPath == right.m_strFileNameAndPath && + m_startTime == right.m_startTime && + m_endTime == right.m_endTime && + m_pvrChannel == right.m_pvrChannel); +} + +bool CEpgInfoTag::operator !=(const CEpgInfoTag& right) const +{ + if (this == &right) return false; + + return !(*this == right); +} + +CEpgInfoTag &CEpgInfoTag::operator =(const CEpgInfoTag &other) +{ + CSingleLock lock(other.m_critSection); + + m_bNotify = other.m_bNotify; + m_bChanged = other.m_bChanged; + m_iBroadcastId = other.m_iBroadcastId; + m_iGenreType = other.m_iGenreType; + m_iGenreSubType = other.m_iGenreSubType; + m_iParentalRating = other.m_iParentalRating; + m_iStarRating = other.m_iStarRating; + m_iSeriesNumber = other.m_iSeriesNumber; + m_iEpisodeNumber = other.m_iEpisodeNumber; + m_iEpisodePart = other.m_iEpisodePart; + m_iUniqueBroadcastID = other.m_iUniqueBroadcastID; + m_strTitle = other.m_strTitle; + m_strPlotOutline = other.m_strPlotOutline; + m_strPlot = other.m_strPlot; + m_genre = other.m_genre; + m_strEpisodeName = other.m_strEpisodeName; + m_strIconPath = other.m_strIconPath; + m_strFileNameAndPath = other.m_strFileNameAndPath; + m_startTime = other.m_startTime; + m_endTime = other.m_endTime; + m_firstAired = other.m_firstAired; + m_timer = other.m_timer; + m_epg = other.m_epg; + m_pvrChannel = other.m_pvrChannel; + + return *this; +} + +bool CEpgInfoTag::Changed(void) const +{ + CSingleLock lock(m_critSection); + return m_bChanged; +} + +bool CEpgInfoTag::IsActive(void) const +{ + CDateTime now = CDateTime::GetUTCDateTime(); + CSingleLock lock(m_critSection); + return (m_startTime <= now && m_endTime > now); +} + +bool CEpgInfoTag::WasActive(void) const +{ + CDateTime now = CDateTime::GetUTCDateTime(); + CSingleLock lock(m_critSection); + return (m_endTime < now); +} + +bool CEpgInfoTag::InTheFuture(void) const +{ + CDateTime now = CDateTime::GetUTCDateTime(); + CSingleLock lock(m_critSection); + return (m_startTime > now); +} + +float CEpgInfoTag::ProgressPercentage(void) const +{ + float fReturn(0); + int iDuration; + time_t currentTime, startTime, endTime; + CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(currentTime); + + CSingleLock lock(m_critSection); + m_startTime.GetAsTime(startTime); + m_endTime.GetAsTime(endTime); + iDuration = endTime - startTime > 0 ? endTime - startTime : 3600; + + if (currentTime >= startTime && currentTime <= endTime) + fReturn = ((float) currentTime - startTime) / iDuration * 100; + else if (currentTime > endTime) + fReturn = 100; + + return fReturn; +} + +CEpgInfoTagPtr CEpgInfoTag::GetNextEvent(void) const +{ + return GetTable()->GetNextEvent(*this); +} + +CEpgInfoTagPtr CEpgInfoTag::GetPreviousEvent(void) const +{ + return GetTable()->GetPreviousEvent(*this); +} + +void CEpgInfoTag::SetUniqueBroadcastID(int iUniqueBroadcastID) +{ + bool bUpdate(false); + { + CSingleLock lock(m_critSection); + if (m_iUniqueBroadcastID != iUniqueBroadcastID) + { + m_iUniqueBroadcastID = iUniqueBroadcastID; + m_bChanged = true; + bUpdate = true; + } + } + if (bUpdate) + UpdatePath(); +} + +int CEpgInfoTag::UniqueBroadcastID(void) const +{ + CSingleLock lock(m_critSection); + return m_iUniqueBroadcastID; +} + +void CEpgInfoTag::SetBroadcastId(int iId) +{ + bool bUpdate(false); + { + CSingleLock lock(m_critSection); + if (m_iBroadcastId != iId) + { + m_iBroadcastId = iId; + m_bChanged = true; + bUpdate = true; + } + } + if (bUpdate) + UpdatePath(); +} + +int CEpgInfoTag::BroadcastId(void) const +{ + CSingleLock lock(m_critSection); + return m_iBroadcastId; +} + +CDateTime CEpgInfoTag::StartAsUTC(void) const +{ + CSingleLock lock(m_critSection); + return m_startTime; +} + +CDateTime CEpgInfoTag::StartAsLocalTime(void) const +{ + CDateTime retVal; + CSingleLock lock(m_critSection); + retVal.SetFromUTCDateTime(m_startTime); + return retVal; +} + +void CEpgInfoTag::SetStartFromUTC(const CDateTime &start) +{ + bool bUpdate(false); + { + CSingleLock lock(m_critSection); + if (m_startTime != start) + { + m_startTime = start; + m_bChanged = true; + bUpdate = true; + } + } + if (bUpdate) + UpdatePath(); +} + +void CEpgInfoTag::SetStartFromLocalTime(const CDateTime &start) +{ + CDateTime tmp = start.GetAsUTCDateTime(); + SetStartFromUTC(tmp); +} + +CDateTime CEpgInfoTag::EndAsUTC(void) const +{ + CDateTime retVal; + CSingleLock lock(m_critSection); + retVal = m_endTime; + return retVal; +} + +CDateTime CEpgInfoTag::EndAsLocalTime(void) const +{ + CDateTime retVal; + CSingleLock lock(m_critSection); + retVal.SetFromUTCDateTime(m_endTime); + return retVal; +} + +void CEpgInfoTag::SetEndFromUTC(const CDateTime &end) +{ + bool bUpdate(false); + { + CSingleLock lock(m_critSection); + if (m_endTime != end) + { + m_endTime = end; + m_bChanged = true; + bUpdate = true; + } + } + if (bUpdate) + UpdatePath(); +} + +void CEpgInfoTag::SetEndFromLocalTime(const CDateTime &end) +{ + CDateTime tmp = end.GetAsUTCDateTime(); + SetEndFromUTC(tmp); +} + +int CEpgInfoTag::GetDuration(void) const +{ + time_t start, end; + CSingleLock lock(m_critSection); + m_startTime.GetAsTime(start); + m_endTime.GetAsTime(end); + return end - start > 0 ? end - start : 3600; +} + +void CEpgInfoTag::SetTitle(const CStdString &strTitle) +{ + bool bUpdate(false); + { + CSingleLock lock(m_critSection); + if (m_strTitle != strTitle) + { + m_strTitle = strTitle; + m_bChanged = true; + bUpdate = true; + } + } + if (bUpdate) + UpdatePath(); +} + +CStdString CEpgInfoTag::Title(bool bOverrideParental /* = false */) const +{ + CStdString strTitle; + bool bParentalLocked(false); + + { + CSingleLock lock(m_critSection); + strTitle = m_strTitle; + if (m_pvrChannel) + bParentalLocked = g_PVRManager.IsParentalLocked(*m_pvrChannel); + } + + if (!bOverrideParental && bParentalLocked) + strTitle = g_localizeStrings.Get(19266); // parental locked + else if (strTitle.empty() && !g_guiSettings.GetBool("epg.hidenoinfoavailable")) + strTitle = g_localizeStrings.Get(19055); // no information available + + return strTitle; +} + +void CEpgInfoTag::SetPlotOutline(const CStdString &strPlotOutline) +{ + bool bUpdate(false); + { + CSingleLock lock(m_critSection); + if (m_strPlotOutline != strPlotOutline) + { + m_strPlotOutline = strPlotOutline; + m_bChanged = true; + bUpdate = true; + } + } + if (bUpdate) + UpdatePath(); +} + +CStdString CEpgInfoTag::PlotOutline(bool bOverrideParental /* = false */) const +{ + CStdString retVal; + CSingleLock lock(m_critSection); + if (bOverrideParental || !m_pvrChannel || !g_PVRManager.IsParentalLocked(*m_pvrChannel)) + retVal = m_strPlotOutline; + + return retVal; +} + +void CEpgInfoTag::SetPlot(const CStdString &strPlot) +{ + bool bUpdate(false); + { + CSingleLock lock(m_critSection); + CStdString strPlotClean = (m_strPlotOutline.length() > 0 && strPlot.Left(m_strPlotOutline.length()).Equals(m_strPlotOutline)) ? + strPlot.Right(strPlot.length() - m_strPlotOutline.length()) : + strPlot; + + if (m_strPlot != strPlotClean) + { + m_strPlot = strPlotClean; + m_bChanged = true; + bUpdate = true; + } + } + if (bUpdate) + UpdatePath(); +} + +CStdString CEpgInfoTag::Plot(bool bOverrideParental /* = false */) const +{ + CStdString retVal; + CSingleLock lock(m_critSection); + if (bOverrideParental || !m_pvrChannel || !g_PVRManager.IsParentalLocked(*m_pvrChannel)) + retVal = m_strPlot; + + return retVal; +} + +void CEpgInfoTag::SetGenre(int iID, int iSubID, const char* strGenre) +{ + bool bUpdate(false); + { + CSingleLock lock(m_critSection); + if (m_iGenreType != iID || m_iGenreSubType != iSubID) + { + m_iGenreType = iID; + m_iGenreSubType = iSubID; + if ((iID == EPG_GENRE_USE_STRING) && (strGenre != NULL) && (strlen(strGenre) > 0)) + { + /* Type and sub type are not given. No EPG color coding possible + * Use the provided genre description as backup. */ + m_genre = StringUtils::Split(strGenre, g_advancedSettings.m_videoItemSeparator); + } + else + { + /* Determine the genre description from the type and subtype IDs */ + m_genre = StringUtils::Split(CEpg::ConvertGenreIdToString(iID, iSubID), g_advancedSettings.m_videoItemSeparator); + } + m_bChanged = true; + bUpdate = true; + } + } + if (bUpdate) + UpdatePath(); +} + +int CEpgInfoTag::GenreType(void) const +{ + CSingleLock lock(m_critSection); + return m_iGenreType; +} + +int CEpgInfoTag::GenreSubType(void) const +{ + CSingleLock lock(m_critSection); + return m_iGenreSubType; +} + +const vector<string> CEpgInfoTag::Genre(void) const +{ + vector<string> retVal; + CSingleLock lock(m_critSection); + retVal = m_genre; + return retVal; +} + +CDateTime CEpgInfoTag::FirstAiredAsUTC(void) const +{ + CDateTime retVal; + CSingleLock lock(m_critSection); + retVal = m_firstAired; + return retVal; +} + +CDateTime CEpgInfoTag::FirstAiredAsLocalTime(void) const +{ + CDateTime retVal; + CSingleLock lock(m_critSection); + retVal.SetFromUTCDateTime(m_firstAired); + return retVal; +} + +void CEpgInfoTag::SetFirstAiredFromUTC(const CDateTime &firstAired) +{ + bool bUpdate(false); + { + CSingleLock lock(m_critSection); + if (m_firstAired != firstAired) + { + m_firstAired = firstAired; + m_bChanged = true; + bUpdate = true; + } + } + if (bUpdate) + UpdatePath(); +} + +void CEpgInfoTag::SetFirstAiredFromLocalTime(const CDateTime &firstAired) +{ + CDateTime tmp = firstAired.GetAsUTCDateTime(); + SetFirstAiredFromUTC(tmp); +} + +void CEpgInfoTag::SetParentalRating(int iParentalRating) +{ + bool bUpdate(false); + { + CSingleLock lock(m_critSection); + if (m_iParentalRating != iParentalRating) + { + m_iParentalRating = iParentalRating; + m_bChanged = true; + bUpdate = true; + } + } + if (bUpdate) + UpdatePath(); +} + +int CEpgInfoTag::ParentalRating(void) const +{ + CSingleLock lock(m_critSection); + return m_iParentalRating; +} + +void CEpgInfoTag::SetStarRating(int iStarRating) +{ + bool bUpdate(false); + { + CSingleLock lock(m_critSection); + if (m_iStarRating != iStarRating) + { + m_iStarRating = iStarRating; + m_bChanged = true; + bUpdate = true; + } + } + if (bUpdate) + UpdatePath(); +} + +int CEpgInfoTag::StarRating(void) const +{ + CSingleLock lock(m_critSection); + return m_iStarRating; +} + +void CEpgInfoTag::SetNotify(bool bNotify) +{ + bool bUpdate(false); + { + CSingleLock lock(m_critSection); + if (m_bNotify != bNotify) + { + m_bNotify = bNotify; + m_bChanged = true; + bUpdate = true; + } + } + if (bUpdate) + UpdatePath(); +} + +bool CEpgInfoTag::Notify(void) const +{ + CSingleLock lock(m_critSection); + return m_bNotify; +} + +void CEpgInfoTag::SetSeriesNum(int iSeriesNum) +{ + bool bUpdate(false); + { + CSingleLock lock(m_critSection); + if (m_iSeriesNumber != iSeriesNum) + { + m_iSeriesNumber = iSeriesNum; + m_bChanged = true; + bUpdate = true; + } + } + if (bUpdate) + UpdatePath(); +} + +int CEpgInfoTag::SeriesNum(void) const +{ + CSingleLock lock(m_critSection); + return m_iSeriesNumber; +} + +void CEpgInfoTag::SetEpisodeNum(int iEpisodeNum) +{ + bool bUpdate(false); + { + CSingleLock lock(m_critSection); + if (m_iEpisodeNumber != iEpisodeNum) + { + m_iEpisodeNumber = iEpisodeNum; + m_bChanged = true; + bUpdate = true; + } + } + if (bUpdate) + UpdatePath(); +} + +int CEpgInfoTag::EpisodeNum(void) const +{ + CSingleLock lock(m_critSection); + return m_iEpisodeNumber; +} + +void CEpgInfoTag::SetEpisodePart(int iEpisodePart) +{ + bool bUpdate(false); + { + CSingleLock lock(m_critSection); + if (m_iEpisodePart != iEpisodePart) + { + m_iEpisodePart = iEpisodePart; + m_bChanged = true; + bUpdate = true; + } + } + if (bUpdate) + UpdatePath(); +} + +int CEpgInfoTag::EpisodePart(void) const +{ + CSingleLock lock(m_critSection); + return m_iEpisodePart; +} + +void CEpgInfoTag::SetEpisodeName(const CStdString &strEpisodeName) +{ + bool bUpdate(false); + { + CSingleLock lock(m_critSection); + if (m_strEpisodeName != strEpisodeName) + { + m_strEpisodeName = strEpisodeName; + m_bChanged = true; + bUpdate = true; + } + } + if (bUpdate) + UpdatePath(); +} + +CStdString CEpgInfoTag::EpisodeName(void) const +{ + CStdString retVal; + CSingleLock lock(m_critSection); + retVal = m_strEpisodeName; + return retVal; +} + +void CEpgInfoTag::SetIcon(const CStdString &strIconPath) +{ + bool bUpdate(false); + { + CSingleLock lock(m_critSection); + if (m_strIconPath != strIconPath) + { + m_strIconPath = strIconPath; + m_bChanged = true; + bUpdate = true; + } + } + if (bUpdate) + UpdatePath(); +} + +CStdString CEpgInfoTag::Icon(void) const +{ + CStdString retVal; + + CSingleLock lock(m_critSection); + retVal = m_strIconPath; + return retVal; +} + +void CEpgInfoTag::SetPath(const CStdString &strFileNameAndPath) +{ + CSingleLock lock(m_critSection); + if (m_strFileNameAndPath != strFileNameAndPath) + { + m_strFileNameAndPath = strFileNameAndPath; + m_bChanged = true; + } +} + +CStdString CEpgInfoTag::Path(void) const +{ + string retVal; + CSingleLock lock(m_critSection); + retVal = m_strFileNameAndPath; + return retVal; +} + +//void CEpgInfoTag::SetTimer(CPVRTimerInfoTagPtr newTimer) +//{ +// CPVRTimerInfoTagPtr oldTimer; +// { +// CSingleLock lock(m_critSection); +// oldTimer = m_timer; +// m_timer = newTimer; +// } +// if (oldTimer) +// oldTimer->ClearEpgTag(); +//} + +bool CEpgInfoTag::HasTimer(void) const +{ + CSingleLock lock(m_critSection); + return m_timer != NULL; +} + +CPVRTimerInfoTagPtr CEpgInfoTag::Timer(void) const +{ + CSingleLock lock(m_critSection); + return m_timer; +} + +void CEpgInfoTag::SetPVRChannel(PVR::CPVRChannelPtr channel) +{ + CSingleLock lock(m_critSection); + m_pvrChannel = channel; +} + +bool CEpgInfoTag::HasPVRChannel(void) const +{ + CSingleLock lock(m_critSection); + return m_pvrChannel != NULL; +} + +int CEpgInfoTag::PVRChannelNumber(void) const +{ + CSingleLock lock(m_critSection); + return m_pvrChannel ? m_pvrChannel->ChannelNumber() : -1; +} + +CStdString CEpgInfoTag::PVRChannelName(void) const +{ + CStdString strReturn; + CSingleLock lock(m_critSection); + if (m_pvrChannel) + strReturn = m_pvrChannel->ChannelName(); + return strReturn; +} + +const PVR::CPVRChannelPtr CEpgInfoTag::ChannelTag(void) const +{ + CSingleLock lock(m_critSection); + return m_pvrChannel; +} + +void CEpgInfoTag::Update(const EPG_TAG &tag) +{ + CSingleLock lock(m_critSection); + SetStartFromUTC(tag.startTime + g_advancedSettings.m_iPVRTimeCorrection); + SetEndFromUTC(tag.endTime + g_advancedSettings.m_iPVRTimeCorrection); + SetTitle(tag.strTitle); + SetPlotOutline(tag.strPlotOutline); + SetPlot(tag.strPlot); + SetGenre(tag.iGenreType, tag.iGenreSubType, tag.strGenreDescription); + SetParentalRating(tag.iParentalRating); + SetUniqueBroadcastID(tag.iUniqueBroadcastId); + SetNotify(tag.bNotify); + SetFirstAiredFromUTC(tag.firstAired + g_advancedSettings.m_iPVRTimeCorrection); + SetEpisodeNum(tag.iEpisodeNumber); + SetEpisodePart(tag.iEpisodePartNumber); + SetEpisodeName(tag.strEpisodeName); + SetStarRating(tag.iStarRating); + SetIcon(tag.strIconPath); +} + +bool CEpgInfoTag::Update(const CEpgInfoTag &tag, bool bUpdateBroadcastId /* = true */) +{ + bool bChanged(false); + { + CSingleLock lock(m_critSection); + bChanged = ( + m_strTitle != tag.m_strTitle || + m_strPlotOutline != tag.m_strPlotOutline || + m_strPlot != tag.m_strPlot || + m_startTime != tag.m_startTime || + m_endTime != tag.m_endTime || + m_iGenreType != tag.m_iGenreType || + m_iGenreSubType != tag.m_iGenreSubType || + m_firstAired != tag.m_firstAired || + m_iParentalRating != tag.m_iParentalRating || + m_iStarRating != tag.m_iStarRating || + m_bNotify != tag.m_bNotify || + m_iEpisodeNumber != tag.m_iEpisodeNumber || + m_iEpisodePart != tag.m_iEpisodePart || + m_iSeriesNumber != tag.m_iSeriesNumber || + m_strEpisodeName != tag.m_strEpisodeName || + m_iUniqueBroadcastID != tag.m_iUniqueBroadcastID || + EpgID() != tag.EpgID() || + m_pvrChannel != tag.m_pvrChannel || + m_genre != tag.m_genre + ); + if (bUpdateBroadcastId) + bChanged = bChanged || m_iBroadcastId != tag.m_iBroadcastId; + + if (bChanged) + { + if (bUpdateBroadcastId) + m_iBroadcastId = tag.m_iBroadcastId; + + m_strTitle = tag.m_strTitle; + m_strPlotOutline = tag.m_strPlotOutline; + m_strPlot = tag.m_strPlot; + m_startTime = tag.m_startTime; + m_endTime = tag.m_endTime; + m_iGenreType = tag.m_iGenreType; + m_iGenreSubType = tag.m_iGenreSubType; + m_epg = tag.m_epg; + m_pvrChannel = tag.m_pvrChannel; + if (m_iGenreType == EPG_GENRE_USE_STRING) + { + /* No type/subtype. Use the provided description */ + m_genre = tag.m_genre; + } + else + { + /* Determine genre description by type/subtype */ + m_genre = StringUtils::Split(CEpg::ConvertGenreIdToString(tag.m_iGenreType, tag.m_iGenreSubType), g_advancedSettings.m_videoItemSeparator); + } + m_firstAired = tag.m_firstAired; + m_iParentalRating = tag.m_iParentalRating; + m_iStarRating = tag.m_iStarRating; + m_bNotify = tag.m_bNotify; + m_iEpisodeNumber = tag.m_iEpisodeNumber; + m_iEpisodePart = tag.m_iEpisodePart; + m_iSeriesNumber = tag.m_iSeriesNumber; + m_strEpisodeName = tag.m_strEpisodeName; + m_iUniqueBroadcastID = tag.m_iUniqueBroadcastID; + + m_bChanged = true; + } + } + if (bChanged) + UpdatePath(); + + return bChanged; +} + +bool CEpgInfoTag::Persist(bool bSingleUpdate /* = true */) +{ + bool bReturn = false; + CSingleLock lock(m_critSection); + if (!m_bChanged) + return true; + CLog::Log(LOGDEBUG, "Epg - %s - Infotag '%s' %s, persisting...", __FUNCTION__, m_strTitle.c_str(), m_iBroadcastId > 0 ? "has changes" : "is new"); + CEpgDatabase *database = g_EpgContainer.GetDatabase(); + if (!database || (bSingleUpdate && !database->IsOpen())) + { + CLog::Log(LOGERROR, "%s - could not open the database", __FUNCTION__); + return bReturn; + } + + int iId = database->Persist(*this, bSingleUpdate); + if (iId >= 0) + { + bReturn = true; + + if (iId > 0) + { + m_iBroadcastId = iId; + m_bChanged = false; + } + } + + return bReturn; +} + +void CEpgInfoTag::UpdatePath(void) +{ + CStdString path; + { + CSingleLock lock(m_critSection); + path.Format("pvr://guide/%04i/%s.epg", EpgID(), m_startTime.GetAsDBDateTime().c_str()); + } + + SetPath(path); +} + +const CEpg *CEpgInfoTag::GetTable() const +{ + return m_epg; +} + +const int CEpgInfoTag::EpgID(void) const +{ + return m_epg ? m_epg->EpgID() : -1; +} + +void CEpgInfoTag::SetTimer(CPVRTimerInfoTagPtr timer) +{ + CSingleLock lock(m_critSection); + m_timer = timer; +} + +void CEpgInfoTag::ClearTimer(void) +{ + CPVRTimerInfoTagPtr previousTag; + { + CSingleLock lock(m_critSection); + previousTag = m_timer; + CPVRTimerInfoTagPtr empty; + m_timer = empty; + } + + if (previousTag) + previousTag->ClearEpgTag(); +} diff --git a/xbmc/epg/EpgInfoTag.h b/xbmc/epg/EpgInfoTag.h new file mode 100644 index 0000000000..cd81a668b1 --- /dev/null +++ b/xbmc/epg/EpgInfoTag.h @@ -0,0 +1,457 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "addons/include/xbmc_pvr_types.h" +#include "XBDateTime.h" +#include "utils/StringUtils.h" +#include "pvr/channels/PVRChannel.h" +#include "pvr/timers/PVRTimerInfoTag.h" + +#include <boost/shared_ptr.hpp> + +/** an EPG info tag */ +namespace EPG +{ + class CEpg; + + class CEpgInfoTag; + typedef boost::shared_ptr<EPG::CEpgInfoTag> CEpgInfoTagPtr; + + class CEpgInfoTag + { + friend class CEpg; + friend class CEpgDatabase; + friend class PVR::CPVRTimerInfoTag; + + public: + CEpgInfoTag(void); + + /*! + * @brief Create a new empty event without a unique ID. + */ + CEpgInfoTag(CEpg *epg, PVR::CPVRChannelPtr pvrChannel, const CStdString &strTableName = StringUtils::EmptyString, const CStdString &strIconPath = StringUtils::EmptyString); + + /*! + * @brief Create a new EPG infotag with 'data' as content. + * @param data The tag's content. + */ + CEpgInfoTag(const EPG_TAG &data); + + /*! + * @brief Create a new EPG infotag with 'tag' as content. + * @param tag The tag's content. + */ + CEpgInfoTag(const CEpgInfoTag &tag); + virtual ~CEpgInfoTag(); + + bool operator ==(const CEpgInfoTag& right) const; + bool operator !=(const CEpgInfoTag& right) const; + CEpgInfoTag &operator =(const CEpgInfoTag &other); + + /*! + * @brief Check whether this tag has changed and unsaved values. + * @return True if it has unsaved values, false otherwise. + */ + bool Changed(void) const; + + /*! + * @brief Check if this event is currently active. + * @return True if it's active, false otherwise. + */ + bool IsActive(void) const; + + /*! + * @return True when this event has already passed, false otherwise. + */ + bool WasActive(void) const; + + /*! + * @return True when this event is in the future, false otherwise. + */ + bool InTheFuture(void) const; + + /*! + * @return The current progress of this tag. + */ + float ProgressPercentage(void) const; + + /*! + * @brief Get a pointer to the next event. Set by CEpg in a call to Sort() + * @return A pointer to the next event or NULL if it's not set. + */ + CEpgInfoTagPtr GetNextEvent(void) const; + + /*! + * @brief Get a pointer to the previous event. Set by CEpg in a call to Sort() + * @return A pointer to the previous event or NULL if it's not set. + */ + CEpgInfoTagPtr GetPreviousEvent(void) const; + + /*! + * @brief The table this event belongs to + * @return The table this event belongs to + */ + const CEpg *GetTable() const; + + const int EpgID(void) const; + + /*! + * @brief Change the unique broadcast ID of this event. + * @param iUniqueBroadcastId The new unique broadcast ID. + */ + void SetUniqueBroadcastID(int iUniqueBroadcastID); + + /*! + * @brief Get the unique broadcast ID. + * @return The unique broadcast ID. + */ + int UniqueBroadcastID(void) const; + + /*! + * @brief Change the event's database ID. + * @param iId The new database ID. + */ + void SetBroadcastId(int iId); + + /*! + * @brief Get the event's database ID. + * @return The database ID. + */ + int BroadcastId(void) const; + + /*! + * @brief Get the event's start time. + * @return The new start time. + */ + CDateTime StartAsUTC(void) const; + CDateTime StartAsLocalTime(void) const; + + /*! + * @brief Change the event's start time. + * @param start The new start time. + */ + void SetStartFromUTC(const CDateTime &start); + void SetStartFromLocalTime(const CDateTime &start); + + /*! + * @brief Get the event's end time. + * @return The new start time. + */ + CDateTime EndAsUTC(void) const; + CDateTime EndAsLocalTime(void) const; + + /*! + * @brief Change the event's end time. + * @param end The new end time. + */ + void SetEndFromUTC(const CDateTime &end); + void SetEndFromLocalTime(const CDateTime &end); + + /*! + * @brief Get the duration of this event in seconds. + * @return The duration in seconds. + */ + int GetDuration(void) const; + + /*! + * @brief Change the title of this event. + * @param strTitle The new title. + */ + void SetTitle(const CStdString &strTitle); + + /*! + * @brief Get the title of this event. + * @param bOverrideParental True to override parental control, false check it. + * @return The title. + */ + CStdString Title(bool bOverrideParental = false) const; + + /*! + * @brief Change the plot outline of this event. + * @param strPlotOutline The new plot outline. + */ + void SetPlotOutline(const CStdString &strPlotOutline); + + /*! + * @brief Get the plot outline of this event. + * @param bOverrideParental True to override parental control, false check it. + * @return The plot outline. + */ + CStdString PlotOutline(bool bOverrideParental = false) const; + + /*! + * @brief Change the plot of this event. + * @param strPlot The new plot. + */ + void SetPlot(const CStdString &strPlot); + + /*! + * @brief Get the plot of this event. + * @param bOverrideParental True to override parental control, false check it. + * @return The plot. + */ + CStdString Plot(bool bOverrideParental = false) const; + + /*! + * @brief Change the genre of this event. + * @param iID The genre type ID. + * @param iSubID The genre subtype ID. + */ + void SetGenre(int iID, int iSubID, const char* strGenre); + + /*! + * @brief Get the genre type ID of this event. + * @return The genre type ID. + */ + int GenreType(void) const; + + /*! + * @brief Get the genre subtype ID of this event. + * @return The genre subtype ID. + */ + int GenreSubType(void) const; + + /*! + * @brief Get the genre as human readable string. + * @return The genre. + */ + const std::vector<std::string> Genre(void) const; + + /*! + * @brief Change the first air date of this event. + * @param firstAired The new first air date. + */ + void SetFirstAiredFromUTC(const CDateTime &firstAired); + void SetFirstAiredFromLocalTime(const CDateTime &firstAired); + + /*! + * @brief Get the first air date of this event. + * @return The first air date. + */ + CDateTime FirstAiredAsUTC(void) const; + CDateTime FirstAiredAsLocalTime(void) const; + + /*! + * @brief Change the parental rating of this event. + * @param iParentalRating The new parental rating. + */ + void SetParentalRating(int iParentalRating); + + /*! + * @brief Get the parental rating of this event. + * @return The parental rating. + */ + int ParentalRating(void) const; + + /*! + * @brief Change the star rating of this event. + * @param iStarRating The new star rating. + */ + void SetStarRating(int iStarRating); + + /*! + * @brief Get the star rating of this event. + * @return The star rating. + */ + int StarRating(void) const; + + /*! + * @brief Change the value of notify on start. + * @param bNotify The new value. + */ + void SetNotify(bool bNotify); + + /*! + * @brief Notify on start if true. + * @return Notify on start. + */ + bool Notify(void) const; + + /*! + * @brief Change the series number of this event. + * @param iSeriesNum The new series number. + */ + void SetSeriesNum(int iSeriesNum); + + /*! + * @brief The series number of this event. + * @return The series number. + */ + int SeriesNum(void) const; + + /*! + * @brief Change the episode number of this event. + * @param iEpisodeNum The new episode number. + */ + void SetEpisodeNum(int iEpisodeNum); + + /*! + * @brief The episode number of this event. + * @return The episode number. + */ + int EpisodeNum(void) const; + + /*! + * @brief Change the episode part number of this event. + * @param iEpisodePart The new episode part number. + */ + void SetEpisodePart(int iEpisodePart); + + /*! + * @brief The episode part number of this event. + * @return The episode part number. + */ + int EpisodePart(void) const; + + /*! + * @brief Change the episode name of this event. + * @param strEpisodeName The new episode name. + */ + void SetEpisodeName(const CStdString &strEpisodeName); + + /*! + * @brief The episode name of this event. + * @return The episode name. + */ + CStdString EpisodeName(void) const; + + /*! + * @brief Change the path to the icon for this event. + * @param strIconPath The new path. + */ + void SetIcon(const CStdString &strIconPath); + + /*! + * @brief Get the path to the icon for this event. + * @return The path to the icon + */ + CStdString Icon(void) const; + + /*! + * @brief Change the path to this event. + * @param strFileNameAndPath The new path. + */ + void SetPath(const CStdString &strFileNameAndPath); + + /*! + * @brief The path to this event. + * @return The path. + */ + CStdString Path(void) const; + + /*! + * @brief Set a timer for this event or NULL to clear it. + * @param newTimer The new timer value. + */ + void SetTimer(PVR::CPVRTimerInfoTagPtr newTimer); + void ClearTimer(void); + + /*! + * @brief Check whether this event has an active timer tag. + * @return True if it has an active timer tag, false if not. + */ + bool HasTimer(void) const; + + /*! + * @brief Get a pointer to the timer for event or NULL if there is none. + * @return A pointer to the timer for event or NULL if there is none. + */ + PVR::CPVRTimerInfoTagPtr Timer(void) const; + + /*! + * @brief Change the channel tag of this epg tag + * @param channel The new channel + */ + void SetPVRChannel(PVR::CPVRChannelPtr channel); + + /*! + * @return True if this tag has a PVR channel set. + */ + bool HasPVRChannel(void) const; + + int PVRChannelNumber(void) const; + + CStdString PVRChannelName(void) const; + + /*! + * @brief Get the channel that plays this event. + * @return a pointer to the channel. + */ + const PVR::CPVRChannelPtr ChannelTag(void) const; + + /*! + * @brief Persist this tag in the database. + * @param bSingleUpdate True if this is a single update, false if more updates will follow. + * @return True if the tag was persisted correctly, false otherwise. + */ + bool Persist(bool bSingleUpdate = true); + + /*! + * @brief Update the information in this tag with the info in the given tag. + * @param tag The new info. + */ + void Update(const EPG_TAG &tag); + + /*! + * @brief Update the information in this tag with the info in the given tag. + * @param tag The new info. + * @param bUpdateBroadcastId If set to false, the tag BroadcastId (locally unique) will not be chacked/updated + * @return True if something changed, false otherwise. + */ + bool Update(const CEpgInfoTag &tag, bool bUpdateBroadcastId = true); + protected: + /*! + * @brief Hook that is called when the start date changed. + */ + void UpdatePath(void); + + bool m_bNotify; /*!< notify on start */ + bool m_bChanged; /*!< keep track of changes to this entry */ + + int m_iBroadcastId; /*!< database ID */ + int m_iGenreType; /*!< genre type */ + int m_iGenreSubType; /*!< genre subtype */ + int m_iParentalRating; /*!< parental rating */ + int m_iStarRating; /*!< star rating */ + int m_iSeriesNumber; /*!< series number */ + int m_iEpisodeNumber; /*!< episode number */ + int m_iEpisodePart; /*!< episode part number */ + int m_iUniqueBroadcastID; /*!< unique broadcast ID */ + CStdString m_strTitle; /*!< title */ + CStdString m_strPlotOutline; /*!< plot outline */ + CStdString m_strPlot; /*!< plot */ + std::vector<std::string> m_genre; /*!< genre */ + CStdString m_strEpisodeName; /*!< episode name */ + CStdString m_strIconPath; /*!< the path to the icon */ + CStdString m_strFileNameAndPath; /*!< the filename and path */ + CDateTime m_startTime; /*!< event start time */ + CDateTime m_endTime; /*!< event end time */ + CDateTime m_firstAired; /*!< first airdate */ + + PVR::CPVRTimerInfoTagPtr m_timer; + CEpg * m_epg; /*!< the schedule that this event belongs to */ + + PVR::CPVRChannelPtr m_pvrChannel; + CCriticalSection m_critSection; + }; +} diff --git a/xbmc/epg/EpgSearchFilter.cpp b/xbmc/epg/EpgSearchFilter.cpp new file mode 100644 index 0000000000..ea6e10a9dd --- /dev/null +++ b/xbmc/epg/EpgSearchFilter.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "guilib/LocalizeStrings.h" +#include "utils/TextSearch.h" +#include "utils/log.h" +#include "FileItem.h" +#include "../addons/include/xbmc_pvr_types.h" + +#include "EpgSearchFilter.h" +#include "EpgContainer.h" + +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/recordings/PVRRecordings.h" +#include "pvr/timers/PVRTimers.h" + +using namespace std; +using namespace EPG; +using namespace PVR; + +void EpgSearchFilter::Reset() +{ + m_strSearchTerm = ""; + m_bIsCaseSensitive = false; + m_bSearchInDescription = false; + m_iGenreType = EPG_SEARCH_UNSET; + m_iGenreSubType = EPG_SEARCH_UNSET; + m_iMinimumDuration = EPG_SEARCH_UNSET; + m_iMaximumDuration = EPG_SEARCH_UNSET; + m_startDateTime.SetFromUTCDateTime(g_EpgContainer.GetFirstEPGDate()); + m_endDateTime.SetFromUTCDateTime(g_EpgContainer.GetLastEPGDate()); + m_bIncludeUnknownGenres = false; + m_bIgnorePresentTimers = false; + m_bIgnorePresentRecordings = false; + m_bPreventRepeats = false; + + /* pvr specific filters */ + m_iChannelNumber = EPG_SEARCH_UNSET; + m_bFTAOnly = false; + m_iChannelGroup = EPG_SEARCH_UNSET; + m_bIgnorePresentTimers = true; + m_bIgnorePresentRecordings = true; +} + +bool EpgSearchFilter::MatchGenre(const CEpgInfoTag &tag) const +{ + bool bReturn(true); + + if (m_iGenreType != EPG_SEARCH_UNSET) + { + bool bIsUnknownGenre(tag.GenreType() > EPG_EVENT_CONTENTMASK_USERDEFINED || + tag.GenreType() < EPG_EVENT_CONTENTMASK_MOVIEDRAMA); + bReturn = ((m_bIncludeUnknownGenres && bIsUnknownGenre) || tag.GenreType() == m_iGenreType); + } + + return bReturn; +} + +bool EpgSearchFilter::MatchDuration(const CEpgInfoTag &tag) const +{ + bool bReturn(true); + + if (m_iMinimumDuration != EPG_SEARCH_UNSET) + bReturn = (tag.GetDuration() > m_iMinimumDuration * 60); + + if (bReturn && m_iMaximumDuration != EPG_SEARCH_UNSET) + bReturn = (tag.GetDuration() < m_iMaximumDuration * 60); + + return bReturn; +} + +bool EpgSearchFilter::MatchStartAndEndTimes(const CEpgInfoTag &tag) const +{ + return (tag.StartAsLocalTime() >= m_startDateTime && tag.EndAsLocalTime() <= m_endDateTime); +} + +bool EpgSearchFilter::MatchSearchTerm(const CEpgInfoTag &tag) const +{ + bool bReturn(true); + + if (!m_strSearchTerm.IsEmpty()) + { + CTextSearch search(m_strSearchTerm, m_bIsCaseSensitive, SEARCH_DEFAULT_OR); + bReturn = search.Search(tag.Title()) || + search.Search(tag.PlotOutline()); + } + + return bReturn; +} + +bool EpgSearchFilter::FilterEntry(const CEpgInfoTag &tag) const +{ + return (MatchGenre(tag) && + MatchDuration(tag) && + MatchStartAndEndTimes(tag) && + MatchSearchTerm(tag)) && + (!tag.HasPVRChannel() || + (MatchChannelNumber(tag) && + MatchChannelGroup(tag) && + (!m_bFTAOnly || !tag.ChannelTag()->IsEncrypted()))); +} + +int EpgSearchFilter::RemoveDuplicates(CFileItemList &results) +{ + unsigned int iSize = results.Size(); + + for (unsigned int iResultPtr = 0; iResultPtr < iSize; iResultPtr++) + { + const CEpgInfoTag *epgentry_1 = results.Get(iResultPtr)->GetEPGInfoTag(); + for (unsigned int iTagPtr = 0; iTagPtr < iSize; iTagPtr++) + { + const CEpgInfoTag *epgentry_2 = results.Get(iTagPtr)->GetEPGInfoTag(); + if (iResultPtr == iTagPtr) + continue; + + if (epgentry_1->Title() != epgentry_2->Title() || + epgentry_1->Plot() != epgentry_2->Plot() || + epgentry_1->PlotOutline() != epgentry_2->PlotOutline()) + continue; + + results.Remove(iTagPtr); + iResultPtr--; + iTagPtr--; + iSize--; + } + } + + return iSize; +} + + +bool EpgSearchFilter::MatchChannelNumber(const CEpgInfoTag &tag) const +{ + bool bReturn(true); + + if (m_iChannelNumber != EPG_SEARCH_UNSET && g_PVRManager.IsStarted()) + { + CPVRChannelGroupPtr group = (m_iChannelGroup != EPG_SEARCH_UNSET) ? g_PVRChannelGroups->GetByIdFromAll(m_iChannelGroup) : g_PVRChannelGroups->GetGroupAllTV(); + if (!group) + group = CPVRManager::Get().ChannelGroups()->GetGroupAllTV(); + + bReturn = (m_iChannelNumber == (int) group->GetChannelNumber(*tag.ChannelTag())); + } + + return bReturn; +} + +bool EpgSearchFilter::MatchChannelGroup(const CEpgInfoTag &tag) const +{ + bool bReturn(true); + + if (m_iChannelGroup != EPG_SEARCH_UNSET && g_PVRManager.IsStarted()) + { + CPVRChannelGroupPtr group = g_PVRChannelGroups->GetByIdFromAll(m_iChannelGroup); + bReturn = (group && group->IsGroupMember(*tag.ChannelTag())); + } + + return bReturn; +} + +int EpgSearchFilter::FilterRecordings(CFileItemList &results) +{ + int iRemoved(0); + if (!g_PVRManager.IsStarted()) + return iRemoved; + + CFileItemList recordings; + g_PVRRecordings->GetAll(recordings); + + // TODO inefficient! + for (int iRecordingPtr = 0; iRecordingPtr < recordings.Size(); iRecordingPtr++) + { + CPVRRecording *recording = recordings.Get(iRecordingPtr)->GetPVRRecordingInfoTag(); + if (!recording) + continue; + + for (int iResultPtr = 0; iResultPtr < results.Size(); iResultPtr++) + { + const CEpgInfoTag *epgentry = results.Get(iResultPtr)->GetEPGInfoTag(); + + /* no match */ + if (!epgentry || + epgentry->Title() != recording->m_strTitle || + epgentry->Plot() != recording->m_strPlot) + continue; + + results.Remove(iResultPtr); + iResultPtr--; + ++iRemoved; + } + } + + return iRemoved; +} + +int EpgSearchFilter::FilterTimers(CFileItemList &results) +{ + int iRemoved(0); + if (!g_PVRManager.IsStarted()) + return iRemoved; + + vector<CFileItemPtr> timers = g_PVRTimers->GetActiveTimers(); + // TODO inefficient! + for (unsigned int iTimerPtr = 0; iTimerPtr < timers.size(); iTimerPtr++) + { + CFileItemPtr fileItem = timers.at(iTimerPtr); + if (!fileItem || !fileItem->HasPVRTimerInfoTag()) + continue; + + CPVRTimerInfoTag *timer = fileItem->GetPVRTimerInfoTag(); + if (!timer) + continue; + + for (int iResultPtr = 0; iResultPtr < results.Size(); iResultPtr++) + { + const CEpgInfoTag *epgentry = results.Get(iResultPtr)->GetEPGInfoTag(); + if (!epgentry || + *epgentry->ChannelTag() != *timer->ChannelTag() || + epgentry->StartAsUTC() < timer->StartAsUTC() || + epgentry->EndAsUTC() > timer->EndAsUTC()) + continue; + + results.Remove(iResultPtr); + iResultPtr--; + ++iRemoved; + } + } + + return iRemoved; +} diff --git a/xbmc/epg/EpgSearchFilter.h b/xbmc/epg/EpgSearchFilter.h new file mode 100644 index 0000000000..ee7aa9b156 --- /dev/null +++ b/xbmc/epg/EpgSearchFilter.h @@ -0,0 +1,81 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "XBDateTime.h" + +class CFileItemList; + +namespace EPG +{ + class CEpgInfoTag; + + #define EPG_SEARCH_UNSET (-1) + + /** Filter to apply with on a CEpgInfoTag */ + + struct EpgSearchFilter + { + static int FilterRecordings(CFileItemList &results); + static int FilterTimers(CFileItemList &results); + + /*! + * @brief Clear this filter. + */ + virtual void Reset(); + + /*! + * @brief Check if a tag will be filtered or not. + * @param tag The tag to check. + * @return True if this tag matches the filter, false if not. + */ + virtual bool FilterEntry(const CEpgInfoTag &tag) const; + + virtual bool MatchGenre(const CEpgInfoTag &tag) const; + virtual bool MatchDuration(const CEpgInfoTag &tag) const; + virtual bool MatchStartAndEndTimes(const CEpgInfoTag &tag) const; + virtual bool MatchSearchTerm(const CEpgInfoTag &tag) const; + virtual bool MatchChannelNumber(const CEpgInfoTag &tag) const; + virtual bool MatchChannelGroup(const CEpgInfoTag &tag) const; + + static int RemoveDuplicates(CFileItemList &results); + + CStdString m_strSearchTerm; /*!< The term to search for */ + bool m_bIsCaseSensitive; /*!< Do a case sensitive search */ + bool m_bSearchInDescription; /*!< Search for strSearchTerm in the description too */ + int m_iGenreType; /*!< The genre type for an entry */ + int m_iGenreSubType; /*!< The genre subtype for an entry */ + int m_iMinimumDuration; /*!< The minimum duration for an entry */ + int m_iMaximumDuration; /*!< The maximum duration for an entry */ + CDateTime m_startDateTime; /*!< The minimum start time for an entry */ + CDateTime m_endDateTime; /*!< The maximum end time for an entry */ + bool m_bIncludeUnknownGenres; /*!< Include unknown genres or not */ + bool m_bPreventRepeats; /*!< True to remove repeating events, false if not */ + + /* PVR specific filters */ + int m_iChannelNumber; /*!< The channel number in the selected channel group */ + bool m_bFTAOnly; /*!< Free to air only or not */ + int m_iChannelGroup; /*!< The group this channel belongs to */ + bool m_bIgnorePresentTimers; /*!< True to ignore currently present timers (future recordings), false if not */ + bool m_bIgnorePresentRecordings; /*!< True to ignore currently active recordings, false if not */ + }; +} diff --git a/xbmc/epg/GUIEPGGridContainer.cpp b/xbmc/epg/GUIEPGGridContainer.cpp new file mode 100644 index 0000000000..1095df170e --- /dev/null +++ b/xbmc/epg/GUIEPGGridContainer.cpp @@ -0,0 +1,1934 @@ +/* +* Copyright (C) 2012 Team XBMC +* http://www.xbmc.org +* +* 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, 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 XBMC; see the file COPYING. If not, write to +* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +* http://www.gnu.org/copyleft/gpl.html +* +*/ + +#include "guilib/Key.h" +#include "guilib/GUIControlFactory.h" +#include "guilib/GUIListItem.h" +#include "guilib/GUIFontManager.h" +#include "guilib/LocalizeStrings.h" +#include "guilib/DirtyRegion.h" +#include <tinyxml.h> +#include "utils/log.h" +#include "utils/Variant.h" +#include "threads/SystemClock.h" +#include "GUIInfoManager.h" + +#include "epg/Epg.h" +#include "pvr/channels/PVRChannel.h" + +#include "GUIEPGGridContainer.h" + +using namespace PVR; +using namespace EPG; +using namespace std; + +#define SHORTGAP 5 // how many blocks is considered a short-gap in nav logic +#define MINSPERBLOCK 5 /// would be nice to offer zooming of busy schedules /// performance cost to increase resolution 5 fold? +#define BLOCKJUMP 4 // how many blocks are jumped with each analogue scroll action + +CGUIEPGGridContainer::CGUIEPGGridContainer(int parentID, int controlID, float posX, float posY, float width, + float height, ORIENTATION orientation, int scrollTime, + int preloadItems, int timeBlocks, int rulerUnit) + : CGUIControl(parentID, controlID, posX, posY, width, height) +{ + ControlType = GUICONTAINER_EPGGRID; + m_blocksPerPage = timeBlocks; + m_rulerUnit = rulerUnit; + m_channelCursor = 0; + m_blockCursor = 0; + m_channelOffset = 0; + m_blockOffset = 0; + m_channelScrollOffset = 0; + m_channelScrollSpeed = 0; + m_channelScrollLastTime = 0; + m_programmeScrollOffset = 0; + m_programmeScrollSpeed = 0; + m_programmeScrollLastTime = 0; + m_scrollTime = scrollTime ? scrollTime : 1; + m_renderTime = 0; + m_item = NULL; + m_lastItem = NULL; + m_lastChannel = NULL; + m_channelWrapAround = true; /// get from settings? + m_orientation = orientation; + m_programmeLayout = NULL; + m_focusedProgrammeLayout= NULL; + m_channelLayout = NULL; + m_focusedChannelLayout = NULL; + m_rulerLayout = NULL; + m_rulerPosX = 0; + m_rulerPosY = 0; + m_rulerHeight = 0; + m_rulerWidth = 0; + m_channelPosX = 0; + m_channelPosY = 0; + m_channelHeight = 0; + m_channelWidth = 0; + m_gridPosX = 0; + m_gridPosY = 0; + m_gridWidth = 0; + m_gridHeight = 0; + m_blockSize = 0; + m_analogScrollCount = 0; + m_cacheChannelItems = preloadItems; + m_cacheRulerItems = preloadItems; + m_cacheProgrammeItems = preloadItems; + m_gridIndex = NULL; +} + +CGUIEPGGridContainer::~CGUIEPGGridContainer(void) +{ + Reset(); +} + +void CGUIEPGGridContainer::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions) +{ + bool changed = false; + m_renderTime = currentTime; + + changed = true; + + if (changed) + MarkDirtyRegion(); + + CGUIControl::Process(currentTime, dirtyregions); +} + +void CGUIEPGGridContainer::Render() +{ + ValidateOffset(); + + if (m_bInvalidated) + UpdateLayout(); + + if (!m_focusedChannelLayout || !m_channelLayout || !m_rulerLayout || !m_focusedProgrammeLayout || !m_programmeLayout || m_rulerItems.size()<=1 || (m_gridEnd - m_gridStart) == CDateTimeSpan(0, 0, 0, 0)) + return; + + UpdateScrollOffset(); + + int chanOffset = (int)floorf(m_channelScrollOffset / m_programmeLayout->Size(m_orientation)); + int blockOffset = (int)floorf(m_programmeScrollOffset / m_blockSize); + int rulerOffset = (int)floorf(m_programmeScrollOffset / m_blockSize); + + /// Render channel names + int cacheBeforeChannel, cacheAfterChannel; + GetChannelCacheOffsets(cacheBeforeChannel, cacheAfterChannel); + + // Free memory not used on screen + if ((int)m_channelItems.size() > m_channelsPerPage + cacheBeforeChannel + cacheAfterChannel) + FreeChannelMemory(CorrectOffset(chanOffset - cacheBeforeChannel, 0), CorrectOffset(chanOffset + m_channelsPerPage + 1 + cacheAfterChannel, 0)); + + if (m_orientation == VERTICAL) + g_graphicsContext.SetClipRegion(m_channelPosX, m_channelPosY, m_channelWidth, m_gridHeight); + else + g_graphicsContext.SetClipRegion(m_channelPosX, m_channelPosY, m_gridWidth, m_channelHeight); + + CPoint originChannel = CPoint(m_channelPosX, m_channelPosY) + m_renderOffset; + float pos = (m_orientation == VERTICAL) ? originChannel.y : originChannel.x; + float end = (m_orientation == VERTICAL) ? m_posY + m_height : m_posX + m_width; + + // we offset our draw position to take into account scrolling and whether or not our focused + // item is offscreen "above" the list. + float drawOffset = (chanOffset - cacheBeforeChannel) * m_channelLayout->Size(m_orientation) - m_channelScrollOffset; + if (m_channelOffset + m_channelCursor < chanOffset) + drawOffset += m_focusedChannelLayout->Size(m_orientation) - m_channelLayout->Size(m_orientation); + pos += drawOffset; + end += cacheAfterChannel * m_channelLayout->Size(m_orientation); + + float focusedPos = 0; + CGUIListItemPtr focusedItem; + int current = chanOffset;// - cacheBeforeChannel; + while (pos < end && (int)m_channelItems.size()) + { + int itemNo = CorrectOffset(current, 0); + if (itemNo >= (int)m_channelItems.size()) + break; + bool focused = (current == m_channelOffset + m_channelCursor); + if (itemNo >= 0) + { + CGUIListItemPtr item = m_channelItems[itemNo]; + // render our item + if (focused) + { + focusedPos = pos; + focusedItem = item; + } + else + { + if (m_orientation == VERTICAL) + RenderChannelItem(originChannel.x, pos, item.get(), false); + else + RenderChannelItem(pos, originChannel.y, item.get(), false); + } + } + // increment our position + pos += focused ? m_focusedChannelLayout->Size(m_orientation) : m_channelLayout->Size(m_orientation); + current++; + } + // render focused item last so it can overlap other items + if (focusedItem) + { + if (m_orientation == VERTICAL) + RenderChannelItem(originChannel.x, focusedPos, focusedItem.get(), true); + else + RenderChannelItem(focusedPos, originChannel.y, focusedItem.get(), true); + } + g_graphicsContext.RestoreClipRegion(); + + /// Render the ruler items + g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height); + CGUIListItemPtr item = m_rulerItems[0]; + g_graphicsContext.SetOrigin(m_posX, m_posY); + item->SetLabel(m_rulerItems[rulerOffset/m_rulerUnit+1]->GetLabel2()); + if (!item->GetLayout()) + { + CGUIListItemLayout *layout = new CGUIListItemLayout(*m_rulerLayout); + if (m_orientation == VERTICAL) + layout->SetWidth(m_channelWidth); + else + layout->SetHeight(m_channelHeight); + item->SetLayout(layout); + } + if (item->GetLayout()) + { + CDirtyRegionList dirtyRegions; + item->GetLayout()->Process(item.get(),m_parentID,m_renderTime,dirtyRegions); + item->GetLayout()->Render(item.get(), m_parentID); + } + g_graphicsContext.RestoreOrigin(); + + int cacheBeforeRuler, cacheAfterRuler; + GetRulerCacheOffsets(cacheBeforeRuler, cacheAfterRuler); + + g_graphicsContext.RestoreClipRegion(); + + // Free memory not used on screen + if ((int)m_rulerItems.size() > m_blocksPerPage + cacheBeforeRuler + cacheAfterRuler) + FreeRulerMemory(CorrectOffset(rulerOffset - cacheBeforeRuler, 0), CorrectOffset(rulerOffset + m_blocksPerPage + 1 + cacheAfterRuler, 0)); + + if (m_orientation == VERTICAL) + g_graphicsContext.SetClipRegion(m_rulerPosX, m_rulerPosY, m_gridWidth, m_rulerHeight); + else + g_graphicsContext.SetClipRegion(m_rulerPosX, m_rulerPosY, m_rulerWidth, m_gridHeight); + + CPoint originRuler = CPoint(m_rulerPosX, m_rulerPosY) + m_renderOffset; + pos = (m_orientation == VERTICAL) ? originRuler.x : originRuler.y; + end = (m_orientation == VERTICAL) ? m_posX + m_width : m_posY + m_height; + drawOffset = (rulerOffset - cacheBeforeRuler) * m_blockSize - m_programmeScrollOffset; + pos += drawOffset; + end += cacheAfterRuler * m_rulerLayout->Size(m_orientation == VERTICAL ? HORIZONTAL : VERTICAL); + + if (rulerOffset % m_rulerUnit != 0) + { + /* first ruler marker starts before current view */ + int startBlock = rulerOffset - 1; + + while (startBlock % m_rulerUnit != 0) + startBlock--; + + int missingSection = rulerOffset - startBlock; + + pos -= missingSection * m_blockSize; + } + while (pos < end && (rulerOffset/m_rulerUnit+1) < m_rulerItems.size()) + { + item = m_rulerItems[rulerOffset/m_rulerUnit+1]; + if (m_orientation == VERTICAL) + { + g_graphicsContext.SetOrigin(pos, originRuler.y); + pos += m_rulerWidth; + } + else + { + g_graphicsContext.SetOrigin(originRuler.x, pos); + pos += m_rulerHeight; + } + if (!item->GetLayout()) + { + CGUIListItemLayout *layout = new CGUIListItemLayout(*m_rulerLayout); + if (m_orientation == VERTICAL) + layout->SetWidth(m_rulerWidth); + else + layout->SetHeight(m_rulerHeight); + + item->SetLayout(layout); + } + if (item->GetLayout()) + { + CDirtyRegionList dirtyRegions; + item->GetLayout()->Process(item.get(),m_parentID,m_renderTime,dirtyRegions); + item->GetLayout()->Render(item.get(), m_parentID); + } + g_graphicsContext.RestoreOrigin(); + + rulerOffset += m_rulerUnit; + } + g_graphicsContext.RestoreClipRegion(); + + /// Render programmes + int cacheBeforeProgramme, cacheAfterProgramme; + GetProgrammeCacheOffsets(cacheBeforeProgramme, cacheAfterProgramme); + + // Free memory not used on screen + if ((int)m_programmeItems.size() > m_ProgrammesPerPage + cacheBeforeProgramme + cacheAfterProgramme) + FreeProgrammeMemory(CorrectOffset(blockOffset - cacheBeforeProgramme, 0), CorrectOffset(blockOffset + m_ProgrammesPerPage + 1 + cacheAfterProgramme, 0)); + + g_graphicsContext.SetClipRegion(m_gridPosX, m_gridPosY, m_gridWidth, m_gridHeight); + CPoint originProgramme = CPoint(m_gridPosX, m_gridPosY) + m_renderOffset; + float posA = (m_orientation != VERTICAL) ? originProgramme.y : originProgramme.x; + float endA = (m_orientation != VERTICAL) ? m_posY + m_height : m_posX + m_width; + float posB = (m_orientation == VERTICAL) ? originProgramme.y : originProgramme.x; + float endB = (m_orientation == VERTICAL) ? m_gridPosY + m_gridHeight : m_posX + m_width; + endA += cacheAfterProgramme * m_blockSize; + + float DrawOffsetA = blockOffset * m_blockSize - m_programmeScrollOffset; + posA += DrawOffsetA; + float DrawOffsetB = (chanOffset - cacheBeforeProgramme) * m_channelLayout->Size(m_orientation) - m_channelScrollOffset; + posB += DrawOffsetB; + + int channel = chanOffset; + + float focusedPosX = 0; + float focusedPosY = 0; + float focusedwidth = 0; + float focusedheight = 0; + while (posB < endB && m_channelItems.size()) + { + if (channel >= (int)m_channelItems.size()) + break; + + int block = blockOffset; + float posA2 = posA; + + CGUIListItemPtr item = m_gridIndex[channel][block].item; + if (blockOffset > 0 && item == m_gridIndex[channel][blockOffset-1].item) + { + /* first program starts before current view */ + int startBlock = blockOffset - 1; + while (m_gridIndex[channel][startBlock].item == item) + startBlock--; + + block = startBlock + 1; + int missingSection = blockOffset - block; + posA2 -= missingSection * m_blockSize; + } + + while (posA2 < endA && m_programmeItems.size()) // FOR EACH ITEM /////////////// + { + item = m_gridIndex[channel][block].item; + if (!item || !item.get()->IsFileItem()) + break; + + bool focused = (channel == m_channelOffset + m_channelCursor) && (item == m_gridIndex[m_channelOffset + m_channelCursor][m_blockOffset + m_blockCursor].item); + + // render our item + if (focused) + { + if (m_orientation == VERTICAL) + { + focusedPosX = posA2; + focusedPosY = posB; + } + else + { + focusedPosX = posB; + focusedPosY = posA2; + } + focusedItem = item; + focusedwidth = m_gridIndex[channel][block].width; + focusedheight = m_gridIndex[channel][block].height; + } + else + { + if (m_orientation == VERTICAL) + RenderProgrammeItem(posA2, posB, m_gridIndex[channel][block].width, m_gridIndex[channel][block].height, item.get(), focused); + else + RenderProgrammeItem(posB, posA2, m_gridIndex[channel][block].width, m_gridIndex[channel][block].height, item.get(), focused); + } + + // increment our X position + if (m_orientation == VERTICAL) + { + posA2 += m_gridIndex[channel][block].width; // assumes focused & unfocused layouts have equal length + block += (int)(m_gridIndex[channel][block].width / m_blockSize); + } + else + { + posA2 += m_gridIndex[channel][block].height; // assumes focused & unfocused layouts have equal length + block += (int)(m_gridIndex[channel][block].height / m_blockSize); + } + } + + // increment our Y position + channel++; + posB += m_orientation == VERTICAL ? m_channelHeight : m_channelWidth; + } + + // and render the focused item last (for overlapping purposes) + if (focusedItem) + RenderProgrammeItem(focusedPosX, focusedPosY, focusedwidth, focusedheight, focusedItem.get(), true); + + g_graphicsContext.RestoreClipRegion(); + + CGUIControl::Render(); +} + +void CGUIEPGGridContainer::RenderChannelItem(float posX, float posY, CGUIListItem *item, bool focused) +{ + if (!m_focusedChannelLayout || !m_channelLayout) return; + + // set the origin + g_graphicsContext.SetOrigin(posX, posY); + + if (m_bInvalidated) + item->SetInvalid(); + if (focused) + { + if (!item->GetFocusedLayout()) + { + CGUIListItemLayout *layout = new CGUIListItemLayout(*m_focusedChannelLayout); + item->SetFocusedLayout(layout); + } + if (item->GetFocusedLayout()) + { + if (item != m_lastChannel || !HasFocus()) + { + item->GetFocusedLayout()->SetFocusedItem(0); + } + if (item != m_lastChannel && HasFocus()) + { + item->GetFocusedLayout()->ResetAnimation(ANIM_TYPE_UNFOCUS); + unsigned int subItem = 1; + if (m_lastChannel && m_lastChannel->GetFocusedLayout()) + subItem = m_lastChannel->GetFocusedLayout()->GetFocusedItem(); + item->GetFocusedLayout()->SetFocusedItem(subItem ? subItem : 1); + } + CDirtyRegionList dirtyRegions; + item->GetFocusedLayout()->Process(item,m_parentID,m_renderTime,dirtyRegions); + item->GetFocusedLayout()->Render(item, m_parentID); + } + m_lastChannel = item; + } + else + { + if (item->GetFocusedLayout()) + item->GetFocusedLayout()->SetFocusedItem(0); // focus is not set + if (!item->GetLayout()) + { + CGUIListItemLayout *layout = new CGUIListItemLayout(*m_channelLayout); + item->SetLayout(layout); + } + if (item->GetFocusedLayout() && item->GetFocusedLayout()->IsAnimating(ANIM_TYPE_UNFOCUS)) + { + CDirtyRegionList dirtyRegions; + item->GetFocusedLayout()->Process(item,m_parentID,m_renderTime,dirtyRegions); + item->GetFocusedLayout()->Render(item, m_parentID); + } + else if (item->GetLayout()) + { + CDirtyRegionList dirtyRegions; + item->GetLayout()->Process(item,m_parentID,m_renderTime,dirtyRegions); + item->GetLayout()->Render(item, m_parentID); + } + } + g_graphicsContext.RestoreOrigin(); +} + +void CGUIEPGGridContainer::RenderProgrammeItem(float posX, float posY, float width, float height, CGUIListItem *item, bool focused) +{ + if (!m_focusedProgrammeLayout || !m_programmeLayout) return; + + // set the origin + g_graphicsContext.SetOrigin(posX, posY); + + if (m_bInvalidated) + item->SetInvalid(); + if (focused) + { + if (!item->GetFocusedLayout()) + { + CGUIListItemLayout *layout = new CGUIListItemLayout(*m_focusedProgrammeLayout); + CFileItem *fileItem = item->IsFileItem() ? (CFileItem *)item : NULL; + if (fileItem) + { + const CEpgInfoTag* tag = fileItem->GetEPGInfoTag(); + if (m_orientation == VERTICAL) + layout->SetWidth(width); + else + layout->SetHeight(height); + + item->SetProperty("GenreType", tag->GenreType()); + } + item->SetFocusedLayout(layout); + } + if (item->GetFocusedLayout()) + { + if (item != m_lastItem || !HasFocus()) + { + item->GetFocusedLayout()->SetFocusedItem(0); + } + if (item != m_lastItem && HasFocus()) + { + item->GetFocusedLayout()->ResetAnimation(ANIM_TYPE_UNFOCUS); + unsigned int subItem = 1; + if (m_lastItem && m_lastItem->GetFocusedLayout()) + subItem = m_lastItem->GetFocusedLayout()->GetFocusedItem(); + item->GetFocusedLayout()->SetFocusedItem(subItem ? subItem : 1); + } + CDirtyRegionList dirtyRegions; + item->GetFocusedLayout()->Process(item,m_parentID,m_renderTime,dirtyRegions); + item->GetFocusedLayout()->Render(item, m_parentID); + } + m_lastItem = item; + } + else + { + if (item->GetFocusedLayout()) + item->GetFocusedLayout()->SetFocusedItem(0); // focus is not set + if (!item->GetLayout()) + { + CGUIListItemLayout *layout = new CGUIListItemLayout(*m_programmeLayout); + CFileItem *fileItem = item->IsFileItem() ? (CFileItem *)item : NULL; + if (fileItem) + { + const CEpgInfoTag* tag = fileItem->GetEPGInfoTag(); + if (m_orientation == VERTICAL) + layout->SetWidth(width); + else + layout->SetHeight(height); + + item->SetProperty("GenreType", tag->GenreType()); + } + item->SetLayout(layout); + } + if (item->GetFocusedLayout() && item->GetFocusedLayout()->IsAnimating(ANIM_TYPE_UNFOCUS)) + { + CDirtyRegionList dirtyRegions; + item->GetFocusedLayout()->Process(item,m_parentID,m_renderTime,dirtyRegions); + item->GetFocusedLayout()->Render(item, m_parentID); + } + else if (item->GetLayout()) + { + CDirtyRegionList dirtyRegions; + item->GetLayout()->Process(item,m_parentID,m_renderTime,dirtyRegions); + item->GetLayout()->Render(item, m_parentID); + } + } + g_graphicsContext.RestoreOrigin(); +} + +bool CGUIEPGGridContainer::OnAction(const CAction &action) +{ + switch (action.GetID()) + { + case ACTION_MOVE_LEFT: + case ACTION_MOVE_RIGHT: + case ACTION_MOVE_DOWN: + case ACTION_MOVE_UP: + { // use base class implementation + + return CGUIControl::OnAction(action); + } + + break; + case ACTION_PAGE_UP: + { + if (m_orientation == VERTICAL) + { + if (m_channelOffset == 0) + { // already on the first page, so move to the first item + SetChannel(0); + } + else + { // scroll up to the previous page + ChannelScroll(-m_channelsPerPage); + } + } + else + ProgrammesScroll(-m_blocksPerPage/4); + + return true; + } + + break; + case ACTION_PAGE_DOWN: + { + if (m_orientation == VERTICAL) + { + if (m_channelOffset == m_channels - m_channelsPerPage || m_channels < m_channelsPerPage) + { // already at the last page, so move to the last item. + SetChannel(m_channels - m_channelOffset - 1); + } + else + { // scroll down to the next page + ChannelScroll(m_channelsPerPage); + } + } + else + ProgrammesScroll(m_blocksPerPage/4); + + return true; + } + + break; + + // smooth scrolling (for analog controls) + case ACTION_TELETEXT_RED: + case ACTION_TELETEXT_GREEN: + case ACTION_SCROLL_UP: // left horizontal scrolling + { + int blocksToJump = action.GetID() == ACTION_TELETEXT_RED ? m_blocksPerPage/2 : m_blocksPerPage/4; + + m_analogScrollCount += action.GetAmount() * action.GetAmount(); + bool handled = false; + + while (m_analogScrollCount > 0.4) + { + handled = true; + m_analogScrollCount -= 0.4f; + + if (m_blockOffset > 0 && m_blockCursor <= m_blocksPerPage / 2) + { + ProgrammesScroll(-blocksToJump); + } + else if (m_blockCursor > blocksToJump) + { + SetBlock(m_blockCursor - blocksToJump); + } + } + + return handled; + } + + break; + + case ACTION_TELETEXT_BLUE: + case ACTION_TELETEXT_YELLOW: + case ACTION_SCROLL_DOWN: // right horizontal scrolling + { + int blocksToJump = action.GetID() == ACTION_TELETEXT_BLUE ? m_blocksPerPage/2 : m_blocksPerPage/4; + + m_analogScrollCount += action.GetAmount() * action.GetAmount(); + bool handled = false; + + while (m_analogScrollCount > 0.4) + { + handled = true; + m_analogScrollCount -= 0.4f; + + if (m_blockOffset + m_blocksPerPage < m_blocks && m_blockCursor >= m_blocksPerPage / 2) + { + ProgrammesScroll(blocksToJump); + } + else if (m_blockCursor < m_blocksPerPage - blocksToJump && m_blockOffset + m_blockCursor < m_blocks - blocksToJump) + { + SetBlock(m_blockCursor + blocksToJump); + } + } + + return handled; + } + + break; + + default: + if (action.GetID()) + return OnClick(action.GetID()); + break; + } + + return false; +} + +bool CGUIEPGGridContainer::OnMessage(CGUIMessage& message) +{ + if (message.GetControlId() == GetID()) + { + if (message.GetMessage() == GUI_MSG_ITEM_SELECTED) + { + message.SetParam1(GetSelectedItem()); + return true; + } + else if (message.GetMessage() == GUI_MSG_LABEL_BIND && message.GetPointer()) + { + Reset(); + CFileItemList *items = (CFileItemList *)message.GetPointer(); + + /* Create Channel items */ + int iLastChannelNumber = -1; + ItemsPtr itemsPointer; + itemsPointer.start = 0; + for (int i = 0; i < items->Size(); ++i) + { + const CEpgInfoTag* tag = items->Get(i)->GetEPGInfoTag(); + if (!tag || !tag->HasPVRChannel()) + continue; + + int iCurrentChannelNumber = tag->PVRChannelNumber(); + if (iCurrentChannelNumber != iLastChannelNumber) + { + CPVRChannelPtr channel = tag->ChannelTag(); + if (!channel) + continue; + + if (i > 0) + { + itemsPointer.stop = i-1; + m_epgItemsPtr.push_back(itemsPointer); + itemsPointer.start = i; + } + iLastChannelNumber = iCurrentChannelNumber; + CGUIListItemPtr item(new CFileItem(*channel)); + m_channelItems.push_back(item); + } + } + if (items->Size() > 0) + { + itemsPointer.stop = items->Size()-1; + m_epgItemsPtr.push_back(itemsPointer); + } + + /* Create programme items */ + for (int i = 0; i < items->Size(); i++) + m_programmeItems.push_back(items->Get(i)); + + ClearGridIndex(); + m_gridIndex = (struct GridItemsPtr **) calloc(1,m_channelItems.size()*sizeof(struct GridItemsPtr)); + if (m_gridIndex != NULL) + { + for (unsigned int i = 0; i < m_channelItems.size(); i++) + { + m_gridIndex[i] = (struct GridItemsPtr*) calloc(1,MAXBLOCKS*sizeof(struct GridItemsPtr)); + } + } + + UpdateLayout(true); // true to refresh all items + + /* Create Ruler items */ + CDateTime ruler; ruler.SetFromUTCDateTime(m_gridStart); + CDateTimeSpan unit(0, 0, m_rulerUnit * MINSPERBLOCK, 0); + CGUIListItemPtr rulerItem(new CFileItem(ruler.GetAsLocalizedDate(true, true))); + rulerItem->SetProperty("DateLabel", true); + m_rulerItems.push_back(rulerItem); + + for (; ruler < m_gridEnd; ruler += unit) + { + CGUIListItemPtr rulerItem(new CFileItem(ruler.GetAsLocalizedTime("", false))); + rulerItem->SetLabel2(ruler.GetAsLocalizedDate(true, true)); + m_rulerItems.push_back(rulerItem); + } + + UpdateItems(); + //SelectItem(message.GetParam1()); + return true; + } + else if (message.GetMessage() == GUI_MSG_REFRESH_LIST) + { // update our list contents + for (unsigned int i = 0; i < m_channelItems.size(); ++i) + m_channelItems[i]->SetInvalid(); + for (unsigned int i = 0; i < m_programmeItems.size(); ++i) + m_programmeItems[i]->SetInvalid(); + for (unsigned int i = 0; i < m_rulerItems.size(); ++i) + m_rulerItems[i]->SetInvalid(); + } + } + + return CGUIControl::OnMessage(message); +} + +void CGUIEPGGridContainer::UpdateItems() +{ + CDateTimeSpan blockDuration, gridDuration; + + /* check for invalid start and end time */ + if (m_gridStart >= m_gridEnd) + { + CLog::Log(LOGERROR, "CGUIEPGGridContainer - %s - invalid start and end time set", __FUNCTION__); + CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), GetParentID()); // message the window + SendWindowMessage(msg); + return; + } + + gridDuration = m_gridEnd - m_gridStart; + + m_blocks = (gridDuration.GetDays()*24*60 + gridDuration.GetHours()*60 + gridDuration.GetMinutes()) / MINSPERBLOCK; + if (m_blocks >= MAXBLOCKS) + m_blocks = MAXBLOCKS; + + /* if less than one page, can't display grid */ + if (m_blocks < m_blocksPerPage) + { + CLog::Log(LOGERROR, "(%s) - Less than one page of data available.", __FUNCTION__); + CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), GetParentID()); // message the window + SendWindowMessage(msg); + return; + } + + blockDuration.SetDateTimeSpan(0, 0, MINSPERBLOCK, 0); + + long tick(XbmcThreads::SystemClockMillis()); + + for (unsigned int row = 0; row < m_channelItems.size(); ++row) + { + CDateTime gridCursor = m_gridStart; //reset cursor for new channel + unsigned long progIdx = m_epgItemsPtr[row].start; + unsigned long lastIdx = m_epgItemsPtr[row].stop; + int iEpgId = ((CFileItem *)m_programmeItems[progIdx].get())->GetEPGInfoTag()->EpgID(); + + /** FOR EACH BLOCK **********************************************************************/ + + for (int block = 0; block < m_blocks; block++) + { + while (progIdx <= lastIdx) + { + CGUIListItemPtr item = m_programmeItems[progIdx]; + const CEpgInfoTag* tag = ((CFileItem *)item.get())->GetEPGInfoTag(); + if (tag == NULL) + progIdx++; + + if (tag->EpgID() != iEpgId) + break; + + if (m_gridEnd <= tag->StartAsUTC()) + { + break; + } + else if (gridCursor >= tag->EndAsUTC()) + { + progIdx++; + } + else if (gridCursor < tag->EndAsUTC()) + { + m_gridIndex[row][block].item = item; + break; + } + else + { + progIdx++; + } + } + + gridCursor += blockDuration; + } + + /** FOR EACH BLOCK **********************************************************************/ + int itemSize = 1; // size of the programme in blocks + int savedBlock = 0; + + for (int block = 0; block < m_blocks; block++) + { + if (m_gridIndex[row][block].item != m_gridIndex[row][block+1].item) + { + if (!m_gridIndex[row][block].item) + { + CEpgInfoTag broadcast; + CFileItemPtr unknown(new CFileItem(broadcast)); + for (int i = block ; i > block - itemSize; i--) + { + m_gridIndex[row][i].item = unknown; + } + } + + CGUIListItemPtr item = m_gridIndex[row][block].item; + CFileItem *fileItem = (CFileItem *)item.get(); + + m_gridIndex[row][savedBlock].item->SetProperty("GenreType", fileItem->GetEPGInfoTag()->GenreType()); + if (m_orientation == VERTICAL) + { + m_gridIndex[row][savedBlock].width = itemSize*m_blockSize; + m_gridIndex[row][savedBlock].height = m_channelHeight; + } + else + { + m_gridIndex[row][savedBlock].width = m_channelWidth; + m_gridIndex[row][savedBlock].height = itemSize*m_blockSize; + } + + itemSize = 1; + savedBlock = block+1; + } + else + { + itemSize++; + } + } + } + + /******************************************* END ******************************************/ + + CLog::Log(LOGDEBUG, "%s completed successfully in %u ms", __FUNCTION__, (unsigned int)(XbmcThreads::SystemClockMillis()-tick)); + + m_channels = (int)m_epgItemsPtr.size(); + m_item = GetItem(m_channelCursor); + if (m_item) + m_blockCursor = GetBlock(m_item->item, m_channelCursor); + + SetInvalid(); +} + +void CGUIEPGGridContainer::ChannelScroll(int amount) +{ + // increase or decrease the vertical offset + int offset = m_channelOffset + amount; + + if (offset > m_channels - m_channelsPerPage) + { + offset = m_channels - m_channelsPerPage; + } + + if (offset < 0) offset = 0; + + ScrollToChannelOffset(offset); +} + +void CGUIEPGGridContainer::ProgrammesScroll(int amount) +{ + // increase or decrease the horizontal offset + int offset = m_blockOffset + amount; + + if (offset > m_blocks - m_blocksPerPage) + { + offset = m_blocks - m_blocksPerPage; + } + + if (offset < 0) offset = 0; + + ScrollToBlockOffset(offset); +} + +bool CGUIEPGGridContainer::MoveChannel(bool direction) +{ + if (direction) + { + if (m_channelCursor > 0) + { + SetChannel(m_channelCursor - 1); + } + else if (m_channelCursor == 0 && m_channelOffset) + { + ScrollToChannelOffset(m_channelOffset - 1); + SetChannel(0); + } + else if (m_channelWrapAround) + { + int offset = m_channels - m_channelsPerPage; + + if (offset < 0) offset = 0; + + SetChannel(m_channels - offset - 1); + + ScrollToChannelOffset(offset); + } + else + return false; + } + else + { + if (m_channelOffset + m_channelCursor + 1 < m_channels) + { + if (m_channelCursor + 1 < m_channelsPerPage) + { + SetChannel(m_channelCursor + 1); + } + else + { + ScrollToChannelOffset(m_channelOffset + 1); + SetChannel(m_channelsPerPage - 1); + } + } + else if (m_channelWrapAround) + { + SetChannel(0); + ScrollToChannelOffset(0); + } + else + return false; + } + return true; +} + +bool CGUIEPGGridContainer::MoveProgrammes(bool direction) +{ + if (!m_gridIndex || !m_item) + return false; + + if (direction) + { + if (m_channelCursor + m_channelOffset < 0 || m_blockOffset < 0) + return false; + + if (m_item->item != m_gridIndex[m_channelCursor + m_channelOffset][m_blockOffset].item) + { + // this is not first item on page + m_item = GetPrevItem(m_channelCursor); + m_blockCursor = GetBlock(m_item->item, m_channelCursor); + } + else if (m_blockCursor <= 0 && m_blockOffset) + { + // we're at the left edge and offset + int itemSize = GetItemSize(m_item); + int block = GetRealBlock(m_item->item, m_channelCursor); + + if (block < m_blockOffset) /* current item begins before current offset, keep selected */ + { + if (itemSize > m_blocksPerPage) /* current item is longer than one page, scroll one page left */ + { + m_blockOffset < m_blocksPerPage ? block = 0 : block = m_blockOffset - m_blocksPerPage; // number blocks left < m_blocksPerPAge + ScrollToBlockOffset(block); + SetBlock(0); + } + else /* current item is shorter than one page, scroll left to start of item */ + { + ScrollToBlockOffset(block); // -1? + SetBlock(0); // align cursor to left edge + } + } + else /* current item starts on this page's edge, select the previous item */ + { + m_item = GetPrevItem(m_channelCursor); + itemSize = GetItemSize(m_item); + + if (itemSize > m_blocksPerPage) // previous item is longer than one page, scroll left to last page of item */ + { + ScrollToBlockOffset(m_blockOffset - m_blocksPerPage); // left one whole page + //SetBlock(m_blocksPerPage -1 ); // helps navigation by setting cursor to far right edge + SetBlock(0); // align cursor to left edge + } + else /* previous item is shorter than one page, scroll left to start of item */ + { + ScrollToBlockOffset(m_blockOffset - itemSize); + SetBlock(0); //should be zero + } + } + } + else + return false; + } + else + { + if (m_item->item != m_gridIndex[m_channelCursor + m_channelOffset][m_blocksPerPage + m_blockOffset - 1].item) + { + // this is not last item on page + m_item = GetNextItem(m_channelCursor); + m_blockCursor = GetBlock(m_item->item, m_channelCursor); + } + else if ((m_blockOffset != m_blocks - m_blocksPerPage) && m_blocks > m_blocksPerPage) + { + // at right edge, more than one page and not at maximum offset + int itemSize = GetItemSize(m_item); + int block = GetRealBlock(m_item->item, m_channelCursor); + + if (itemSize > m_blocksPerPage - m_blockCursor) // current item extends into next page, keep selected + { + if (itemSize > m_blocksPerPage) // current item is longer than one page, scroll one page right + { + if (m_blockOffset && m_blockOffset + m_blocksPerPage > m_blocks) + block = m_blocks - m_blocksPerPage; + else + block = m_blockOffset + m_blocksPerPage; + + ScrollToBlockOffset(block); + + SetBlock(0); + } + else // current item is shorter than one page, scroll so end of item sits on end of grid + { + ScrollToBlockOffset(block + itemSize - m_blocksPerPage); + SetBlock(GetBlock(m_item->item, m_channelCursor)); /// change to middle block of item? + } + } + else // current item finishes on this page's edge, select the next item + { + m_item = GetNextItem(m_channelCursor); + itemSize = GetItemSize(m_item); + + if (itemSize > m_blocksPerPage) // next item is longer than one page, scroll to first page of this item + { + ScrollToBlockOffset(m_blockOffset + m_blocksPerPage); + SetBlock(0); + } + else // next item is shorter than one page, scroll so end of item sits on end of grid + { + ScrollToBlockOffset(m_blockOffset + itemSize); + SetBlock(m_blocksPerPage - itemSize); /// change to middle block of item? + } + } + } + else + return false; + } + return true; +} + +void CGUIEPGGridContainer::OnUp() +{ + if (m_orientation == VERTICAL) + { + if (!MoveChannel(true)) + CGUIControl::OnUp(); + } + else + { + if (!MoveProgrammes(true)) + CGUIControl::OnUp(); + } +} + +void CGUIEPGGridContainer::OnDown() +{ + if (m_orientation == VERTICAL) + { + if (!MoveChannel(false)) + CGUIControl::OnDown(); + } + else + { + if (!MoveProgrammes(false)) + CGUIControl::OnDown(); + } +} + +void CGUIEPGGridContainer::OnLeft() +{ + if (m_orientation == VERTICAL) + { + if (!MoveProgrammes(true)) + CGUIControl::OnLeft(); + } + else + { + if (!MoveChannel(true)) + CGUIControl::OnLeft(); + } +} + +void CGUIEPGGridContainer::OnRight() +{ + if (m_orientation == VERTICAL) + { + if (!MoveProgrammes(false)) + CGUIControl::OnRight(); + } + else + { + if (!MoveChannel(false)) + CGUIControl::OnRight(); + } +} + +void CGUIEPGGridContainer::SetChannel(const CStdString &channel) +{ + int iChannelIndex(-1); + for (unsigned int iIndex = 0; iIndex < m_channelItems.size(); iIndex++) + { + CStdString strPath = m_channelItems[iIndex]->GetProperty("path").asString(StringUtils::EmptyString); + if (strPath == channel) + { + iChannelIndex = iIndex; + break; + } + } + + if (iChannelIndex >= 0) + ScrollToChannelOffset(iChannelIndex); +} + +void CGUIEPGGridContainer::SetChannel(const CPVRChannel &channel) +{ + int iChannelIndex(-1); + for (unsigned int iIndex = 0; iIndex < m_channelItems.size(); iIndex++) + { + int iChannelId = m_channelItems[iIndex]->GetProperty("channelid").asInteger(-1); + if (iChannelId == channel.ChannelID()) + { + iChannelIndex = iIndex; + break; + } + } + + if (iChannelIndex >= 0) + ScrollToChannelOffset(iChannelIndex); +} + +void CGUIEPGGridContainer::SetChannel(int channel) +{ + if (m_blockCursor + m_blockOffset == 0 || m_blockOffset + m_blockCursor + GetItemSize(m_item) == m_blocks) + { + m_item = GetItem(channel); + if (m_item) + { + m_blockCursor = GetBlock(m_item->item, channel); + m_channelCursor = channel; + } + return; + } + + /* basic checks failed, need to correctly identify nearest item */ + m_item = GetClosestItem(channel); + if (m_item) + { + m_channelCursor = channel; + m_blockCursor = GetBlock(m_item->item, m_channelCursor); + } +} + +void CGUIEPGGridContainer::SetBlock(int block) +{ + m_blockCursor = block; + m_item = GetItem(m_channelCursor); +} + +CGUIListItemLayout *CGUIEPGGridContainer::GetFocusedLayout() const +{ + CGUIListItemPtr item = GetListItem(0); + + if (item.get()) return item->GetFocusedLayout(); + + return NULL; +} + +bool CGUIEPGGridContainer::SelectItemFromPoint(const CPoint &point) +{ + /* point has already had origin set to m_posX, m_posY */ + if (!m_focusedProgrammeLayout || !m_programmeLayout) + return false; + + int channel = (int)(point.y / m_channelHeight); + int block = (int)(point.x / m_blockSize); + + if (channel > m_channelsPerPage) channel = m_channelsPerPage - 1; + if (channel >= m_channels) channel = m_channels - 1; + if (channel < 0) channel = 0; + if (block > m_blocksPerPage) block = m_blocksPerPage - 1; + if (block < 0) block = 0; + + SetChannel(channel); + SetBlock(block); + return true; +} + +EVENT_RESULT CGUIEPGGridContainer::OnMouseEvent(const CPoint &point, const CMouseEvent &event) +{ + switch (event.m_id) + { + case ACTION_MOUSE_LEFT_CLICK: + OnMouseClick(0, point); + return EVENT_RESULT_HANDLED; + case ACTION_MOUSE_RIGHT_CLICK: + OnMouseClick(1, point); + return EVENT_RESULT_HANDLED; + case ACTION_MOUSE_DOUBLE_CLICK: + OnMouseDoubleClick(0, point); + return EVENT_RESULT_HANDLED; + case ACTION_MOUSE_WHEEL_UP: + OnMouseWheel(-1, point); + return EVENT_RESULT_HANDLED; + case ACTION_MOUSE_WHEEL_DOWN: + OnMouseWheel(1, point); + return EVENT_RESULT_HANDLED; + default: + return EVENT_RESULT_UNHANDLED; + } +} + +bool CGUIEPGGridContainer::OnMouseOver(const CPoint &point) +{ + // select the item under the pointer + SelectItemFromPoint(point - CPoint(m_gridPosX, m_posY + m_rulerHeight)); + return CGUIControl::OnMouseOver(point); +} + +bool CGUIEPGGridContainer::OnMouseClick(int dwButton, const CPoint &point) +{ + if (SelectItemFromPoint(point - CPoint(m_gridPosX, m_posY + m_rulerHeight))) + { // send click message to window + OnClick(ACTION_MOUSE_LEFT_CLICK + dwButton); + return true; + } + + return false; +} + +bool CGUIEPGGridContainer::OnMouseDoubleClick(int dwButton, const CPoint &point) +{ + if (SelectItemFromPoint(point - CPoint(m_gridPosX, m_posY + m_rulerHeight))) + { // send double click message to window + OnClick(ACTION_MOUSE_DOUBLE_CLICK + dwButton); + return true; + } + + return false; +} + +bool CGUIEPGGridContainer::OnClick(int actionID) +{ + int subItem = 0; + + if (actionID == ACTION_SELECT_ITEM || actionID == ACTION_MOUSE_LEFT_CLICK) + { + // grab the currently focused subitem (if applicable) + CGUIListItemLayout *focusedLayout = GetFocusedLayout(); + + if (focusedLayout) + subItem = focusedLayout->GetFocusedItem(); + } + + // Don't know what to do, so send to our parent window. + CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID(), actionID, subItem); + return SendWindowMessage(msg); +} + +bool CGUIEPGGridContainer::OnMouseWheel(char wheel, const CPoint &point) +{ + ///doesn't work while an item is selected? + ProgrammesScroll(-wheel); + return true; +} + +int CGUIEPGGridContainer::GetSelectedItem() const +{ + if (!m_gridIndex || + !m_epgItemsPtr.size() || + m_channelCursor + m_channelCursor >= (int)m_channelItems.size() || + m_blockCursor + m_blockOffset >= (int)m_programmeItems.size()) + return 0; + + CGUIListItemPtr currentItem = m_gridIndex[m_channelCursor + m_channelOffset][m_blockCursor + m_blockOffset].item; + if (!currentItem) + return 0; + + for (int i = 0; i < (int)m_programmeItems.size(); i++) + { + if (currentItem == m_programmeItems[i]) + return i; + } + return 0; +} + +CGUIListItemPtr CGUIEPGGridContainer::GetListItem(int offset) const +{ + if (!m_epgItemsPtr.size()) + return CGUIListItemPtr(); + + return m_item->item; +} + +GridItemsPtr *CGUIEPGGridContainer::GetClosestItem(const int &channel) +{ + GridItemsPtr *closest = GetItem(channel); + + if(!closest) + return NULL; + + int block = GetBlock(closest->item, channel); + int left; // num blocks to start of previous item + int right; // num blocks to start of next item + + if (block == m_blockCursor) + return closest; // item & m_item start together + + if (block + GetItemSize(closest) == m_blockCursor + GetItemSize(m_item)) + return closest; // closest item ends when current does + + if (block > m_blockCursor) // item starts after m_item + { + left = m_blockCursor - GetBlock(closest->item, channel); + right = block - m_blockCursor; + } + else + { + left = m_blockCursor - block; + right = GetBlock(GetNextItem(channel)->item, channel) - m_blockCursor; + } + + if (right <= SHORTGAP && right <= left && m_blockCursor + right < m_blocksPerPage) + return &m_gridIndex[channel + m_channelOffset][m_blockCursor + right + m_blockOffset]; + + return &m_gridIndex[channel + m_channelOffset][m_blockCursor - left + m_blockOffset]; +} + +int CGUIEPGGridContainer::GetItemSize(GridItemsPtr *item) +{ + if (!item) + return (int) m_blockSize; /// stops it crashing + + return (int) ((m_orientation == VERTICAL ? item->width : item->height) / m_blockSize); +} + +int CGUIEPGGridContainer::GetBlock(const CGUIListItemPtr &item, const int &channel) +{ + if (!item) + return 0; + + return GetRealBlock(item, channel) - m_blockOffset; +} + +int CGUIEPGGridContainer::GetRealBlock(const CGUIListItemPtr &item, const int &channel) +{ + int block = 0; + + while (m_gridIndex[channel + m_channelOffset][block].item != item && block < m_blocks) + block++; + + return block; +} + +GridItemsPtr *CGUIEPGGridContainer::GetNextItem(const int &channel) +{ + int i = m_blockCursor; + + while (m_gridIndex[channel + m_channelOffset][i + m_blockOffset].item == m_gridIndex[channel + m_channelOffset][m_blockCursor + m_blockOffset].item && i < m_blocksPerPage) + i++; + + return &m_gridIndex[channel + m_channelOffset][i + m_blockOffset]; +} + +GridItemsPtr *CGUIEPGGridContainer::GetPrevItem(const int &channel) +{ + int i = m_blockCursor; + + while (m_gridIndex[channel + m_channelOffset][i + m_blockOffset].item == m_gridIndex[channel + m_channelOffset][m_blockCursor + m_blockOffset].item && i > 0) + i--; + + return &m_gridIndex[channel + m_channelOffset][i + m_blockOffset]; + +// return &m_gridIndex[channel + m_channelOffset][m_blockCursor + m_blockOffset - 1]; +} + +GridItemsPtr *CGUIEPGGridContainer::GetItem(const int &channel) +{ + if ( (channel >= 0) && (channel < m_channels) ) + return &m_gridIndex[channel + m_channelOffset][m_blockCursor + m_blockOffset]; + else + return NULL; +} + +void CGUIEPGGridContainer::SetFocus(bool bOnOff) +{ + if (bOnOff != HasFocus()) + { + SetInvalid(); + /*m_lastItem.reset(); + m_lastChannel.reset();*/ + } + + CGUIControl::SetFocus(bOnOff); +} + +void CGUIEPGGridContainer::DoRender() +{ + CGUIControl::DoRender(); + m_wasReset = false; +} + +void CGUIEPGGridContainer::ScrollToChannelOffset(int offset) +{ + float size = m_programmeLayout->Size(VERTICAL); + int range = m_channelsPerPage / 4; + + if (range <= 0) range = 1; + + if (offset * size < m_channelScrollOffset && m_channelScrollOffset - offset * size > size * range) + { // scrolling up, and we're jumping more than 0.5 of a screen + m_channelScrollOffset = (offset + range) * size; + } + + if (offset * size > m_channelScrollOffset && offset * size - m_channelScrollOffset > size * range) + { // scrolling down, and we're jumping more than 0.5 of a screen + m_channelScrollOffset = (offset - range) * size; + } + + m_channelScrollSpeed = (offset * size - m_channelScrollOffset) / m_scrollTime; + + m_channelOffset = offset; +} + +void CGUIEPGGridContainer::ScrollToBlockOffset(int offset) +{ + float size = m_blockSize; + int range = m_blocksPerPage / 1; + + if (range <= 0) range = 1; + + if (offset * size < m_programmeScrollOffset && m_programmeScrollOffset - offset * size > size * range) + { // scrolling left, and we're jumping more than 0.5 of a screen + m_programmeScrollOffset = (offset + range) * size; + } + + if (offset * size > m_programmeScrollOffset && offset * size - m_programmeScrollOffset > size * range) + { // scrolling right, and we're jumping more than 0.5 of a screen + m_programmeScrollOffset = (offset - range) * size; + } + + m_programmeScrollSpeed = (offset * size - m_programmeScrollOffset) / m_scrollTime; + + m_blockOffset = offset; +} + +void CGUIEPGGridContainer::ValidateOffset() +{ + if (!m_programmeLayout) + return; + + if (m_channelOffset > m_channels - m_channelsPerPage) + { + m_channelOffset = m_channels - m_channelsPerPage; + m_channelScrollOffset = m_channelOffset * m_channelHeight; + } + + if (m_channelOffset < 0) + { + m_channelOffset = 0; + m_channelScrollOffset = 0; + } + + if (m_blockOffset > m_blocks - m_blocksPerPage) + { + m_blockOffset = m_blocks - m_blocksPerPage; + m_programmeScrollOffset = m_blockOffset * m_blockSize; + } + + if (m_blockOffset < 0) + { + m_blockOffset = 0; + m_programmeScrollOffset = 0; + } +} + +void CGUIEPGGridContainer::LoadLayout(TiXmlElement *layout) +{ + /* layouts for the channel column */ + TiXmlElement *itemElement = layout->FirstChildElement("channellayout"); + while (itemElement) + { // we have a new item layout + CGUIListItemLayout itemLayout; + itemLayout.LoadLayout(itemElement, GetParentID(), false); + m_channelLayouts.push_back(itemLayout); + itemElement = itemElement->NextSiblingElement("channellayout"); + } + itemElement = layout->FirstChildElement("focusedchannellayout"); + while (itemElement) + { // we have a new item layout + CGUIListItemLayout itemLayout; + itemLayout.LoadLayout(itemElement, GetParentID(), true); + m_focusedChannelLayouts.push_back(itemLayout); + itemElement = itemElement->NextSiblingElement("focusedchannellayout"); + } + + /* layouts for the grid items */ + itemElement = layout->FirstChildElement("focusedlayout"); + while (itemElement) + { + CGUIListItemLayout itemLayout; + itemLayout.LoadLayout(itemElement, GetParentID(), true); + m_focusedProgrammeLayouts.push_back(itemLayout); + itemElement = itemElement->NextSiblingElement("focusedlayout"); + } + itemElement = layout->FirstChildElement("itemlayout"); + while (itemElement) + { + CGUIListItemLayout itemLayout; + itemLayout.LoadLayout(itemElement, GetParentID(), false); + m_programmeLayouts.push_back(itemLayout); + itemElement = itemElement->NextSiblingElement("itemlayout"); + } + + /* layout for the timeline above the grid */ + itemElement = layout->FirstChildElement("rulerlayout"); + while (itemElement) + { + CGUIListItemLayout itemLayout; + itemLayout.LoadLayout(itemElement, GetParentID(), false); + m_rulerLayouts.push_back(itemLayout); + itemElement = itemElement->NextSiblingElement("rulerlayout"); + } +} + +void CGUIEPGGridContainer::UpdateLayout(bool updateAllItems) +{ + // if container is invalid, either new data has arrived, or m_blockSize has changed + // need to run UpdateItems rather than CalculateLayout? + if (updateAllItems) + { // free memory of items + for (iItems it = m_channelItems.begin(); it != m_channelItems.end(); it++) + (*it)->FreeMemory(); + for (iItems it = m_rulerItems.begin(); it != m_rulerItems.end(); it++) + (*it)->FreeMemory(); + for (iItems it = m_programmeItems.begin(); it != m_programmeItems.end(); it++) + (*it)->FreeMemory(); + } + + // and recalculate the layout + CalculateLayout(); +} + +CStdString CGUIEPGGridContainer::GetDescription() const +{ + CStdString strLabel; + int item = GetSelectedItem(); + if (item >= 0 && item < (int)m_programmeItems.size()) + { + CGUIListItemPtr pItem = m_programmeItems[item]; + strLabel = pItem->GetLabel(); + } + return strLabel; +} + +void CGUIEPGGridContainer::ClearGridIndex(void) +{ + if (m_gridIndex) + { + for (unsigned int i = 0; i < m_channelItems.size(); i++) + { + for (int block = 0; block < m_blocks; block++) + { + if (m_gridIndex[i][block].item) + m_gridIndex[i][block].item.get()->ClearProperties(); + } + free(m_gridIndex[i]); + } + free(m_gridIndex); + } +} + +void CGUIEPGGridContainer::Reset() +{ + ClearGridIndex(); + + m_wasReset = true; + m_channelItems.clear(); + m_programmeItems.clear(); + m_rulerItems.clear(); + m_epgItemsPtr.clear(); + + m_lastItem = NULL; + m_lastChannel = NULL; + m_gridIndex = NULL; +} + +void CGUIEPGGridContainer::GoToBegin() +{ + ScrollToBlockOffset(0); + SetBlock(0); +} + +void CGUIEPGGridContainer::GoToEnd() +{ + int blocksEnd = 0; // the end block of the last epg element for the selected channel + int blocksStart = 0; // the start block of the last epg element for the selected channel + int blockOffset = 0; // the block offset to scroll to + for (int blockIndex = m_blocks; blockIndex >= 0 && (!blocksEnd || !blocksStart); blockIndex--) + { + if (!blocksEnd && m_gridIndex[m_channelCursor + m_channelOffset][blockIndex].item != NULL) + blocksEnd = blockIndex; + if (blocksEnd && m_gridIndex[m_channelCursor + m_channelOffset][blocksEnd].item != + m_gridIndex[m_channelCursor + m_channelOffset][blockIndex].item) + blocksStart = blockIndex + 1; + } + if (blocksEnd - blocksStart > m_blocksPerPage) + blockOffset = blocksStart; + else if (blocksEnd > m_blocksPerPage) + blockOffset = blocksEnd - m_blocksPerPage; + + ScrollToBlockOffset(blockOffset); // scroll to the start point of the last epg element + SetBlock(m_blocksPerPage - 1); // select the last epg element +} + +void CGUIEPGGridContainer::SetStartEnd(CDateTime start, CDateTime end) +{ + m_gridStart = CDateTime(start.GetYear(), start.GetMonth(), start.GetDay(), start.GetHour(), start.GetMinute() >= 30 ? 30 : 0, 0); + m_gridEnd = CDateTime(end.GetYear(), end.GetMonth(), end.GetDay(), end.GetHour(), end.GetMinute() >= 30 ? 30 : 0, 0); + + CLog::Log(LOGDEBUG, "CGUIEPGGridContainer - %s - start=%s end=%s", + __FUNCTION__, m_gridStart.GetAsLocalizedDateTime(false, true).c_str(), m_gridEnd.GetAsLocalizedDateTime(false, true).c_str()); +} + +void CGUIEPGGridContainer::CalculateLayout() +{ + CGUIListItemLayout *oldFocusedChannelLayout = m_focusedChannelLayout; + CGUIListItemLayout *oldChannelLayout = m_channelLayout; + CGUIListItemLayout *oldFocusedProgrammeLayout = m_focusedProgrammeLayout; + CGUIListItemLayout *oldProgrammeLayout = m_programmeLayout; + CGUIListItemLayout *oldRulerLayout = m_rulerLayout; + GetCurrentLayouts(); + + if (!m_focusedProgrammeLayout || !m_programmeLayout || !m_focusedChannelLayout || !m_channelLayout || !m_rulerLayout) + return; + + if (oldChannelLayout == m_channelLayout && oldFocusedChannelLayout == m_focusedChannelLayout && + oldProgrammeLayout == m_programmeLayout && oldFocusedProgrammeLayout == m_focusedProgrammeLayout && + oldRulerLayout == m_rulerLayout) + return; // nothing has changed, so don't update stuff + + m_channelHeight = m_channelLayout->Size(VERTICAL); + m_channelWidth = m_channelLayout->Size(HORIZONTAL); + if (m_orientation == VERTICAL) + { + m_rulerHeight = m_rulerLayout->Size(VERTICAL); + m_gridPosX = m_posX + m_channelWidth; + m_gridPosY = m_posY + m_rulerHeight; + m_gridWidth = m_width - m_channelWidth; + m_gridHeight = m_height - m_rulerHeight; + m_blockSize = m_gridWidth / m_blocksPerPage; + m_rulerWidth = m_rulerUnit * m_blockSize; + m_channelPosX = m_posX; + m_channelPosY = m_posY + m_rulerHeight; + m_rulerPosX = m_posX + m_channelWidth; + m_rulerPosY = m_posY; + m_channelsPerPage = (int)(m_gridHeight / m_channelHeight); + m_ProgrammesPerPage = (int)(m_gridWidth / m_blockSize) + 1; + } + else + { + m_rulerWidth = m_rulerLayout->Size(HORIZONTAL); + m_gridPosX = m_posX + m_rulerWidth; + m_gridPosY = m_posY + m_channelHeight; + m_gridWidth = m_width - m_rulerWidth; + m_gridHeight = m_height - m_channelHeight; + m_blockSize = m_gridHeight / m_blocksPerPage; + m_rulerHeight = m_rulerUnit * m_blockSize; + m_channelPosX = m_posX + m_rulerWidth; + m_channelPosY = m_posY; + m_rulerPosX = m_posX; + m_rulerPosY = m_posY + m_channelHeight; + m_channelsPerPage = (int)(m_gridWidth / m_channelWidth); + m_ProgrammesPerPage = (int)(m_gridHeight / m_blockSize) + 1; + } + + // ensure that the scroll offsets are a multiple of our sizes + m_channelScrollOffset = m_channelOffset * m_programmeLayout->Size(m_orientation); + m_programmeScrollOffset = m_blockOffset * m_blockSize; +} + +void CGUIEPGGridContainer::UpdateScrollOffset() +{ + m_channelScrollOffset += m_channelScrollSpeed * (m_renderTime - m_channelScrollLastTime); + if ((m_channelScrollSpeed < 0 && m_channelScrollOffset < m_channelOffset * m_programmeLayout->Size(m_orientation)) || + (m_channelScrollSpeed > 0 && m_channelScrollOffset > m_channelOffset * m_programmeLayout->Size(m_orientation))) + { + m_channelScrollOffset = m_channelOffset * m_programmeLayout->Size(m_orientation); + m_channelScrollSpeed = 0; + } + m_channelScrollLastTime = m_renderTime; + + m_programmeScrollOffset += m_programmeScrollSpeed * (m_renderTime - m_programmeScrollLastTime); + if ((m_programmeScrollSpeed < 0 && m_programmeScrollOffset < m_blockOffset * m_blockSize) || + (m_programmeScrollSpeed > 0 && m_programmeScrollOffset > m_blockOffset * m_blockSize)) + { + m_programmeScrollOffset = m_blockOffset * m_blockSize; + m_programmeScrollSpeed = 0; + } + m_programmeScrollLastTime = m_renderTime; +} + +void CGUIEPGGridContainer::GetCurrentLayouts() +{ + m_channelLayout = NULL; + for (unsigned int i = 0; i < m_channelLayouts.size(); i++) + { + if (m_channelLayouts[i].CheckCondition()) + { + m_channelLayout = &m_channelLayouts[i]; + break; + } + } + if (!m_channelLayout && m_channelLayouts.size()) + m_channelLayout = &m_channelLayouts[0]; // failsafe + + m_focusedChannelLayout = NULL; + for (unsigned int i = 0; i < m_focusedChannelLayouts.size(); i++) + { + if (m_focusedChannelLayouts[i].CheckCondition()) + { + m_focusedChannelLayout = &m_focusedChannelLayouts[i]; + break; + } + } + if (!m_focusedChannelLayout && m_focusedChannelLayouts.size()) + m_focusedChannelLayout = &m_focusedChannelLayouts[0]; // failsafe + + m_programmeLayout = NULL; + for (unsigned int i = 0; i < m_programmeLayouts.size(); i++) + { + if (m_programmeLayouts[i].CheckCondition()) + { + m_programmeLayout = &m_programmeLayouts[i]; + break; + } + } + if (!m_programmeLayout && m_programmeLayouts.size()) + m_programmeLayout = &m_programmeLayouts[0]; // failsafe + + m_focusedProgrammeLayout = NULL; + for (unsigned int i = 0; i < m_focusedProgrammeLayouts.size(); i++) + { + if (m_focusedProgrammeLayouts[i].CheckCondition()) + { + m_focusedProgrammeLayout = &m_focusedProgrammeLayouts[i]; + break; + } + } + if (!m_focusedProgrammeLayout && m_focusedProgrammeLayouts.size()) + m_focusedProgrammeLayout = &m_focusedProgrammeLayouts[0]; // failsafe + + m_rulerLayout = NULL; + for (unsigned int i = 0; i < m_rulerLayouts.size(); i++) + { + if (m_rulerLayouts[i].CheckCondition()) + { + m_rulerLayout = &m_rulerLayouts[i]; + break; + } + } + if (!m_rulerLayout && m_rulerLayouts.size()) + m_rulerLayout = &m_rulerLayouts[0]; // failsafe +} + +int CGUIEPGGridContainer::CorrectOffset(int offset, int cursor) const +{ + return offset + cursor; +} + +void CGUIEPGGridContainer::SetRenderOffset(const CPoint &offset) +{ + m_renderOffset = offset; +} + +void CGUIEPGGridContainer::FreeChannelMemory(int keepStart, int keepEnd) +{ + if (keepStart < keepEnd) + { // remove before keepStart and after keepEnd + for (int i = 0; i < keepStart && i < (int)m_channelItems.size(); ++i) + m_channelItems[i]->FreeMemory(); + for (int i = keepEnd + 1; i < (int)m_channelItems.size(); ++i) + m_channelItems[i]->FreeMemory(); + } + else + { // wrapping + for (int i = keepEnd + 1; i < keepStart && i < (int)m_channelItems.size(); ++i) + m_channelItems[i]->FreeMemory(); + } +} + +void CGUIEPGGridContainer::FreeProgrammeMemory(int keepStart, int keepEnd) +{ + if (keepStart < keepEnd) + { // remove before keepStart and after keepEnd + for (unsigned int i = 0; i < m_epgItemsPtr.size(); i++) + { + unsigned long progIdx = m_epgItemsPtr[i].start; + unsigned long lastIdx = m_epgItemsPtr[i].stop; + + for (unsigned int j = progIdx; j < keepStart+progIdx && j < lastIdx; ++j) + m_programmeItems[j]->FreeMemory(); + for (unsigned int j = keepEnd+progIdx + 1; j < lastIdx; ++j) + m_programmeItems[j]->FreeMemory(); + } + } + else + { // wrapping + for (unsigned int i = 0; i < m_epgItemsPtr.size(); i++) + { + unsigned long progIdx = m_epgItemsPtr[i].start; + unsigned long lastIdx = m_epgItemsPtr[i].stop; + + for (unsigned int j = keepEnd+progIdx + 1; j < keepStart+progIdx && j < lastIdx; ++j) + m_programmeItems[j]->FreeMemory(); + } + } +} + +void CGUIEPGGridContainer::FreeRulerMemory(int keepStart, int keepEnd) +{ + if (keepStart < keepEnd) + { // remove before keepStart and after keepEnd + for (int i = 1; i < keepStart && i < (int)m_rulerItems.size(); ++i) + m_rulerItems[i]->FreeMemory(); + for (int i = keepEnd + 1; i < (int)m_rulerItems.size(); ++i) + m_rulerItems[i]->FreeMemory(); + } + else + { // wrapping + for (int i = keepEnd + 1; i < keepStart && i < (int)m_rulerItems.size(); ++i) + { + if (i == 0) + continue; + m_rulerItems[i]->FreeMemory(); + } + } +} + +void CGUIEPGGridContainer::GetChannelCacheOffsets(int &cacheBefore, int &cacheAfter) +{ + if (m_channelScrollSpeed > 0) + { + cacheBefore = 0; + cacheAfter = m_cacheChannelItems; + } + else if (m_channelScrollSpeed < 0) + { + cacheBefore = m_cacheChannelItems; + cacheAfter = 0; + } + else + { + cacheBefore = m_cacheChannelItems / 2; + cacheAfter = m_cacheChannelItems / 2; + } +} + +void CGUIEPGGridContainer::GetProgrammeCacheOffsets(int &cacheBefore, int &cacheAfter) +{ + if (m_programmeScrollSpeed > 0) + { + cacheBefore = 0; + cacheAfter = m_cacheProgrammeItems; + } + else if (m_programmeScrollSpeed < 0) + { + cacheBefore = m_cacheProgrammeItems; + cacheAfter = 0; + } + else + { + cacheBefore = m_cacheProgrammeItems / 2; + cacheAfter = m_cacheProgrammeItems / 2; + } +} + +void CGUIEPGGridContainer::GetRulerCacheOffsets(int &cacheBefore, int &cacheAfter) +{ + if (m_programmeScrollSpeed > 0) + { + cacheBefore = 0; + cacheAfter = m_cacheRulerItems; + } + else if (m_programmeScrollSpeed < 0) + { + cacheBefore = m_cacheRulerItems; + cacheAfter = 0; + } + else + { + cacheBefore = m_cacheRulerItems / 2; + cacheAfter = m_cacheRulerItems / 2; + } +} diff --git a/xbmc/epg/GUIEPGGridContainer.h b/xbmc/epg/GUIEPGGridContainer.h new file mode 100644 index 0000000000..ec79f47070 --- /dev/null +++ b/xbmc/epg/GUIEPGGridContainer.h @@ -0,0 +1,230 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "XBDateTime.h" +#include "FileItem.h" +#include "guilib/GUIControl.h" +#include "guilib/GUIListItemLayout.h" + +namespace PVR +{ + class CGUIWindowPVRGuide; +} + +namespace EPG +{ + #define MAXCHANNELS 20 + #define MAXBLOCKS 2304 //! !!_EIGHT_!! days of 5 minute blocks + + struct GridItemsPtr + { + CGUIListItemPtr item; + float width; + float height; + }; + + class CGUIEPGGridContainer : public CGUIControl + { + friend class PVR::CGUIWindowPVRGuide; + + public: + CGUIEPGGridContainer(int parentID, int controlID, float posX, float posY, float width, float height, + ORIENTATION orientation, int scrollTime, int preloadItems, int minutesPerPage, + int rulerUnit); + virtual ~CGUIEPGGridContainer(void); + virtual CGUIEPGGridContainer *Clone() const { return new CGUIEPGGridContainer(*this); }; + + virtual bool OnAction(const CAction &action); + virtual void OnDown(); + virtual void OnUp(); + virtual void OnLeft(); + virtual void OnRight(); + virtual bool OnMouseOver(const CPoint &point); + virtual bool OnMouseClick(int dwButton, const CPoint &point); + virtual bool OnMouseDoubleClick(int dwButton, const CPoint &point); + virtual bool OnMouseWheel(char wheel, const CPoint &point); + virtual bool OnMessage(CGUIMessage& message); + virtual void SetFocus(bool bOnOff); + + virtual CStdString GetDescription() const; + const int GetNumChannels() { return m_channels; }; + virtual int GetSelectedItem() const; + const int GetSelectedChannel() { return m_channelCursor + m_channelOffset; } + virtual EVENT_RESULT OnMouseEvent(const CPoint &point, const CMouseEvent &event); + + virtual void Process(unsigned int currentTime, CDirtyRegionList &dirtyregions); + virtual void DoRender(); + virtual void Render(); + void LoadLayout(TiXmlElement *layout); + void LoadContent(TiXmlElement *content); + + virtual bool IsContainer() const { return true; }; + CGUIListItemPtr GetListItem(int offset) const; + + virtual int CorrectOffset(int offset, int cursor) const; + + /*! \brief Set the offset of the first item in the container from the container's position + Useful for lists/panels where the focused item may be larger than the non-focused items and thus + normally cut off from the clipping window defined by the container's position + size. + \param offset CPoint holding the offset in skin coordinates. + */ + void SetRenderOffset(const CPoint &offset); + + void GoToBegin(); + void GoToEnd(); + void SetStartEnd(CDateTime start, CDateTime end); + void SetChannel(const PVR::CPVRChannel &channel); + void SetChannel(const CStdString &channel); + + protected: + bool OnClick(int actionID); + bool SelectItemFromPoint(const CPoint &point); + + void UpdateItems(); + + void SetChannel(int channel); + void SetBlock(int block); + void ChannelScroll(int amount); + void ProgrammesScroll(int amount); + void ValidateOffset(); + void UpdateLayout(bool refreshAllItems = false); + void CalculateLayout(); + void Reset(); + void ClearGridIndex(void); + + GridItemsPtr *GetItem(const int &channel); + GridItemsPtr *GetNextItem(const int &channel); + GridItemsPtr *GetPrevItem(const int &channel); + GridItemsPtr *GetClosestItem(const int &channel); + + int GetItemSize(GridItemsPtr *item); + int GetBlock(const CGUIListItemPtr &item, const int &channel); + int GetRealBlock(const CGUIListItemPtr &item, const int &channel); + void MoveToRow(int row); + bool MoveChannel(bool direction); + bool MoveProgrammes(bool direction); + + CGUIListItemLayout *GetFocusedLayout() const; + + void ScrollToBlockOffset(int offset); + void ScrollToChannelOffset(int offset); + void UpdateScrollOffset(); + void RenderChannelItem(float posX, float posY, CGUIListItem *item, bool focused); + void RenderProgrammeItem(float posX, float posY, float width, float height, CGUIListItem *item, bool focused); + void GetCurrentLayouts(); + + CPoint m_renderOffset; ///< \brief render offset of the first item in the list \sa SetRenderOffset + + ORIENTATION m_orientation; + + struct ItemsPtr + { + long start; + long stop; + }; + std::vector< ItemsPtr > m_epgItemsPtr; + std::vector< CGUIListItemPtr > m_channelItems; + std::vector< CGUIListItemPtr > m_rulerItems; + std::vector< CGUIListItemPtr > m_programmeItems; + typedef std::vector<CGUIListItemPtr> ::iterator iItems; + + std::vector<CGUIListItemLayout> m_channelLayouts; + std::vector<CGUIListItemLayout> m_focusedChannelLayouts; + std::vector<CGUIListItemLayout> m_focusedProgrammeLayouts; + std::vector<CGUIListItemLayout> m_programmeLayouts; + std::vector<CGUIListItemLayout> m_rulerLayouts; + + CGUIListItemLayout *m_channelLayout; + CGUIListItemLayout *m_focusedChannelLayout; + CGUIListItemLayout *m_programmeLayout; + CGUIListItemLayout *m_focusedProgrammeLayout; + CGUIListItemLayout *m_rulerLayout; + + bool m_wasReset; // true if we've received a Reset message until we've rendered once. Allows + // us to make sure we don't tell the infomanager that we've been moving when + // the "movement" was simply due to the list being repopulated (thus cursor position + // changing around) + + void FreeChannelMemory(int keepStart, int keepEnd); + void FreeProgrammeMemory(int keepStart, int keepEnd); + void FreeRulerMemory(int keepStart, int keepEnd); + + void GetChannelCacheOffsets(int &cacheBefore, int &cacheAfter); + void GetProgrammeCacheOffsets(int &cacheBefore, int &cacheAfter); + void GetRulerCacheOffsets(int &cacheBefore, int &cacheAfter); + + private: + int m_rulerUnit; //! number of blocks that makes up one element of the ruler + int m_channels; + int m_channelsPerPage; + int m_ProgrammesPerPage; + int m_channelCursor; + int m_channelOffset; + int m_blocks; + int m_blocksPerPage; + int m_blockCursor; + int m_blockOffset; + int m_cacheChannelItems; + int m_cacheProgrammeItems; + int m_cacheRulerItems; + + float m_rulerPosX; //! X position of first ruler item + float m_rulerPosY; //! Y position of first ruler item + float m_rulerHeight; //! height of the scrolling timeline above the ruler items + float m_rulerWidth; //! width of each element of the ruler + float m_channelPosX; //! Y position of first channel row + float m_channelPosY; //! Y position of first channel row + float m_channelHeight; //! height of each channel row (& every grid item) + float m_channelWidth; //! width of the channel item + float m_gridPosX; //! X position of first grid item + float m_gridPosY; //! Y position of first grid item + float m_gridWidth; + float m_gridHeight; + float m_blockSize; //! a block's width in pixels + float m_analogScrollCount; + + CDateTime m_gridStart; + CDateTime m_gridEnd; + + struct GridItemsPtr **m_gridIndex; + GridItemsPtr *m_item; + CGUIListItem *m_lastItem; + CGUIListItem *m_lastChannel; + + unsigned int m_renderTime; + + int m_scrollTime; + bool m_channelWrapAround; + bool m_gridWrapAround; //! only when no more data available should this be true + + int m_programmeScrollLastTime; + float m_programmeScrollSpeed; + float m_programmeScrollOffset; + + int m_channelScrollLastTime; + float m_channelScrollSpeed; + float m_channelScrollOffset; + + CStdString m_label; + }; +} diff --git a/xbmc/epg/Makefile b/xbmc/epg/Makefile new file mode 100644 index 0000000000..a52ad6113e --- /dev/null +++ b/xbmc/epg/Makefile @@ -0,0 +1,13 @@ +INCLUDES=-I. -I.. -I../../ -I../linux -I../cores -I../../guilib -I../posix -I../utils + +SRCS=EpgInfoTag.cpp \ + EpgSearchFilter.cpp \ + Epg.cpp \ + EpgContainer.cpp \ + EpgDatabase.cpp \ + GUIEPGGridContainer.cpp + +LIB=epg.a + +include ../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/xbmc/filesystem/AddonsDirectory.cpp b/xbmc/filesystem/AddonsDirectory.cpp index 3b36459915..da51ee7566 100644 --- a/xbmc/filesystem/AddonsDirectory.cpp +++ b/xbmc/filesystem/AddonsDirectory.cpp @@ -31,6 +31,7 @@ #include "addons/PluginSource.h" #include "guilib/TextureManager.h" #include "File.h" +#include "SpecialProtocol.h" #include "utils/URIUtils.h" #include "URL.h" @@ -201,6 +202,7 @@ bool CAddonsDirectory::GetDirectory(const CStdString& strPath, CFileItemList &it void CAddonsDirectory::GenerateListing(CURL &path, VECADDONS& addons, CFileItemList &items, bool reposAsFolders) { + CStdString xbmcPath = CSpecialProtocol::TranslatePath("special://xbmc/addons"); items.ClearItems(); for (unsigned i=0; i < addons.size(); i++) { @@ -213,6 +215,9 @@ void CAddonsDirectory::GenerateListing(CURL &path, VECADDONS& addons, CFileItemL AddonPtr addon2; if (CAddonMgr::Get().GetAddon(addon->ID(),addon2)) pItem->SetProperty("Addon.Status",g_localizeStrings.Get(305)); + else if ((addon->Type() == ADDON_PVRDLL) && (CStdString(pItem->GetProperty("Addon.Path").asString()).Left(xbmcPath.size()).Equals(xbmcPath))) + pItem->SetProperty("Addon.Status",g_localizeStrings.Get(24023)); + if (!addon->Props().broken.IsEmpty()) pItem->SetProperty("Addon.Status",g_localizeStrings.Get(24098)); if (addon2 && addon2->Version() < addon->Version()) diff --git a/xbmc/filesystem/DirectoryFactory.cpp b/xbmc/filesystem/DirectoryFactory.cpp index 00eb9a3dab..289934ea35 100644 --- a/xbmc/filesystem/DirectoryFactory.cpp +++ b/xbmc/filesystem/DirectoryFactory.cpp @@ -77,6 +77,9 @@ #ifdef HAS_FILESYSTEM_HTSP #include "HTSPDirectory.h" #endif +#ifdef HAS_PVRCLIENTS +#include "PVRDirectory.h" +#endif #if defined(TARGET_ANDROID) #include "APKDirectory.h" #endif @@ -206,6 +209,9 @@ IDirectory* CDirectoryFactory::Create(const CStdString& strPath) #ifdef HAS_FILESYSTEM_HTSP if (strProtocol == "htsp") return new CHTSPDirectory(); #endif +#ifdef HAS_PVRCLIENTS + if (strProtocol == "pvr") return new CPVRDirectory(); +#endif #ifdef HAS_ZEROCONF if (strProtocol == "zeroconf") return new CZeroconfDirectory(); #endif diff --git a/xbmc/filesystem/DllLibCMyth.h b/xbmc/filesystem/DllLibCMyth.h index 2c3e9f36af..829a9b9e80 100644 --- a/xbmc/filesystem/DllLibCMyth.h +++ b/xbmc/filesystem/DllLibCMyth.h @@ -38,11 +38,17 @@ public: virtual cmyth_recorder_t conn_get_free_recorder (cmyth_conn_t conn)=0; virtual cmyth_recorder_t conn_get_recorder_from_num(cmyth_conn_t conn, int num)=0; + virtual int conn_get_freespace (cmyth_conn_t control,long long *total, long long *used)=0; + virtual int conn_hung (cmyth_conn_t control)=0; virtual cmyth_event_t event_get (cmyth_conn_t conn, char * data, int len)=0; virtual int event_select (cmyth_conn_t conn, struct timeval *timeout)=0; virtual cmyth_proglist_t proglist_get_all_recorded(cmyth_conn_t control)=0; + virtual cmyth_proglist_t proglist_get_all_scheduled(cmyth_conn_t control)=0; + virtual cmyth_proglist_t proglist_get_all_pending (cmyth_conn_t control)=0; + virtual cmyth_proglist_t proglist_get_conflicting (cmyth_conn_t control)=0; + virtual int mysql_get_guide(cmyth_database_t db, cmyth_program_t **prog, time_t starttime, time_t endtime) = 0; virtual cmyth_proginfo_t proglist_get_item (cmyth_proglist_t pl, int index)=0; virtual int proglist_get_count (cmyth_proglist_t pl)=0; @@ -140,11 +146,17 @@ class DllLibCMyth : public DllDynamic, DllLibCMythInterface DEFINE_METHOD1(cmyth_recorder_t, conn_get_free_recorder, (cmyth_conn_t p1)) DEFINE_METHOD2(cmyth_recorder_t, conn_get_recorder_from_num,(cmyth_conn_t p1, int p2)) + DEFINE_METHOD3(int, conn_get_freespace, (cmyth_conn_t p1, long long *p2, long long *p3)) + DEFINE_METHOD1(int, conn_hung, (cmyth_conn_t p1)) DEFINE_METHOD3(cmyth_event_t, event_get, (cmyth_conn_t p1, char * p2, int p3)) DEFINE_METHOD2(int, event_select, (cmyth_conn_t p1, struct timeval *p2)) DEFINE_METHOD1(cmyth_proglist_t, proglist_get_all_recorded, (cmyth_conn_t p1)) + DEFINE_METHOD1(cmyth_proglist_t, proglist_get_all_scheduled, (cmyth_conn_t p1)) + DEFINE_METHOD1(cmyth_proglist_t, proglist_get_all_pending, (cmyth_conn_t p1)) + DEFINE_METHOD1(cmyth_proglist_t, proglist_get_conflicting, (cmyth_conn_t p1)) + DEFINE_METHOD4(int, mysql_get_guide, (cmyth_database_t p1, cmyth_program_t **p2, time_t p3, time_t p4)) DEFINE_METHOD2(cmyth_proginfo_t, proglist_get_item, (cmyth_proglist_t p1, int p2)) DEFINE_METHOD1(int, proglist_get_count, (cmyth_proglist_t p1)) @@ -236,10 +248,15 @@ class DllLibCMyth : public DllDynamic, DllLibCMythInterface RESOLVE_METHOD_RENAME(cmyth_conn_connect_path, conn_connect_path) RESOLVE_METHOD_RENAME(cmyth_conn_get_free_recorder, conn_get_free_recorder) RESOLVE_METHOD_RENAME(cmyth_conn_get_recorder_from_num, conn_get_recorder_from_num) + RESOLVE_METHOD_RENAME(cmyth_conn_get_freespace, conn_get_freespace) + RESOLVE_METHOD_RENAME(cmyth_conn_hung, conn_hung) RESOLVE_METHOD_RENAME(cmyth_event_get, event_get) RESOLVE_METHOD_RENAME(cmyth_event_select, event_select) RESOLVE_METHOD_RENAME(cmyth_proglist_get_all_recorded, proglist_get_all_recorded) + RESOLVE_METHOD_RENAME(cmyth_proglist_get_all_scheduled, proglist_get_all_scheduled) + RESOLVE_METHOD_RENAME(cmyth_proglist_get_all_pending, proglist_get_all_pending) + RESOLVE_METHOD_RENAME(cmyth_proglist_get_conflicting, proglist_get_conflicting) RESOLVE_METHOD_RENAME(cmyth_mysql_get_guide, mysql_get_guide) RESOLVE_METHOD_RENAME(cmyth_proglist_get_item, proglist_get_item) RESOLVE_METHOD_RENAME(cmyth_proglist_get_count, proglist_get_count) diff --git a/xbmc/filesystem/FileFactory.cpp b/xbmc/filesystem/FileFactory.cpp index 9be1fb7b1c..6ee61eb325 100644 --- a/xbmc/filesystem/FileFactory.cpp +++ b/xbmc/filesystem/FileFactory.cpp @@ -55,6 +55,9 @@ #ifdef HAS_FILESYSTEM_VTP #include "VTPFile.h" #endif +#ifdef HAS_PVRCLIENTS +#include "PVRFile.h" +#endif #if defined(TARGET_ANDROID) #include "APKFile.h" #endif @@ -178,6 +181,9 @@ IFile* CFileFactory::CreateLoader(const CURL& url) #ifdef HAS_FILESYSTEM_VTP else if (strProtocol == "vtp") return new CVTPFile(); #endif +#ifdef HAS_PVRCLIENTS + else if (strProtocol == "pvr") return new CPVRFile(); +#endif #ifdef HAS_FILESYSTEM_NFS else if (strProtocol == "nfs") return new CNFSFile(); #endif diff --git a/xbmc/filesystem/ILiveTV.h b/xbmc/filesystem/ILiveTV.h index 36ac07d01c..3b2277bd2c 100644 --- a/xbmc/filesystem/ILiveTV.h +++ b/xbmc/filesystem/ILiveTV.h @@ -28,8 +28,8 @@ class ILiveTVInterface { public: virtual ~ILiveTVInterface() {} - virtual bool NextChannel() = 0; - virtual bool PrevChannel() = 0; + virtual bool NextChannel(bool preview = false) = 0; + virtual bool PrevChannel(bool preview = false) = 0; virtual bool SelectChannel(unsigned int channel) = 0; virtual int GetTotalTime() = 0; diff --git a/xbmc/filesystem/Makefile.in b/xbmc/filesystem/Makefile.in index b7519b7b04..26bee60640 100644 --- a/xbmc/filesystem/Makefile.in +++ b/xbmc/filesystem/Makefile.in @@ -60,6 +60,8 @@ SRCS=AddonsDirectory.cpp \ PipeFile.cpp \ PipesManager.cpp \ PluginDirectory.cpp \ + PVRFile.cpp \ + PVRDirectory.cpp \ RSSDirectory.cpp \ RTVDirectory.cpp \ RTVFile.cpp \ diff --git a/xbmc/filesystem/MythFile.cpp b/xbmc/filesystem/MythFile.cpp index 9711981370..47501c046b 100644 --- a/xbmc/filesystem/MythFile.cpp +++ b/xbmc/filesystem/MythFile.cpp @@ -632,12 +632,12 @@ bool CMythFile::ChangeChannel(int direction, const CStdString &channel) return true; } -bool CMythFile::NextChannel() +bool CMythFile::NextChannel(bool preview) { return ChangeChannel(CHANNEL_DIRECTION_UP, ""); } -bool CMythFile::PrevChannel() +bool CMythFile::PrevChannel(bool preview) { return ChangeChannel(CHANNEL_DIRECTION_DOWN, ""); } diff --git a/xbmc/filesystem/MythFile.h b/xbmc/filesystem/MythFile.h index 55de1d4246..74d7962e3c 100644 --- a/xbmc/filesystem/MythFile.h +++ b/xbmc/filesystem/MythFile.h @@ -62,8 +62,8 @@ public: virtual ILiveTVInterface* GetLiveTV() {return (ILiveTVInterface*)this;} - virtual bool NextChannel(); - virtual bool PrevChannel(); + virtual bool NextChannel(bool preview = false); + virtual bool PrevChannel(bool preview = false); virtual bool SelectChannel(unsigned int channel); virtual int GetTotalTime(); diff --git a/xbmc/filesystem/PVRDirectory.cpp b/xbmc/filesystem/PVRDirectory.cpp new file mode 100644 index 0000000000..f5c12b085a --- /dev/null +++ b/xbmc/filesystem/PVRDirectory.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "PVRDirectory.h" +#include "FileItem.h" +#include "Util.h" +#include "URL.h" +#include "utils/log.h" +#include "utils/URIUtils.h" +#include "guilib/LocalizeStrings.h" + +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/channels/PVRChannelGroup.h" +#include "pvr/recordings/PVRRecordings.h" +#include "pvr/timers/PVRTimers.h" + +using namespace std; +using namespace XFILE; +using namespace PVR; + +CPVRDirectory::CPVRDirectory() +{ +} + +CPVRDirectory::~CPVRDirectory() +{ +} + +bool CPVRDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items) +{ + CStdString base(strPath); + URIUtils::RemoveSlashAtEnd(base); + + CURL url(strPath); + CStdString fileName = url.GetFileName(); + URIUtils::RemoveSlashAtEnd(fileName); + CLog::Log(LOGDEBUG, "CPVRDirectory::GetDirectory(%s)", base.c_str()); + items.SetCacheToDisc(CFileItemList::CACHE_NEVER); + + if (!g_PVRManager.IsStarted()) + return false; + + if (fileName == "") + { + CFileItemPtr item; + + item.reset(new CFileItem(base + "/channels/", true)); + item->SetLabel(g_localizeStrings.Get(19019)); + item->SetLabelPreformated(true); + items.Add(item); + + item.reset(new CFileItem(base + "/recordings/", true)); + item->SetLabel(g_localizeStrings.Get(19017)); + item->SetLabelPreformated(true); + items.Add(item); + + item.reset(new CFileItem(base + "/timers/", true)); + item->SetLabel(g_localizeStrings.Get(19040)); + item->SetLabelPreformated(true); + items.Add(item); + + item.reset(new CFileItem(base + "/guide/", true)); + item->SetLabel(g_localizeStrings.Get(19029)); + item->SetLabelPreformated(true); + items.Add(item); + + // Sort by name only. Labels are preformated. + items.AddSortMethod(SORT_METHOD_LABEL, 551 /* Name */, LABEL_MASKS("%L", "", "%L", "")); + + return true; + } + else if (fileName.Left(10) == "recordings") + { + return g_PVRRecordings->GetDirectory(strPath, items); + } + else if (fileName.Left(8) == "channels") + { + return g_PVRChannelGroups->GetDirectory(strPath, items); + } + else if (fileName.Left(6) == "timers") + { + return g_PVRTimers->GetDirectory(strPath, items); + } + + return false; +} + +bool CPVRDirectory::SupportsFileOperations(const CStdString& strPath) +{ + CURL url(strPath); + CStdString filename = url.GetFileName(); + + return URIUtils::IsPVRRecording(filename); +} + +bool CPVRDirectory::IsLiveTV(const CStdString& strPath) +{ + CURL url(strPath); + CStdString filename = url.GetFileName(); + + return URIUtils::IsLiveTV(filename); +} + +bool CPVRDirectory::HasRecordings() +{ + return g_PVRRecordings->GetNumRecordings() > 0; +} diff --git a/xbmc/filesystem/PVRDirectory.h b/xbmc/filesystem/PVRDirectory.h new file mode 100644 index 0000000000..e7744dc44a --- /dev/null +++ b/xbmc/filesystem/PVRDirectory.h @@ -0,0 +1,46 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "IDirectory.h" + +class CPVRSession; + +namespace XFILE { + +class CPVRDirectory + : public IDirectory +{ +public: + CPVRDirectory(); + virtual ~CPVRDirectory(); + + virtual bool GetDirectory(const CStdString& strPath, CFileItemList &items); + virtual bool IsAllowed(const CStdString &strFile) const { return true; }; + + static bool SupportsFileOperations(const CStdString& strPath); + static bool IsLiveTV(const CStdString& strPath); + static bool HasRecordings(); + +private: +}; + +} diff --git a/xbmc/filesystem/PVRFile.cpp b/xbmc/filesystem/PVRFile.cpp new file mode 100644 index 0000000000..d3be8c3674 --- /dev/null +++ b/xbmc/filesystem/PVRFile.cpp @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "PVRFile.h" +#include "Util.h" +#include "cores/dvdplayer/DVDInputStreams/DVDInputStream.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/recordings/PVRRecordings.h" +#include "pvr/addons/PVRClients.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "URL.h" + +using namespace std; +using namespace XFILE; +using namespace PVR; + +CPVRFile::CPVRFile() +{ + m_isPlayRecording = false; + m_playingItem = -1; +} + +CPVRFile::~CPVRFile() +{ +} + +bool CPVRFile::Open(const CURL& url) +{ + Close(); + + if (!g_PVRManager.IsStarted()) + return false; + + CStdString strURL = url.Get(); + + if (strURL.Left(18) == "pvr://channels/tv/" || strURL.Left(21) == "pvr://channels/radio/") + { + CFileItemPtr tag = g_PVRChannelGroups->GetByPath(strURL); + if (tag && tag->HasPVRChannelInfoTag()) + { + if (!g_PVRManager.OpenLiveStream(*tag)) + return false; + + m_isPlayRecording = false; + CLog::Log(LOGDEBUG, "PVRFile - %s - playback has started on filename %s", __FUNCTION__, strURL.c_str()); + } + else + { + CLog::Log(LOGERROR, "PVRFile - %s - channel not found with filename %s", __FUNCTION__, strURL.c_str()); + return false; + } + } + else if (strURL.Left(17) == "pvr://recordings/") + { + CFileItemPtr tag = g_PVRRecordings->GetByPath(strURL); + if (tag && tag->HasPVRRecordingInfoTag()) + { + if (!g_PVRManager.OpenRecordedStream(*tag->GetPVRRecordingInfoTag())) + return false; + + m_isPlayRecording = true; + CLog::Log(LOGDEBUG, "%s - Recording has started on filename %s", __FUNCTION__, strURL.c_str()); + } + else + { + CLog::Log(LOGERROR, "PVRFile - Recording not found with filename %s", strURL.c_str()); + return false; + } + } + else + { + CLog::Log(LOGERROR, "%s - invalid path specified %s", __FUNCTION__, strURL.c_str()); + return false; + } + + return true; +} + +void CPVRFile::Close() +{ + g_PVRManager.CloseStream(); +} + +unsigned int CPVRFile::Read(void* buffer, int64_t size) +{ + return g_PVRManager.IsStarted() ? g_PVRClients->ReadStream((BYTE*)buffer, size) : 0; +} + +int64_t CPVRFile::GetLength() +{ + return g_PVRManager.IsStarted() ? g_PVRClients->GetStreamLength() : 0; +} + +int64_t CPVRFile::Seek(int64_t pos, int whence) +{ + return g_PVRManager.IsStarted() ? g_PVRClients->SeekStream(pos, whence) : 0; +} + +int64_t CPVRFile::GetPosition() +{ + return g_PVRManager.IsStarted() ? g_PVRClients->GetStreamPosition() : 0; +} + +int CPVRFile::GetTotalTime() +{ + return g_PVRManager.GetTotalTime(); +} + +int CPVRFile::GetStartTime() +{ + return g_PVRManager.GetStartTime(); +} + +bool CPVRFile::NextChannel(bool preview/* = false*/) +{ + unsigned int newchannel; + + if (m_isPlayRecording) + { + /* We are inside a recording, skip channelswitch */ + return true; + } + + /* Do channel switch and save new channel number, it is not always + * increased by one in a case if next channel is encrypted or we + * on the beginning or end of the channel list! + */ + if (g_PVRManager.ChannelUp(&newchannel, preview)) + { + m_playingItem = newchannel; + return true; + } + else + { + return false; + } +} + +bool CPVRFile::PrevChannel(bool preview/* = false*/) +{ + unsigned int newchannel; + + if (m_isPlayRecording) + { + /* We are inside a recording, skip channelswitch */ + return true; + } + + /* Do channel switch and save new channel number, it is not always + * increased by one in a case if next channel is encrypted or we + * on the beginning or end of the channel list! + */ + if (g_PVRManager.ChannelDown(&newchannel, preview)) + { + m_playingItem = newchannel; + return true; + } + else + { + return false; + } +} + +bool CPVRFile::SelectChannel(unsigned int channel) +{ + if (m_isPlayRecording) + { + /* We are inside a recording, skip channelswitch */ + /** TODO: + ** Add support for cutting keys (functions becomes the numeric keys as integer) + **/ + return true; + } + + if (g_PVRManager.ChannelSwitch(channel)) + { + m_playingItem = channel; + return true; + } + else + { + return false; + } +} + +bool CPVRFile::UpdateItem(CFileItem& item) +{ + return g_PVRManager.UpdateItem(item); +} + +CStdString CPVRFile::TranslatePVRFilename(const CStdString& pathFile) +{ + if (!g_PVRManager.IsStarted()) + return StringUtils::EmptyString; + + CStdString FileName = pathFile; + if (FileName.substr(0, 14) == "pvr://channels") + { + CFileItemPtr channel = g_PVRChannelGroups->GetByPath(FileName); + if (channel && channel->HasPVRChannelInfoTag()) + { + CStdString stream = channel->GetPVRChannelInfoTag()->StreamURL(); + if(!stream.IsEmpty()) + { + if (stream.compare(6, 7, "stream/") == 0) + { + // pvr://stream + // This function was added to retrieve the stream URL for this item + // Is is used for the MediaPortal (ffmpeg) PVR addon + // see PVRManager.cpp + return g_PVRClients->GetStreamURL(*channel->GetPVRChannelInfoTag()); + } + else + { + return stream; + } + } + } + } + return FileName; +} + +bool CPVRFile::CanRecord() +{ + if (m_isPlayRecording || !g_PVRManager.IsStarted()) + return false; + + return g_PVRClients->CanRecordInstantly(); +} + +bool CPVRFile::IsRecording() +{ + return g_PVRManager.IsStarted() && g_PVRClients->IsRecordingOnPlayingChannel(); +} + +bool CPVRFile::Record(bool bOnOff) +{ + return g_PVRManager.StartRecordingOnPlayingChannel(bOnOff); +} + +bool CPVRFile::Delete(const CURL& url) +{ + if (!g_PVRManager.IsStarted()) + return false; + + CStdString path(url.GetFileName()); + if (path.Left(11) == "recordings/" && path[path.size()-1] != '/') + { + CStdString strURL = url.Get(); + CFileItemPtr tag = g_PVRRecordings->GetByPath(strURL); + if (tag && tag->HasPVRRecordingInfoTag()) + return tag->GetPVRRecordingInfoTag()->Delete(); + } + return false; +} + +bool CPVRFile::Rename(const CURL& url, const CURL& urlnew) +{ + if (!g_PVRManager.IsStarted()) + return false; + + CStdString path(url.GetFileName()); + CStdString newname(urlnew.GetFileName()); + + size_t found = newname.find_last_of("/"); + if (found != CStdString::npos) + newname = newname.substr(found+1); + + if (path.Left(11) == "recordings/" && path[path.size()-1] != '/') + { + CStdString strURL = url.Get(); + CFileItemPtr tag = g_PVRRecordings->GetByPath(strURL); + if (tag && tag->HasPVRRecordingInfoTag()) + return tag->GetPVRRecordingInfoTag()->Rename(newname); + } + return false; +} + +bool CPVRFile::Exists(const CURL& url) +{ + return g_PVRManager.IsStarted() && + g_PVRRecordings->GetByPath(url.Get())->HasPVRRecordingInfoTag(); +} + +int CPVRFile::IoControl(EIoControl request, void *param) +{ + if (request == IOCTRL_SEEK_POSSIBLE) + { + if (!g_PVRManager.IsStarted()) + return 0; + else if (g_PVRClients->GetStreamLength() && g_PVRClients->SeekStream(0, SEEK_CUR) >= 0) + return 1; + else + return 0; + } + + return -1; +} diff --git a/xbmc/filesystem/PVRFile.h b/xbmc/filesystem/PVRFile.h new file mode 100644 index 0000000000..a673e014f0 --- /dev/null +++ b/xbmc/filesystem/PVRFile.h @@ -0,0 +1,81 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "IFile.h" +#include "ILiveTV.h" +#include "video/VideoInfoTag.h" + + +class CPVRSession; + +namespace XFILE { + +class CPVRFile + : public IFile + , ILiveTVInterface + , IRecordable +{ +public: + CPVRFile(); + virtual ~CPVRFile(); + virtual bool Open(const CURL& url); + virtual int64_t Seek(int64_t pos, int whence=SEEK_SET); + virtual int64_t GetPosition(); + virtual int64_t GetLength(); + virtual int Stat(const CURL& url, struct __stat64* buffer) { return -1; } + virtual void Close(); + virtual unsigned int Read(void* buffer, int64_t size); + virtual CStdString GetContent() { return ""; } + virtual bool SkipNext() { return true; } + + virtual bool Delete(const CURL& url); + virtual bool Rename(const CURL& url, const CURL& urlnew); + virtual bool Exists(const CURL& url); + + virtual ILiveTVInterface* GetLiveTV() {return (ILiveTVInterface*)this;} + + virtual bool NextChannel(bool preview = false); + virtual bool PrevChannel(bool preview = false); + virtual bool SelectChannel(unsigned int channel); + + virtual int GetTotalTime(); + virtual int GetStartTime(); + virtual bool UpdateItem(CFileItem& item); + + virtual IRecordable* GetRecordable() {return (IRecordable*)this;} + + virtual bool CanRecord(); + virtual bool IsRecording(); + virtual bool Record(bool bOnOff); + + virtual int IoControl(EIoControl request, void *param); + + static CStdString TranslatePVRFilename(const CStdString& pathFile); + +protected: + bool m_isPlayRecording; + int m_playingItem; +}; + +} + + diff --git a/xbmc/filesystem/SlingboxFile.cpp b/xbmc/filesystem/SlingboxFile.cpp index 5804f65713..0fa832a200 100644 --- a/xbmc/filesystem/SlingboxFile.cpp +++ b/xbmc/filesystem/SlingboxFile.cpp @@ -194,7 +194,7 @@ bool CSlingboxFile::SkipNext() return m_pSlingbox->IsConnected(); } -bool CSlingboxFile::NextChannel() +bool CSlingboxFile::NextChannel(bool bPreview /* = false */) { // Prepare variables bool bSuccess = true; @@ -280,7 +280,7 @@ bool CSlingboxFile::NextChannel() return bSuccess; } -bool CSlingboxFile::PrevChannel() +bool CSlingboxFile::PrevChannel(bool bPreview /* = false */) { // Prepare variables bool bSuccess = true; diff --git a/xbmc/filesystem/SlingboxFile.h b/xbmc/filesystem/SlingboxFile.h index 45082c981e..2aa9855d99 100644 --- a/xbmc/filesystem/SlingboxFile.h +++ b/xbmc/filesystem/SlingboxFile.h @@ -50,8 +50,8 @@ namespace XFILE virtual ILiveTVInterface * GetLiveTV() { return (ILiveTVInterface *)this; } - virtual bool NextChannel(); - virtual bool PrevChannel(); + virtual bool NextChannel(bool bPreview = false); // TODO bPreview is not implemented + virtual bool PrevChannel(bool bPreview = false); // TODO bPreview is not implemented virtual bool SelectChannel(unsigned int uiChannel); protected: diff --git a/xbmc/filesystem/VTPFile.cpp b/xbmc/filesystem/VTPFile.cpp index 5a3306d55d..2d1f4679c3 100644 --- a/xbmc/filesystem/VTPFile.cpp +++ b/xbmc/filesystem/VTPFile.cpp @@ -140,7 +140,7 @@ int64_t CVTPFile::Seek(int64_t pos, int whence) return -1; } -bool CVTPFile::NextChannel() +bool CVTPFile::NextChannel(bool preview/* = false*/) { if(m_session == NULL) return false; @@ -166,7 +166,7 @@ bool CVTPFile::NextChannel() return false; } -bool CVTPFile::PrevChannel() +bool CVTPFile::PrevChannel(bool preview/* = false*/) { if(m_session == NULL) return false; diff --git a/xbmc/filesystem/VTPFile.h b/xbmc/filesystem/VTPFile.h index 236e46b771..5474336ad6 100644 --- a/xbmc/filesystem/VTPFile.h +++ b/xbmc/filesystem/VTPFile.h @@ -51,8 +51,8 @@ public: virtual ILiveTVInterface* GetLiveTV() {return (ILiveTVInterface*)this;} - virtual bool NextChannel(); - virtual bool PrevChannel(); + virtual bool NextChannel(bool preview = false); + virtual bool PrevChannel(bool preview = false); virtual bool SelectChannel(unsigned int channel); virtual int GetTotalTime() { return 0; } diff --git a/xbmc/guilib/GUIControl.h b/xbmc/guilib/GUIControl.h index b283bea3c1..0c641266f7 100644 --- a/xbmc/guilib/GUIControl.h +++ b/xbmc/guilib/GUIControl.h @@ -274,6 +274,7 @@ public: GUICONTAINER_LIST, GUICONTAINER_WRAPLIST, GUICONTAINER_FIXEDLIST, + GUICONTAINER_EPGGRID, GUICONTAINER_PANEL }; GUICONTROLTYPES GetControlType() const { return ControlType; } diff --git a/xbmc/guilib/GUIControlFactory.cpp b/xbmc/guilib/GUIControlFactory.cpp index 5f00ad2669..45b62dbf11 100644 --- a/xbmc/guilib/GUIControlFactory.cpp +++ b/xbmc/guilib/GUIControlFactory.cpp @@ -49,6 +49,7 @@ #include "GUIListContainer.h" #include "GUIFixedListContainer.h" #include "GUIWrappingListContainer.h" +#include "epg/GUIEPGGridContainer.h" #include "GUIPanelContainer.h" #include "GUIMultiSelectText.h" #include "GUIListLabel.h" @@ -64,6 +65,7 @@ #include "GUIAction.h" using namespace std; +using namespace EPG; typedef struct { @@ -106,6 +108,7 @@ static const ControlMapping controls[] = {"list", CGUIControl::GUICONTAINER_LIST}, {"wraplist", CGUIControl::GUICONTAINER_WRAPLIST}, {"fixedlist", CGUIControl::GUICONTAINER_FIXEDLIST}, + {"epggrid", CGUIControl::GUICONTAINER_EPGGRID}, {"panel", CGUIControl::GUICONTAINER_PANEL}}; CGUIControl::GUICONTROLTYPES CGUIControlFactory::TranslateControlType(const CStdString &type) @@ -672,6 +675,8 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl int focusPosition = 0; int scrollTime = 200; + int timeBlocks = 36; + int rulerUnit = 12; bool useControlCoords = false; bool renderFocusedLast = false; @@ -888,6 +893,8 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl GetAspectRatio(pControlNode, "aspectratio", aspect); XMLUtils::GetBoolean(pControlNode, "scroll", bScrollLabel); XMLUtils::GetBoolean(pControlNode,"pulseonselect", bPulse); + XMLUtils::GetInt(pControlNode, "timeblocks", timeBlocks); + XMLUtils::GetInt(pControlNode, "rulerunit", rulerUnit); GetInfoTexture(pControlNode, "imagepath", texture, texturePath, parentID); @@ -1185,6 +1192,7 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl parentID, id, posX, posY, width, height, textureBackground, textureLeft, textureMid, textureRight, textureOverlay, bReveal); + ((CGUIProgressControl *)control)->SetInfo(singleInfo); } else if (type == CGUIControl::GUICONTROL_IMAGE) @@ -1236,6 +1244,12 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl ((CGUIWrappingListContainer *)control)->SetPageControl(pageControl); ((CGUIWrappingListContainer *)control)->SetRenderOffset(offset); } + else if (type == CGUIControl::GUICONTAINER_EPGGRID) + { + control = new CGUIEPGGridContainer(parentID, id, posX, posY, width, height, orientation, scrollTime, preloadItems, timeBlocks, rulerUnit); + ((CGUIEPGGridContainer *)control)->LoadLayout(pControlNode); + ((CGUIEPGGridContainer *)control)->SetRenderOffset(offset); + } else if (type == CGUIControl::GUICONTAINER_FIXEDLIST) { CScroller scroller; diff --git a/xbmc/guilib/GUIDialog.cpp b/xbmc/guilib/GUIDialog.cpp index 92bdd9986a..ef7bf65acc 100644 --- a/xbmc/guilib/GUIDialog.cpp +++ b/xbmc/guilib/GUIDialog.cpp @@ -39,6 +39,7 @@ CGUIDialog::CGUIDialog(int id, const CStdString &xmlFile) m_showStartTime = 0; m_showDuration = 0; m_enableSound = true; + m_bAutoClosed = false; } CGUIDialog::~CGUIDialog(void) @@ -239,7 +240,10 @@ void CGUIDialog::FrameMove() else { if (m_showStartTime + m_showDuration < CTimeUtils::GetFrameTime() && !m_closing) + { + m_bAutoClosed = true; Close(); + } } } CGUIWindow::FrameMove(); @@ -263,8 +267,11 @@ void CGUIDialog::SetAutoClose(unsigned int timeoutMs) { m_autoClosing = true; m_showDuration = timeoutMs; - if (m_active) - m_showStartTime = CTimeUtils::GetFrameTime(); + ResetAutoClose(); } - +void CGUIDialog::ResetAutoClose(void) +{ + if (m_autoClosing && m_active) + m_showStartTime = CTimeUtils::GetFrameTime(); +} diff --git a/xbmc/guilib/GUIDialog.h b/xbmc/guilib/GUIDialog.h index e79ad6b745..b38ef402b6 100644 --- a/xbmc/guilib/GUIDialog.h +++ b/xbmc/guilib/GUIDialog.h @@ -56,6 +56,8 @@ public: virtual bool IsModalDialog() const { return m_bModal; }; void SetAutoClose(unsigned int timeoutMs); + void ResetAutoClose(void); + bool IsAutoClosed(void) const { return m_bAutoClosed; }; void SetSound(bool OnOff) { m_enableSound = OnOff; }; virtual bool IsSoundEnabled() const { return m_enableSound; }; @@ -74,4 +76,5 @@ protected: bool m_enableSound; unsigned int m_showStartTime; unsigned int m_showDuration; + bool m_bAutoClosed; }; diff --git a/xbmc/guilib/GUIEditControl.cpp b/xbmc/guilib/GUIEditControl.cpp index 054e47221c..75b546dee7 100644 --- a/xbmc/guilib/GUIEditControl.cpp +++ b/xbmc/guilib/GUIEditControl.cpp @@ -267,6 +267,20 @@ void CGUIEditControl::OnClick() case INPUT_TYPE_SECONDS: textChanged = CGUIDialogNumeric::ShowAndGetSeconds(utf8, g_localizeStrings.Get(21420)); break; + case INPUT_TYPE_TIME: + { + CDateTime dateTime; + dateTime.SetFromDBTime(utf8); + SYSTEMTIME time; + dateTime.GetAsSystemTime(time); + if (CGUIDialogNumeric::ShowAndGetTime(time, heading > 0 ? heading : g_localizeStrings.Get(21420))) + { + dateTime = CDateTime(time); + utf8 = dateTime.GetAsLocalizedTime("", false); + textChanged = true; + } + break; + } case INPUT_TYPE_DATE: { CDateTime dateTime; @@ -275,7 +289,7 @@ void CGUIEditControl::OnClick() dateTime = CDateTime(2000, 1, 1, 0, 0, 0); SYSTEMTIME date; dateTime.GetAsSystemTime(date); - if (CGUIDialogNumeric::ShowAndGetDate(date, g_localizeStrings.Get(21420))) + if (CGUIDialogNumeric::ShowAndGetDate(date, heading > 0 ? heading : g_localizeStrings.Get(21420))) { dateTime = CDateTime(date); utf8 = dateTime.GetAsDBDate(); @@ -292,6 +306,9 @@ void CGUIEditControl::OnClick() case INPUT_TYPE_FILTER: textChanged = CGUIKeyboardFactory::ShowAndGetFilter(utf8, false); break; + case INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW: + textChanged = CGUIDialogNumeric::ShowAndVerifyNewPassword(utf8); + break; case INPUT_TYPE_PASSWORD_MD5: utf8 = ""; // TODO: Ideally we'd send this to the keyboard and tell the keyboard we have this type of input // fallthrough @@ -458,7 +475,7 @@ void CGUIEditControl::SetHint(const CGUIInfoLabel& hint) CStdStringW CGUIEditControl::GetDisplayedText() const { - if (m_inputType == INPUT_TYPE_PASSWORD || m_inputType == INPUT_TYPE_PASSWORD_MD5) + if (m_inputType == INPUT_TYPE_PASSWORD || m_inputType == INPUT_TYPE_PASSWORD_MD5 || m_inputType == INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW) { CStdStringW text; text.append(m_text2.size(), L'*'); @@ -485,7 +502,7 @@ void CGUIEditControl::SetLabel2(const std::string &text) g_charsetConverter.utf8ToW(text, newText); if (newText != m_text2) { - m_isMD5 = m_inputType == INPUT_TYPE_PASSWORD_MD5; + m_isMD5 = (m_inputType == INPUT_TYPE_PASSWORD_MD5 || m_inputType == INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW); m_text2 = newText; m_cursorPos = m_text2.size(); SetInvalid(); @@ -503,12 +520,13 @@ CStdString CGUIEditControl::GetLabel2() const bool CGUIEditControl::ClearMD5() { - if (m_inputType != INPUT_TYPE_PASSWORD_MD5 || !m_isMD5) + if (!(m_inputType == INPUT_TYPE_PASSWORD_MD5 || m_inputType == INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW) || !m_isMD5) return false; m_text2.Empty(); m_cursorPos = 0; - m_isMD5 = false; + if (m_inputType != INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW) + m_isMD5 = false; return true; } diff --git a/xbmc/guilib/GUIEditControl.h b/xbmc/guilib/GUIEditControl.h index 5c9177ff1f..1f62c196d1 100644 --- a/xbmc/guilib/GUIEditControl.h +++ b/xbmc/guilib/GUIEditControl.h @@ -45,12 +45,14 @@ public: INPUT_TYPE_TEXT = 0, INPUT_TYPE_NUMBER, INPUT_TYPE_SECONDS, + INPUT_TYPE_TIME, INPUT_TYPE_DATE, INPUT_TYPE_IPADDRESS, INPUT_TYPE_PASSWORD, INPUT_TYPE_PASSWORD_MD5, INPUT_TYPE_SEARCH, - INPUT_TYPE_FILTER + INPUT_TYPE_FILTER, + INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW }; CGUIEditControl(int parentID, int controlID, float posX, float posY, diff --git a/xbmc/guilib/GUILabelControl.cpp b/xbmc/guilib/GUILabelControl.cpp index f78c8a9a28..75129c3696 100644 --- a/xbmc/guilib/GUILabelControl.cpp +++ b/xbmc/guilib/GUILabelControl.cpp @@ -175,6 +175,13 @@ float CGUILabelControl::GetWidth() const return m_width; } +void CGUILabelControl::SetWidth(float width) +{ + m_width = width; + m_label.SetMaxRect(m_posX, m_posY, m_width, m_height); + CGUIControl::SetWidth(m_width); +} + bool CGUILabelControl::OnMessage(CGUIMessage& message) { if ( message.GetControlId() == GetID() ) diff --git a/xbmc/guilib/GUILabelControl.h b/xbmc/guilib/GUILabelControl.h index 1e151293fd..912b0515a2 100644 --- a/xbmc/guilib/GUILabelControl.h +++ b/xbmc/guilib/GUILabelControl.h @@ -51,6 +51,7 @@ public: virtual bool OnMessage(CGUIMessage& message); virtual CStdString GetDescription() const; virtual float GetWidth() const; + virtual void SetWidth(float width); virtual CRect CalcRenderRegion() const; const CLabelInfo& GetLabelInfo() const { return m_label.GetLabelInfo(); }; diff --git a/xbmc/guilib/GUIListGroup.cpp b/xbmc/guilib/GUIListGroup.cpp index cc23bbe2dc..88e9ea72d9 100644 --- a/xbmc/guilib/GUIListGroup.cpp +++ b/xbmc/guilib/GUIListGroup.cpp @@ -118,6 +118,50 @@ void CGUIListGroup::UpdateInfo(const CGUIListItem *item) } } +void CGUIListGroup::EnlargeWidth(float difference) +{ + // Alters the width of the controls that have an ID of 1 + for (iControls it = m_children.begin(); it != m_children.end(); it++) + { + CGUIControl *child = *it; + if (child->GetID() >= 1 && child->GetID() <= 14) + { + if (child->GetID() == 1) // label + { + child->SetWidth(child->GetWidth() + difference - 10); + child->SetVisible(child->GetWidth() > 10); /// + } + else + { + child->SetWidth(child->GetWidth() + difference); + } + } + } + SetInvalid(); +} + +void CGUIListGroup::EnlargeHeight(float difference) +{ + // Alters the width of the controls that have an ID of 1 + for (iControls it = m_children.begin(); it != m_children.end(); it++) + { + CGUIControl *child = *it; + if (child->GetID() >= 1 && child->GetID() <= 14) + { + if (child->GetID() == 1) // label + { + child->SetHeight(child->GetHeight() + difference); + child->SetVisible(child->GetHeight() > 10); /// + } + else + { + child->SetHeight(child->GetHeight() + difference); + } + } + } + SetInvalid(); +} + void CGUIListGroup::SetInvalid() { if (!m_bInvalidated) diff --git a/xbmc/guilib/GUIListGroup.h b/xbmc/guilib/GUIListGroup.h index e98d799f34..deb0db7331 100644 --- a/xbmc/guilib/GUIListGroup.h +++ b/xbmc/guilib/GUIListGroup.h @@ -48,6 +48,8 @@ public: virtual void UpdateInfo(const CGUIListItem *item); virtual void SetInvalid(); + void EnlargeWidth(float difference); + void EnlargeHeight(float difference); void SetFocusedItem(unsigned int subfocus); unsigned int GetFocusedItem() const; bool MoveLeft(); diff --git a/xbmc/guilib/GUIListItemLayout.cpp b/xbmc/guilib/GUIListItemLayout.cpp index 743ef83e0f..22dc2381ac 100644 --- a/xbmc/guilib/GUIListItemLayout.cpp +++ b/xbmc/guilib/GUIListItemLayout.cpp @@ -106,6 +106,20 @@ unsigned int CGUIListItemLayout::GetFocusedItem() const return m_group.GetFocusedItem(); } +void CGUIListItemLayout::SetWidth(float width) +{ + m_group.EnlargeWidth(width - m_width); + m_width = width; + SetInvalid(); +} + +void CGUIListItemLayout::SetHeight(float height) +{ + m_group.EnlargeHeight(height - m_height); + m_height = height; + SetInvalid(); +} + void CGUIListItemLayout::SelectItemFromPoint(const CPoint &point) { m_group.SelectItemFromPoint(point); diff --git a/xbmc/guilib/GUIListItemLayout.h b/xbmc/guilib/GUIListItemLayout.h index b571dd69a0..f36d8d8cda 100644 --- a/xbmc/guilib/GUIListItemLayout.h +++ b/xbmc/guilib/GUIListItemLayout.h @@ -50,6 +50,8 @@ public: void CreateListControlLayouts(float width, float height, bool focused, const CLabelInfo &labelInfo, const CLabelInfo &labelInfo2, const CTextureInfo &texture, const CTextureInfo &textureFocus, float texHeight, float iconWidth, float iconHeight, const CStdString &nofocusCondition, const CStdString &focusCondition); //#endif + void SetWidth(float width); + void SetHeight(float height); void SelectItemFromPoint(const CPoint &point); bool MoveLeft(); bool MoveRight(); diff --git a/xbmc/guilib/GUIListLabel.cpp b/xbmc/guilib/GUIListLabel.cpp index 794ceb59de..255807bc33 100644 --- a/xbmc/guilib/GUIListLabel.cpp +++ b/xbmc/guilib/GUIListLabel.cpp @@ -105,6 +105,18 @@ void CGUIListLabel::SetInvalid() CGUIControl::SetInvalid(); } +void CGUIListLabel::SetWidth(float width) +{ + m_width = width; + if (m_label.GetLabelInfo().align & XBFONT_RIGHT) + m_label.SetMaxRect(m_posX - m_width, m_posY, m_width, m_height); + else if (m_label.GetLabelInfo().align & XBFONT_CENTER_X) + m_label.SetMaxRect(m_posX - m_width*0.5f, m_posY, m_width, m_height); + else + m_label.SetMaxRect(m_posX, m_posY, m_posX + m_width, m_posY + m_height); + CGUIControl::SetWidth(m_width); +} + void CGUIListLabel::SetLabel(const CStdString &label) { m_label.SetText(label); diff --git a/xbmc/guilib/GUIListLabel.h b/xbmc/guilib/GUIListLabel.h index 43812b0308..bde065da68 100644 --- a/xbmc/guilib/GUIListLabel.h +++ b/xbmc/guilib/GUIListLabel.h @@ -47,6 +47,7 @@ public: virtual void UpdateInfo(const CGUIListItem *item = NULL); virtual void SetFocus(bool focus); virtual void SetInvalid(); + virtual void SetWidth(float width); void SetLabel(const CStdString &label); void SetSelected(bool selected); diff --git a/xbmc/guilib/GUIProgressControl.cpp b/xbmc/guilib/GUIProgressControl.cpp index c9ade8d4fd..70e1f7f585 100644 --- a/xbmc/guilib/GUIProgressControl.cpp +++ b/xbmc/guilib/GUIProgressControl.cpp @@ -21,6 +21,9 @@ #include "GUIProgressControl.h" #include "GUIInfoManager.h" +#include "GUIListItem.h" +#include "GUIWindowManager.h" +#include "FileItem.h" CGUIProgressControl::CGUIProgressControl(int parentID, int controlID, float posX, float posY, float width, @@ -41,6 +44,7 @@ CGUIProgressControl::CGUIProgressControl(int parentID, int controlID, m_iInfoCode = 0; ControlType = GUICONTROL_PROGRESS; m_bReveal = reveal; + m_bChanged = false; } CGUIProgressControl::~CGUIProgressControl(void) diff --git a/xbmc/guilib/GUIProgressControl.h b/xbmc/guilib/GUIProgressControl.h index 310879a4ee..083dcaab12 100644 --- a/xbmc/guilib/GUIProgressControl.h +++ b/xbmc/guilib/GUIProgressControl.h @@ -77,5 +77,6 @@ protected: int m_iInfoCode; float m_fPercent; bool m_bReveal; + bool m_bChanged; }; #endif diff --git a/xbmc/guilib/GUIWindow.cpp b/xbmc/guilib/GUIWindow.cpp index 03c4e7cfa8..458c247ab4 100644 --- a/xbmc/guilib/GUIWindow.cpp +++ b/xbmc/guilib/GUIWindow.cpp @@ -54,7 +54,7 @@ CGUIWindow::CGUIWindow(int id, const CStdString &xmlFile) { SetID(id); SetProperty("xmlfile", xmlFile); - m_idRange = 1; + m_idRange.push_back(id); m_lastControlID = 0; m_overlayState = OVERLAY_STATE_PARENT_WINDOW; // Use parent or previous window's state m_isDialog = false; @@ -366,13 +366,13 @@ void CGUIWindow::Close_Internal(bool forceClose /*= false*/, int nextWindowID /* OnMessage(msg); } -void CGUIWindow::Close(bool forceClose /*= false*/, int nextWindowID /*= 0*/, bool enableSound /*= true*/) +void CGUIWindow::Close(bool forceClose /*= false*/, int nextWindowID /*= 0*/, bool enableSound /*= true*/, bool bWait /* = true */) { if (!g_application.IsCurrentThread()) { // make sure graphics lock is not held CSingleExit leaveIt(g_graphicsContext); - CApplicationMessenger::Get().Close(this, forceClose, true, nextWindowID, enableSound); + CApplicationMessenger::Get().Close(this, forceClose, bWait, nextWindowID, enableSound); } else Close_Internal(forceClose, nextWindowID, enableSound); @@ -994,3 +994,13 @@ void CGUIWindow::ClearBackground() if (color) g_graphicsContext.Clear(color); } + +bool CGUIWindow::HasID(int controlID) const +{ + for (std::vector<int>::const_iterator it = m_idRange.begin(); it != m_idRange.end() ; it++) + { + if (controlID == *it) + return true; + } + return false; +} diff --git a/xbmc/guilib/GUIWindow.h b/xbmc/guilib/GUIWindow.h index 7654d9d04a..6ad99f6e08 100644 --- a/xbmc/guilib/GUIWindow.h +++ b/xbmc/guilib/GUIWindow.h @@ -106,7 +106,7 @@ public: */ virtual void FrameMove() {}; - void Close(bool forceClose = false, int nextWindowID = 0, bool enableSound = true); + void Close(bool forceClose = false, int nextWindowID = 0, bool enableSound = true, bool bWait = true); // OnAction() is called by our window manager. We should process any messages // that should be handled at the window level in the derived classes, and any @@ -125,9 +125,8 @@ public: virtual bool OnMessage(CGUIMessage& message); bool ControlGroupHasFocus(int groupID, int controlID); - virtual bool HasID(int controlID) const { return controlID >= m_controlID && controlID < m_controlID + m_idRange; }; - void SetIDRange(int range) { m_idRange = range; }; - int GetIDRange() const { return m_idRange; }; + virtual bool HasID(int controlID) const; + const std::vector<int>& GetIDRange() const { return m_idRange; }; int GetPreviousWindow() { return m_previousWindow; }; CRect GetScaledBounds() const; virtual void ClearAll(); @@ -231,7 +230,7 @@ protected: void ChangeButtonToEdit(int id, bool singleLabel = false); //#endif - int m_idRange; + std::vector<int> m_idRange; OVERLAY_STATE m_overlayState; RESOLUTION_INFO m_coordsRes; // resolution that the window coordinates are in. bool m_needsScaling; diff --git a/xbmc/guilib/GUIWindowManager.cpp b/xbmc/guilib/GUIWindowManager.cpp index 028bb173c1..e267373412 100644 --- a/xbmc/guilib/GUIWindowManager.cpp +++ b/xbmc/guilib/GUIWindowManager.cpp @@ -184,17 +184,17 @@ void CGUIWindowManager::Add(CGUIWindow* pWindow) } // push back all the windows if there are more than one covered by this class CSingleLock lock(g_graphicsContext); - for (int i = 0; i < pWindow->GetIDRange(); i++) + const vector<int>& idRange = pWindow->GetIDRange(); + for (vector<int>::const_iterator idIt = idRange.begin(); idIt != idRange.end() ; idIt++) { - WindowMap::iterator it = m_mapWindows.find(pWindow->GetID() + i); + WindowMap::iterator it = m_mapWindows.find(*idIt); if (it != m_mapWindows.end()) { CLog::Log(LOGERROR, "Error, trying to add a second window with id %u " - "to the window manager", - pWindow->GetID()); + "to the window manager", *idIt); return; } - m_mapWindows.insert(pair<int, CGUIWindow *>(pWindow->GetID() + i, pWindow)); + m_mapWindows.insert(pair<int, CGUIWindow *>(*idIt, pWindow)); } } diff --git a/xbmc/guilib/Key.h b/xbmc/guilib/Key.h index 73f730d032..1f8d731992 100644 --- a/xbmc/guilib/Key.h +++ b/xbmc/guilib/Key.h @@ -268,6 +268,8 @@ #define ACTION_AUDIO_DELAY 161 #define ACTION_SUBTITLE_DELAY 162 +#define ACTION_RECORD 170 + #define ACTION_PASTE 180 #define ACTION_NEXT_CONTROL 181 #define ACTION_PREV_CONTROL 182 @@ -321,6 +323,7 @@ #define WINDOW_TEST_PATTERN 10008 #define WINDOW_SCREEN_CALIBRATION 10011 +#define WINDOW_SETTINGS_START 10012 #define WINDOW_SETTINGS_MYPICTURES 10012 #define WINDOW_SETTINGS_MYPROGRAMS 10013 #define WINDOW_SETTINGS_MYWEATHER 10014 @@ -331,6 +334,7 @@ #define WINDOW_SETTINGS_APPEARANCE 10019 #define WINDOW_SCRIPTS 10020 // virtual window for backward compatibility +#define WINDOW_SETTINGS_MYPVR 10021 #define WINDOW_VIDEO_FILES 10024 #define WINDOW_VIDEO_NAV 10025 @@ -386,6 +390,7 @@ #define WINDOW_DIALOG_PLAY_EJECT 10148 #define WINDOW_DIALOG_PERIPHERAL_MANAGER 10149 #define WINDOW_DIALOG_PERIPHERAL_SETTINGS 10150 +#define WINDOW_DIALOG_EXT_PROGRESS 10151 #define WINDOW_MUSIC_PLAYLIST 10500 #define WINDOW_MUSIC_FILES 10501 @@ -394,6 +399,22 @@ #define WINDOW_DIALOG_OSD_TELETEXT 10600 +// PVR related Window and Dialog ID's +#define WINDOW_PVR 10601 +#define WINDOW_DIALOG_PVR_GUIDE_INFO 10602 +#define WINDOW_DIALOG_PVR_RECORDING_INFO 10603 +#define WINDOW_DIALOG_PVR_TIMER_SETTING 10604 +#define WINDOW_DIALOG_PVR_GROUP_MANAGER 10605 +#define WINDOW_DIALOG_PVR_CHANNEL_MANAGER 10606 +#define WINDOW_DIALOG_PVR_GUIDE_SEARCH 10607 +#define WINDOW_DIALOG_PVR_CHANNEL_SCAN 10608 +#define WINDOW_DIALOG_PVR_UPDATE_PROGRESS 10609 +#define WINDOW_DIALOG_PVR_OSD_CHANNELS 10610 +#define WINDOW_DIALOG_PVR_OSD_GUIDE 10611 +#define WINDOW_DIALOG_PVR_OSD_DIRECTOR 10612 +#define WINDOW_DIALOG_PVR_OSD_CUTTER 10613 +// PVR_WINDOW VIEWS = 10694-10699 + //#define WINDOW_VIRTUAL_KEYBOARD 11000 #define WINDOW_DIALOG_SELECT 12000 #define WINDOW_DIALOG_MUSIC_INFO 12001 @@ -421,6 +442,11 @@ #define WINDOW_PYTHON_START 13000 #define WINDOW_PYTHON_END 13099 +// WINDOW_ID's from 14000 to 14099 reserved for Addons + +#define WINDOW_ADDON_START 14000 +#define WINDOW_ADDON_END 14099 + #define ICON_TYPE_NONE 101 #define ICON_TYPE_PROGRAMS 102 #define ICON_TYPE_MUSIC 103 diff --git a/xbmc/input/ButtonTranslator.cpp b/xbmc/input/ButtonTranslator.cpp index 0e8e00b2fa..e8ec8740c7 100644 --- a/xbmc/input/ButtonTranslator.cpp +++ b/xbmc/input/ButtonTranslator.cpp @@ -235,6 +235,23 @@ static const ActionMapping windows[] = {"music" , WINDOW_MUSIC}, {"video" , WINDOW_VIDEOS}, {"videos" , WINDOW_VIDEO_NAV}, + {"tv" , WINDOW_PVR}, // backward compat + {"pvr" , WINDOW_PVR}, + + {"pvrguideinfo" , WINDOW_DIALOG_PVR_GUIDE_INFO}, + {"pvrrecordinginfo" , WINDOW_DIALOG_PVR_RECORDING_INFO}, + {"pvrtimersetting" , WINDOW_DIALOG_PVR_TIMER_SETTING}, + {"pvrgroupmanager" , WINDOW_DIALOG_PVR_GROUP_MANAGER}, + {"pvrchannelmanager" , WINDOW_DIALOG_PVR_CHANNEL_MANAGER}, + {"pvrguidesearch" , WINDOW_DIALOG_PVR_GUIDE_SEARCH}, + {"pvrchannelscan" , WINDOW_DIALOG_PVR_CHANNEL_SCAN}, + {"pvrupdateprogress" , WINDOW_DIALOG_PVR_UPDATE_PROGRESS}, + {"pvrosdchannels" , WINDOW_DIALOG_PVR_OSD_CHANNELS}, + {"pvrosdguide" , WINDOW_DIALOG_PVR_OSD_GUIDE}, + {"pvrosddirector" , WINDOW_DIALOG_PVR_OSD_DIRECTOR}, + {"pvrosdcutter" , WINDOW_DIALOG_PVR_OSD_CUTTER}, + {"pvrosdteletext" , WINDOW_DIALOG_OSD_TELETEXT}, + {"systeminfo" , WINDOW_SYSTEM_INFORMATION}, {"testpattern" , WINDOW_TEST_PATTERN}, {"screencalibration" , WINDOW_SCREEN_CALIBRATION}, @@ -248,6 +265,8 @@ static const ActionMapping windows[] = {"networksettings" , WINDOW_SETTINGS_SERVICE}, // backward compat {"servicesettings" , WINDOW_SETTINGS_SERVICE}, {"appearancesettings" , WINDOW_SETTINGS_APPEARANCE}, + {"pvrsettings" , WINDOW_SETTINGS_MYPVR}, + {"tvsettings" , WINDOW_SETTINGS_MYPVR}, // backward compat {"scripts" , WINDOW_PROGRAMS}, // backward compat {"videofiles" , WINDOW_VIDEO_FILES}, {"videolibrary" , WINDOW_VIDEO_NAV}, @@ -260,6 +279,10 @@ static const ActionMapping windows[] = {"virtualkeyboard" , WINDOW_DIALOG_KEYBOARD}, {"volumebar" , WINDOW_DIALOG_VOLUME_BAR}, {"submenu" , WINDOW_DIALOG_SUB_MENU}, + {"pvrosdchannels" , WINDOW_DIALOG_PVR_OSD_CHANNELS}, + {"pvrosdguide" , WINDOW_DIALOG_PVR_OSD_GUIDE}, + {"pvrosddirector" , WINDOW_DIALOG_PVR_OSD_DIRECTOR}, + {"pvrosdcutter" , WINDOW_DIALOG_PVR_OSD_CUTTER}, {"favourites" , WINDOW_DIALOG_FAVOURITES}, {"contextmenu" , WINDOW_DIALOG_CONTEXT_MENU}, {"infodialog" , WINDOW_DIALOG_KAI_TOAST}, @@ -1152,13 +1175,17 @@ uint32_t CButtonTranslator::TranslateRemoteString(const char *szButton) else if (strButton.Equals("pageminus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_MINUS; else if (strButton.Equals("mute")) buttonCode = XINPUT_IR_REMOTE_MUTE; else if (strButton.Equals("recordedtv")) buttonCode = XINPUT_IR_REMOTE_RECORDED_TV; - else if (strButton.Equals("guide")) buttonCode = XINPUT_IR_REMOTE_TITLE; // same as title + else if (strButton.Equals("guide")) buttonCode = XINPUT_IR_REMOTE_GUIDE; else if (strButton.Equals("livetv")) buttonCode = XINPUT_IR_REMOTE_LIVE_TV; + else if (strButton.Equals("liveradio")) buttonCode = XINPUT_IR_REMOTE_LIVE_RADIO; + else if (strButton.Equals("epgsearch")) buttonCode = XINPUT_IR_REMOTE_EPG_SEARCH; else if (strButton.Equals("star")) buttonCode = XINPUT_IR_REMOTE_STAR; else if (strButton.Equals("hash")) buttonCode = XINPUT_IR_REMOTE_HASH; else if (strButton.Equals("clear")) buttonCode = XINPUT_IR_REMOTE_CLEAR; else if (strButton.Equals("enter")) buttonCode = XINPUT_IR_REMOTE_ENTER; else if (strButton.Equals("xbox")) buttonCode = XINPUT_IR_REMOTE_DISPLAY; // same as display + else if (strButton.Equals("playlist")) buttonCode = XINPUT_IR_REMOTE_PLAYLIST; + else if (strButton.Equals("guide")) buttonCode = XINPUT_IR_REMOTE_GUIDE; else if (strButton.Equals("teletext")) buttonCode = XINPUT_IR_REMOTE_TELETEXT; else if (strButton.Equals("red")) buttonCode = XINPUT_IR_REMOTE_RED; else if (strButton.Equals("green")) buttonCode = XINPUT_IR_REMOTE_GREEN; diff --git a/xbmc/input/XBIRRemote.h b/xbmc/input/XBIRRemote.h index bc7437336f..134d694c63 100644 --- a/xbmc/input/XBIRRemote.h +++ b/xbmc/input/XBIRRemote.h @@ -88,6 +88,11 @@ #define XINPUT_IR_REMOTE_GREEN 252 #define XINPUT_IR_REMOTE_YELLOW 253 #define XINPUT_IR_REMOTE_BLUE 254 +#define XINPUT_IR_REMOTE_PLAYLIST 255 +#define XINPUT_IR_REMOTE_GUIDE 256 + +#define XINPUT_IR_REMOTE_LIVE_RADIO 248 +#define XINPUT_IR_REMOTE_EPG_SEARCH 246 typedef struct _XINPUT_IR_REMOTE { diff --git a/xbmc/interfaces/Builtins.cpp b/xbmc/interfaces/Builtins.cpp index 2102b03ad1..40cfd0120c 100644 --- a/xbmc/interfaces/Builtins.cpp +++ b/xbmc/interfaces/Builtins.cpp @@ -114,11 +114,14 @@ const BUILT_IN commands[] = { { "Quit", false, "Quit XBMC" }, { "Hibernate", false, "Hibernates the system" }, { "Suspend", false, "Suspends the system" }, + { "InhibitIdleShutdown", false, "Inhibit idle shutdown" }, + { "AllowIdleShutdown", false, "Allow idle shutdown" }, { "RestartApp", false, "Restart XBMC" }, { "Minimize", false, "Minimize XBMC" }, { "Reset", false, "Reset the system (same as reboot)" }, { "Mastermode", false, "Control master mode" }, { "ActivateWindow", true, "Activate the specified window" }, + { "ActivateWindowAndFocus", true, "Activate the specified window and sets focus to the specified id" }, { "ReplaceWindow", true, "Replaces the current window with the new one" }, { "TakeScreenshot", false, "Takes a Screenshot" }, { "RunScript", true, "Run the specified script" }, @@ -276,6 +279,11 @@ int CBuiltins::Execute(const CStdString& execString) { CApplicationMessenger::Get().Quit(); } + else if (execute.Equals("inhibitidleshutdown")) + { + bool inhibit = (params.size() == 1 && params[0].Equals("true")); + CApplicationMessenger::Get().InhibitIdleShutdown(inhibit); + } else if (execute.Equals("minimize")) { CApplicationMessenger::Get().Minimize(); @@ -351,6 +359,39 @@ int CBuiltins::Execute(const CStdString& execString) CGUIMessage msg(GUI_MSG_SETFOCUS, g_windowManager.GetFocusedWindow(), controlID, subItem); g_windowManager.SendMessage(msg); } + else if ((execute.Equals("activatewindowandfocus")) && params.size()) + { + CStdString strWindow = params[0]; + + // confirm the window destination is valid prior to switching + int iWindow = CButtonTranslator::TranslateWindow(strWindow); + if (iWindow != WINDOW_INVALID) + { + // disable the screensaver + g_application.WakeUpScreenSaverAndDPMS(); +#if defined(__APPLE__) && defined(__arm__) + if (params[0].Equals("shutdownmenu")) + CBuiltins::Execute("Quit"); +#endif + vector<CStdString> dummy; + g_windowManager.ActivateWindow(iWindow, dummy, !execute.Equals("activatewindow")); + + unsigned int iPtr = 1; + while (params.size() > iPtr + 1) + { + CGUIMessage msg(GUI_MSG_SETFOCUS, g_windowManager.GetFocusedWindow(), + atol(params[iPtr].c_str()), + (params.size() >= iPtr + 2) ? atol(params[iPtr + 1].c_str())+1 : 0); + g_windowManager.SendMessage(msg); + iPtr += 2; + } + } + else + { + CLog::Log(LOGERROR, "ActivateWindowAndFocus called with invalid destination window: %s", strWindow.c_str()); + return false; + } + } else if (execute.Equals("runscript") && params.size()) { #if defined(TARGET_DARWIN_OSX) diff --git a/xbmc/interfaces/python/xbmcmodule/xbmcmodule.cpp b/xbmc/interfaces/python/xbmcmodule/xbmcmodule.cpp index 303b6bcd27..03132d78cd 100644 --- a/xbmc/interfaces/python/xbmcmodule/xbmcmodule.cpp +++ b/xbmc/interfaces/python/xbmcmodule/xbmcmodule.cpp @@ -54,6 +54,9 @@ #include "utils/FileUtils.h" #include "pythreadstate.h" #include "utils/log.h" +#include "utils/Weather.h" +#include "guilib/GUIFontManager.h" +#include "filesystem/Directory.h" #include "pyrendercapture.h" #include "monitor.h" #include "URL.h" diff --git a/xbmc/pvr/Makefile b/xbmc/pvr/Makefile new file mode 100644 index 0000000000..3b28f9bd29 --- /dev/null +++ b/xbmc/pvr/Makefile @@ -0,0 +1,8 @@ +SRCS=PVRGUIInfo.cpp \ + PVRManager.cpp \ + PVRDatabase.cpp + +LIB=pvr.a + +include ../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/xbmc/pvr/PVRDatabase.cpp b/xbmc/pvr/PVRDatabase.cpp new file mode 100644 index 0000000000..9ef3f1c6de --- /dev/null +++ b/xbmc/pvr/PVRDatabase.cpp @@ -0,0 +1,932 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "PVRDatabase.h" +#include "dbwrappers/dataset.h" +#include "settings/AdvancedSettings.h" +#include "settings/VideoSettings.h" +#include "utils/log.h" + +#include "PVRManager.h" +#include "channels/PVRChannelGroupsContainer.h" +#include "channels/PVRChannelGroupInternal.h" +#include "addons/PVRClient.h" + +using namespace std; +using namespace dbiplus; +using namespace PVR; +using namespace ADDON; + +bool CPVRDatabase::Open() +{ + return CDatabase::Open(g_advancedSettings.m_databaseTV); +} + +bool CPVRDatabase::CreateTables() +{ + bool bReturn(false); + + try + { + if (!CDatabase::CreateTables()) + return false; + + BeginTransaction(); + CLog::Log(LOGINFO, "PVR - %s - creating tables", __FUNCTION__); + + CLog::Log(LOGDEBUG, "PVR - %s - creating table 'clients'", __FUNCTION__); + m_pDS->exec( + "CREATE TABLE clients (" + "idClient integer primary key, " + "sName varchar(64), " + "sUid varchar(32)" + ")" + ); + + CLog::Log(LOGDEBUG, "PVR - %s - creating table 'channels'", __FUNCTION__); + m_pDS->exec( + "CREATE TABLE channels (" + "idChannel integer primary key, " + "iUniqueId integer, " + "bIsRadio bool, " + "bIsHidden bool, " + "bIsUserSetIcon bool, " + "bIsLocked bool, " + "sIconPath varchar(255), " + "sChannelName varchar(64), " + "bIsVirtual bool, " + "bEPGEnabled bool, " + "sEPGScraper varchar(32), " + "iLastWatched integer," + + // TODO use mapping table + "iClientId integer, " + "iClientChannelNumber integer, " + "sInputFormat varchar(32), " + "sStreamURL varchar(255), " + "iEncryptionSystem integer, " + + "idEpg integer" + ")" + ); + m_pDS->exec("CREATE UNIQUE INDEX idx_channels_iClientId_iUniqueId on channels(iClientId, iUniqueId);"); + + // TODO use a mapping table so multiple backends per channel can be implemented + // CLog::Log(LOGDEBUG, "PVR - %s - creating table 'map_channels_clients'", __FUNCTION__); + // m_pDS->exec( + // "CREATE TABLE map_channels_clients (" + // "idChannel integer primary key, " + // "idClient integer, " + // "iClientChannelNumber integer," + // "sInputFormat string," + // "sStreamURL string," + // "iEncryptionSystem integer" + // ");" + // ); + // m_pDS->exec("CREATE UNIQUE INDEX idx_idChannel_idClient on map_channels_clients(idChannel, idClient);"); + + CLog::Log(LOGDEBUG, "PVR - %s - creating table 'channelgroups'", __FUNCTION__); + m_pDS->exec( + "CREATE TABLE channelgroups (" + "idGroup integer primary key," + "bIsRadio bool, " + "iGroupType integer, " + "sName varchar(64)" + ")" + ); + m_pDS->exec("CREATE INDEX idx_channelgroups_bIsRadio on channelgroups(bIsRadio);"); + + CLog::Log(LOGDEBUG, "PVR - %s - creating table 'map_channelgroups_channels'", __FUNCTION__); + m_pDS->exec( + "CREATE TABLE map_channelgroups_channels (" + "idChannel integer, " + "idGroup integer, " + "iChannelNumber integer" + ")" + ); + m_pDS->exec("CREATE UNIQUE INDEX idx_idGroup_idChannel on map_channelgroups_channels(idGroup, idChannel);"); + + CLog::Log(LOGDEBUG, "PVR - %s - creating table 'channelsettings'", __FUNCTION__); + m_pDS->exec( + "CREATE TABLE channelsettings (" + "idChannel integer primary key, " + "iInterlaceMethod integer, " + "iViewMode integer, " + "fCustomZoomAmount float, " + "fPixelRatio float, " + "iAudioStream integer, " + "iSubtitleStream integer," + "fSubtitleDelay float, " + "bSubtitles bool, " + "fBrightness float, " + "fContrast float, " + "fGamma float," + "fVolumeAmplification float, " + "fAudioDelay float, " + "bOutputToAllSpeakers bool, " + "bCrop bool, " + "iCropLeft integer, " + "iCropRight integer, " + "iCropTop integer, " + "iCropBottom integer, " + "fSharpness float, " + "fNoiseReduction float, " + "fCustomVerticalShift float, " + "bCustomNonLinStretch bool, " + "bPostProcess bool, " + "iScalingMethod integer, " + "iDeinterlaceMode integer " + ")" + ); + + CommitTransaction(); + bReturn = true; + } + catch (...) + { + CLog::Log(LOGERROR, "PVR - %s - unable to create PVR database tables (error %i)", __FUNCTION__, (int)GetLastError()); + RollbackTransaction(); + bReturn = false; + } + + if (bReturn) + { + // disable all PVR add-on when started the first time + ADDON::VECADDONS addons; + if ((bReturn = CAddonMgr::Get().GetAddons(ADDON_PVRDLL, addons, true)) == false) + CLog::Log(LOGERROR, "PVR - %s - failed to get add-ons from the add-on manager", __FUNCTION__); + else + { + CAddonDatabase database; + database.Open(); + for (IVECADDONS it = addons.begin(); it != addons.end(); it++) + database.DisableAddon(it->get()->ID()); + database.Close(); + } + } + + return bReturn; +} + +bool CPVRDatabase::UpdateOldVersion(int iVersion) +{ + bool bReturn = true; + + BeginTransaction(); + + try + { + if (iVersion < 11) + { + CLog::Log(LOGERROR, "PVR - %s - updating from table versions < 11 not supported. please delete '%s'", + __FUNCTION__, GetBaseDBName()); + bReturn = false; + } + else + { + if (iVersion < 12) + m_pDS->exec("DROP VIEW vw_last_watched;"); + + if (iVersion < 13) + m_pDS->exec("ALTER TABLE channels ADD idEpg integer;"); + + if (iVersion < 14) + m_pDS->exec("ALTER TABLE channelsettings ADD fCustomVerticalShift float;"); + + if (iVersion < 15) + { + m_pDS->exec("ALTER TABLE channelsettings ADD bCustomNonLinStretch bool;"); + m_pDS->exec("ALTER TABLE channelsettings ADD bPostProcess bool;"); + m_pDS->exec("ALTER TABLE channelsettings ADD iScalingMethod integer;"); + } + if (iVersion < 16) + { + /* sqlite apparently can't delete columns from an existing table, so just leave the extra column alone */ + } + if (iVersion < 17) + { + m_pDS->exec("ALTER TABLE channelsettings ADD iDeinterlaceMode integer"); + m_pDS->exec("UPDATE channelsettings SET iDeinterlaceMode = 2 WHERE iInterlaceMethod NOT IN (0,1)"); // anything other than none: method auto => mode force + m_pDS->exec("UPDATE channelsettings SET iDeinterlaceMode = 1 WHERE iInterlaceMethod = 1"); // method auto => mode auto + m_pDS->exec("UPDATE channelsettings SET iDeinterlaceMode = 0, iInterlaceMethod = 1 WHERE iInterlaceMethod = 0"); // method none => mode off, method auto + } + if (iVersion < 18) + { + m_pDS->exec("DROP INDEX idx_channels_iClientId;"); + m_pDS->exec("DROP INDEX idx_channels_iLastWatched;"); + m_pDS->exec("DROP INDEX idx_channels_bIsRadio;"); + m_pDS->exec("DROP INDEX idx_channels_bIsHidden;"); + m_pDS->exec("DROP INDEX idx_idChannel_idGroup;"); + m_pDS->exec("DROP INDEX idx_idGroup_iChannelNumber;"); + m_pDS->exec("CREATE UNIQUE INDEX idx_channels_iClientId_iUniqueId on channels(iClientId, iUniqueId);"); + m_pDS->exec("CREATE UNIQUE INDEX idx_idGroup_idChannel on map_channelgroups_channels(idGroup, idChannel);"); + } + if (iVersion < 19) + { + // bit of a hack, but we need to keep the version/contents of the non-pvr databases the same to allow clean upgrades + ADDON::VECADDONS addons; + if ((bReturn = CAddonMgr::Get().GetAddons(ADDON_PVRDLL, addons, true)) == false) + CLog::Log(LOGERROR, "PVR - %s - failed to get add-ons from the add-on manager", __FUNCTION__); + else + { + CAddonDatabase database; + database.Open(); + for (IVECADDONS it = addons.begin(); it != addons.end(); it++) + { + if (!database.IsSystemPVRAddonEnabled(it->get()->ID())) + database.DisableAddon(it->get()->ID()); + } + database.Close(); + } + } + if (iVersion < 20) + m_pDS->exec("ALTER TABLE channels ADD bIsUserSetIcon bool"); + + if (iVersion < 21) + m_pDS->exec("ALTER TABLE channelgroups ADD iGroupType integer"); + + if (iVersion < 22) + m_pDS->exec("ALTER TABLE channels ADD bIsLocked bool"); + } + } + catch (...) + { + CLog::Log(LOGERROR, "PVR - %s - error attempting to update the database version!", __FUNCTION__); + bReturn = false; + } + + if (bReturn) + CommitTransaction(); + else + RollbackTransaction(); + + return bReturn; +} + +int CPVRDatabase::GetLastChannelId(void) +{ + int iReturn(0); + + CStdString strQuery = FormatSQL("SELECT MAX(idChannel) as iMaxChannel FROM channels"); + if (ResultQuery(strQuery)) + { + try + { + if (!m_pDS->eof()) + iReturn = m_pDS->fv("iMaxChannel").get_asInt(); + } + catch (...) {} + } + + return iReturn; +} + +/********** Channel methods **********/ + +bool CPVRDatabase::DeleteChannels(void) +{ + CLog::Log(LOGDEBUG, "PVR - %s - deleting all channels from the database", __FUNCTION__); + return DeleteValues("channels"); +} + +bool CPVRDatabase::DeleteClientChannels(const CPVRClient &client) +{ + /* invalid client Id */ + if (client.GetID() <= 0) + { + CLog::Log(LOGERROR, "PVR - %s - invalid client id: %i", __FUNCTION__, client.GetID()); + return false; + } + + CLog::Log(LOGDEBUG, "PVR - %s - deleting all channels from client '%i' from the database", __FUNCTION__, client.GetID()); + CStdString strWhereClause = FormatSQL("iClientId = %u", client.GetID()); + return DeleteValues("channels", strWhereClause); +} + +bool CPVRDatabase::Delete(const CPVRChannel &channel) +{ + /* invalid channel */ + if (channel.ChannelID() <= 0) + return false; + + CLog::Log(LOGDEBUG, "PVR - %s - deleting channel '%s' from the database", __FUNCTION__, channel.ChannelName().c_str()); + CStdString strWhereClause = FormatSQL("idChannel = %u", channel.ChannelID()); + return DeleteValues("channels", strWhereClause); +} + +int CPVRDatabase::Get(CPVRChannelGroupInternal &results) +{ + int iReturn(0); + + CStdString strQuery = FormatSQL("SELECT channels.idChannel, channels.iUniqueId, channels.bIsRadio, channels.bIsHidden, channels.bIsUserSetIcon, " + "channels.sIconPath, channels.sChannelName, channels.bIsVirtual, channels.bEPGEnabled, channels.sEPGScraper, channels.iLastWatched, channels.iClientId, channels.bIsLocked, " + "channels.iClientChannelNumber, channels.sInputFormat, channels.sInputFormat, channels.sStreamURL, channels.iEncryptionSystem, map_channelgroups_channels.iChannelNumber, channels.idEpg " + "FROM map_channelgroups_channels " + "LEFT JOIN channels ON channels.idChannel = map_channelgroups_channels.idChannel " + "WHERE map_channelgroups_channels.idGroup = %u", results.IsRadio() ? XBMC_INTERNAL_GROUP_RADIO : XBMC_INTERNAL_GROUP_TV); + if (ResultQuery(strQuery)) + { + try + { + while (!m_pDS->eof()) + { + CPVRChannelPtr channel = CPVRChannelPtr(new CPVRChannel()); + + channel->m_iChannelId = m_pDS->fv("idChannel").get_asInt(); + channel->m_iUniqueId = m_pDS->fv("iUniqueId").get_asInt(); + channel->m_bIsRadio = m_pDS->fv("bIsRadio").get_asBool(); + channel->m_bIsHidden = m_pDS->fv("bIsHidden").get_asBool(); + channel->m_bIsUserSetIcon = m_pDS->fv("bIsUserSetIcon").get_asBool(); + channel->m_bIsLocked = m_pDS->fv("bIsLocked").get_asBool(); + channel->m_strIconPath = m_pDS->fv("sIconPath").get_asString(); + channel->m_strChannelName = m_pDS->fv("sChannelName").get_asString(); + channel->m_bIsVirtual = m_pDS->fv("bIsVirtual").get_asBool(); + channel->m_bEPGEnabled = m_pDS->fv("bEPGEnabled").get_asBool(); + channel->m_strEPGScraper = m_pDS->fv("sEPGScraper").get_asString(); + channel->m_iLastWatched = (time_t) m_pDS->fv("iLastWatched").get_asInt(); + channel->m_iClientId = m_pDS->fv("iClientId").get_asInt(); + channel->m_iClientChannelNumber = m_pDS->fv("iClientChannelNumber").get_asInt(); + channel->m_strInputFormat = m_pDS->fv("sInputFormat").get_asString(); + channel->m_strStreamURL = m_pDS->fv("sStreamURL").get_asString(); + channel->m_iClientEncryptionSystem = m_pDS->fv("iEncryptionSystem").get_asInt(); + channel->m_iEpgId = m_pDS->fv("idEpg").get_asInt(); + + CLog::Log(LOGDEBUG, "PVR - %s - channel '%s' loaded from the database", __FUNCTION__, channel->m_strChannelName.c_str()); + PVRChannelGroupMember newMember = { channel, m_pDS->fv("iChannelNumber").get_asInt() }; + results.m_members.push_back(newMember); + + m_pDS->next(); + ++iReturn; + } + m_pDS->close(); + } + catch (...) + { + CLog::Log(LOGERROR, "PVR - %s - couldn't load channels from the database", __FUNCTION__); + } + } + else + { + CLog::Log(LOGERROR, "PVR - %s - query failed", __FUNCTION__); + } + + m_pDS->close(); + return iReturn; +} + +bool CPVRDatabase::DeleteChannelSettings() +{ + CLog::Log(LOGDEBUG, "PVR - %s - deleting all channel settings from the database", __FUNCTION__); + return DeleteValues("channelsettings"); +} + +bool CPVRDatabase::DeleteChannelSettings(const CPVRChannel &channel) +{ + bool bReturn(false); + + /* invalid channel */ + if (channel.ChannelID() <= 0) + { + CLog::Log(LOGERROR, "PVR - %s - invalid channel id: %i", __FUNCTION__, channel.ChannelID()); + return bReturn; + } + + CStdString strWhereClause = FormatSQL("idChannel = %u", channel.ChannelID()); + return DeleteValues("channelsettings", strWhereClause); +} + +bool CPVRDatabase::GetChannelSettings(const CPVRChannel &channel, CVideoSettings &settings) +{ + bool bReturn(false); + + /* invalid channel */ + if (channel.ChannelID() <= 0) + { + CLog::Log(LOGERROR, "PVR - %s - invalid channel id: %i", __FUNCTION__, channel.ChannelID()); + return bReturn; + } + + CStdString strQuery = FormatSQL("SELECT * FROM channelsettings WHERE idChannel = %u;", channel.ChannelID()); + + if (ResultQuery(strQuery)) + { + try + { + if (m_pDS->num_rows() > 0) + { + settings.m_AudioDelay = m_pDS->fv("fAudioDelay").get_asFloat(); + settings.m_AudioStream = m_pDS->fv("iAudioStream").get_asInt(); + settings.m_Brightness = m_pDS->fv("fBrightness").get_asFloat(); + settings.m_Contrast = m_pDS->fv("fContrast").get_asFloat(); + settings.m_CustomPixelRatio = m_pDS->fv("fPixelRatio").get_asFloat(); + settings.m_CustomNonLinStretch = m_pDS->fv("bCustomNonLinStretch").get_asBool(); + settings.m_NoiseReduction = m_pDS->fv("fNoiseReduction").get_asFloat(); + settings.m_PostProcess = m_pDS->fv("bPostProcess").get_asBool(); + settings.m_Sharpness = m_pDS->fv("fSharpness").get_asFloat(); + settings.m_CustomZoomAmount = m_pDS->fv("fCustomZoomAmount").get_asFloat(); + settings.m_CustomVerticalShift = m_pDS->fv("fCustomVerticalShift").get_asFloat(); + settings.m_Gamma = m_pDS->fv("fGamma").get_asFloat(); + settings.m_SubtitleDelay = m_pDS->fv("fSubtitleDelay").get_asFloat(); + settings.m_SubtitleOn = m_pDS->fv("bSubtitles").get_asBool(); + settings.m_SubtitleStream = m_pDS->fv("iSubtitleStream").get_asInt(); + settings.m_ViewMode = m_pDS->fv("iViewMode").get_asInt(); + settings.m_Crop = m_pDS->fv("bCrop").get_asBool(); + settings.m_CropLeft = m_pDS->fv("iCropLeft").get_asInt(); + settings.m_CropRight = m_pDS->fv("iCropRight").get_asInt(); + settings.m_CropTop = m_pDS->fv("iCropTop").get_asInt(); + settings.m_CropBottom = m_pDS->fv("iCropBottom").get_asInt(); + settings.m_InterlaceMethod = (EINTERLACEMETHOD)m_pDS->fv("iInterlaceMethod").get_asInt(); + settings.m_DeinterlaceMode = (EDEINTERLACEMODE)m_pDS->fv("iDeinterlaceMode").get_asInt(); + settings.m_VolumeAmplification = m_pDS->fv("fVolumeAmplification").get_asFloat(); + settings.m_OutputToAllSpeakers = m_pDS->fv("bOutputToAllSpeakers").get_asBool(); + settings.m_ScalingMethod = (ESCALINGMETHOD)m_pDS->fv("iScalingMethod").get_asInt(); + + bReturn = true; + } + + m_pDS->close(); + } + catch(...) + { + CLog::Log(LOGERROR, "PVR - %s - failed to get channel settings for channel '%s'", __FUNCTION__, channel.ChannelName().c_str()); + } + } + else + { + CLog::Log(LOGERROR, "PVR - %s - query failed", __FUNCTION__); + } + + return bReturn; +} + +bool CPVRDatabase::PersistChannelSettings(const CPVRChannel &channel, const CVideoSettings &settings) +{ + /* invalid channel */ + if (channel.ChannelID() <= 0) + { + CLog::Log(LOGERROR, "PVR - %s - invalid channel id: %i", __FUNCTION__, channel.ChannelID()); + return false; + } + + CStdString strQuery = FormatSQL( + "REPLACE INTO channelsettings " + "(idChannel, iInterlaceMethod, iViewMode, fCustomZoomAmount, fPixelRatio, iAudioStream, iSubtitleStream, fSubtitleDelay, " + "bSubtitles, fBrightness, fContrast, fGamma, fVolumeAmplification, fAudioDelay, bOutputToAllSpeakers, bCrop, iCropLeft, " + "iCropRight, iCropTop, iCropBottom, fSharpness, fNoiseReduction, fCustomVerticalShift, bCustomNonLinStretch, bPostProcess, iScalingMethod, iDeinterlaceMode) VALUES " + "(%i, %i, %i, %f, %f, %i, %i, %f, %i, %f, %f, %f, %f, %f, %i, %i, %i, %i, %i, %i, %f, %f, %f, %i, %i, %i, %i);", + channel.ChannelID(), settings.m_InterlaceMethod, settings.m_ViewMode, settings.m_CustomZoomAmount, settings.m_CustomPixelRatio, + settings.m_AudioStream, settings.m_SubtitleStream, settings.m_SubtitleDelay, settings.m_SubtitleOn ? 1 :0, + settings.m_Brightness, settings.m_Contrast, settings.m_Gamma, settings.m_VolumeAmplification, settings.m_AudioDelay, + settings.m_OutputToAllSpeakers ? 1 : 0, settings.m_Crop ? 1 : 0, settings.m_CropLeft, settings.m_CropRight, settings.m_CropTop, + settings.m_CropBottom, settings.m_Sharpness, settings.m_NoiseReduction, settings.m_CustomVerticalShift, + settings.m_CustomNonLinStretch ? 1 : 0, settings.m_PostProcess ? 1 : 0, settings.m_ScalingMethod, settings.m_DeinterlaceMode); + + return ExecuteQuery(strQuery); +} + +/********** Channel group methods **********/ + +bool CPVRDatabase::RemoveChannelsFromGroup(const CPVRChannelGroup &group) +{ + CStdString strWhereClause = FormatSQL("idGroup = %u", group.GroupID()); + return DeleteValues("map_channelgroups_channels", strWhereClause); +} + +bool CPVRDatabase::GetCurrentGroupMembers(const CPVRChannelGroup &group, vector<int> &members) +{ + bool bReturn(false); + /* invalid group id */ + if (group.GroupID() <= 0) + { + CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID()); + return false; + } + + CStdString strCurrentMembersQuery = FormatSQL("SELECT idChannel FROM map_channelgroups_channels WHERE idGroup = %u", group.GroupID()); + if (ResultQuery(strCurrentMembersQuery)) + { + try + { + while (!m_pDS->eof()) + { + members.push_back(m_pDS->fv("idChannel").get_asInt()); + m_pDS->next(); + } + m_pDS->close(); + bReturn = true; + } + catch (...) + { + CLog::Log(LOGERROR, "PVR - %s - couldn't load channels from the database", __FUNCTION__); + } + } + else + { + CLog::Log(LOGERROR, "PVR - %s - query failed", __FUNCTION__); + } + + return bReturn; +} + +bool CPVRDatabase::DeleteChannelsFromGroup(const CPVRChannelGroup &group) +{ + /* invalid group id */ + if (group.GroupID() <= 0) + { + CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID()); + return false; + } + + CStdString strWhereClause; + strWhereClause = FormatSQL("idGroup = %u", group.GroupID()); + return DeleteValues("map_channelgroups_channels", strWhereClause); +} + +bool CPVRDatabase::DeleteChannelsFromGroup(const CPVRChannelGroup &group, const vector<int> &channelsToDelete) +{ + bool bDelete(true); + unsigned int iDeletedChannels(0); + /* invalid group id */ + if (group.GroupID() <= 0) + { + CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID()); + return false; + } + + while (iDeletedChannels < channelsToDelete.size()) + { + CStdString strChannelsToDelete; + CStdString strWhereClause; + + for (unsigned int iChannelPtr = 0; iChannelPtr + iDeletedChannels < channelsToDelete.size() && iChannelPtr < 50; iChannelPtr++) + strChannelsToDelete.AppendFormat(", %d", channelsToDelete.at(iDeletedChannels + iChannelPtr)); + + if (!strChannelsToDelete.IsEmpty()) + { + strChannelsToDelete = strChannelsToDelete.Right(strChannelsToDelete.length() - 2); + strWhereClause = FormatSQL("idGroup = %u AND idChannel IN (%s)", group.GroupID(), strChannelsToDelete.c_str()); + bDelete = DeleteValues("map_channelgroups_channels", strWhereClause) && bDelete; + } + + iDeletedChannels += 50; + } + + return bDelete; +} + +bool CPVRDatabase::RemoveStaleChannelsFromGroup(const CPVRChannelGroup &group) +{ + bool bDelete(true); + /* invalid group id */ + if (group.GroupID() <= 0) + { + CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID()); + return false; + } + + if (!group.IsInternalGroup()) + { + /* First remove channels that don't exist in the main channels table */ + CStdString strWhereClause = FormatSQL("idChannel IN (SELECT map_channelgroups_channels.idChannel FROM map_channelgroups_channels LEFT JOIN channels on map_channelgroups_channels.idChannel = channels.idChannel WHERE channels.idChannel IS NULL)"); + bDelete = DeleteValues("map_channelgroups_channels", strWhereClause); + } + + if (group.m_members.size() > 0) + { + vector<int> currentMembers; + if (GetCurrentGroupMembers(group, currentMembers)) + { + vector<int> channelsToDelete; + for (unsigned int iChannelPtr = 0; iChannelPtr < currentMembers.size(); iChannelPtr++) + { + if (!group.IsGroupMember(currentMembers.at(iChannelPtr))) + channelsToDelete.push_back(currentMembers.at(iChannelPtr)); + } + + bDelete = DeleteChannelsFromGroup(group, channelsToDelete) && bDelete; + } + } + else + { + CStdString strWhereClause = FormatSQL("idGroup = %u", group.GroupID()); + bDelete = DeleteValues("map_channelgroups_channels", strWhereClause) && bDelete; + } + + return bDelete; +} + +bool CPVRDatabase::DeleteChannelGroups(void) +{ + CLog::Log(LOGDEBUG, "PVR - %s - deleting all channel groups from the database", __FUNCTION__); + + return DeleteValues("channelgroups") && + DeleteValues("map_channelgroups_channels"); +} + +bool CPVRDatabase::Delete(const CPVRChannelGroup &group) +{ + /* invalid group id */ + if (group.GroupID() <= 0) + { + CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID()); + return false; + } + + CStdString strWhereClause = FormatSQL("idGroup = %u AND bIsRadio = %u", group.GroupID(), group.IsRadio()); + return RemoveChannelsFromGroup(group) && + DeleteValues("channelgroups", strWhereClause); +} + +bool CPVRDatabase::Get(CPVRChannelGroups &results) +{ + bool bReturn = false; + CStdString strQuery = FormatSQL("SELECT * from channelgroups WHERE bIsRadio = %u", results.IsRadio()); + + if (ResultQuery(strQuery)) + { + try + { + while (!m_pDS->eof()) + { + CPVRChannelGroup data(m_pDS->fv("bIsRadio").get_asBool(), m_pDS->fv("idGroup").get_asInt(), m_pDS->fv("sName").get_asString()); + data.SetGroupType(m_pDS->fv("iGroupType").get_asInt()); + results.Update(data); + + CLog::Log(LOGDEBUG, "PVR - %s - group '%s' loaded from the database", __FUNCTION__, data.GroupName().c_str()); + m_pDS->next(); + } + m_pDS->close(); + bReturn = true; + } + catch (...) + { + CLog::Log(LOGERROR, "%s - couldn't load channels from the database", __FUNCTION__); + } + } + + return bReturn; +} + +int CPVRDatabase::Get(CPVRChannelGroup &group) +{ + int iReturn = -1; + + /* invalid group id */ + if (group.GroupID() < 0) + { + CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID()); + return -1; + } + + CStdString strQuery = FormatSQL("SELECT idChannel, iChannelNumber FROM map_channelgroups_channels WHERE idGroup = %u ORDER BY iChannelNumber", group.GroupID()); + if (ResultQuery(strQuery)) + { + iReturn = 0; + + try + { + while (!m_pDS->eof()) + { + int iChannelId = m_pDS->fv("idChannel").get_asInt(); + int iChannelNumber = m_pDS->fv("iChannelNumber").get_asInt(); + CPVRChannelPtr channel = g_PVRChannelGroups->GetGroupAll(group.IsRadio())->GetByChannelID(iChannelId); + + if (channel && group.AddToGroup(*channel, iChannelNumber)) + ++iReturn; + + m_pDS->next(); + } + m_pDS->close(); + } + catch(...) + { + CLog::Log(LOGERROR, "PVR - %s - failed to get channels", __FUNCTION__); + } + } + + return iReturn; +} + +bool CPVRDatabase::PersistChannels(CPVRChannelGroup &group) +{ + bool bReturn(true); + int iLastChannel(0); + + /* we can only safely get this from a local db */ + if (m_sqlite) + iLastChannel = GetLastChannelId(); + + for (unsigned int iChannelPtr = 0; iChannelPtr < group.m_members.size(); iChannelPtr++) + { + PVRChannelGroupMember member = group.m_members.at(iChannelPtr); + if (member.channel->IsChanged() || member.channel->IsNew()) + { + if (m_sqlite && member.channel->IsNew()) + member.channel->SetChannelID(++iLastChannel); + bReturn &= Persist(*member.channel, m_sqlite || !member.channel->IsNew()); + } + } + return CommitInsertQueries(); +} + +bool CPVRDatabase::PersistGroupMembers(CPVRChannelGroup &group) +{ + bool bReturn = true; + bool bRemoveChannels = true; + CStdString strQuery; + CSingleLock lock(group.m_critSection); + + if (group.m_members.size() > 0) + { + for (unsigned int iChannelPtr = 0; iChannelPtr < group.m_members.size(); iChannelPtr++) + { + PVRChannelGroupMember member = group.m_members.at(iChannelPtr); + + CStdString strWhereClause = FormatSQL("idChannel = %u AND idGroup = %u AND iChannelNumber = %u", + member.channel->ChannelID(), group.GroupID(), member.iChannelNumber); + + CStdString strValue = GetSingleValue("map_channelgroups_channels", "idChannel", strWhereClause); + if (strValue.IsEmpty()) + { + strQuery = FormatSQL("REPLACE INTO map_channelgroups_channels (" + "idGroup, idChannel, iChannelNumber) " + "VALUES (%i, %i, %i);", + group.GroupID(), member.channel->ChannelID(), member.iChannelNumber); + QueueInsertQuery(strQuery); + } + } + lock.Leave(); + + bReturn = CommitInsertQueries(); + bRemoveChannels = RemoveStaleChannelsFromGroup(group); + } + + return bReturn && bRemoveChannels; +} + +/********** Client methods **********/ + +bool CPVRDatabase::DeleteClients() +{ + CLog::Log(LOGDEBUG, "PVR - %s - deleting all clients from the database", __FUNCTION__); + + return DeleteValues("clients"); + //TODO && DeleteValues("map_channels_clients"); +} + +bool CPVRDatabase::Delete(const CPVRClient &client) +{ + /* invalid client uid */ + if (client.ID().IsEmpty()) + { + CLog::Log(LOGERROR, "PVR - %s - invalid client uid", __FUNCTION__); + return false; + } + + CStdString strWhereClause = FormatSQL("sUid = '%s'", client.ID().c_str()); + return DeleteValues("clients", strWhereClause); +} + +int CPVRDatabase::GetClientId(const CStdString &strClientUid) +{ + CStdString strWhereClause = FormatSQL("sUid = '%s'", strClientUid.c_str()); + CStdString strValue = GetSingleValue("clients", "idClient", strWhereClause); + + if (strValue.IsEmpty()) + return -1; + + return atol(strValue.c_str()); +} + +bool CPVRDatabase::Persist(CPVRChannelGroup &group) +{ + bool bReturn(false); + if (group.GroupName().IsEmpty()) + { + CLog::Log(LOGERROR, "%s - empty group name", __FUNCTION__); + return bReturn; + } + + CStdString strQuery; + bReturn = true; + { + CSingleLock lock(group.m_critSection); + + /* insert a new entry when this is a new group, or replace the existing one otherwise */ + if (group.GroupID() <= 0) + strQuery = FormatSQL("INSERT INTO channelgroups (bIsRadio, iGroupType, sName) VALUES (%i, %i, '%s')", + (group.IsRadio() ? 1 :0), group.GroupType(), group.GroupName().c_str()); + else + strQuery = FormatSQL("REPLACE INTO channelgroups (idGroup, bIsRadio, iGroupType, sName) VALUES (%i, %i, %i, '%s')", + group.GroupID(), (group.IsRadio() ? 1 :0), group.GroupType(), group.GroupName().c_str()); + + bReturn = ExecuteQuery(strQuery); + + /* set the group id if it was <= 0 */ + if (bReturn && group.GroupID() <= 0) + group.m_iGroupId = (int) m_pDS->lastinsertid(); + } + + /* only persist the channel data for the internal groups */ + if (group.IsInternalGroup()) + bReturn &= PersistChannels(group); + + /* persist the group member entries */ + if (bReturn) + bReturn = PersistGroupMembers(group); + + return bReturn; +} + +int CPVRDatabase::Persist(const AddonPtr client) +{ + int iReturn(-1); + + /* invalid client uid or name */ + if (client->Name().IsEmpty() || client->ID().IsEmpty()) + { + CLog::Log(LOGERROR, "PVR - %s - invalid client uid or name", __FUNCTION__); + return iReturn; + } + + CStdString strQuery = FormatSQL("REPLACE INTO clients (sName, sUid) VALUES ('%s', '%s');", + client->Name().c_str(), client->ID().c_str()); + + if (ExecuteQuery(strQuery)) + iReturn = (int) m_pDS->lastinsertid(); + + return iReturn; +} + +bool CPVRDatabase::Persist(CPVRChannel &channel, bool bQueueWrite /* = false */) +{ + bool bReturn(false); + + /* invalid channel */ + if (channel.UniqueID() <= 0) + { + CLog::Log(LOGERROR, "PVR - %s - invalid channel uid: %d", __FUNCTION__, channel.UniqueID()); + return bReturn; + } + + CStdString strQuery; + if (channel.ChannelID() <= 0) + { + /* new channel */ + strQuery = FormatSQL("INSERT INTO channels (" + "iUniqueId, bIsRadio, bIsHidden, bIsUserSetIcon, bIsLocked, " + "sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iLastWatched, iClientId, " + "iClientChannelNumber, sInputFormat, sStreamURL, iEncryptionSystem, idEpg) " + "VALUES (%i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %i, '%s', '%s', %i, %i)", + channel.UniqueID(), (channel.IsRadio() ? 1 :0), (channel.IsHidden() ? 1 : 0), (channel.IsUserSetIcon() ? 1 : 0), (channel.IsLocked() ? 1 : 0), + channel.IconPath().c_str(), channel.ChannelName().c_str(), (channel.IsVirtual() ? 1 : 0), (channel.EPGEnabled() ? 1 : 0), channel.EPGScraper().c_str(), channel.LastWatched(), channel.ClientID(), + channel.ClientChannelNumber(), channel.InputFormat().c_str(), channel.StreamURL().c_str(), channel.EncryptionSystem(), + channel.EpgID()); + } + else + { + /* update channel */ + strQuery = FormatSQL("REPLACE INTO channels (" + "iUniqueId, bIsRadio, bIsHidden, bIsUserSetIcon, bIsLocked, " + "sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iLastWatched, iClientId, " + "iClientChannelNumber, sInputFormat, sStreamURL, iEncryptionSystem, idChannel, idEpg) " + "VALUES (%i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %i, '%s', '%s', %i, %i, %i)", + channel.UniqueID(), (channel.IsRadio() ? 1 :0), (channel.IsHidden() ? 1 : 0), (channel.IsUserSetIcon() ? 1 : 0), (channel.IsLocked() ? 1 : 0), + channel.IconPath().c_str(), channel.ChannelName().c_str(), (channel.IsVirtual() ? 1 : 0), (channel.EPGEnabled() ? 1 : 0), channel.EPGScraper().c_str(), channel.LastWatched(), channel.ClientID(), + channel.ClientChannelNumber(), channel.InputFormat().c_str(), channel.StreamURL().c_str(), channel.EncryptionSystem(), channel.ChannelID(), + channel.EpgID()); + } + + if (bQueueWrite) + { + QueueInsertQuery(strQuery); + bReturn = true; + } + else if (ExecuteQuery(strQuery)) + { + CSingleLock lock(channel.m_critSection); + if (channel.m_iChannelId <= 0) + channel.m_iChannelId = (int)m_pDS->lastinsertid(); + bReturn = true; + } + + return bReturn; +} diff --git a/xbmc/pvr/PVRDatabase.h b/xbmc/pvr/PVRDatabase.h new file mode 100644 index 0000000000..c14ced9d0e --- /dev/null +++ b/xbmc/pvr/PVRDatabase.h @@ -0,0 +1,257 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "addons/Addon.h" +#include "addons/AddonDll.h" +#include "addons/DllPVRClient.h" +#include "PVRManager.h" +#include "dbwrappers/Database.h" +#include "XBDateTime.h" +#include "utils/log.h" + +class CVideoSettings; + +namespace PVR +{ + class CPVRChannelGroup; + class CPVRChannelGroupInternal; + class CPVRChannelsContainer; + class CPVRChannel; + class CPVRChannelGroups; + class CPVRClient; + + /** The PVR database */ + + class CPVRDatabase : public CDatabase + { + public: + /*! + * @brief Create a new instance of the PVR database. + */ + CPVRDatabase(void) {}; + virtual ~CPVRDatabase(void) {}; + + /*! + * @brief Open the database. + * @return True if it was opened successfully, false otherwise. + */ + virtual bool Open(); + + /*! + * @brief Get the minimal database version that is required to operate correctly. + * @return The minimal database version. + */ + virtual int GetMinVersion() const { return 22; }; + + /*! + * @brief Get the default sqlite database filename. + * @return The default filename. + */ + const char *GetBaseDBName() const { return "TV"; }; + + /*! @name Channel methods */ + //@{ + + /*! + * @brief Remove all channels from the database. + * @return True if all channels were removed, false otherwise. + */ + bool DeleteChannels(void); + + /*! + * @brief Remove all channels from a client from the database. + * @param client The client to delete the channels for. + * @return True if the channels were deleted, false otherwise. + */ + bool DeleteClientChannels(const CPVRClient &client); + + /*! + * @brief Add or update a channel entry in the database + * @param channel The channel to persist. + * @param bQueueWrite If true, don't write immediately + * @return True when persisted or queued, false otherwise. + */ + bool Persist(CPVRChannel &channel, bool bQueueWrite = false); + + /*! + * @brief Remove a channel entry from the database + * @param channel The channel to remove. + * @return True if the channel was removed, false otherwise. + */ + bool Delete(const CPVRChannel &channel); + + /*! + * @brief Get the list of channels from the database + * @param results The channel group to store the results in. + * @return The amount of channels that were added. + */ + int Get(CPVRChannelGroupInternal &results); + + //@} + + /*! @name Channel settings methods */ + //@{ + + /*! + * @brief Remove all channel settings from the database. + * @return True if all channels were removed successfully, false if not. + */ + bool DeleteChannelSettings(); + + /*! + * @brief Remove channel settings from the database. + * @return True if channel were removed successfully, false if not. + */ + bool DeleteChannelSettings(const CPVRChannel &channel); + + /*! + * @brief Get the channel settings from the database. + * @param channel The channel to get the settings for. + * @param settings Store the settings in here. + * @return True if the settings were fetched successfully, false if not. + */ + bool GetChannelSettings(const CPVRChannel &channel, CVideoSettings &settings); + + /*! + * @brief Store channel settings in the database. + * @param channel The channel to store the settings for. + * @param settings The settings to store. + * @return True if the settings were stored successfully, false if not. + */ + bool PersistChannelSettings(const CPVRChannel &channel, const CVideoSettings &settings); + + //@} + + /*! @name Channel group methods */ + //@{ + + /*! + * @brief Remove all channel groups from the database + * @return True if all channel groups were removed. + */ + bool DeleteChannelGroups(void); + + /*! + * @brief Delete a channel group from the database. + * @param group The group to delete. + * @return True if the group was deleted successfully, false otherwise. + */ + bool Delete(const CPVRChannelGroup &group); + + /*! + * @brief Get the channel groups. + * @param results The container to store the results in. + * @return True if the list was fetched successfully, false otherwise. + */ + bool Get(CPVRChannelGroups &results); + + /*! + * @brief Add the group members to a group. + * @param group The group to get the channels for. + * @return The amount of channels that were added. + */ + int Get(CPVRChannelGroup &group); + + /*! + * @brief Add or update a channel group entry in the database. + * @param group The group to persist. + * @return True if the group was persisted successfully, false otherwise. + */ + bool Persist(CPVRChannelGroup &group); + + //@} + + /*! @name Client methods */ + //@{ + /*! + * @brief Remove all client information from the database. + * @return True if all clients were removed successfully. + */ + bool DeleteClients(); + + /*! + * @brief Add a client to the database if it's not already in there. + * @param strClientName The name of the client. + * @param strGuid The unique ID of the client. + * @return The database ID of the client. + */ + int Persist(const ADDON::AddonPtr addon); + + /*! + * @brief Remove a client from the database + * @param strGuid The unique ID of the client. + * @return True if the client was removed successfully, false otherwise. + */ + bool Delete(const CPVRClient &client); + + /*! + * @brief Get the database ID of a client. + * @param strClientUid The unique ID of the client. + * @return The database ID of the client or -1 if it wasn't found. + */ + int GetClientId(const CStdString &strClientUid); + //@} + + private: + /*! + * @brief Create the PVR database tables. + * @return True if the tables were created successfully, false otherwise. + */ + bool CreateTables(); + + bool DeleteChannelsFromGroup(const CPVRChannelGroup &group); + bool DeleteChannelsFromGroup(const CPVRChannelGroup &group, const std::vector<int> &channelsToDelete); + + bool GetCurrentGroupMembers(const CPVRChannelGroup &group, std::vector<int> &members); + int GetLastChannelId(void); + bool RemoveStaleChannelsFromGroup(const CPVRChannelGroup &group); + + /*! + * @brief Update an old version of the database. + * @param version The version to update the database from. + * @return True if it was updated successfully, false otherwise. + */ + bool UpdateOldVersion(int version); + + bool PersistGroupMembers(CPVRChannelGroup &group); + + bool PersistChannels(CPVRChannelGroup &group); + + bool RemoveChannelsFromGroup(const CPVRChannelGroup &group); + }; + + /*! + * @brief Try to open the PVR database. + * @return The opened database or NULL if the database failed to open. + */ + inline CPVRDatabase *GetPVRDatabase(void) + { + CPVRDatabase *database = g_PVRManager.GetTVDatabase(); + if (!database || !database->IsOpen()) + { + CLog::Log(LOGERROR, "PVR - failed to open the database"); + database = NULL; + } + + return database; + } +} diff --git a/xbmc/pvr/PVRGUIInfo.cpp b/xbmc/pvr/PVRGUIInfo.cpp new file mode 100644 index 0000000000..fee1131985 --- /dev/null +++ b/xbmc/pvr/PVRGUIInfo.cpp @@ -0,0 +1,857 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "Application.h" +#include "PVRGUIInfo.h" +#include "guilib/LocalizeStrings.h" +#include "utils/StringUtils.h" +#include "GUIInfoManager.h" +#include "Util.h" +#include "threads/SingleLock.h" +#include "PVRManager.h" +#include "pvr/addons/PVRClients.h" +#include "pvr/timers/PVRTimers.h" +#include "pvr/recordings/PVRRecordings.h" +#include "pvr/channels/PVRChannel.h" +#include "epg/EpgInfoTag.h" +#include "settings/AdvancedSettings.h" +#include "settings/GUISettings.h" + +using namespace PVR; +using namespace EPG; +using namespace std; + +CPVRGUIInfo::CPVRGUIInfo(void) : + CThread("PVR GUI info updater"), + m_playingEpgTag(NULL) +{ + ResetProperties(); +} + +CPVRGUIInfo::~CPVRGUIInfo(void) +{ + Stop(); +} + +void CPVRGUIInfo::ResetProperties(void) +{ + CSingleLock lock(m_critSection); + m_strActiveTimerTitle = StringUtils::EmptyString; + m_strActiveTimerChannelName = StringUtils::EmptyString; + m_strActiveTimerChannelIcon = StringUtils::EmptyString; + m_strActiveTimerTime = StringUtils::EmptyString; + m_strNextTimerInfo = StringUtils::EmptyString; + m_strNextRecordingTitle = StringUtils::EmptyString; + m_strNextRecordingChannelName = StringUtils::EmptyString; + m_strNextRecordingChannelIcon = StringUtils::EmptyString; + m_strNextRecordingTime = StringUtils::EmptyString; + m_iTimerAmount = 0; + m_bHasRecordings = false; + m_iRecordingTimerAmount = 0; + m_iActiveClients = 0; + m_strPlayingClientName = StringUtils::EmptyString; + m_strBackendName = StringUtils::EmptyString; + m_strBackendVersion = StringUtils::EmptyString; + m_strBackendHost = StringUtils::EmptyString; + m_strBackendDiskspace = StringUtils::EmptyString; + m_strBackendTimers = StringUtils::EmptyString; + m_strBackendRecordings = StringUtils::EmptyString; + m_strBackendChannels = StringUtils::EmptyString; + m_strTotalDiskspace = StringUtils::EmptyString; + m_iAddonInfoToggleStart = 0; + m_iAddonInfoToggleCurrent = 0; + m_iTimerInfoToggleStart = 0; + m_iTimerInfoToggleCurrent = 0; + m_iToggleShowInfo = 0; + m_iDuration = 0; + m_bHasNonRecordingTimers = false; + m_bIsPlayingTV = false; + m_bIsPlayingRadio = false; + m_bIsPlayingRecording = false; + m_bIsPlayingEncryptedStream = false; + + ResetPlayingTag(); + ClearQualityInfo(m_qualityInfo); +} + +void CPVRGUIInfo::ClearQualityInfo(PVR_SIGNAL_STATUS &qualityInfo) +{ + memset(&qualityInfo, 0, sizeof(qualityInfo)); + strncpy(qualityInfo.strAdapterName, g_localizeStrings.Get(13106).c_str(), 1024); + strncpy(qualityInfo.strAdapterStatus, g_localizeStrings.Get(13106).c_str(), 1024); +} + +void CPVRGUIInfo::Start(void) +{ + ResetProperties(); + Create(); + SetPriority(-1); +} + +void CPVRGUIInfo::Stop(void) +{ + StopThread(); + if (g_PVRTimers) + g_PVRTimers->UnregisterObserver(this); +} + +void CPVRGUIInfo::Notify(const Observable &obs, const ObservableMessage msg) +{ + if (msg == ObservableMessageTimers) + UpdateTimersCache(); +} + +void CPVRGUIInfo::ShowPlayerInfo(int iTimeout) +{ + CSingleLock lock(m_critSection); + + if (iTimeout > 0) + m_iToggleShowInfo = (int) XbmcThreads::SystemClockMillis() + iTimeout * 1000; + + g_infoManager.SetShowInfo(true); +} + +void CPVRGUIInfo::ToggleShowInfo(void) +{ + CSingleLock lock(m_critSection); + + if (m_iToggleShowInfo > 0 && m_iToggleShowInfo < (unsigned int) XbmcThreads::SystemClockMillis()) + { + m_iToggleShowInfo = 0; + g_infoManager.SetShowInfo(false); + } +} + +bool CPVRGUIInfo::AddonInfoToggle(void) +{ + CSingleLock lock(m_critSection); + if (m_iAddonInfoToggleStart == 0) + { + m_iAddonInfoToggleStart = XbmcThreads::SystemClockMillis(); + m_iAddonInfoToggleCurrent = 0; + return true; + } + + if ((int) (XbmcThreads::SystemClockMillis() - m_iAddonInfoToggleStart) > g_advancedSettings.m_iPVRInfoToggleInterval) + { + unsigned int iPrevious = m_iAddonInfoToggleCurrent; + if (((int) ++m_iAddonInfoToggleCurrent) > m_iActiveClients - 1) + m_iAddonInfoToggleCurrent = 0; + + return m_iAddonInfoToggleCurrent != iPrevious; + } + + return false; +} + +bool CPVRGUIInfo::TimerInfoToggle(void) +{ + CSingleLock lock(m_critSection); + if (m_iTimerInfoToggleStart == 0) + { + m_iTimerInfoToggleStart = XbmcThreads::SystemClockMillis(); + m_iTimerInfoToggleCurrent = 0; + return true; + } + + if ((int) (XbmcThreads::SystemClockMillis() - m_iTimerInfoToggleStart) > g_advancedSettings.m_iPVRInfoToggleInterval) + { + unsigned int iPrevious = m_iTimerInfoToggleCurrent; + unsigned int iBoundary = m_iRecordingTimerAmount > 0 ? m_iRecordingTimerAmount : m_iTimerAmount; + if (++m_iTimerInfoToggleCurrent > iBoundary - 1) + m_iTimerInfoToggleCurrent = 0; + + return m_iTimerInfoToggleCurrent != iPrevious; + } + + return false; +} + +void CPVRGUIInfo::Process(void) +{ + unsigned int mLoop(0); + + /* updated on request */ + g_PVRTimers->RegisterObserver(this); + UpdateTimersCache(); + + while (!g_application.m_bStop && !m_bStop) + { + if (!m_bStop) + ToggleShowInfo(); + Sleep(0); + + if (!m_bStop) + UpdateQualityData(); + Sleep(0); + + if (!m_bStop) + UpdateMisc(); + Sleep(0); + + if (!m_bStop) + UpdatePlayingTag(); + Sleep(0); + + if (!m_bStop) + UpdateTimersToggle(); + Sleep(0); + + if (!m_bStop) + UpdateNextTimer(); + Sleep(0); + + if (!m_bStop && mLoop % 10 == 0) + UpdateBackendCache(); /* updated every 10 iterations */ + + if (++mLoop == 1000) + mLoop = 0; + + if (!m_bStop) + Sleep(1000); + } + + if (!m_bStop) + ResetPlayingTag(); +} + +void CPVRGUIInfo::UpdateQualityData(void) +{ + PVR_SIGNAL_STATUS qualityInfo; + ClearQualityInfo(qualityInfo); + + PVR_CLIENT client; + if (g_guiSettings.GetBool("pvrplayback.signalquality") && + g_PVRClients->GetPlayingClient(client)) + { + client->SignalQuality(qualityInfo); + } + + memcpy(&m_qualityInfo, &qualityInfo, sizeof(m_qualityInfo)); +} + +void CPVRGUIInfo::UpdateMisc(void) +{ + bool bStarted = g_PVRManager.IsStarted(); + CStdString strPlayingClientName = bStarted ? g_PVRClients->GetPlayingClientName() : StringUtils::EmptyString; + bool bHasRecordings = bStarted && g_PVRRecordings->GetNumRecordings() > 0; + bool bIsPlayingTV = bStarted && g_PVRClients->IsPlayingTV(); + bool bIsPlayingRadio = bStarted && g_PVRClients->IsPlayingRadio(); + bool bIsPlayingRecording = bStarted && g_PVRClients->IsPlayingRecording(); + bool bIsPlayingEncryptedStream = bStarted && g_PVRClients->IsEncrypted(); + /* safe to fetch these unlocked, since they're updated from the same thread as this one */ + bool bHasNonRecordingTimers = bStarted && m_iTimerAmount - m_iRecordingTimerAmount > 0; + + CSingleLock lock(m_critSection); + m_strPlayingClientName = strPlayingClientName; + m_bHasRecordings = bHasRecordings; + m_bHasNonRecordingTimers = bHasNonRecordingTimers; + m_bIsPlayingTV = bIsPlayingTV; + m_bIsPlayingRadio = bIsPlayingRadio; + m_bIsPlayingRecording = bIsPlayingRecording; + m_bIsPlayingEncryptedStream = bIsPlayingEncryptedStream; +} + +bool CPVRGUIInfo::TranslateCharInfo(DWORD dwInfo, CStdString &strValue) const +{ + bool bReturn(true); + CSingleLock lock(m_critSection); + + switch(dwInfo) + { + case PVR_NOW_RECORDING_TITLE: + CharInfoActiveTimerTitle(strValue); + break; + case PVR_NOW_RECORDING_CHANNEL: + CharInfoActiveTimerChannelName(strValue); + break; + case PVR_NOW_RECORDING_CHAN_ICO: + CharInfoActiveTimerChannelIcon(strValue); + break; + case PVR_NOW_RECORDING_DATETIME: + CharInfoActiveTimerDateTime(strValue); + break; + case PVR_NEXT_RECORDING_TITLE: + CharInfoNextTimerTitle(strValue); + break; + case PVR_NEXT_RECORDING_CHANNEL: + CharInfoNextTimerChannelName(strValue); + break; + case PVR_NEXT_RECORDING_CHAN_ICO: + CharInfoNextTimerChannelIcon(strValue); + break; + case PVR_NEXT_RECORDING_DATETIME: + CharInfoNextTimerDateTime(strValue); + break; + case PVR_PLAYING_DURATION: + CharInfoPlayingDuration(strValue); + break; + case PVR_PLAYING_TIME: + CharInfoPlayingTime(strValue); + break; + case PVR_NEXT_TIMER: + CharInfoNextTimer(strValue); + break; + case PVR_ACTUAL_STREAM_VIDEO_BR: + CharInfoVideoBR(strValue); + break; + case PVR_ACTUAL_STREAM_AUDIO_BR: + CharInfoAudioBR(strValue); + break; + case PVR_ACTUAL_STREAM_DOLBY_BR: + CharInfoDolbyBR(strValue); + break; + case PVR_ACTUAL_STREAM_SIG: + CharInfoSignal(strValue); + break; + case PVR_ACTUAL_STREAM_SNR: + CharInfoSNR(strValue); + break; + case PVR_ACTUAL_STREAM_BER: + CharInfoBER(strValue); + break; + case PVR_ACTUAL_STREAM_UNC: + CharInfoUNC(strValue); + break; + case PVR_ACTUAL_STREAM_CLIENT: + CharInfoPlayingClientName(strValue); + break; + case PVR_ACTUAL_STREAM_DEVICE: + CharInfoFrontendName(strValue); + break; + case PVR_ACTUAL_STREAM_STATUS: + CharInfoFrontendStatus(strValue); + break; + case PVR_ACTUAL_STREAM_CRYPTION: + CharInfoEncryption(strValue); + break; + case PVR_BACKEND_NAME: + CharInfoBackendName(strValue); + break; + case PVR_BACKEND_VERSION: + CharInfoBackendVersion(strValue); + break; + case PVR_BACKEND_HOST: + CharInfoBackendHost(strValue); + break; + case PVR_BACKEND_DISKSPACE: + CharInfoBackendDiskspace(strValue); + break; + case PVR_BACKEND_CHANNELS: + CharInfoBackendChannels(strValue); + break; + case PVR_BACKEND_TIMERS: + CharInfoBackendTimers(strValue); + break; + case PVR_BACKEND_RECORDINGS: + CharInfoBackendRecordings(strValue); + break; + case PVR_BACKEND_NUMBER: + CharInfoBackendNumber(strValue); + break; + case PVR_TOTAL_DISKSPACE: + CharInfoTotalDiskSpace(strValue); + break; + default: + strValue = StringUtils::EmptyString; + bReturn = false; + break; + } + + return bReturn; +} + +bool CPVRGUIInfo::TranslateBoolInfo(DWORD dwInfo) const +{ + bool bReturn(false); + CSingleLock lock(m_critSection); + + switch (dwInfo) + { + case PVR_IS_RECORDING: + bReturn = m_iRecordingTimerAmount > 0; + break; + case PVR_HAS_TIMER: + bReturn = m_iTimerAmount > 0; + break; + case PVR_HAS_NONRECORDING_TIMER: + bReturn = m_bHasNonRecordingTimers; + break; + case PVR_IS_PLAYING_TV: + bReturn = m_bIsPlayingTV; + break; + case PVR_IS_PLAYING_RADIO: + bReturn = m_bIsPlayingRadio; + break; + case PVR_IS_PLAYING_RECORDING: + bReturn = m_bIsPlayingRecording; + break; + case PVR_ACTUAL_STREAM_ENCRYPTED: + bReturn = m_bIsPlayingEncryptedStream; + break; + default: + break; + } + + return bReturn; +} + +int CPVRGUIInfo::TranslateIntInfo(DWORD dwInfo) const +{ + int iReturn(0); + CSingleLock lock(m_critSection); + + if (dwInfo == PVR_PLAYING_PROGRESS) + iReturn = (int) ((float) GetStartTime() / m_iDuration * 100); + else if (dwInfo == PVR_ACTUAL_STREAM_SIG_PROGR) + iReturn = (int) ((float) m_qualityInfo.iSignal / 0xFFFF * 100); + else if (dwInfo == PVR_ACTUAL_STREAM_SNR_PROGR) + iReturn = (int) ((float) m_qualityInfo.iSNR / 0xFFFF * 100); + + return iReturn; +} + +void CPVRGUIInfo::CharInfoActiveTimerTitle(CStdString &strValue) const +{ + strValue.Format("%s", m_strActiveTimerTitle); +} + +void CPVRGUIInfo::CharInfoActiveTimerChannelName(CStdString &strValue) const +{ + strValue.Format("%s", m_strActiveTimerChannelName); +} + +void CPVRGUIInfo::CharInfoActiveTimerChannelIcon(CStdString &strValue) const +{ + strValue.Format("%s", m_strActiveTimerChannelIcon); +} + +void CPVRGUIInfo::CharInfoActiveTimerDateTime(CStdString &strValue) const +{ + strValue.Format("%s", m_strActiveTimerTime); +} + +void CPVRGUIInfo::CharInfoNextTimerTitle(CStdString &strValue) const +{ + strValue.Format("%s", m_strNextRecordingTitle); +} + +void CPVRGUIInfo::CharInfoNextTimerChannelName(CStdString &strValue) const +{ + strValue.Format("%s", m_strNextRecordingChannelName); +} + +void CPVRGUIInfo::CharInfoNextTimerChannelIcon(CStdString &strValue) const +{ + strValue.Format("%s", m_strNextRecordingChannelIcon); +} + +void CPVRGUIInfo::CharInfoNextTimerDateTime(CStdString &strValue) const +{ + strValue.Format("%s", m_strNextRecordingTime); +} + +void CPVRGUIInfo::CharInfoPlayingDuration(CStdString &strValue) const +{ + strValue.Format("%s", StringUtils::SecondsToTimeString(m_iDuration / 1000, TIME_FORMAT_GUESS)); +} + +void CPVRGUIInfo::CharInfoPlayingTime(CStdString &strValue) const +{ + strValue.Format("%s", StringUtils::SecondsToTimeString(GetStartTime()/1000, TIME_FORMAT_GUESS)); +} + +void CPVRGUIInfo::CharInfoNextTimer(CStdString &strValue) const +{ + strValue.Format("%s", m_strNextTimerInfo); +} + +void CPVRGUIInfo::CharInfoBackendNumber(CStdString &strValue) const +{ + if (m_iActiveClients > 0) + strValue.Format("%u %s %u", m_iAddonInfoToggleCurrent+1, g_localizeStrings.Get(20163), m_iActiveClients); + else + strValue.Format("%s", g_localizeStrings.Get(14023)); +} + +void CPVRGUIInfo::CharInfoTotalDiskSpace(CStdString &strValue) const +{ + strValue.Format("%s", m_strTotalDiskspace); +} + +void CPVRGUIInfo::CharInfoVideoBR(CStdString &strValue) const +{ + strValue.Format("%.2f Mbit/s", m_qualityInfo.dVideoBitrate); +} + +void CPVRGUIInfo::CharInfoAudioBR(CStdString &strValue) const +{ + strValue.Format("%.0f kbit/s", m_qualityInfo.dAudioBitrate); +} + +void CPVRGUIInfo::CharInfoDolbyBR(CStdString &strValue) const +{ + strValue.Format("%.0f kbit/s", m_qualityInfo.dDolbyBitrate); +} + +void CPVRGUIInfo::CharInfoSignal(CStdString &strValue) const +{ + strValue.Format("%d %%", m_qualityInfo.iSignal / 655); +} + +void CPVRGUIInfo::CharInfoSNR(CStdString &strValue) const +{ + strValue.Format("%d %%", m_qualityInfo.iSNR / 655); +} + +void CPVRGUIInfo::CharInfoBER(CStdString &strValue) const +{ + strValue.Format("%08X", m_qualityInfo.iBER); +} + +void CPVRGUIInfo::CharInfoUNC(CStdString &strValue) const +{ + strValue.Format("%08X", m_qualityInfo.iUNC); +} + +void CPVRGUIInfo::CharInfoFrontendName(CStdString &strValue) const +{ + if (!strcmp(m_qualityInfo.strAdapterName, StringUtils::EmptyString)) + strValue.Format("%s", g_localizeStrings.Get(13205)); + else + strValue.Format("%s", m_qualityInfo.strAdapterName); +} + +void CPVRGUIInfo::CharInfoFrontendStatus(CStdString &strValue) const +{ + if (!strcmp(m_qualityInfo.strAdapterStatus, StringUtils::EmptyString)) + strValue.Format("%s", g_localizeStrings.Get(13205)); + else + strValue.Format("%s", m_qualityInfo.strAdapterStatus); +} + +void CPVRGUIInfo::CharInfoBackendName(CStdString &strValue) const +{ + if (m_strBackendName.IsEmpty()) + strValue.Format("%s", g_localizeStrings.Get(13205)); + else + strValue.Format("%s", m_strBackendName); +} + +void CPVRGUIInfo::CharInfoBackendVersion(CStdString &strValue) const +{ + if (m_strBackendVersion.IsEmpty()) + strValue.Format("%s", g_localizeStrings.Get(13205)); + else + strValue.Format("%s", m_strBackendVersion); +} + +void CPVRGUIInfo::CharInfoBackendHost(CStdString &strValue) const +{ + if (m_strBackendHost.IsEmpty()) + strValue.Format("%s", g_localizeStrings.Get(13205)); + else + strValue.Format("%s", m_strBackendHost); +} + +void CPVRGUIInfo::CharInfoBackendDiskspace(CStdString &strValue) const +{ + if (m_strBackendDiskspace.IsEmpty()) + strValue.Format("%s", g_localizeStrings.Get(13205)); + else + strValue.Format("%s", m_strBackendDiskspace); +} + +void CPVRGUIInfo::CharInfoBackendChannels(CStdString &strValue) const +{ + if (m_strBackendChannels.IsEmpty()) + strValue.Format("%s", g_localizeStrings.Get(13205)); + else + strValue.Format("%s", m_strBackendChannels); +} + +void CPVRGUIInfo::CharInfoBackendTimers(CStdString &strValue) const +{ + if (m_strBackendTimers.IsEmpty()) + strValue.Format("%s", g_localizeStrings.Get(13205)); + else + strValue.Format("%s", m_strBackendTimers); +} + +void CPVRGUIInfo::CharInfoBackendRecordings(CStdString &strValue) const +{ + if (m_strBackendRecordings.IsEmpty()) + strValue.Format("%s", g_localizeStrings.Get(13205)); + else + strValue.Format("%s", m_strBackendRecordings); +} + +void CPVRGUIInfo::CharInfoPlayingClientName(CStdString &strValue) const +{ + if (m_strPlayingClientName.IsEmpty()) + strValue.Format("%s", g_localizeStrings.Get(13205)); + else + strValue.Format("%s", m_strPlayingClientName); +} + +void CPVRGUIInfo::CharInfoEncryption(CStdString &strValue) const +{ + CPVRChannelPtr channel; + if (g_PVRClients->GetPlayingChannel(channel)) + strValue.Format("%s", channel->EncryptionName().c_str()); + else + strValue = StringUtils::EmptyString; +} + +void CPVRGUIInfo::UpdateBackendCache(void) +{ + CStdString strBackendName; + CStdString strBackendVersion; + CStdString strBackendHost; + CStdString strBackendDiskspace; + CStdString strBackendTimers; + CStdString strBackendRecordings; + CStdString strBackendChannels; + int iActiveClients(0); + + if (!AddonInfoToggle()) + return; + + CPVRClients *clients = g_PVRClients; + PVR_CLIENTMAP activeClients; + iActiveClients = clients->GetConnectedClients(activeClients); + if (iActiveClients > 0) + { + PVR_CLIENTMAP_CITR activeClient = activeClients.begin(); + /* safe to read unlocked */ + for (unsigned int i = 0; i < m_iAddonInfoToggleCurrent; i++) + activeClient++; + + long long kBTotal = 0; + long long kBUsed = 0; + + if (activeClient->second->GetDriveSpace(&kBTotal, &kBUsed) == PVR_ERROR_NO_ERROR) + { + kBTotal /= 1024; // Convert to MBytes + kBUsed /= 1024; // Convert to MBytes + strBackendDiskspace.Format("%s %.1f GByte - %s: %.1f GByte", + g_localizeStrings.Get(20161), (float) kBTotal / 1024, g_localizeStrings.Get(20162), (float) kBUsed / 1024); + } + else + { + strBackendDiskspace = g_localizeStrings.Get(19055); + } + + int NumChannels = activeClient->second->GetChannelsAmount(); + if (NumChannels >= 0) + strBackendChannels.Format("%i", NumChannels); + else + strBackendChannels = g_localizeStrings.Get(161); + + int NumTimers = activeClient->second->GetTimersAmount(); + if (NumTimers >= 0) + strBackendTimers.Format("%i", NumTimers); + else + strBackendTimers = g_localizeStrings.Get(161); + + int NumRecordings = activeClient->second->GetRecordingsAmount(); + if (NumRecordings >= 0) + strBackendRecordings.Format("%i", NumRecordings); + else + strBackendRecordings = g_localizeStrings.Get(161); + + strBackendName = activeClient->second->GetBackendName(); + strBackendVersion = activeClient->second->GetBackendVersion(); + strBackendHost = activeClient->second->GetConnectionString(); + } + + CSingleLock lock(m_critSection); + m_strBackendName = strBackendName; + m_strBackendVersion = strBackendVersion; + m_strBackendHost = strBackendHost; + m_strBackendDiskspace = strBackendDiskspace; + m_strBackendTimers = strBackendTimers; + m_strBackendRecordings = strBackendRecordings; + m_strBackendChannels = strBackendChannels; + m_iActiveClients = iActiveClients; +} + +void CPVRGUIInfo::UpdateTimersCache(void) +{ + int iTimerAmount = g_PVRTimers->AmountActiveTimers(); + int iRecordingTimerAmount = g_PVRTimers->AmountActiveRecordings(); + + { + CSingleLock lock(m_critSection); + m_iTimerAmount = iTimerAmount; + m_iRecordingTimerAmount = iRecordingTimerAmount; + m_iTimerInfoToggleStart = 0; + } + + UpdateTimersToggle(); +} + +void CPVRGUIInfo::UpdateNextTimer(void) +{ + CStdString strNextRecordingTitle; + CStdString strNextRecordingChannelName; + CStdString strNextRecordingChannelIcon; + CStdString strNextRecordingTime; + CStdString strNextTimerInfo; + + CFileItemPtr tag = g_PVRTimers->GetNextActiveTimer(); + if (tag && tag->HasPVRTimerInfoTag()) + { + CPVRTimerInfoTag *timer = tag->GetPVRTimerInfoTag(); + strNextRecordingTitle.Format("%s", timer->Title()); + strNextRecordingChannelName.Format("%s", timer->ChannelName()); + strNextRecordingChannelIcon.Format("%s", timer->ChannelIcon()); + strNextRecordingTime.Format("%s", timer->StartAsLocalTime().GetAsLocalizedDateTime(false, false)); + + strNextTimerInfo.Format("%s %s %s %s", + g_localizeStrings.Get(19106), + timer->StartAsLocalTime().GetAsLocalizedDate(true), + g_localizeStrings.Get(19107), + timer->StartAsLocalTime().GetAsLocalizedTime("HH:mm", false)); + } + + CSingleLock lock(m_critSection); + m_strNextRecordingTitle = strNextRecordingTitle; + m_strNextRecordingChannelName = strNextRecordingChannelName; + m_strNextRecordingChannelIcon = strNextRecordingChannelIcon; + m_strNextRecordingTime = strNextRecordingTime; + m_strNextTimerInfo = strNextTimerInfo; +} + +void CPVRGUIInfo::UpdateTimersToggle(void) +{ + if (!TimerInfoToggle()) + return; + + CStdString strActiveTimerTitle; + CStdString strActiveTimerChannelName; + CStdString strActiveTimerChannelIcon; + CStdString strActiveTimerTime; + + /* safe to fetch these unlocked, since they're updated from the same thread as this one */ + if (m_iRecordingTimerAmount > 0) + { + vector<CFileItemPtr> activeTags = g_PVRTimers->GetActiveRecordings(); + if (m_iTimerInfoToggleCurrent < activeTags.size() && activeTags.at(m_iTimerInfoToggleCurrent)->HasPVRTimerInfoTag()) + { + CPVRTimerInfoTag *tag = activeTags.at(m_iTimerInfoToggleCurrent)->GetPVRTimerInfoTag(); + strActiveTimerTitle.Format("%s", tag->Title()); + strActiveTimerChannelName.Format("%s", tag->ChannelName()); + strActiveTimerChannelIcon.Format("%s", tag->ChannelIcon()); + strActiveTimerTime.Format("%s", tag->StartAsLocalTime().GetAsLocalizedDateTime(false, false)); + } + } + + CSingleLock lock(m_critSection); + m_strActiveTimerTitle = strActiveTimerTitle; + m_strActiveTimerChannelName = strActiveTimerChannelName; + m_strActiveTimerChannelIcon = strActiveTimerChannelIcon; + m_strActiveTimerTime = strActiveTimerTime; +} + +int CPVRGUIInfo::GetDuration(void) const +{ + CSingleLock lock(m_critSection); + return m_iDuration; +} + +int CPVRGUIInfo::GetStartTime(void) const +{ + CSingleLock lock(m_critSection); + if (m_playingEpgTag) + { + /* Calculate here the position we have of the running live TV event. + * "position in ms" = ("current local time" - "event start local time") * 1000 + */ + CDateTime current = CDateTime::GetCurrentDateTime(); + CDateTime start = m_playingEpgTag->StartAsLocalTime(); + CDateTimeSpan time = current > start ? current - start : CDateTimeSpan(0, 0, 0, 0); + return (time.GetDays() * 60 * 60 * 24 + + time.GetHours() * 60 * 60 + + time.GetMinutes() * 60 + + time.GetSeconds()) * 1000; + } + else + { + return 0; + } +} + +void CPVRGUIInfo::ResetPlayingTag(void) +{ + CSingleLock lock(m_critSection); + SAFE_DELETE(m_playingEpgTag); + m_iDuration = 0; +} + +bool CPVRGUIInfo::GetPlayingTag(CEpgInfoTag &tag) const +{ + bool bReturn(false); + + CSingleLock lock(m_critSection); + if (m_playingEpgTag) + { + tag = *m_playingEpgTag; + bReturn = true; + } + + return bReturn; +} + +void CPVRGUIInfo::UpdatePlayingTag(void) +{ + CPVRChannelPtr currentChannel; + CPVRRecording recording; + if (g_PVRManager.GetCurrentChannel(currentChannel)) + { + CEpgInfoTag epgTag; + bool bHasEpgTag = GetPlayingTag(epgTag); + CPVRChannelPtr channel; + if (bHasEpgTag) + channel = epgTag.ChannelTag(); + + if (!bHasEpgTag || !epgTag.IsActive() || + !channel || *channel != *currentChannel) + { + CEpgInfoTag newTag; + { + CSingleLock lock(m_critSection); + ResetPlayingTag(); + if (currentChannel->GetEPGNow(newTag)) + { + m_playingEpgTag = new CEpgInfoTag(newTag); + m_iDuration = m_playingEpgTag->GetDuration() * 1000; + } + } + g_PVRManager.UpdateCurrentFile(); + } + } + else if (g_PVRClients->GetPlayingRecording(recording)) + { + ResetPlayingTag(); + m_iDuration = recording.GetDuration() * 1000; + } +} diff --git a/xbmc/pvr/PVRGUIInfo.h b/xbmc/pvr/PVRGUIInfo.h new file mode 100644 index 0000000000..544c06423a --- /dev/null +++ b/xbmc/pvr/PVRGUIInfo.h @@ -0,0 +1,172 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "threads/CriticalSection.h" +#include "utils/Observer.h" +#include "threads/Thread.h" +#include "addons/include/xbmc_pvr_types.h" + +namespace EPG +{ + class CEpgInfoTag; +} + +namespace PVR +{ + class CPVRTimerInfoTag; + class CPVRRecording; + + class CPVRGUIInfo : private CThread, + private Observer + { + public: + CPVRGUIInfo(void); + virtual ~CPVRGUIInfo(void); + + void Start(void); + void Stop(void); + + void Notify(const Observable &obs, const ObservableMessage msg); + + bool TranslateBoolInfo(DWORD dwInfo) const; + bool TranslateCharInfo(DWORD dwInfo, CStdString &strValue) const; + int TranslateIntInfo(DWORD dwInfo) const; + + /*! + * @brief Get the total duration of the currently playing LiveTV item. + * @return The total duration in milliseconds or NULL if no channel is playing. + */ + int GetDuration(void) const; + + /*! + * @brief Get the current position in milliseconds since the start of a LiveTV item. + * @return The position in milliseconds or NULL if no channel is playing. + */ + int GetStartTime(void) const; + + /*! + * @brief Show the player info. + * @param iTimeout Hide the player info after iTimeout seconds. + * @todo not really the right place for this :-) + */ + void ShowPlayerInfo(int iTimeout); + + /*! + * @brief Clear the playing EPG tag. + */ + void ResetPlayingTag(void); + + bool GetPlayingTag(EPG::CEpgInfoTag &tag) const; + + private: + void ResetProperties(void); + void ClearQualityInfo(PVR_SIGNAL_STATUS &qualityInfo); + void Process(void); + + void UpdatePlayingTag(void); + void UpdateTimersCache(void); + void UpdateBackendCache(void); + void UpdateQualityData(void); + void UpdateMisc(void); + void UpdateNextTimer(void); + + bool AddonInfoToggle(void); + bool TimerInfoToggle(void); + void UpdateTimersToggle(void); + void ToggleShowInfo(void); + + void CharInfoActiveTimerTitle(CStdString &strValue) const; + void CharInfoActiveTimerChannelName(CStdString &strValue) const; + void CharInfoActiveTimerChannelIcon(CStdString &strValue) const; + void CharInfoActiveTimerDateTime(CStdString &strValue) const; + void CharInfoNextTimerTitle(CStdString &strValue) const; + void CharInfoNextTimerChannelName(CStdString &strValue) const; + void CharInfoNextTimerChannelIcon(CStdString &strValue) const; + void CharInfoNextTimerDateTime(CStdString &strValue) const; + void CharInfoPlayingDuration(CStdString &strValue) const; + void CharInfoPlayingTime(CStdString &strValue) const; + void CharInfoNextTimer(CStdString &strValue) const; + void CharInfoBackendNumber(CStdString &strValue) const; + void CharInfoTotalDiskSpace(CStdString &strValue) const; + void CharInfoVideoBR(CStdString &strValue) const; + void CharInfoAudioBR(CStdString &strValue) const; + void CharInfoDolbyBR(CStdString &strValue) const; + void CharInfoSignal(CStdString &strValue) const; + void CharInfoSNR(CStdString &strValue) const; + void CharInfoBER(CStdString &strValue) const; + void CharInfoUNC(CStdString &strValue) const; + void CharInfoFrontendName(CStdString &strValue) const; + void CharInfoFrontendStatus(CStdString &strValue) const; + void CharInfoBackendName(CStdString &strValue) const; + void CharInfoBackendVersion(CStdString &strValue) const; + void CharInfoBackendHost(CStdString &strValue) const; + void CharInfoBackendDiskspace(CStdString &strValue) const; + void CharInfoBackendChannels(CStdString &strValue) const; + void CharInfoBackendTimers(CStdString &strValue) const; + void CharInfoBackendRecordings(CStdString &strValue) const; + void CharInfoPlayingClientName(CStdString &strValue) const; + void CharInfoEncryption(CStdString &strValue) const; + + /** @name GUIInfoManager data */ + //@{ + CStdString m_strActiveTimerTitle; + CStdString m_strActiveTimerChannelName; + CStdString m_strActiveTimerChannelIcon; + CStdString m_strActiveTimerTime; + CStdString m_strNextTimerInfo; + CStdString m_strNextRecordingTitle; + CStdString m_strNextRecordingChannelName; + CStdString m_strNextRecordingChannelIcon; + CStdString m_strNextRecordingTime; + bool m_bHasRecordings; + unsigned int m_iTimerAmount; + unsigned int m_iRecordingTimerAmount; + int m_iActiveClients; + CStdString m_strPlayingClientName; + CStdString m_strBackendName; + CStdString m_strBackendVersion; + CStdString m_strBackendHost; + CStdString m_strBackendDiskspace; + CStdString m_strBackendTimers; + CStdString m_strBackendRecordings; + CStdString m_strBackendChannels; + CStdString m_strTotalDiskspace; + unsigned int m_iDuration; + + bool m_bHasNonRecordingTimers; + bool m_bIsPlayingTV; + bool m_bIsPlayingRadio; + bool m_bIsPlayingRecording; + bool m_bIsPlayingEncryptedStream; + //@} + + PVR_SIGNAL_STATUS m_qualityInfo; /*!< stream quality information */ + unsigned int m_iAddonInfoToggleStart; + unsigned int m_iAddonInfoToggleCurrent; + unsigned int m_iTimerInfoToggleStart; + unsigned int m_iTimerInfoToggleCurrent; + unsigned int m_iToggleShowInfo; + EPG::CEpgInfoTag * m_playingEpgTag; + + CCriticalSection m_critSection; + }; +} diff --git a/xbmc/pvr/PVRManager.cpp b/xbmc/pvr/PVRManager.cpp new file mode 100644 index 0000000000..15fe68a18a --- /dev/null +++ b/xbmc/pvr/PVRManager.cpp @@ -0,0 +1,1223 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "Application.h" +#include "ApplicationMessenger.h" +#include "GUIInfoManager.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogNumeric.h" +#include "dialogs/GUIDialogProgress.h" +#include "dialogs/GUIDialogExtendedProgressBar.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "music/tags/MusicInfoTag.h" +#include "settings/AdvancedSettings.h" +#include "settings/GUISettings.h" +#include "settings/Settings.h" +#include "threads/SingleLock.h" +#include "windows/GUIWindowPVR.h" +#include "utils/log.h" +#include "utils/Stopwatch.h" +#include "utils/StringUtils.h" +#include "threads/Atomics.h" +#include "windows/GUIWindowPVRCommon.h" + +#include "PVRManager.h" +#include "PVRDatabase.h" +#include "PVRGUIInfo.h" +#include "addons/PVRClients.h" +#include "channels/PVRChannel.h" +#include "channels/PVRChannelGroupsContainer.h" +#include "channels/PVRChannelGroupInternal.h" +#include "epg/EpgContainer.h" +#include "recordings/PVRRecordings.h" +#include "timers/PVRTimers.h" + +using namespace std; +using namespace MUSIC_INFO; +using namespace PVR; +using namespace EPG; + +CPVRManager::CPVRManager(void) : + CThread("PVR manager"), + m_channelGroups(NULL), + m_recordings(NULL), + m_timers(NULL), + m_addons(NULL), + m_guiInfo(NULL), + m_triggerEvent(true), + m_currentFile(NULL), + m_database(NULL), + m_bFirstStart(true), + m_progressHandle(NULL), + m_managerState(ManagerStateStopped) +{ + ResetProperties(); +} + +CPVRManager::~CPVRManager(void) +{ + Stop(); + CLog::Log(LOGDEBUG,"PVRManager - destroyed"); +} + +CPVRManager &CPVRManager::Get(void) +{ + static CPVRManager pvrManagerInstance; + return pvrManagerInstance; +} + +void CPVRManager::Cleanup(void) +{ + CSingleLock lock(m_critSection); + + SAFE_DELETE(m_addons); + SAFE_DELETE(m_guiInfo); + SAFE_DELETE(m_timers); + SAFE_DELETE(m_recordings); + SAFE_DELETE(m_channelGroups); + SAFE_DELETE(m_parentalTimer); + SAFE_DELETE(m_database); + m_triggerEvent.Set(); + + m_currentFile = NULL; + m_bIsSwitchingChannels = false; + + for (unsigned int iJobPtr = 0; iJobPtr < m_pendingUpdates.size(); iJobPtr++) + delete m_pendingUpdates.at(iJobPtr); + m_pendingUpdates.clear(); + + SetState(ManagerStateStopped); +} + +void CPVRManager::ResetProperties(void) +{ + CSingleLock lock(m_critSection); + Cleanup(); + + if (!g_application.m_bStop) + { + m_addons = new CPVRClients; + m_channelGroups = new CPVRChannelGroupsContainer; + m_recordings = new CPVRRecordings; + m_timers = new CPVRTimers; + m_guiInfo = new CPVRGUIInfo; + m_parentalTimer = new CStopWatch; + } +} + +void CPVRManager::Start(void) +{ + CSingleLock lock(m_critSection); + + /* first stop and remove any clients */ + Stop(); + + /* don't start if Settings->Video->TV->Enable isn't checked */ + if (!g_guiSettings.GetBool("pvrmanager.enabled")) + return; + + ResetProperties(); + SetState(ManagerStateStarting); + + /* create and open database */ + if (!m_database) + m_database = new CPVRDatabase; + m_database->Open(); + + /* create the supervisor thread to do all background activities */ + StartUpdateThreads(); +} + +void CPVRManager::Stop(void) +{ + /* check whether the pvrmanager is loaded */ + if (GetState() == ManagerStateStopping || + GetState() == ManagerStateStopped) + return; + + SetState(ManagerStateStopping); + + /* stop the EPG updater, since it might be using the pvr add-ons */ + g_EpgContainer.Unload(); + + CLog::Log(LOGNOTICE, "PVRManager - stopping"); + + /* stop playback if needed */ + if (IsPlaying()) + { + CLog::Log(LOGNOTICE,"PVRManager - %s - stopping PVR playback", __FUNCTION__); + CApplicationMessenger::Get().MediaStop(); + } + + /* stop all update threads */ + StopUpdateThreads(); + + /* executes the set wakeup command */ + SetWakeupCommand(); + + /* close database */ + if (m_database->IsOpen()) + m_database->Close(); + + /* unload all data */ + Cleanup(); +} + +ManagerState CPVRManager::GetState(void) const +{ + CSingleLock lock(m_managerStateMutex); + return m_managerState; +} + +void CPVRManager::SetState(ManagerState state) +{ + CSingleLock lock(m_managerStateMutex); + m_managerState = state; +} + +void CPVRManager::Process(void) +{ + g_EpgContainer.Stop(); + + /* load the pvr data from the db and clients if it's not already loaded */ + if (!Load()) + { + CLog::Log(LOGERROR, "PVRManager - %s - failed to load PVR data", __FUNCTION__); + return; + } + + if (GetState() == ManagerStateStarting) + SetState(ManagerStateStarted); + else + return; + + /* main loop */ + CLog::Log(LOGDEBUG, "PVRManager - %s - entering main loop", __FUNCTION__); + g_EpgContainer.Start(); + + bool bRestart(false); + while (GetState() == ManagerStateStarted && m_addons && m_addons->HasConnectedClients() && !bRestart) + { + /* continue last watched channel after first startup */ + if (m_bFirstStart && g_guiSettings.GetInt("pvrplayback.startlast") != START_LAST_CHANNEL_OFF) + ContinueLastChannel(); + + /* execute the next pending jobs if there are any */ + try + { + ExecutePendingJobs(); + } + catch (...) + { + CLog::Log(LOGERROR, "PVRManager - %s - an error occured while trying to execute the last update job, trying to recover", __FUNCTION__); + bRestart = true; + } + + if (GetState() == ManagerStateStarted && !bRestart) + m_triggerEvent.WaitMSec(1000); + } + + if (GetState() == ManagerStateStarted) + { + CLog::Log(LOGNOTICE, "PVRManager - %s - no add-ons enabled anymore. restarting the pvrmanager", __FUNCTION__); + Stop(); + Start(); + return; + } +} + +bool CPVRManager::SetWakeupCommand(void) +{ + if (!g_guiSettings.GetBool("pvrpowermanagement.enabled")) + return false; + + const CStdString strWakeupCommand = g_guiSettings.GetString("pvrpowermanagement.setwakeupcmd", false); + if (!strWakeupCommand.IsEmpty() && m_timers) + { + time_t iWakeupTime; + const CDateTime nextEvent = m_timers->GetNextEventTime(); + nextEvent.GetAsTime(iWakeupTime); + + CStdString strExecCommand; + strExecCommand.Format("%s %d", strWakeupCommand, iWakeupTime); + + const int iReturn = system(strExecCommand.c_str()); + if (iReturn != 0) + CLog::Log(LOGERROR, "%s - failed to execute wakeup command '%s': %s (%d)", __FUNCTION__, strExecCommand.c_str(), strerror(iReturn), iReturn); + + return iReturn == 0; + } + + return false; +} + +bool CPVRManager::StartUpdateThreads(void) +{ + StopUpdateThreads(); + CLog::Log(LOGNOTICE, "PVRManager - starting up"); + + /* create the pvrmanager thread, which will ensure that all data will be loaded */ + SetState(ManagerStateStarting); + Create(); + SetPriority(-1); + + return true; +} + +void CPVRManager::StopUpdateThreads(void) +{ + SetState(ManagerStateInterrupted); + + StopThread(); + if (m_guiInfo) + m_guiInfo->Stop(); + if (m_addons) + m_addons->Stop(); +} + +bool CPVRManager::Load(void) +{ + /* start the add-on update thread */ + m_addons->Start(); + + /* load at least one client */ + while (GetState() == ManagerStateStarting && m_addons && !m_addons->HasConnectedClients()) + Sleep(50); + + if (GetState() != ManagerStateStarting || !m_addons || !m_addons->HasConnectedClients()) + return false; + + CLog::Log(LOGDEBUG, "PVRManager - %s - active clients found. continue to start", __FUNCTION__); + + CGUIWindowPVR *pWindow = (CGUIWindowPVR *) g_windowManager.GetWindow(WINDOW_PVR); + if (pWindow) + pWindow->Reset(); + + /* load all channels and groups */ + ShowProgressDialog(g_localizeStrings.Get(19236), 0); // Loading channels from clients + if (!m_channelGroups->Load() || GetState() != ManagerStateStarting) + return false; + + /* get timers from the backends */ + ShowProgressDialog(g_localizeStrings.Get(19237), 50); // Loading timers from clients + m_timers->Load(); + + /* get recordings from the backend */ + ShowProgressDialog(g_localizeStrings.Get(19238), 75); // Loading recordings from clients + m_recordings->Load(); + + CSingleLock lock(m_critSection); + if (GetState() != ManagerStateStarting) + return false; + + /* start the other pvr related update threads */ + ShowProgressDialog(g_localizeStrings.Get(19239), 85); // Starting background threads + m_guiInfo->Start(); + + /* close the progess dialog */ + HideProgressDialog(); + + return true; +} + +void CPVRManager::ShowProgressDialog(const CStdString &strText, int iProgress) +{ + if (!m_progressHandle) + { + CGUIDialogExtendedProgressBar *loadingProgressDialog = (CGUIDialogExtendedProgressBar *)g_windowManager.GetWindow(WINDOW_DIALOG_EXT_PROGRESS); + m_progressHandle = loadingProgressDialog->GetHandle(g_localizeStrings.Get(19235)); // PVR manager is starting up + } + + m_progressHandle->SetPercentage((float)iProgress); + m_progressHandle->SetText(strText); +} + +void CPVRManager::HideProgressDialog(void) +{ + if (m_progressHandle) + { + m_progressHandle->MarkFinished(); + m_progressHandle = NULL; + } +} + +bool CPVRManager::ChannelSwitch(unsigned int iChannelNumber) +{ + CSingleLock lock(m_critSection); + + CPVRChannelGroupPtr playingGroup = GetPlayingGroup(m_addons->IsPlayingRadio()); + if (!playingGroup) + { + CLog::Log(LOGERROR, "PVRManager - %s - cannot get the selected group", __FUNCTION__); + return false; + } + + CFileItemPtr channel = playingGroup->GetByChannelNumber(iChannelNumber); + if (!channel || !channel->HasPVRChannelInfoTag()) + { + CLog::Log(LOGERROR, "PVRManager - %s - cannot find channel %d", __FUNCTION__, iChannelNumber); + return false; + } + + return PerformChannelSwitch(*channel->GetPVRChannelInfoTag(), false); +} + +bool CPVRManager::ChannelUpDown(unsigned int *iNewChannelNumber, bool bPreview, bool bUp) +{ + bool bReturn = false; + if (IsPlayingTV() || IsPlayingRadio()) + { + CFileItem currentFile(g_application.CurrentFileItem()); + CPVRChannel *currentChannel = currentFile.GetPVRChannelInfoTag(); + CPVRChannelGroupPtr group = GetPlayingGroup(currentChannel->IsRadio()); + if (group) + { + CFileItemPtr newChannel = bUp ? + group->GetByChannelUp(*currentChannel) : + group->GetByChannelDown(*currentChannel); + + if (newChannel && newChannel->HasPVRChannelInfoTag() && + PerformChannelSwitch(*newChannel->GetPVRChannelInfoTag(), bPreview)) + { + *iNewChannelNumber = newChannel->GetPVRChannelInfoTag()->ChannelNumber(); + bReturn = true; + } + } + } + + return bReturn; +} + +bool CPVRManager::ContinueLastChannel(void) +{ + { + CSingleLock lock(m_critSection); + if (!m_bFirstStart) + return true; + m_bFirstStart = false; + } + + bool bReturn(false); + CFileItemPtr channel = m_channelGroups->GetLastPlayedChannel(); + if (channel && channel->HasPVRChannelInfoTag()) + { + CLog::Log(LOGNOTICE, "PVRManager - %s - continue playback on channel '%s'", __FUNCTION__, channel->GetPVRChannelInfoTag()->ChannelName().c_str()); + bReturn = StartPlayback(channel->GetPVRChannelInfoTag(), (g_guiSettings.GetInt("pvrplayback.startlast") == START_LAST_CHANNEL_MIN)); + } + + return bReturn; +} + +void CPVRManager::ResetDatabase(bool bShowProgress /* = true */) +{ + CLog::Log(LOGNOTICE,"PVRManager - %s - clearing the PVR database", __FUNCTION__); + + g_EpgContainer.Stop(); + + CGUIDialogProgress* pDlgProgress = NULL; + if (bShowProgress) + { + pDlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS); + pDlgProgress->SetLine(0, StringUtils::EmptyString); + pDlgProgress->SetLine(1, g_localizeStrings.Get(19186)); // All data in the PVR database is being erased + pDlgProgress->SetLine(2, StringUtils::EmptyString); + pDlgProgress->StartModal(); + pDlgProgress->Progress(); + } + + if (m_addons && m_addons->IsPlaying()) + { + CLog::Log(LOGNOTICE,"PVRManager - %s - stopping playback", __FUNCTION__); + CApplicationMessenger::Get().MediaStop(); + } + + if (bShowProgress) + { + pDlgProgress->SetPercentage(10); + pDlgProgress->Progress(); + } + + /* stop the thread */ + if (g_guiSettings.GetBool("pvrmanager.enabled")) + Stop(); + + if (bShowProgress) + { + pDlgProgress->SetPercentage(20); + pDlgProgress->Progress(); + } + + if (!m_database) + m_database = new CPVRDatabase; + + if (m_database && m_database->Open()) + { + /* clean the EPG database */ + g_EpgContainer.Clear(true); + if (bShowProgress) + { + pDlgProgress->SetPercentage(30); + pDlgProgress->Progress(); + } + + m_database->DeleteChannelGroups(); + if (bShowProgress) + { + pDlgProgress->SetPercentage(50); + pDlgProgress->Progress(); + } + + /* delete all channels */ + m_database->DeleteChannels(); + if (bShowProgress) + { + pDlgProgress->SetPercentage(70); + pDlgProgress->Progress(); + } + + /* delete all channel settings */ + m_database->DeleteChannelSettings(); + if (bShowProgress) + { + pDlgProgress->SetPercentage(80); + pDlgProgress->Progress(); + } + + /* delete all client information */ + m_database->DeleteClients(); + if (bShowProgress) + { + pDlgProgress->SetPercentage(90); + pDlgProgress->Progress(); + } + + m_database->Close(); + } + + CLog::Log(LOGNOTICE,"PVRManager - %s - PVR database cleared", __FUNCTION__); + + g_EpgContainer.Start(); + + if (g_guiSettings.GetBool("pvrmanager.enabled")) + { + CLog::Log(LOGNOTICE,"PVRManager - %s - restarting the PVRManager", __FUNCTION__); + m_database->Open(); + Cleanup(); + Start(); + } + + if (bShowProgress) + { + pDlgProgress->SetPercentage(100); + pDlgProgress->Close(); + } +} + +void CPVRManager::ResetEPG(void) +{ + CLog::Log(LOGNOTICE,"PVRManager - %s - clearing the EPG database", __FUNCTION__); + + StopUpdateThreads(); + g_EpgContainer.Stop(); + g_EpgContainer.Reset(); + + if (g_guiSettings.GetBool("pvrmanager.enabled")) + { + static_cast<CPVRChannelGroupInternal *>(m_channelGroups->GetGroupAllTV().get())->CreateChannelEpgs(true); + static_cast<CPVRChannelGroupInternal *>(m_channelGroups->GetGroupAllRadio().get())->CreateChannelEpgs(true); + g_EpgContainer.Start(); + StartUpdateThreads(); + } +} + +bool CPVRManager::IsPlaying(void) const +{ + return IsStarted() && m_addons && m_addons->IsPlaying(); +} + +bool CPVRManager::GetCurrentChannel(CPVRChannelPtr &channel) const +{ + return m_addons && m_addons->GetPlayingChannel(channel); +} + +int CPVRManager::GetCurrentEpg(CFileItemList &results) const +{ + int iReturn = -1; + + CPVRChannelPtr channel; + if (m_addons->GetPlayingChannel(channel)) + iReturn = channel->GetEPG(results); + else + CLog::Log(LOGDEBUG,"PVRManager - %s - no current channel set", __FUNCTION__); + + return iReturn; +} + +void CPVRManager::ResetPlayingTag(void) +{ + CSingleLock lock(m_critSection); + if (GetState() == ManagerStateStarted && m_guiInfo) + m_guiInfo->ResetPlayingTag(); +} + +int CPVRManager::GetPreviousChannel(void) +{ + CPVRChannelPtr currentChannel; + if (GetCurrentChannel(currentChannel)) + { + CPVRChannelGroupPtr selectedGroup = GetPlayingGroup(currentChannel->IsRadio()); + CFileItemPtr channel = selectedGroup->GetLastPlayedChannel(currentChannel->ChannelID()); + if (channel && channel->HasPVRChannelInfoTag()) + return channel->GetPVRChannelInfoTag()->ChannelNumber(); + } + return -1; +} + +bool CPVRManager::ToggleRecordingOnChannel(unsigned int iChannelId) +{ + bool bReturn = false; + + CPVRChannelPtr channel = m_channelGroups->GetChannelById(iChannelId); + if (!channel) + return bReturn; + + if (m_addons->HasTimerSupport(channel->ClientID())) + { + /* timers are supported on this channel */ + if (!channel->IsRecording()) + { + bReturn = m_timers->InstantTimer(*channel); + if (!bReturn) + CGUIDialogOK::ShowAndGetInput(19033,0,19164,0); + } + else + { + /* delete active timers */ + bReturn = m_timers->DeleteTimersOnChannel(*channel, false, true); + } + } + + return bReturn; +} + +bool CPVRManager::StartRecordingOnPlayingChannel(bool bOnOff) +{ + bool bReturn = false; + + CPVRChannelPtr channel; + if (!m_addons->GetPlayingChannel(channel)) + return bReturn; + + if (m_addons->HasTimerSupport(channel->ClientID())) + { + /* timers are supported on this channel */ + if (bOnOff && !channel->IsRecording()) + { + bReturn = m_timers->InstantTimer(*channel); + if (!bReturn) + CGUIDialogOK::ShowAndGetInput(19033,0,19164,0); + } + else if (!bOnOff && channel->IsRecording()) + { + /* delete active timers */ + bReturn = m_timers->DeleteTimersOnChannel(*channel, false, true); + } + } + + return bReturn; +} + +bool CPVRManager::CheckParentalLock(const CPVRChannel &channel) +{ + bool bReturn = !IsParentalLocked(channel) || + CheckParentalPIN(); + + if (!bReturn) + CLog::Log(LOGERROR, "PVRManager - %s - parental lock verification failed for channel '%s': wrong PIN entered.", __FUNCTION__, channel.ChannelName().c_str()); + + return bReturn; +} + +bool CPVRManager::IsParentalLocked(const CPVRChannel &channel) +{ + bool bReturn(false); + CPVRChannelPtr currentChannel(new CPVRChannel(false)); + + if (// different channel + (!GetCurrentChannel(currentChannel) || channel != *currentChannel) && + // parental control enabled + g_guiSettings.GetBool("pvrparental.enabled") && + // channel is locked + channel.IsLocked()) + { + float parentalDurationMs = g_guiSettings.GetInt("pvrparental.duration") * 1000.0f; + bReturn = m_parentalTimer && + (!m_parentalTimer->IsRunning() || + m_parentalTimer->GetElapsedMilliseconds() > parentalDurationMs); + } + + return bReturn; +} + +bool CPVRManager::CheckParentalPIN(const char *strTitle /* = NULL */) +{ + CStdString pinCode = g_guiSettings.GetString("pvrparental.pin"); + + if (!g_guiSettings.GetBool("pvrparental.enabled") || pinCode.empty()) + return true; + + // Locked channel. Enter PIN: + bool bValidPIN = CGUIDialogNumeric::ShowAndVerifyInput(pinCode, strTitle ? strTitle : g_localizeStrings.Get(19263).c_str(), true); + if (!bValidPIN) + // display message: The entered PIN number was incorrect + CGUIDialogOK::ShowAndGetInput(19264,0,19265,0); + else if (m_parentalTimer) + { + // reset the timer + m_parentalTimer->StartZero(); + } + + return bValidPIN; +} + +void CPVRManager::SaveCurrentChannelSettings(void) +{ + m_addons->SaveCurrentChannelSettings(); +} + +void CPVRManager::LoadCurrentChannelSettings() +{ + m_addons->LoadCurrentChannelSettings(); +} + +void CPVRManager::SetPlayingGroup(CPVRChannelGroupPtr group) +{ + m_channelGroups->Get(group->IsRadio())->SetSelectedGroup(group); +} + +CPVRChannelGroupPtr CPVRManager::GetPlayingGroup(bool bRadio /* = false */) +{ + return m_channelGroups->GetSelectedGroup(bRadio); +} + +bool CPVRRecordingsUpdateJob::DoWork(void) +{ + g_PVRRecordings->Update(); + return true; +} + +bool CPVRTimersUpdateJob::DoWork(void) +{ + return g_PVRTimers->Update(); +} + +bool CPVRChannelsUpdateJob::DoWork(void) +{ + return g_PVRChannelGroups->Update(true); +} + +bool CPVRChannelGroupsUpdateJob::DoWork(void) +{ + return g_PVRChannelGroups->Update(false); +} + +bool CPVRChannelSettingsSaveJob::DoWork(void) +{ + g_PVRManager.SaveCurrentChannelSettings(); + return true; +} + +bool CPVRManager::OpenLiveStream(const CFileItem &channel) +{ + bool bReturn(false); + if (!channel.HasPVRChannelInfoTag()) + return bReturn; + + CLog::Log(LOGDEBUG,"PVRManager - %s - opening live stream on channel '%s'", + __FUNCTION__, channel.GetPVRChannelInfoTag()->ChannelName().c_str()); + + // check if we're allowed to play this file + if (IsParentalLocked(*channel.GetPVRChannelInfoTag())) + return bReturn; + + if ((bReturn = m_addons->OpenStream(*channel.GetPVRChannelInfoTag(), false)) != false) + { + CSingleLock lock(m_critSection); + if(m_currentFile) + delete m_currentFile; + m_currentFile = new CFileItem(channel); + } + + return bReturn; +} + +bool CPVRManager::OpenRecordedStream(const CPVRRecording &tag) +{ + bool bReturn = false; + CSingleLock lock(m_critSection); + + CLog::Log(LOGDEBUG,"PVRManager - %s - opening recorded stream '%s'", + __FUNCTION__, tag.m_strFile.c_str()); + + if ((bReturn = m_addons->OpenStream(tag)) != false) + { + delete m_currentFile; + m_currentFile = new CFileItem(tag); + } + + return bReturn; +} + +void CPVRManager::CloseStream(void) +{ + CPVRChannelPtr channel; + bool bPersistChannel(false); + + { + CSingleLock lock(m_critSection); + + if (m_addons->GetPlayingChannel(channel)) + { + /* store current time in iLastWatched */ + time_t tNow; + CDateTime::GetCurrentDateTime().GetAsTime(tNow); + channel->SetLastWatched(tNow); + bPersistChannel = true; + } + + m_addons->CloseStream(); + SAFE_DELETE(m_currentFile); + } + + if (bPersistChannel) + channel->Persist(); +} + +void CPVRManager::UpdateCurrentFile(void) +{ + CSingleLock lock(m_critSection); + if (m_currentFile) + UpdateItem(*m_currentFile); +} + +bool CPVRManager::UpdateItem(CFileItem& item) +{ + /* Don't update if a recording is played */ + if (item.IsPVRRecording()) + return false; + + if (!item.IsPVRChannel()) + { + CLog::Log(LOGERROR, "CPVRManager - %s - no channel tag provided", __FUNCTION__); + return false; + } + + CSingleLock lock(m_critSection); + if (!m_currentFile || *m_currentFile->GetPVRChannelInfoTag() == *item.GetPVRChannelInfoTag()) + return false; + + g_application.CurrentFileItem() = *m_currentFile; + g_infoManager.SetCurrentItem(*m_currentFile); + + CPVRChannel* channelTag = item.GetPVRChannelInfoTag(); + CEpgInfoTag epgTagNow; + bool bHasTagNow = channelTag->GetEPGNow(epgTagNow); + + if (channelTag->IsRadio()) + { + CMusicInfoTag* musictag = item.GetMusicInfoTag(); + if (musictag) + { + musictag->SetTitle(bHasTagNow ? + epgTagNow.Title() : + g_guiSettings.GetBool("epg.hidenoinfoavailable") ? + StringUtils::EmptyString : + g_localizeStrings.Get(19055)); // no information available + if (bHasTagNow) + musictag->SetGenre(epgTagNow.Genre()); + musictag->SetDuration(bHasTagNow ? epgTagNow.GetDuration() : 3600); + musictag->SetURL(channelTag->Path()); + musictag->SetArtist(channelTag->ChannelName()); + musictag->SetAlbumArtist(channelTag->ChannelName()); + musictag->SetLoaded(true); + musictag->SetComment(StringUtils::EmptyString); + musictag->SetLyrics(StringUtils::EmptyString); + } + } + else + { + CVideoInfoTag *videotag = item.GetVideoInfoTag(); + if (videotag) + { + videotag->m_strTitle = bHasTagNow ? + epgTagNow.Title() : + g_guiSettings.GetBool("epg.hidenoinfoavailable") ? + StringUtils::EmptyString : + g_localizeStrings.Get(19055); // no information available + if (bHasTagNow) + videotag->m_genre = epgTagNow.Genre(); + videotag->m_strPath = channelTag->Path(); + videotag->m_strFileNameAndPath = channelTag->Path(); + videotag->m_strPlot = bHasTagNow ? epgTagNow.Plot() : StringUtils::EmptyString; + videotag->m_strPlotOutline = bHasTagNow ? epgTagNow.PlotOutline() : StringUtils::EmptyString; + videotag->m_iEpisode = bHasTagNow ? epgTagNow.EpisodeNum() : 0; + } + } + + return false; +} + + +bool CPVRManager::UpdateCurrentLastPlayedPosition(int lastplayedposition) +{ + // Only anything but recordings we fake success + if (!IsPlayingRecording()) + return true; + + bool rc = false; + CPVRRecording currentRecording; + + if (m_addons) + { + PVR_ERROR error; + rc = m_addons->GetPlayingRecording(currentRecording) && m_addons->SetRecordingLastPlayedPosition(currentRecording, lastplayedposition, &error); + } + return rc; +} + +bool CPVRManager::SetRecordingLastPlayedPosition(const CPVRRecording &recording, int lastplayedposition) +{ + bool rc = false; + + if (m_addons) + { + PVR_ERROR error; + rc = m_addons->SetRecordingLastPlayedPosition(recording, lastplayedposition, &error); + } + return rc; +} + +int CPVRManager::GetRecordingLastPlayedPosition(const CPVRRecording &recording) +{ + int rc = 0; + + if (m_addons) + { + rc = m_addons->GetRecordingLastPlayedPosition(recording); + } + return rc; +} + +bool CPVRManager::StartPlayback(const CPVRChannel *channel, bool bPreview /* = false */) +{ + g_settings.m_bStartVideoWindowed = bPreview; + CApplicationMessenger::Get().MediaPlay(CFileItem(*channel)); + CLog::Log(LOGNOTICE, "PVRManager - %s - started playback on channel '%s'", + __FUNCTION__, channel->ChannelName().c_str()); + return true; +} + +bool CPVRManager::PerformChannelSwitch(const CPVRChannel &channel, bool bPreview) +{ + bool bSwitched(false); + + if (IsParentalLocked(channel)) + return false; + + CSingleLock lock(m_critSection); + if (m_bIsSwitchingChannels) + { + CLog::Log(LOGDEBUG, "PVRManager - %s - can't switch to channel '%s'. waiting for the previous switch to complete", + __FUNCTION__, channel.ChannelName().c_str()); + return bSwitched; + } + m_bIsSwitchingChannels = true; + + CLog::Log(LOGDEBUG, "PVRManager - %s - switching to channel '%s'", + __FUNCTION__, channel.ChannelName().c_str()); + + /* make sure that channel settings are persisted */ + if (!bPreview) + { + CPVRChannelPtr currentChannel; + if (m_addons->GetPlayingChannel(currentChannel)) + { + /* store current time in iLastWatched */ + time_t tNow; + CDateTime::GetCurrentDateTime().GetAsTime(tNow); + currentChannel->SetLastWatched(tNow); + } + + SaveCurrentChannelSettings(); + } + + SAFE_DELETE(m_currentFile); + + lock.Leave(); + + if (!bPreview && (channel.ClientID() < 0 || !m_addons->SwitchChannel(channel))) + { + lock.Enter(); + m_bIsSwitchingChannels = false; + lock.Leave(); + + CLog::Log(LOGERROR, "PVRManager - %s - failed to switch to channel '%s'", + __FUNCTION__, channel.ChannelName().c_str()); + } + else + { + bSwitched = true; + + lock.Enter(); + m_currentFile = new CFileItem(channel); + + if (!bPreview) + CLog::Log(LOGNOTICE, "PVRManager - %s - switched to channel '%s'", + __FUNCTION__, channel.ChannelName().c_str()); + + m_bIsSwitchingChannels = false; + } + + if (!bSwitched) + { + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, + g_localizeStrings.Get(19166), // PVR information + g_localizeStrings.Get(19035)); // This channel cannot be played. Check the log for details. + } + + return bSwitched; +} + +int CPVRManager::GetTotalTime(void) const +{ + return IsStarted() && m_guiInfo ? m_guiInfo->GetDuration() : 0; +} + +int CPVRManager::GetStartTime(void) const +{ + return IsStarted() && m_guiInfo ? m_guiInfo->GetStartTime() : 0; +} + +bool CPVRManager::TranslateBoolInfo(DWORD dwInfo) const +{ + return IsStarted() && m_guiInfo ? m_guiInfo->TranslateBoolInfo(dwInfo) : false; +} + +bool CPVRManager::TranslateCharInfo(DWORD dwInfo, CStdString &strValue) const +{ + return IsStarted() && m_guiInfo ? m_guiInfo->TranslateCharInfo(dwInfo, strValue) : false; +} + +int CPVRManager::TranslateIntInfo(DWORD dwInfo) const +{ + return IsStarted() && m_guiInfo ? m_guiInfo->TranslateIntInfo(dwInfo) : 0; +} + +bool CPVRManager::HasTimers(void) const +{ + return IsStarted() && m_timers ? m_timers->HasActiveTimers() : false; +} + +bool CPVRManager::IsRecording(void) const +{ + return IsStarted() && m_timers ? m_timers->IsRecording() : false; +} + +bool CPVRManager::IsIdle(void) const +{ + if (!IsStarted()) + return true; + + if (IsRecording() || IsPlaying()) // pvr recording or playing? + { + return false; + } + else if (m_timers) // has active timers, etc.? + { + const CDateTime now = CDateTime::GetUTCDateTime(); + const CDateTimeSpan idle(0, 0, g_guiSettings.GetInt("pvrpowermanagement.backendidletime"), 0); + + const CDateTime next = m_timers->GetNextEventTime(); + const CDateTimeSpan delta = next - now; + + if (delta < idle) + { + return false; + } + } + + return true; +} + +void CPVRManager::ShowPlayerInfo(int iTimeout) +{ + if (IsStarted() && m_guiInfo) + m_guiInfo->ShowPlayerInfo(iTimeout); +} + +void CPVRManager::LocalizationChanged(void) +{ + CSingleLock lock(m_critSection); + if (IsStarted()) + { + static_cast<CPVRChannelGroupInternal *>(m_channelGroups->GetGroupAllRadio().get())->CheckGroupName(); + static_cast<CPVRChannelGroupInternal *>(m_channelGroups->GetGroupAllTV().get())->CheckGroupName(); + } +} + +bool CPVRManager::IsInitialising(void) const +{ + return GetState() == ManagerStateStarting; +} + +bool CPVRManager::IsStarted(void) const +{ + return GetState() == ManagerStateStarted; +} + +bool CPVRManager::IsPlayingTV(void) const +{ + return IsStarted() && m_addons && m_addons->IsPlayingTV(); +} + +bool CPVRManager::IsPlayingRadio(void) const +{ + return IsStarted() && m_addons && m_addons->IsPlayingRadio(); +} + +bool CPVRManager::IsPlayingRecording(void) const +{ + return IsStarted() && m_addons && m_addons->IsPlayingRecording(); +} + +bool CPVRManager::IsRunningChannelScan(void) const +{ + return IsStarted() && m_addons && m_addons->IsRunningChannelScan(); +} + +void CPVRManager::StartChannelScan(void) +{ + if (IsStarted() && m_addons) + m_addons->StartChannelScan(); +} + +void CPVRManager::SearchMissingChannelIcons(void) +{ + if (IsStarted() && m_channelGroups) + m_channelGroups->SearchMissingChannelIcons(); +} + +bool CPVRManager::IsJobPending(const char *strJobName) const +{ + bool bReturn(false); + CSingleLock lock(m_critSectionTriggers); + for (unsigned int iJobPtr = 0; IsStarted() && iJobPtr < m_pendingUpdates.size(); iJobPtr++) + { + if (!strcmp(m_pendingUpdates.at(iJobPtr)->GetType(), strJobName)) + { + bReturn = true; + break; + } + } + + return bReturn; +} + +void CPVRManager::TriggerRecordingsUpdate(void) +{ + CSingleLock lock(m_critSectionTriggers); + if (!IsStarted() || IsJobPending("pvr-update-recordings")) + return; + + m_pendingUpdates.push_back(new CPVRRecordingsUpdateJob()); + + lock.Leave(); + m_triggerEvent.Set(); +} + +void CPVRManager::TriggerTimersUpdate(void) +{ + CSingleLock lock(m_critSectionTriggers); + if (!IsStarted() || IsJobPending("pvr-update-timers")) + return; + + m_pendingUpdates.push_back(new CPVRTimersUpdateJob()); + + lock.Leave(); + m_triggerEvent.Set(); +} + +void CPVRManager::TriggerChannelsUpdate(void) +{ + CSingleLock lock(m_critSectionTriggers); + if (!IsStarted() || IsJobPending("pvr-update-channels")) + return; + + m_pendingUpdates.push_back(new CPVRChannelsUpdateJob()); + + lock.Leave(); + m_triggerEvent.Set(); +} + +void CPVRManager::TriggerChannelGroupsUpdate(void) +{ + CSingleLock lock(m_critSectionTriggers); + if (!IsStarted() || IsJobPending("pvr-update-channelgroups")) + return; + + m_pendingUpdates.push_back(new CPVRChannelGroupsUpdateJob()); + + lock.Leave(); + m_triggerEvent.Set(); +} + +void CPVRManager::TriggerSaveChannelSettings(void) +{ + CSingleLock lock(m_critSectionTriggers); + if (!IsStarted() || IsJobPending("pvr-save-channelsettings")) + return; + + m_pendingUpdates.push_back(new CPVRChannelSettingsSaveJob()); + + lock.Leave(); + m_triggerEvent.Set(); +} + +void CPVRManager::ExecutePendingJobs(void) +{ + CSingleLock lock(m_critSectionTriggers); + + while (m_pendingUpdates.size() > 0) + { + CJob *job = m_pendingUpdates.at(0); + m_pendingUpdates.erase(m_pendingUpdates.begin()); + lock.Leave(); + + job->DoWork(); + delete job; + + lock.Enter(); + } + + m_triggerEvent.Reset(); +} diff --git a/xbmc/pvr/PVRManager.h b/xbmc/pvr/PVRManager.h new file mode 100644 index 0000000000..19e5d414be --- /dev/null +++ b/xbmc/pvr/PVRManager.h @@ -0,0 +1,629 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "threads/Thread.h" +#include "utils/JobManager.h" +#include "threads/Event.h" +#include "addons/include/xbmc_pvr_types.h" + +class CGUIDialogProgressBarHandle; +class CStopWatch; + +namespace EPG +{ + class CEpgContainer; +} + +namespace PVR +{ + class CPVRClients; + class CPVRChannel; + typedef boost::shared_ptr<PVR::CPVRChannel> CPVRChannelPtr; + class CPVRChannelGroupsContainer; + class CPVRChannelGroup; + class CPVRRecording; + class CPVRRecordings; + class CPVRTimers; + class CPVRGUIInfo; + class CPVRDatabase; + class CGUIWindowPVRCommon; + + enum ManagerState + { + ManagerStateError = 0, + ManagerStateStopped, + ManagerStateStarting, + ManagerStateStopping, + ManagerStateInterrupted, + ManagerStateStarted + }; + + #define g_PVRManager CPVRManager::Get() + #define g_PVRChannelGroups g_PVRManager.ChannelGroups() + #define g_PVRTimers g_PVRManager.Timers() + #define g_PVRRecordings g_PVRManager.Recordings() + #define g_PVRClients g_PVRManager.Clients() + + typedef boost::shared_ptr<PVR::CPVRChannelGroup> CPVRChannelGroupPtr; + + class CPVRManager : private CThread + { + friend class CPVRClients; + + private: + /*! + * @brief Create a new CPVRManager instance, which handles all PVR related operations in XBMC. + */ + CPVRManager(void); + + public: + /*! + * @brief Stop the PVRManager and destroy all objects it created. + */ + virtual ~CPVRManager(void); + + /*! + * @brief Get the instance of the PVRManager. + * @return The PVRManager instance. + */ + static CPVRManager &Get(void); + + /*! + * @brief Get the channel groups container. + * @return The groups container. + */ + CPVRChannelGroupsContainer *ChannelGroups(void) const { return m_channelGroups; } + + /*! + * @brief Get the recordings container. + * @return The recordings container. + */ + CPVRRecordings *Recordings(void) const { return m_recordings; } + + /*! + * @brief Get the timers container. + * @return The timers container. + */ + CPVRTimers *Timers(void) const { return m_timers; } + + /*! + * @brief Get the timers container. + * @return The timers container. + */ + CPVRClients *Clients(void) const { return m_addons; } + + /*! + * @brief Start the PVRManager, which loads all PVR data and starts some threads to update the PVR data. + */ + void Start(void); + + /*! + * @brief Stop the PVRManager and destroy all objects it created. + */ + void Stop(void); + + /*! + * @brief Delete PVRManager's objects. + */ + void Cleanup(void); + + public: + + /*! + * @brief Get the TV database. + * @return The TV database. + */ + CPVRDatabase *GetTVDatabase(void) const { return m_database; } + + /*! + * @brief Updates the recordings and the "now" and "next" timers. + */ + void UpdateRecordingsCache(void); + + /*! + * @brief Get a GUIInfoManager character string. + * @param dwInfo The string to get. + * @return The requested string or an empty one if it wasn't found. + */ + bool TranslateCharInfo(DWORD dwInfo, CStdString &strValue) const; + + /*! + * @brief Get a GUIInfoManager integer. + * @param dwInfo The integer to get. + * @return The requested integer or 0 if it wasn't found. + */ + int TranslateIntInfo(DWORD dwInfo) const; + + /*! + * @brief Get a GUIInfoManager boolean. + * @param dwInfo The boolean to get. + * @return The requested boolean or false if it wasn't found. + */ + bool TranslateBoolInfo(DWORD dwInfo) const; + + /*! + * @brief Show the player info. + * @param iTimeout Hide the player info after iTimeout seconds. + * @todo not really the right place for this :-) + */ + void ShowPlayerInfo(int iTimeout); + + /*! + * @brief Reset the TV database to it's initial state and delete all the data inside. + * @param bShowProgress True to show a progress bar, false otherwise. + */ + void ResetDatabase(bool bShowProgress = true); + + /*! + * @brief Delete all EPG data from the database and reload it from the clients. + */ + void ResetEPG(void); + + /*! + * @brief Check if a TV channel, radio channel or recording is playing. + * @return True if it's playing, false otherwise. + */ + bool IsPlaying(void) const; + + /*! + * @return True while the PVRManager is initialising. + */ + bool IsInitialising(void) const; + + /*! + * @brief Return the channel that is currently playing. + * @param channel The channel or NULL if none is playing. + * @return True if a channel is playing, false otherwise. + */ + bool GetCurrentChannel(CPVRChannelPtr &channel) const; + + /*! + * @brief Return the EPG for the channel that is currently playing. + * @param channel The EPG or NULL if no channel is playing. + * @return The amount of results that was added or -1 if none. + */ + int GetCurrentEpg(CFileItemList &results) const; + + /*! + * @brief Check whether the PVRManager has fully started. + * @return True if started, false otherwise. + */ + bool IsStarted(void) const; + + /*! + * @brief Reset the playing EPG tag. + */ + void ResetPlayingTag(void); + + /*! + * @brief Switch to the given channel. + * @param channel The channel to switch to. + * @param bPreview True to show a preview, false otherwise. + * @return Trrue if the switch was successful, false otherwise. + */ + bool PerformChannelSwitch(const CPVRChannel &channel, bool bPreview); + + /*! + * @brief Close an open PVR stream. + */ + void CloseStream(void); + + /*! + * @brief Open a stream from the given channel. + * @param channel The channel to open. + * @return True if the stream was opened, false otherwise. + */ + bool OpenLiveStream(const CFileItem &channel); + + /*! + * @brief Open a stream from the given recording. + * @param tag The recording to open. + * @return True if the stream was opened, false otherwise. + */ + bool OpenRecordedStream(const CPVRRecording &tag); + + /*! + * @brief Start recording on a given channel if it is not already recording, stop if it is. + * @param channel the channel to start/stop recording. + * @return True if the recording was started or stopped successfully, false otherwise. + */ + bool ToggleRecordingOnChannel(unsigned int iChannelId); + + /*! + * @brief Start or stop recording on the channel that is currently being played. + * @param bOnOff True to start recording, false to stop. + * @return True if the recording was started or stopped successfully, false otherwise. + */ + bool StartRecordingOnPlayingChannel(bool bOnOff); + + /*! + * @brief Get the channel number of the previously selected channel. + * @return The requested channel number or -1 if it wasn't found. + */ + int GetPreviousChannel(void); + + /*! + * @brief Check whether there are active timers. + * @return True if there are active timers, false otherwise. + */ + bool HasTimers(void) const; + + /*! + * @brief Check whether there are active recordings. + * @return True if there are active recordings, false otherwise. + */ + bool IsRecording(void) const; + + /*! + * @brief Check whether the pvr backend is idle. + * @return True if there are no active timers/recordings/wake-ups within the configured time span. + */ + bool IsIdle(void) const; + + /*! + * @brief Set the current playing group, used to load the right channel. + * @param group The new group. + */ + void SetPlayingGroup(CPVRChannelGroupPtr group); + + /*! + * @brief Get the current playing group, used to load the right channel. + * @param bRadio True to get the current radio group, false to get the current TV group. + * @return The current group or the group containing all channels if it's not set. + */ + CPVRChannelGroupPtr GetPlayingGroup(bool bRadio = false); + + /*! + * @brief Let the background thread update the recordings list. + */ + void TriggerRecordingsUpdate(void); + + /*! + * @brief Let the background thread update the timer list. + */ + void TriggerTimersUpdate(void); + + /*! + * @brief Let the background thread update the channel list. + */ + void TriggerChannelsUpdate(void); + + /*! + * @brief Let the background thread update the channel groups list. + */ + void TriggerChannelGroupsUpdate(void); + + /*! + * @brief Let the background thread save the current video settings. + */ + void TriggerSaveChannelSettings(void); + + /*! + * @brief Update the channel that is currently active. + * @param item The new channel. + * @return True if it was updated correctly, false otherwise. + */ + bool UpdateItem(CFileItem& item); + + /*! + * @brief Switch to a channel given it's channel number. + * @param iChannelNumber The channel number to switch to. + * @return True if the channel was switched, false otherwise. + */ + bool ChannelSwitch(unsigned int iChannelNumber); + + /*! + * @brief Switch to the next channel in this group. + * @param iNewChannelNumber The new channel number after the switch. + * @param bPreview If true, don't do the actual switch but just update channel pointers. + * Used to display event info while doing "fast channel switching" + * @return True if the channel was switched, false otherwise. + */ + bool ChannelUp(unsigned int *iNewChannelNumber, bool bPreview = false) { return ChannelUpDown(iNewChannelNumber, bPreview, true); } + + /*! + * @brief Switch to the previous channel in this group. + * @param iNewChannelNumber The new channel number after the switch. + * @param bPreview If true, don't do the actual switch but just update channel pointers. + * Used to display event info while doing "fast channel switching" + * @return True if the channel was switched, false otherwise. + */ + bool ChannelDown(unsigned int *iNewChannelNumber, bool bPreview = false) { return ChannelUpDown(iNewChannelNumber, bPreview, false); } + + /*! + * @brief Get the total duration of the currently playing LiveTV item. + * @return The total duration in milliseconds or NULL if no channel is playing. + */ + int GetTotalTime(void) const; + + /*! + * @brief Get the current position in milliseconds since the start of a LiveTV item. + * @return The position in milliseconds or NULL if no channel is playing. + */ + int GetStartTime(void) const; + + /*! + * @brief Start playback on a channel. + * @param channel The channel to start to play. + * @param bPreview If true, open minimised. + * @return True if playback was started, false otherwise. + */ + bool StartPlayback(const CPVRChannel *channel, bool bPreview = false); + + /*! + * @brief Update the current playing file in the guiinfomanager and application. + */ + void UpdateCurrentFile(void); + + /*! + * @brief Check whether names are still correct after the language settings changed. + */ + void LocalizationChanged(void); + + /*! + * @brief Check if a TV channel is playing. + * @return True if it's playing, false otherwise. + */ + bool IsPlayingTV(void) const; + + /*! + * @brief Check if a radio channel is playing. + * @return True if it's playing, false otherwise. + */ + bool IsPlayingRadio(void) const; + + /*! + * @brief Check if a recording is playing. + * @return True if it's playing, false otherwise. + */ + bool IsPlayingRecording(void) const; + + /*! + * @return True when a channel scan is currently running, false otherwise. + */ + bool IsRunningChannelScan(void) const; + + /*! + * @brief Open a selection dialog and start a channel scan on the selected client. + */ + void StartChannelScan(void); + + /*! + * @brief Try to find missing channel icons automatically + */ + void SearchMissingChannelIcons(void); + + /*! + * @brief Persist the current channel settings in the database. + */ + void SaveCurrentChannelSettings(void); + + /*! + * @brief Load the settings for the current channel from the database. + */ + void LoadCurrentChannelSettings(void); + + /*! + * @brief Check if channel is parental locked. Ask for PIN if neccessary. + * @param channel The channel to open. + * @return True if channel is unlocked (by default or PIN unlocked), false otherwise. + */ + bool CheckParentalLock(const CPVRChannel &channel); + + /*! + * @brief Check if parental lock is overriden at the given moment. + * @param channel The channel to open. + * @return True if parental lock is overriden, false otherwise. + */ + bool IsParentalLocked(const CPVRChannel &channel); + + /*! + * @brief Open Numeric dialog to check for parental PIN. + * @param strTitle Override the title of the dialog if set. + * @return True if entered PIN was correct, false otherwise. + */ + bool CheckParentalPIN(const char *strTitle = NULL); + + /*! + * @brief Executes "pvrpowermanagement.setwakeupcmd" + */ + bool SetWakeupCommand(void); + + /*! + * @brief Update the last played position for the current playing file + * @param lastplayedposition channel The channel to start to play. + */ + bool UpdateCurrentLastPlayedPosition(int lastplayedposition); + + /*! + * @brief Set the last watched position of a recording on the backend. + * @param recording The recording. + * @param position The last watched position in seconds + * @return True if the last played position was updated successfully, false otherwise + */ + bool SetRecordingLastPlayedPosition(const CPVRRecording &recording, int lastplayedposition); + + /*! + * @brief Retrieve the last watched position of a recording on the backend. + * @param recording The recording. + * @return The last watched position in seconds + */ + int GetRecordingLastPlayedPosition(const CPVRRecording &recording); + + protected: + /*! + * @brief PVR update and control thread. + */ + virtual void Process(void); + + private: + /*! + * @brief Load at least one client and load all other PVR data after loading the client. + * If some clients failed to load here, the pvrmanager will retry to load them every second. + * @return If at least one client and all pvr data was loaded, false otherwise. + */ + bool Load(void); + + /*! + * @brief Update all recordings. + */ + void UpdateRecordings(void); + + /*! + * @brief Update all timers. + */ + void UpdateTimers(void); + + /*! + * @brief Update all channels. + */ + void UpdateChannels(void); + + /*! + * @brief Update all channel groups and channels in them. + */ + void UpdateChannelGroups(void); + + /*! + * @brief Reset all properties. + */ + void ResetProperties(void); + + /*! + * @brief Called by ChannelUp() and ChannelDown() to perform a channel switch. + * @param iNewChannelNumber The new channel number after the switch. + * @param bPreview Preview window if true. + * @param bUp Go one channel up if true, one channel down if false. + * @return True if the switch was successful, false otherwise. + */ + bool ChannelUpDown(unsigned int *iNewChannelNumber, bool bPreview, bool bUp); + + /*! + * @brief Stop the EPG and PVR threads but do not remove their data. + */ + void StopUpdateThreads(void); + + /*! + * @brief Restart the EPG and PVR threads after they've been stopped by StopUpdateThreads() + */ + bool StartUpdateThreads(void); + + /*! + * @brief Continue playback on the last channel if it was stored in the database. + * @return True if playback was continued, false otherwise. + */ + bool ContinueLastChannel(void); + + /*! + * @brief Show or update the progress dialog. + * @param strText The current status. + * @param iProgress The current progress in %. + */ + void ShowProgressDialog(const CStdString &strText, int iProgress); + + /*! + * @brief Hide the progress dialog if it's visible. + */ + void HideProgressDialog(void); + + void ExecutePendingJobs(void); + + bool IsJobPending(const char *strJobName) const; + + ManagerState GetState(void) const; + + void SetState(ManagerState state); + /** @name containers */ + //@{ + CPVRChannelGroupsContainer * m_channelGroups; /*!< pointer to the channel groups container */ + CPVRRecordings * m_recordings; /*!< pointer to the recordings container */ + CPVRTimers * m_timers; /*!< pointer to the timers container */ + CPVRClients * m_addons; /*!< pointer to the pvr addon container */ + CPVRGUIInfo * m_guiInfo; /*!< pointer to the guiinfo data */ + //@} + + CCriticalSection m_critSectionTriggers; /*!< critical section for triggered updates */ + CEvent m_triggerEvent; /*!< triggers an update */ + std::vector<CJob *> m_pendingUpdates; /*!< vector of pending pvr updates */ + + CFileItem * m_currentFile; /*!< the PVR file that is currently playing */ + CPVRDatabase * m_database; /*!< the database for all PVR related data */ + CCriticalSection m_critSection; /*!< critical section for all changes to this class, except for changes to triggers */ + bool m_bFirstStart; /*!< true when the PVR manager was started first, false otherwise */ + bool m_bIsSwitchingChannels; /*!< true while switching channels */ + CGUIDialogProgressBarHandle * m_progressHandle; /*!< progress dialog that is displayed while the pvrmanager is loading */ + + CCriticalSection m_managerStateMutex; + ManagerState m_managerState; + CStopWatch *m_parentalTimer; + }; + + class CPVRRecordingsUpdateJob : public CJob + { + public: + CPVRRecordingsUpdateJob(void) {} + virtual ~CPVRRecordingsUpdateJob() {} + virtual const char *GetType() const { return "pvr-update-recordings"; } + + virtual bool DoWork(); + }; + + class CPVRTimersUpdateJob : public CJob + { + public: + CPVRTimersUpdateJob(void) {} + virtual ~CPVRTimersUpdateJob() {} + virtual const char *GetType() const { return "pvr-update-timers"; } + + virtual bool DoWork(); + }; + + class CPVRChannelsUpdateJob : public CJob + { + public: + CPVRChannelsUpdateJob(void) {} + virtual ~CPVRChannelsUpdateJob() {} + virtual const char *GetType() const { return "pvr-update-channels"; } + + virtual bool DoWork(); + }; + + class CPVRChannelGroupsUpdateJob : public CJob + { + public: + CPVRChannelGroupsUpdateJob(void) {} + virtual ~CPVRChannelGroupsUpdateJob() {} + virtual const char *GetType() const { return "pvr-update-channelgroups"; } + + virtual bool DoWork(); + }; + + class CPVRChannelSettingsSaveJob : public CJob + { + public: + CPVRChannelSettingsSaveJob(void) {} + virtual ~CPVRChannelSettingsSaveJob() {} + virtual const char *GetType() const { return "pvr-save-channelsettings"; } + + virtual bool DoWork(); + }; +} diff --git a/xbmc/pvr/addons/Makefile b/xbmc/pvr/addons/Makefile new file mode 100644 index 0000000000..8bda61097b --- /dev/null +++ b/xbmc/pvr/addons/Makefile @@ -0,0 +1,7 @@ +SRCS=PVRClient.cpp \ + PVRClients.cpp + +LIB=pvraddons.a + +include ../../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/xbmc/pvr/addons/PVRClient.cpp b/xbmc/pvr/addons/PVRClient.cpp new file mode 100644 index 0000000000..1be0f700d7 --- /dev/null +++ b/xbmc/pvr/addons/PVRClient.cpp @@ -0,0 +1,1334 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include <vector> +#include "PVRClient.h" +#include "pvr/PVRManager.h" +#include "epg/Epg.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/timers/PVRTimers.h" +#include "pvr/timers/PVRTimerInfoTag.h" +#include "pvr/recordings/PVRRecordings.h" +#include "settings/AdvancedSettings.h" +#include "utils/log.h" +#include "settings/GUISettings.h" + +using namespace std; +using namespace ADDON; +using namespace PVR; +using namespace EPG; + +#define DEFAULT_INFO_STRING_VALUE "unknown" + +CPVRClient::CPVRClient(const AddonProps& props) : + CAddonDll<DllPVRClient, PVRClient, PVR_PROPERTIES>(props), + m_apiVersion("0.0.0") +{ + ResetProperties(); +} + +CPVRClient::CPVRClient(const cp_extension_t *ext) : + CAddonDll<DllPVRClient, PVRClient, PVR_PROPERTIES>(ext), + m_apiVersion("0.0.0") +{ + ResetProperties(); +} + +CPVRClient::~CPVRClient(void) +{ + Destroy(); + SAFE_DELETE(m_pInfo); +} + +void CPVRClient::ResetProperties(int iClientId /* = PVR_INVALID_CLIENT_ID */) +{ + /* initialise members */ + SAFE_DELETE(m_pInfo); + m_pInfo = new PVR_PROPERTIES; + m_strUserPath = CSpecialProtocol::TranslatePath(Profile()); + m_pInfo->strUserPath = m_strUserPath.c_str(); + m_strClientPath = CSpecialProtocol::TranslatePath(Path()); + m_pInfo->strClientPath = m_strClientPath.c_str(); + m_menuhooks.clear(); + m_bReadyToUse = false; + m_iClientId = iClientId; + m_strBackendVersion = DEFAULT_INFO_STRING_VALUE; + m_strConnectionString = DEFAULT_INFO_STRING_VALUE; + m_strFriendlyName = DEFAULT_INFO_STRING_VALUE; + m_strBackendName = DEFAULT_INFO_STRING_VALUE; + m_bIsPlayingTV = false; + m_bIsPlayingRecording = false; + memset(&m_addonCapabilities, 0, sizeof(m_addonCapabilities)); + ResetQualityData(m_qualityInfo); + m_apiVersion = AddonVersion("0.0.0"); +} + +bool CPVRClient::Create(int iClientId) +{ + if (iClientId <= PVR_INVALID_CLIENT_ID || iClientId == PVR_VIRTUAL_CLIENT_ID) + return false; + + /* ensure that a previous instance is destroyed */ + Destroy(); + + /* reset all properties to defaults */ + ResetProperties(iClientId); + + /* initialise the add-on */ + bool bReadyToUse(false); + CLog::Log(LOGDEBUG, "PVR - %s - creating PVR add-on instance '%s'", __FUNCTION__, Name().c_str()); + try + { + bReadyToUse = CAddonDll<DllPVRClient, PVRClient, PVR_PROPERTIES>::Create() && + GetAddonProperties(); + } + catch (exception &e) { LogException(e, __FUNCTION__); } + + m_bReadyToUse = bReadyToUse; + if (!bReadyToUse) + ResetProperties(iClientId); + + return bReadyToUse; +} + +bool CPVRClient::DllLoaded(void) const +{ + try { return CAddonDll<DllPVRClient, PVRClient, PVR_PROPERTIES>::DllLoaded(); } + catch (exception &e) { LogException(e, __FUNCTION__); } + + return false; +} + +void CPVRClient::Destroy(void) +{ + if (!m_bReadyToUse) + return; + m_bReadyToUse = false; + + /* reset 'ready to use' to false */ + CLog::Log(LOGDEBUG, "PVR - %s - destroying PVR add-on '%s'", __FUNCTION__, GetFriendlyName().c_str()); + + /* destroy the add-on */ + try { CAddonDll<DllPVRClient, PVRClient, PVR_PROPERTIES>::Destroy(); } + catch (exception &e) { LogException(e, __FUNCTION__); } + + /* reset all properties to defaults */ + ResetProperties(); +} + +void CPVRClient::ReCreate(void) +{ + int iClientID(m_iClientId); + Destroy(); + + /* recreate the instance */ + Create(iClientID); +} + +bool CPVRClient::ReadyToUse(void) const +{ + return m_bReadyToUse; +} + +int CPVRClient::GetID(void) const +{ + return m_iClientId; +} + +/*! + * @brief Copy over group info from xbmcGroup to addonGroup. + * @param xbmcGroup The group on XBMC's side. + * @param addonGroup The group on the addon's side. + */ +void CPVRClient::WriteClientGroupInfo(const CPVRChannelGroup &xbmcGroup, PVR_CHANNEL_GROUP &addonGroup) +{ + memset(&addonGroup, 0, sizeof(addonGroup)); + + addonGroup.bIsRadio = xbmcGroup.IsRadio(); + strncpy(addonGroup.strGroupName, xbmcGroup.GroupName().c_str(), sizeof(addonGroup.strGroupName) - 1); +} + +/*! + * @brief Copy over recording info from xbmcRecording to addonRecording. + * @param xbmcRecording The recording on XBMC's side. + * @param addonRecording The recording on the addon's side. + */ +void CPVRClient::WriteClientRecordingInfo(const CPVRRecording &xbmcRecording, PVR_RECORDING &addonRecording) +{ + time_t recTime; + xbmcRecording.RecordingTimeAsUTC().GetAsTime(recTime); + + memset(&addonRecording, 0, sizeof(addonRecording)); + + addonRecording.recordingTime = recTime - g_advancedSettings.m_iPVRTimeCorrection; + strncpy(addonRecording.strRecordingId, xbmcRecording.m_strRecordingId.c_str(), sizeof(addonRecording.strRecordingId) - 1); + strncpy(addonRecording.strTitle, xbmcRecording.m_strTitle.c_str(), sizeof(addonRecording.strTitle) - 1); + strncpy(addonRecording.strPlotOutline, xbmcRecording.m_strPlotOutline.c_str(), sizeof(addonRecording.strPlotOutline) - 1); + strncpy(addonRecording.strPlot, xbmcRecording.m_strPlot.c_str(), sizeof(addonRecording.strPlot) - 1); + strncpy(addonRecording.strChannelName, xbmcRecording.m_strChannelName.c_str(), sizeof(addonRecording.strChannelName) - 1); + addonRecording.iDuration = xbmcRecording.GetDuration(); + addonRecording.iPriority = xbmcRecording.m_iPriority; + addonRecording.iLifetime = xbmcRecording.m_iLifetime; + strncpy(addonRecording.strDirectory, xbmcRecording.m_strDirectory.c_str(), sizeof(addonRecording.strDirectory) - 1); + strncpy(addonRecording.strStreamURL, xbmcRecording.m_strStreamURL.c_str(), sizeof(addonRecording.strStreamURL) - 1); +} + +/*! + * @brief Copy over timer info from xbmcTimer to addonTimer. + * @param xbmcTimer The timer on XBMC's side. + * @param addonTimer The timer on the addon's side. + */ +void CPVRClient::WriteClientTimerInfo(const CPVRTimerInfoTag &xbmcTimer, PVR_TIMER &addonTimer) +{ + time_t start, end, firstDay; + xbmcTimer.StartAsUTC().GetAsTime(start); + xbmcTimer.EndAsUTC().GetAsTime(end); + xbmcTimer.FirstDayAsUTC().GetAsTime(firstDay); + CEpgInfoTagPtr epgTag = xbmcTimer.GetEpgInfoTag(); + + memset(&addonTimer, 0, sizeof(addonTimer)); + + addonTimer.iClientIndex = xbmcTimer.m_iClientIndex; + addonTimer.state = xbmcTimer.m_state; + addonTimer.iClientIndex = xbmcTimer.m_iClientIndex; + addonTimer.iClientChannelUid = xbmcTimer.m_iClientChannelUid; + strncpy(addonTimer.strTitle, xbmcTimer.m_strTitle.c_str(), sizeof(addonTimer.strTitle) - 1); + strncpy(addonTimer.strDirectory, xbmcTimer.m_strDirectory.c_str(), sizeof(addonTimer.strDirectory) - 1); + addonTimer.iPriority = xbmcTimer.m_iPriority; + addonTimer.iLifetime = xbmcTimer.m_iLifetime; + addonTimer.bIsRepeating = xbmcTimer.m_bIsRepeating; + addonTimer.iWeekdays = xbmcTimer.m_iWeekdays; + addonTimer.startTime = start - g_advancedSettings.m_iPVRTimeCorrection; + addonTimer.endTime = end - g_advancedSettings.m_iPVRTimeCorrection; + addonTimer.firstDay = firstDay - g_advancedSettings.m_iPVRTimeCorrection; + addonTimer.iEpgUid = epgTag ? epgTag->UniqueBroadcastID() : -1; + strncpy(addonTimer.strSummary, xbmcTimer.m_strSummary.c_str(), sizeof(addonTimer.strSummary) - 1); + addonTimer.iMarginStart = xbmcTimer.m_iMarginStart; + addonTimer.iMarginEnd = xbmcTimer.m_iMarginEnd; + addonTimer.iGenreType = xbmcTimer.m_iGenreType; + addonTimer.iGenreSubType = xbmcTimer.m_iGenreSubType; +} + +/*! + * @brief Copy over channel info from xbmcChannel to addonClient. + * @param xbmcChannel The channel on XBMC's side. + * @param addonChannel The channel on the addon's side. + */ +void CPVRClient::WriteClientChannelInfo(const CPVRChannel &xbmcChannel, PVR_CHANNEL &addonChannel) +{ + memset(&addonChannel, 0, sizeof(addonChannel)); + + addonChannel.iUniqueId = xbmcChannel.UniqueID(); + addonChannel.iChannelNumber = xbmcChannel.ClientChannelNumber(); + strncpy(addonChannel.strChannelName, xbmcChannel.ClientChannelName().c_str(), sizeof(addonChannel.strChannelName) - 1); + strncpy(addonChannel.strIconPath, xbmcChannel.IconPath().c_str(), sizeof(addonChannel.strIconPath) - 1); + addonChannel.iEncryptionSystem = xbmcChannel.EncryptionSystem(); + addonChannel.bIsRadio = xbmcChannel.IsRadio(); + addonChannel.bIsHidden = xbmcChannel.IsHidden(); + strncpy(addonChannel.strInputFormat, xbmcChannel.InputFormat().c_str(), sizeof(addonChannel.strInputFormat) - 1); + strncpy(addonChannel.strStreamURL, xbmcChannel.StreamURL().c_str(), sizeof(addonChannel.strStreamURL) - 1); +} + +bool CPVRClient::IsCompatibleAPIVersion(const ADDON::AddonVersion &minVersion, const ADDON::AddonVersion &version) +{ + AddonVersion myMinVersion = AddonVersion(XBMC_PVR_MIN_API_VERSION); + AddonVersion myVersion = AddonVersion(XBMC_PVR_API_VERSION); + return (version >= myMinVersion && minVersion <= myVersion); +} + +bool CPVRClient::GetAddonProperties(void) +{ + CStdString strHostName, strBackendName, strConnectionString, strFriendlyName, strBackendVersion; + PVR_ADDON_CAPABILITIES addonCapabilities; + + /* check the API version */ + AddonVersion minVersion = AddonVersion("0.0.0"); + try { m_apiVersion = AddonVersion(m_pStruct->GetPVRAPIVersion()); } + catch (exception &e) { LogException(e, "GetPVRAPIVersion()"); return false; } + + try { minVersion = AddonVersion(m_pStruct->GetMininumPVRAPIVersion()); } + catch (exception &e) { LogException(e, "GetMininumPVRAPIVersion()"); return false; } + + if (!IsCompatibleAPIVersion(minVersion, m_apiVersion)) + { + CLog::Log(LOGERROR, "PVR - Add-on '%s' is using an incompatible API version. Please contact the developer of this add-on: %s", GetFriendlyName().c_str(), Author().c_str()); + return false; + } + + /* get the capabilities */ + try + { + memset(&addonCapabilities, 0, sizeof(addonCapabilities)); + PVR_ERROR retVal = m_pStruct->GetAddonCapabilities(&addonCapabilities); + if (retVal != PVR_ERROR_NO_ERROR) + { + CLog::Log(LOGERROR, "PVR - couldn't get the capabilities for add-on '%s'. Please contact the developer of this add-on: %s", GetFriendlyName().c_str(), Author().c_str()); + return false; + } + } + catch (exception &e) { LogException(e, "GetAddonCapabilities()"); return false; } + + /* get the name of the backend */ + try { strBackendName = m_pStruct->GetBackendName(); } + catch (exception &e) { LogException(e, "GetBackendName()"); return false; } + + /* get the connection string */ + try { strConnectionString = m_pStruct->GetConnectionString(); } + catch (exception &e) { LogException(e, "GetConnectionString()"); return false; } + + /* display name = backend name:connection string */ + strFriendlyName.Format("%s:%s", strBackendName.c_str(), strConnectionString.c_str()); + + /* backend version number */ + try { strBackendVersion = m_pStruct->GetBackendVersion(); } + catch (exception &e) { LogException(e, "GetBackendVersion()"); return false; } + + /* update the members */ + m_strBackendName = strBackendName; + m_strConnectionString = strConnectionString; + m_strFriendlyName = strFriendlyName; + m_strBackendVersion = strBackendVersion; + m_addonCapabilities = addonCapabilities; + + return true; +} + +PVR_ADDON_CAPABILITIES CPVRClient::GetAddonCapabilities(void) const +{ + PVR_ADDON_CAPABILITIES addonCapabilities(m_addonCapabilities); + return addonCapabilities; +} + +CStdString CPVRClient::GetBackendName(void) const +{ + CStdString strReturn(m_strBackendName); + return strReturn; +} + +CStdString CPVRClient::GetBackendVersion(void) const +{ + CStdString strReturn(m_strBackendVersion); + return strReturn; +} + +CStdString CPVRClient::GetConnectionString(void) const +{ + CStdString strReturn(m_strConnectionString); + return strReturn; +} + +CStdString CPVRClient::GetFriendlyName(void) const +{ + CStdString strReturn(m_strFriendlyName); + return strReturn; +} + +PVR_ERROR CPVRClient::GetDriveSpace(long long *iTotal, long long *iUsed) +{ + if (!m_bReadyToUse) + return PVR_ERROR_REJECTED; + + try { return m_pStruct->GetDriveSpace(iTotal, iUsed); } + catch (exception &e) { LogException(e, __FUNCTION__); } + + /* default to 0 on error */ + *iTotal = 0; + *iUsed = 0; + + return PVR_ERROR_UNKNOWN; +} + +PVR_ERROR CPVRClient::StartChannelScan(void) +{ + if (!m_bReadyToUse) + return PVR_ERROR_REJECTED; + + if (!m_addonCapabilities.bSupportsChannelScan) + return PVR_ERROR_NOT_IMPLEMENTED; + + try { return m_pStruct->DialogChannelScan(); } + catch (exception &e) { LogException(e, __FUNCTION__); } + + return PVR_ERROR_UNKNOWN; +} + +void CPVRClient::CallMenuHook(const PVR_MENUHOOK &hook) +{ + if (!m_bReadyToUse) + return; + + try { m_pStruct->MenuHook(hook); } + catch (exception &e) { LogException(e, __FUNCTION__); } +} + +PVR_ERROR CPVRClient::GetEPGForChannel(const CPVRChannel &channel, CEpg *epg, time_t start /* = 0 */, time_t end /* = 0 */, bool bSaveInDb /* = false*/) +{ + if (!m_bReadyToUse) + return PVR_ERROR_REJECTED; + + if (!m_addonCapabilities.bSupportsEPG) + return PVR_ERROR_NOT_IMPLEMENTED; + + PVR_ERROR retVal(PVR_ERROR_UNKNOWN); + try + { + PVR_CHANNEL addonChannel; + WriteClientChannelInfo(channel, addonChannel); + + ADDON_HANDLE_STRUCT handle; + handle.callerAddress = this; + handle.dataAddress = epg; + handle.dataIdentifier = bSaveInDb ? 1 : 0; // used by the callback method CAddonCallbacksPVR::PVRTransferEpgEntry() + retVal = m_pStruct->GetEpg(&handle, + addonChannel, + start ? start - g_advancedSettings.m_iPVRTimeCorrection : 0, + end ? end - g_advancedSettings.m_iPVRTimeCorrection : 0); + + LogError(retVal, __FUNCTION__); + } + catch (exception &e) + { + LogException(e, __FUNCTION__); + } + + return retVal; +} + +int CPVRClient::GetChannelGroupsAmount(void) +{ + int iReturn(-EINVAL); + + if (!m_bReadyToUse) + return iReturn; + + if (!m_addonCapabilities.bSupportsChannelGroups) + return iReturn; + + try { iReturn = m_pStruct->GetChannelGroupsAmount(); } + catch (exception &e) { LogException(e, __FUNCTION__); } + + return iReturn; +} + +PVR_ERROR CPVRClient::GetChannelGroups(CPVRChannelGroups *groups) +{ + if (!m_bReadyToUse) + return PVR_ERROR_REJECTED; + + if (!m_addonCapabilities.bSupportsChannelGroups) + return PVR_ERROR_NOT_IMPLEMENTED; + + PVR_ERROR retVal(PVR_ERROR_UNKNOWN); + try + { + ADDON_HANDLE_STRUCT handle; + handle.callerAddress = this; + handle.dataAddress = groups; + retVal = m_pStruct->GetChannelGroups(&handle, groups->IsRadio()); + + LogError(retVal, __FUNCTION__); + } + catch (exception &e) + { + LogException(e, __FUNCTION__); + } + + return retVal; +} + +PVR_ERROR CPVRClient::GetChannelGroupMembers(CPVRChannelGroup *group) +{ + if (!m_bReadyToUse) + return PVR_ERROR_REJECTED; + + if (!m_addonCapabilities.bSupportsChannelGroups) + return PVR_ERROR_NOT_IMPLEMENTED; + + PVR_ERROR retVal(PVR_ERROR_UNKNOWN); + try + { + ADDON_HANDLE_STRUCT handle; + handle.callerAddress = this; + handle.dataAddress = group; + + PVR_CHANNEL_GROUP tag; + WriteClientGroupInfo(*group, tag); + + CLog::Log(LOGDEBUG, "PVR - %s - get group members for group '%s' from add-on '%s'", + __FUNCTION__, tag.strGroupName, GetFriendlyName().c_str()); + retVal = m_pStruct->GetChannelGroupMembers(&handle, tag); + + LogError(retVal, __FUNCTION__); + } + catch (exception &e) + { + LogException(e, __FUNCTION__); + } + + return retVal; +} + +int CPVRClient::GetChannelsAmount(void) +{ + int iReturn(-EINVAL); + if (m_bReadyToUse && (m_addonCapabilities.bSupportsTV || m_addonCapabilities.bSupportsRadio)) + { + try { iReturn = m_pStruct->GetChannelsAmount(); } + catch (exception &e) { LogException(e, __FUNCTION__); } + } + + return iReturn; +} + +PVR_ERROR CPVRClient::GetChannels(CPVRChannelGroup &channels, bool radio) +{ + if (!m_bReadyToUse) + return PVR_ERROR_REJECTED; + + if ((!m_addonCapabilities.bSupportsRadio && radio) || + (!m_addonCapabilities.bSupportsTV && !radio)) + return PVR_ERROR_NOT_IMPLEMENTED; + + PVR_ERROR retVal(PVR_ERROR_UNKNOWN); + + try + { + ADDON_HANDLE_STRUCT handle; + handle.callerAddress = this; + handle.dataAddress = (CPVRChannelGroup*) &channels; + retVal = m_pStruct->GetChannels(&handle, radio); + + LogError(retVal, __FUNCTION__); + } + catch (exception &e) + { + LogException(e, __FUNCTION__); + } + + return retVal; +} + +int CPVRClient::GetRecordingsAmount(void) +{ + int iReturn(-EINVAL); + + if (m_addonCapabilities.bSupportsRecordings) + { + try { iReturn = m_pStruct->GetRecordingsAmount(); } + catch (exception &e) { LogException(e, __FUNCTION__); } + } + + return iReturn; +} + +PVR_ERROR CPVRClient::GetRecordings(CPVRRecordings *results) +{ + if (!m_bReadyToUse) + return PVR_ERROR_REJECTED; + + if (!m_addonCapabilities.bSupportsRecordings) + return PVR_ERROR_NOT_IMPLEMENTED; + + PVR_ERROR retVal(PVR_ERROR_UNKNOWN); + try + { + ADDON_HANDLE_STRUCT handle; + handle.callerAddress = this; + handle.dataAddress = (CPVRRecordings*) results; + retVal = m_pStruct->GetRecordings(&handle); + + LogError(retVal, __FUNCTION__); + } + catch (exception &e) + { + LogException(e, __FUNCTION__); + } + + return retVal; +} + +PVR_ERROR CPVRClient::DeleteRecording(const CPVRRecording &recording) +{ + if (!m_bReadyToUse) + return PVR_ERROR_REJECTED; + + if (!m_addonCapabilities.bSupportsRecordings) + return PVR_ERROR_NOT_IMPLEMENTED; + + PVR_ERROR retVal(PVR_ERROR_UNKNOWN); + try + { + PVR_RECORDING tag; + WriteClientRecordingInfo(recording, tag); + + retVal = m_pStruct->DeleteRecording(tag); + + LogError(retVal, __FUNCTION__); + } + catch (exception &e) + { + LogException(e, __FUNCTION__); + } + + return retVal; +} + +PVR_ERROR CPVRClient::RenameRecording(const CPVRRecording &recording) +{ + if (!m_bReadyToUse) + return PVR_ERROR_REJECTED; + + if (!m_addonCapabilities.bSupportsRecordings) + return PVR_ERROR_NOT_IMPLEMENTED; + + PVR_ERROR retVal(PVR_ERROR_UNKNOWN); + try + { + PVR_RECORDING tag; + WriteClientRecordingInfo(recording, tag); + + retVal = m_pStruct->RenameRecording(tag); + + LogError(retVal, __FUNCTION__); + } + catch (exception &e) + { + LogException(e, __FUNCTION__); + } + + return retVal; +} + +PVR_ERROR CPVRClient::SetRecordingPlayCount(const CPVRRecording &recording, int count) +{ + if (!m_bReadyToUse) + return PVR_ERROR_REJECTED; + + if (!m_addonCapabilities.bSupportsRecordingPlayCount) + return PVR_ERROR_NOT_IMPLEMENTED; + + PVR_ERROR retVal(PVR_ERROR_UNKNOWN); + try + { + PVR_RECORDING tag; + WriteClientRecordingInfo(recording, tag); + + retVal = m_pStruct->SetRecordingPlayCount(tag, count); + + LogError(retVal, __FUNCTION__); + } + catch (exception &e) + { + LogException(e, __FUNCTION__); + } + + return retVal; +} + +PVR_ERROR CPVRClient::SetRecordingLastPlayedPosition(const CPVRRecording &recording, int lastplayedposition) +{ + if (!m_bReadyToUse) + return PVR_ERROR_REJECTED; + + if (!m_addonCapabilities.bSupportsLastPlayedPosition) + return PVR_ERROR_NOT_IMPLEMENTED; + + PVR_ERROR retVal(PVR_ERROR_UNKNOWN); + try + { + PVR_RECORDING tag; + WriteClientRecordingInfo(recording, tag); + + retVal = m_pStruct->SetRecordingLastPlayedPosition(tag, lastplayedposition); + + LogError(retVal, __FUNCTION__); + } + catch (exception &e) + { + LogException(e, __FUNCTION__); + } + + return retVal; +} + +int CPVRClient::GetRecordingLastPlayedPosition(const CPVRRecording &recording) +{ + int iReturn(-EINVAL); + if (!m_bReadyToUse) + return iReturn; + + if (!m_addonCapabilities.bSupportsLastPlayedPosition) + return iReturn; + + try + { + PVR_RECORDING tag; + WriteClientRecordingInfo(recording, tag); + + iReturn = m_pStruct->GetRecordingLastPlayedPosition(tag); + } + catch (exception &e) + { + LogException(e, __FUNCTION__); + } + + return iReturn; +} + +int CPVRClient::GetTimersAmount(void) +{ + int iReturn(-EINVAL); + if (!m_bReadyToUse) + return iReturn; + + if (!m_addonCapabilities.bSupportsTimers) + return iReturn; + + try { iReturn = m_pStruct->GetTimersAmount(); } + catch (exception &e) { LogException(e, __FUNCTION__); } + + return iReturn; +} + +PVR_ERROR CPVRClient::GetTimers(CPVRTimers *results) +{ + if (!m_bReadyToUse) + return PVR_ERROR_REJECTED; + + if (!m_addonCapabilities.bSupportsTimers) + return PVR_ERROR_NOT_IMPLEMENTED; + + PVR_ERROR retVal(PVR_ERROR_UNKNOWN); + try + { + ADDON_HANDLE_STRUCT handle; + handle.callerAddress = this; + handle.dataAddress = (CPVRTimers*) results; + retVal = m_pStruct->GetTimers(&handle); + + LogError(retVal, __FUNCTION__); + } + catch (exception &e) + { + LogException(e, __FUNCTION__); + } + + return retVal; +} + +PVR_ERROR CPVRClient::AddTimer(const CPVRTimerInfoTag &timer) +{ + if (!m_bReadyToUse) + return PVR_ERROR_REJECTED; + + if (!m_addonCapabilities.bSupportsTimers) + return PVR_ERROR_NOT_IMPLEMENTED; + + PVR_ERROR retVal(PVR_ERROR_UNKNOWN); + try + { + PVR_TIMER tag; + WriteClientTimerInfo(timer, tag); + + retVal = m_pStruct->AddTimer(tag); + + LogError(retVal, __FUNCTION__); + } + catch (exception &e) + { + LogException(e, __FUNCTION__); + } + + return retVal; +} + +PVR_ERROR CPVRClient::DeleteTimer(const CPVRTimerInfoTag &timer, bool bForce /* = false */) +{ + if (!m_bReadyToUse) + return PVR_ERROR_REJECTED; + + if (!m_addonCapabilities.bSupportsTimers) + return PVR_ERROR_NOT_IMPLEMENTED; + + PVR_ERROR retVal(PVR_ERROR_UNKNOWN); + try + { + PVR_TIMER tag; + WriteClientTimerInfo(timer, tag); + + retVal = m_pStruct->DeleteTimer(tag, bForce); + + LogError(retVal, __FUNCTION__); + } + catch (exception &e) + { + LogException(e, __FUNCTION__); + } + + return retVal; +} + +PVR_ERROR CPVRClient::RenameTimer(const CPVRTimerInfoTag &timer, const CStdString &strNewName) +{ + if (!m_bReadyToUse) + return PVR_ERROR_REJECTED; + + if (!m_addonCapabilities.bSupportsTimers) + return PVR_ERROR_NOT_IMPLEMENTED; + + PVR_ERROR retVal(PVR_ERROR_UNKNOWN); + try + { + PVR_TIMER tag; + WriteClientTimerInfo(timer, tag); + + retVal = m_pStruct->UpdateTimer(tag); + + LogError(retVal, __FUNCTION__); + } + catch (exception &e) + { + LogException(e, __FUNCTION__); + } + + return retVal; +} + +PVR_ERROR CPVRClient::UpdateTimer(const CPVRTimerInfoTag &timer) +{ + if (!m_bReadyToUse) + return PVR_ERROR_REJECTED; + + if (!m_addonCapabilities.bSupportsTimers) + return PVR_ERROR_NOT_IMPLEMENTED; + + PVR_ERROR retVal(PVR_ERROR_UNKNOWN); + try + { + PVR_TIMER tag; + WriteClientTimerInfo(timer, tag); + + retVal = m_pStruct->UpdateTimer(tag); + + LogError(retVal, __FUNCTION__); + } + catch (exception &e) + { + LogException(e, __FUNCTION__); + } + + return retVal; +} + +int CPVRClient::ReadStream(void* lpBuf, int64_t uiBufSize) +{ + if (IsPlayingLiveStream()) + { + try { return m_pStruct->ReadLiveStream((unsigned char *)lpBuf, (int)uiBufSize); } + catch (exception &e) { LogException(e, "ReadLiveStream()"); } + } + else if (IsPlayingRecording()) + { + try { return m_pStruct->ReadRecordedStream((unsigned char *)lpBuf, (int)uiBufSize); } + catch (exception &e) { LogException(e, "ReadRecordedStream()"); } + } + return -EINVAL; +} + +int64_t CPVRClient::SeekStream(int64_t iFilePosition, int iWhence/* = SEEK_SET*/) +{ + if (IsPlayingLiveStream()) + { + try { return m_pStruct->SeekLiveStream(iFilePosition, iWhence); } + catch (exception &e) { LogException(e, "SeekLiveStream()"); } + } + else if (IsPlayingRecording()) + { + try { return m_pStruct->SeekRecordedStream(iFilePosition, iWhence); } + catch (exception &e) { LogException(e, "SeekRecordedStream()"); } + } + return -EINVAL; +} + +int64_t CPVRClient::GetStreamPosition(void) +{ + if (IsPlayingLiveStream()) + { + try { return m_pStruct->PositionLiveStream(); } + catch (exception &e) { LogException(e, "PositionLiveStream()"); } + } + else if (IsPlayingRecording()) + { + try { return m_pStruct->PositionRecordedStream(); } + catch (exception &e) { LogException(e, "PositionRecordedStream()"); } + } + return -EINVAL; +} + +int64_t CPVRClient::GetStreamLength(void) +{ + if (IsPlayingLiveStream()) + { + try { return m_pStruct->LengthLiveStream(); } + catch (exception &e) { LogException(e, "LengthLiveStream()"); } + } + else if (IsPlayingRecording()) + { + try { return m_pStruct->LengthRecordedStream(); } + catch (exception &e) { LogException(e, "PositionRecordedStream()"); } + } + return -EINVAL; +} + +int CPVRClient::GetCurrentClientChannel(void) +{ + if (IsPlayingLiveStream()) + { + try { return m_pStruct->GetCurrentClientChannel(); } + catch (exception &e) { LogException(e, __FUNCTION__); } + } + return -EINVAL; +} + +bool CPVRClient::SwitchChannel(const CPVRChannel &channel) +{ + bool bSwitched(false); + + if (IsPlayingLiveStream() && CanPlayChannel(channel)) + { + PVR_CHANNEL tag; + WriteClientChannelInfo(channel, tag); + try { bSwitched = m_pStruct->SwitchChannel(tag); } + catch (exception &e) { LogException(e, __FUNCTION__); } + } + + if (bSwitched) + { + CPVRChannelPtr currentChannel = g_PVRChannelGroups->GetByUniqueID(channel.UniqueID(), channel.ClientID()); + CSingleLock lock(m_critSection); + ResetQualityData(m_qualityInfo); + m_playingChannel = currentChannel; + } + + return bSwitched; +} + +bool CPVRClient::SignalQuality(PVR_SIGNAL_STATUS &qualityinfo) +{ + if (IsPlayingLiveStream()) + { + try + { + PVR_ERROR retVal = m_pStruct->SignalStatus(qualityinfo); + return LogError(retVal, __FUNCTION__); + } + catch (exception &e) + { + LogException(e, __FUNCTION__); + } + } + return false; +} + +CStdString CPVRClient::GetLiveStreamURL(const CPVRChannel &channel) +{ + CStdString strReturn; + + if (!m_bReadyToUse || !CanPlayChannel(channel)) + return strReturn; + + try + { + PVR_CHANNEL tag; + WriteClientChannelInfo(channel, tag); + strReturn = m_pStruct->GetLiveStreamURL(tag); + } + catch (exception &e) + { + LogException(e, __FUNCTION__); + } + + return strReturn; +} + +PVR_ERROR CPVRClient::GetStreamProperties(PVR_STREAM_PROPERTIES *props) +{ + if (!IsPlaying()) + return PVR_ERROR_REJECTED; + + try { return m_pStruct->GetStreamProperties(props); } + catch (exception &e) { LogException(e, __FUNCTION__); } + + return PVR_ERROR_UNKNOWN; +} + +void CPVRClient::DemuxReset(void) +{ + if (m_bReadyToUse && m_addonCapabilities.bHandlesDemuxing) + { + try { m_pStruct->DemuxReset(); } + catch (exception &e) { LogException(e, __FUNCTION__); } + } +} + +void CPVRClient::DemuxAbort(void) +{ + if (m_bReadyToUse && m_addonCapabilities.bHandlesDemuxing) + { + try { m_pStruct->DemuxAbort(); } + catch (exception &e) { LogException(e, __FUNCTION__); } + } +} + +void CPVRClient::DemuxFlush(void) +{ + if (m_bReadyToUse && m_addonCapabilities.bHandlesDemuxing) + { + try { m_pStruct->DemuxFlush(); } + catch (exception &e) { LogException(e, __FUNCTION__); } + } +} + +DemuxPacket* CPVRClient::DemuxRead(void) +{ + if (m_bReadyToUse && m_addonCapabilities.bHandlesDemuxing) + { + try { return m_pStruct->DemuxRead(); } + catch (exception &e) { LogException(e, __FUNCTION__); } + } + return NULL; +} + +bool CPVRClient::HaveMenuHooks(void) const +{ + return m_bReadyToUse ? m_menuhooks.size() > 0 : false; +} + +PVR_MENUHOOKS *CPVRClient::GetMenuHooks(void) +{ + return &m_menuhooks; +} + +const char *CPVRClient::ToString(const PVR_ERROR error) +{ + switch (error) + { + case PVR_ERROR_NO_ERROR: + return "no error"; + case PVR_ERROR_NOT_IMPLEMENTED: + return "not implemented"; + case PVR_ERROR_SERVER_ERROR: + return "server error"; + case PVR_ERROR_SERVER_TIMEOUT: + return "server timeout"; + case PVR_ERROR_RECORDING_RUNNING: + return "recording already running"; + case PVR_ERROR_ALREADY_PRESENT: + return "already present"; + case PVR_ERROR_REJECTED: + return "rejected by the backend"; + case PVR_ERROR_INVALID_PARAMETERS: + return "invalid parameters for this method"; + case PVR_ERROR_FAILED: + return "the command failed"; + case PVR_ERROR_UNKNOWN: + default: + return "unknown error"; + } +} + +bool CPVRClient::LogError(const PVR_ERROR error, const char *strMethod) const +{ + if (error != PVR_ERROR_NO_ERROR) + { + CLog::Log(LOGERROR, "PVR - %s - addon '%s' returned an error: %s", + strMethod, GetFriendlyName().c_str(), ToString(error)); + return false; + } + return true; +} + +void CPVRClient::LogException(const exception &e, const char *strFunctionName) const +{ + CLog::Log(LOGERROR, "PVR - exception '%s' caught while trying to call '%s' on add-on '%s'. Please contact the developer of this add-on: %s", e.what(), strFunctionName, GetFriendlyName().c_str(), Author().c_str()); +} + +bool CPVRClient::CanPlayChannel(const CPVRChannel &channel) const +{ + return (m_bReadyToUse && + ((m_addonCapabilities.bSupportsTV && !channel.IsRadio()) || + (m_addonCapabilities.bSupportsRadio && channel.IsRadio()))); +} + +bool CPVRClient::SupportsChannelGroups(void) const +{ + return m_addonCapabilities.bSupportsChannelGroups; +} + +bool CPVRClient::SupportsChannelScan(void) const +{ + return m_addonCapabilities.bSupportsChannelScan; +} + +bool CPVRClient::SupportsEPG(void) const +{ + return m_addonCapabilities.bSupportsEPG; +} + +bool CPVRClient::SupportsLastPlayedPosition(void) const +{ + return m_addonCapabilities.bSupportsLastPlayedPosition; +} + +bool CPVRClient::SupportsRadio(void) const +{ + return m_addonCapabilities.bSupportsRadio; +} + +bool CPVRClient::SupportsRecordings(void) const +{ + return m_addonCapabilities.bSupportsRecordings; +} + +bool CPVRClient::SupportsRecordingFolders(void) const +{ + return m_addonCapabilities.bSupportsRecordingFolders; +} + +bool CPVRClient::SupportsRecordingPlayCount(void) const +{ + return m_addonCapabilities.bSupportsRecordingPlayCount; +} + +bool CPVRClient::SupportsTimers(void) const +{ + return m_addonCapabilities.bSupportsTimers; +} + +bool CPVRClient::SupportsTV(void) const +{ + return m_addonCapabilities.bSupportsTV; +} + +bool CPVRClient::HandlesDemuxing(void) const +{ + return m_addonCapabilities.bHandlesDemuxing; +} + +bool CPVRClient::HandlesInputStream(void) const +{ + return m_addonCapabilities.bHandlesInputStream; +} + +bool CPVRClient::IsPlayingLiveStream(void) const +{ + CSingleLock lock(m_critSection); + return m_bReadyToUse && m_bIsPlayingTV; +} + +bool CPVRClient::IsPlayingLiveTV(void) const +{ + CSingleLock lock(m_critSection); + return m_bReadyToUse && m_bIsPlayingTV && !m_playingChannel->IsRadio(); +} + +bool CPVRClient::IsPlayingLiveRadio(void) const +{ + CSingleLock lock(m_critSection); + return m_bReadyToUse && m_bIsPlayingTV && m_playingChannel->IsRadio(); +} + +bool CPVRClient::IsPlayingEncryptedChannel(void) const +{ + CSingleLock lock(m_critSection); + return m_bReadyToUse && m_bIsPlayingTV && m_playingChannel->IsEncrypted(); +} + +bool CPVRClient::IsPlayingRecording(void) const +{ + CSingleLock lock(m_critSection); + return m_bReadyToUse && m_bIsPlayingRecording; +} + +bool CPVRClient::IsPlaying(void) const +{ + return IsPlayingLiveStream() || + IsPlayingRecording(); +} + +bool CPVRClient::GetPlayingChannel(CPVRChannelPtr &channel) const +{ + CSingleLock lock(m_critSection); + if (m_bReadyToUse && m_bIsPlayingTV) + { + channel = m_playingChannel; + return true; + } + return false; +} + +bool CPVRClient::GetPlayingRecording(CPVRRecording &recording) const +{ + CSingleLock lock(m_critSection); + if (m_bReadyToUse && m_bIsPlayingRecording) + { + recording = m_playingRecording; + return true; + } + return false; +} + +bool CPVRClient::OpenStream(const CPVRChannel &channel, bool bIsSwitchingChannel) +{ + bool bReturn(false); + CloseStream(); + + if(!CanPlayChannel(channel)) + { + CLog::Log(LOGDEBUG, "add-on '%s' can not play channel '%s'", GetFriendlyName().c_str(), channel.ChannelName().c_str()); + } + else if (!channel.StreamURL().IsEmpty()) + { + CLog::Log(LOGDEBUG, "opening live stream on url '%s'", channel.StreamURL().c_str()); + bReturn = true; + + // the Njoy N7 sometimes doesn't switch channels, but opens a stream to the previous channel + // when not waiting for a short period. + // added in 1.1.0 + AddonVersion checkVersion("1.1.0"); + if (m_apiVersion >= checkVersion) + { + unsigned int iWaitTimeMs = m_pStruct->GetChannelSwitchDelay(); + if (iWaitTimeMs > 0) + XbmcThreads::ThreadSleep(iWaitTimeMs); + } + } + else + { + CLog::Log(LOGDEBUG, "opening live stream for channel '%s'", channel.ChannelName().c_str()); + PVR_CHANNEL tag; + WriteClientChannelInfo(channel, tag); + + try { bReturn = m_pStruct->OpenLiveStream(tag); } + catch (exception &e) { LogException(e, __FUNCTION__); } + } + + if (bReturn) + { + CPVRChannelPtr currentChannel = g_PVRChannelGroups->GetByUniqueID(channel.UniqueID(), channel.ClientID()); + CSingleLock lock(m_critSection); + m_playingChannel = currentChannel; + m_bIsPlayingTV = true; + m_bIsPlayingRecording = false; + } + + return bReturn; +} + +bool CPVRClient::OpenStream(const CPVRRecording &recording) +{ + bool bReturn(false); + CloseStream(); + + if (m_bReadyToUse && m_addonCapabilities.bSupportsRecordings) + { + PVR_RECORDING tag; + WriteClientRecordingInfo(recording, tag); + + try { bReturn = m_pStruct->OpenRecordedStream(tag); } + catch (exception &e) { LogException(e, __FUNCTION__); } + } + + if (bReturn) + { + CSingleLock lock(m_critSection); + m_playingRecording = recording; + m_bIsPlayingTV = false; + m_bIsPlayingRecording = true; + } + + return bReturn; +} + +void CPVRClient::CloseStream(void) +{ + if (IsPlayingLiveStream()) + { + try { m_pStruct->CloseLiveStream(); } + catch (exception &e) { LogException(e, "CloseLiveStream()"); } + + CSingleLock lock(m_critSection); + m_bIsPlayingTV = false; + } + else if (IsPlayingRecording()) + { + try { return m_pStruct->CloseRecordedStream(); } + catch (exception &e) { LogException(e, "CloseRecordedStream()"); } + + CSingleLock lock(m_critSection); + m_bIsPlayingRecording = false; + } +} + +void CPVRClient::ResetQualityData(PVR_SIGNAL_STATUS &qualityInfo) +{ + memset(&qualityInfo, 0, sizeof(qualityInfo)); + if (g_guiSettings.GetBool("pvrplayback.signalquality")) + { + strncpy(qualityInfo.strAdapterName, g_localizeStrings.Get(13205).c_str(), 1024); + strncpy(qualityInfo.strAdapterStatus, g_localizeStrings.Get(13205).c_str(), 1024); + } + else + { + strncpy(qualityInfo.strAdapterName, g_localizeStrings.Get(13106).c_str(), 1024); + strncpy(qualityInfo.strAdapterStatus, g_localizeStrings.Get(13106).c_str(), 1024); + } +} + +void CPVRClient::GetQualityData(PVR_SIGNAL_STATUS *status) const +{ + CSingleLock lock(m_critSection); + *status = m_qualityInfo; +} + +int CPVRClient::GetSignalLevel(void) const +{ + CSingleLock lock(m_critSection); + return (int) ((float) m_qualityInfo.iSignal / 0xFFFF * 100); +} + +int CPVRClient::GetSNR(void) const +{ + CSingleLock lock(m_critSection); + return (int) ((float) m_qualityInfo.iSNR / 0xFFFF * 100); +} + +void CPVRClient::UpdateCharInfoSignalStatus(void) +{ + PVR_SIGNAL_STATUS qualityInfo; + ResetQualityData(qualityInfo); + + if (g_guiSettings.GetBool("pvrplayback.signalquality")) + SignalQuality(qualityInfo); + + CSingleLock lock(m_critSection); + m_qualityInfo = qualityInfo; +} diff --git a/xbmc/pvr/addons/PVRClient.h b/xbmc/pvr/addons/PVRClient.h new file mode 100644 index 0000000000..e2ee8fb489 --- /dev/null +++ b/xbmc/pvr/addons/PVRClient.h @@ -0,0 +1,563 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "addons/Addon.h" +#include "addons/AddonDll.h" +#include "addons/DllPVRClient.h" +#include "pvr/channels/PVRChannel.h" +#include "pvr/recordings/PVRRecordings.h" + +namespace EPG +{ + class CEpg; +} + +namespace PVR +{ + class CPVRChannelGroup; + class CPVRChannelGroupInternal; + class CPVRChannelGroups; + class CPVRTimers; + class CPVRTimerInfoTag; + class CPVRRecordings; + class CPVREpgContainer; + class CPVRClient; + + typedef std::vector<PVR_MENUHOOK> PVR_MENUHOOKS; + typedef boost::shared_ptr<CPVRClient> PVR_CLIENT; + #define PVR_INVALID_CLIENT_ID (-2) + #define PVR_VIRTUAL_CLIENT_ID (-1) + + /*! + * Interface from XBMC to a PVR add-on. + * + * Also translates XBMC's C++ structures to the addon's C structures. + */ + class CPVRClient : public ADDON::CAddonDll<DllPVRClient, PVRClient, PVR_PROPERTIES> + { + public: + CPVRClient(const ADDON::AddonProps& props); + CPVRClient(const cp_extension_t *ext); + ~CPVRClient(void); + + /** @name PVR add-on methods */ + //@{ + + /*! + * @brief Initialise the instance of this add-on. + * @param iClientId The ID of this add-on. + */ + bool Create(int iClientId); + + /*! + * @return True when the dll for this add-on was loaded, false otherwise (e.g. unresolved symbols) + */ + bool DllLoaded(void) const; + + /*! + * @brief Destroy the instance of this add-on. + */ + void Destroy(void); + + /*! + * @brief Destroy and recreate this add-on. + */ + void ReCreate(void); + + /*! + * @return True if this instance is initialised, false otherwise. + */ + bool ReadyToUse(void) const; + + /*! + * @return The ID of this instance. + */ + int GetID(void) const; + + //@} + /** @name PVR server methods */ + //@{ + + /*! + * @brief Query this add-on's capabilities. + * @return pCapabilities The add-on's capabilities. + */ + PVR_ADDON_CAPABILITIES GetAddonCapabilities(void) const; + + /*! + * @brief Get the stream properties of the stream that's currently being read. + * @param pProperties The properties. + * @return PVR_ERROR_NO_ERROR if the properties have been fetched successfully. + */ + PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES *pProperties); + + /*! + * @return The name reported by the backend. + */ + CStdString GetBackendName(void) const; + + /*! + * @return The version string reported by the backend. + */ + CStdString GetBackendVersion(void) const; + + /*! + * @return The connection string reported by the backend. + */ + CStdString GetConnectionString(void) const; + + /*! + * @return A friendly name for this add-on that can be used in log messages. + */ + CStdString GetFriendlyName(void) const; + + /*! + * @brief Get the disk space reported by the server. + * @param iTotal The total disk space. + * @param iUsed The used disk space. + * @return PVR_ERROR_NO_ERROR if the drive space has been fetched successfully. + */ + PVR_ERROR GetDriveSpace(long long *iTotal, long long *iUsed); + + /*! + * @brief Start a channel scan on the server. + * @return PVR_ERROR_NO_ERROR if the channel scan has been started successfully. + */ + PVR_ERROR StartChannelScan(void); + + /*! + * @return True if this add-on has menu hooks, false otherwise. + */ + bool HaveMenuHooks(void) const; + + /*! + * @return The menu hooks for this add-on. + */ + PVR_MENUHOOKS *GetMenuHooks(void); + + /*! + * @brief Call one of the menu hooks of this client. + * @param hook The hook to call. + */ + void CallMenuHook(const PVR_MENUHOOK &hook); + + //@} + /** @name PVR EPG methods */ + //@{ + + /*! + * @brief Request an EPG table for a channel from the client. + * @param channel The channel to get the EPG table for. + * @param epg The table to write the data to. + * @param start The start time to use. + * @param end The end time to use. + * @param bSaveInDb If true, tell the callback method to save any new entry in the database or not. see CAddonCallbacksPVR::PVRTransferEpgEntry() + * @return PVR_ERROR_NO_ERROR if the table has been fetched successfully. + */ + PVR_ERROR GetEPGForChannel(const CPVRChannel &channel, EPG::CEpg *epg, time_t start = 0, time_t end = 0, bool bSaveInDb = false); + + //@} + /** @name PVR channel group methods */ + //@{ + + /*! + * @return The total amount of channel groups on the server or -1 on error. + */ + int GetChannelGroupsAmount(void); + + /*! + * @brief Request the list of all channel groups from the backend. + * @param groups The groups container to get the groups for. + * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully. + */ + PVR_ERROR GetChannelGroups(CPVRChannelGroups *groups); + + /*! + * @brief Request the list of all group members from the backend. + * @param groups The group to get the members for. + * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully. + */ + PVR_ERROR GetChannelGroupMembers(CPVRChannelGroup *group); + + //@} + /** @name PVR channel methods */ + //@{ + + /*! + * @return The total amount of channels on the server or -1 on error. + */ + int GetChannelsAmount(void); + + /*! + * @brief Request the list of all channels from the backend. + * @param channels The channel group to add the channels to. + * @param bRadio True to get the radio channels, false to get the TV channels. + * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully. + */ + PVR_ERROR GetChannels(CPVRChannelGroup &channels, bool bRadio); + + //@} + /** @name PVR recording methods */ + //@{ + + /*! + * @return The total amount of channels on the server or -1 on error. + */ + int GetRecordingsAmount(void); + + /*! + * @brief Request the list of all recordings from the backend. + * @param results The container to add the recordings to. + * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully. + */ + PVR_ERROR GetRecordings(CPVRRecordings *results); + + /*! + * @brief Delete a recording on the backend. + * @param recording The recording to delete. + * @return PVR_ERROR_NO_ERROR if the recording has been deleted successfully. + */ + PVR_ERROR DeleteRecording(const CPVRRecording &recording); + + /*! + * @brief Rename a recording on the backend. + * @param recording The recording to rename. + * @return PVR_ERROR_NO_ERROR if the recording has been renamed successfully. + */ + PVR_ERROR RenameRecording(const CPVRRecording &recording); + + /*! + * @brief Set the play count of a recording on the backend. + * @param recording The recording to set the play count. + * @param count Play count. + * @return PVR_ERROR_NO_ERROR if the recording's play count has been set successfully. + */ + PVR_ERROR SetRecordingPlayCount(const CPVRRecording &recording, int count); + + /*! + * @brief Set the last watched position of a recording on the backend. + * @param recording The recording. + * @param position The last watched position in seconds + * @return PVR_ERROR_NO_ERROR if the position has been stored successfully. + */ + PVR_ERROR SetRecordingLastPlayedPosition(const CPVRRecording &recording, int lastplayedposition); + + /*! + * @brief Retrieve the last watched position of a recording on the backend. + * @param recording The recording. + * @return The last watched position in seconds or -1 on error + */ + int GetRecordingLastPlayedPosition(const CPVRRecording &recording); + + //@} + /** @name PVR timer methods */ + //@{ + + /*! + * @return The total amount of timers on the backend or -1 on error. + */ + int GetTimersAmount(void); + + /*! + * @brief Request the list of all timers from the backend. + * @param results The container to store the result in. + * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully. + */ + PVR_ERROR GetTimers(CPVRTimers *results); + + /*! + * @brief Add a timer on the backend. + * @param timer The timer to add. + * @return PVR_ERROR_NO_ERROR if the timer has been added successfully. + */ + PVR_ERROR AddTimer(const CPVRTimerInfoTag &timer); + + /*! + * @brief Delete a timer on the backend. + * @param timer The timer to delete. + * @param bForce Set to true to delete a timer that is currently recording a program. + * @return PVR_ERROR_NO_ERROR if the timer has been deleted successfully. + */ + PVR_ERROR DeleteTimer(const CPVRTimerInfoTag &timer, bool bForce = false); + + /*! + * @brief Rename a timer on the server. + * @param timer The timer to rename. + * @param strNewName The new name of the timer. + * @return PVR_ERROR_NO_ERROR if the timer has been renamed successfully. + */ + PVR_ERROR RenameTimer(const CPVRTimerInfoTag &timer, const CStdString &strNewName); + + /*! + * @brief Update the timer information on the server. + * @param timer The timer to update. + * @return PVR_ERROR_NO_ERROR if the timer has been updated successfully. + */ + PVR_ERROR UpdateTimer(const CPVRTimerInfoTag &timer); + + //@} + /** @name PVR live stream methods */ + //@{ + + /*! + * @brief Open a live stream on the server. + * @param channel The channel to stream. + * @param bIsSwitchingChannel True when switching channels, false otherwise. + * @return True if the stream opened successfully, false otherwise. + */ + bool OpenStream(const CPVRChannel &channel, bool bIsSwitchingChannel); + + /*! + * @brief Close an open live stream. + */ + void CloseStream(void); + + /*! + * @brief Read from an open live stream. + * @param lpBuf The buffer to store the data in. + * @param uiBufSize The amount of bytes to read. + * @return The amount of bytes that were actually read from the stream. + */ + int ReadStream(void* lpBuf, int64_t uiBufSize); + + /*! + * @brief Seek in a live stream on a backend that supports timeshifting. + * @param iFilePosition The position to seek to. + * @param iWhence ? + * @return The new position. + */ + int64_t SeekStream(int64_t iFilePosition, int iWhence = SEEK_SET); + + /*! + * @return The position in the stream that's currently being read. + */ + int64_t GetStreamPosition(void); + + /*! + * @return The total length of the stream that's currently being read. + */ + int64_t GetStreamLength(void); + + /*! + * @return The channel number on the server of the live stream that's currently being read. + */ + int GetCurrentClientChannel(void); + + /*! + * @brief Switch to another channel. Only to be called when a live stream has already been opened. + * @param channel The channel to switch to. + * @return True if the switch was successful, false otherwise. + */ + bool SwitchChannel(const CPVRChannel &channel); + + /*! + * @brief Get the signal quality of the stream that's currently open. + * @param qualityinfo The signal quality. + * @return True if the signal quality has been read successfully, false otherwise. + */ + bool SignalQuality(PVR_SIGNAL_STATUS &qualityinfo); + + /*! + * @brief Get the stream URL for a channel from the server. Used by the MediaPortal add-on. + * @param channel The channel to get the stream URL for. + * @return The requested URL. + */ + CStdString GetLiveStreamURL(const CPVRChannel &channel); + + //@} + /** @name PVR recording stream methods */ + //@{ + + /*! + * @brief Open a recording on the server. + * @param recording The recording to open. + * @return True if the stream has been opened succesfully, false otherwise. + */ + bool OpenStream(const CPVRRecording &recording); + + //@} + /** @name PVR demultiplexer methods */ + //@{ + + /*! + * @brief Reset the demultiplexer in the add-on. + */ + void DemuxReset(void); + + /*! + * @brief Abort the demultiplexer thread in the add-on. + */ + void DemuxAbort(void); + + /*! + * @brief Flush all data that's currently in the demultiplexer buffer in the add-on. + */ + void DemuxFlush(void); + + /*! + * @brief Read a packet from the demultiplexer. + * @return The packet. + */ + DemuxPacket *DemuxRead(void); + + //@} + + bool SupportsChannelGroups(void) const; + bool SupportsChannelScan(void) const; + bool SupportsEPG(void) const; + bool SupportsLastPlayedPosition(void) const; + bool SupportsRadio(void) const; + bool SupportsRecordings(void) const; + bool SupportsRecordingFolders(void) const; + bool SupportsRecordingPlayCount(void) const; + bool SupportsTimers(void) const; + bool SupportsTV(void) const; + bool HandlesDemuxing(void) const; + bool HandlesInputStream(void) const; + + bool IsPlayingLiveStream(void) const; + bool IsPlayingLiveTV(void) const; + bool IsPlayingLiveRadio(void) const; + bool IsPlayingEncryptedChannel(void) const; + bool IsPlayingRecording(void) const; + bool IsPlaying(void) const; + bool GetPlayingChannel(CPVRChannelPtr &channel) const; + bool GetPlayingRecording(CPVRRecording &recording) const; + + /*! @name Signal status methods */ + //@{ + + /*! + * @brief Get the quality data for the live stream that is currently playing. + * @param status A copy of the quality data. + */ + void GetQualityData(PVR_SIGNAL_STATUS *status) const; + + /*! + * @return The current signal quality level. + */ + int GetSignalLevel(void) const; + + /*! + * @return The current signal/noise ratio. + */ + int GetSNR(void) const; + + /*! + * @brief Update the signal status for the tv stream that's currently being read. + */ + void UpdateCharInfoSignalStatus(void); + + //@} + + static const char *ToString(const PVR_ERROR error); + + private: + /*! + * @brief Checks whether the provided API version is compatible with XBMC + * @param minVersion The add-on's XBMC_PVR_MIN_API_VERSION version + * @param version The add-on's XBMC_PVR_API_VERSION version + * @return True when compatible, false otherwise + */ + static bool IsCompatibleAPIVersion(const ADDON::AddonVersion &minVersion, const ADDON::AddonVersion &version); + + /*! + * @brief Reset the signal quality data to the initial values. + */ + void ResetQualityData(PVR_SIGNAL_STATUS &qualityInfo); + + /*! + * @brief Resets all class members to their defaults. Called by the constructors. + */ + void ResetProperties(int iClientId = PVR_INVALID_CLIENT_ID); + + bool GetAddonProperties(void); + + /*! + * @brief Copy over group info from xbmcGroup to addonGroup. + * @param xbmcGroup The group on XBMC's side. + * @param addonGroup The group on the addon's side. + */ + static void WriteClientGroupInfo(const CPVRChannelGroup &xbmcGroup, PVR_CHANNEL_GROUP &addonGroup); + + /*! + * @brief Copy over recording info from xbmcRecording to addonRecording. + * @param xbmcRecording The recording on XBMC's side. + * @param addonRecording The recording on the addon's side. + */ + static void WriteClientRecordingInfo(const CPVRRecording &xbmcRecording, PVR_RECORDING &addonRecording); + + /*! + * @brief Copy over timer info from xbmcTimer to addonTimer. + * @param xbmcTimer The timer on XBMC's side. + * @param addonTimer The timer on the addon's side. + */ + static void WriteClientTimerInfo(const CPVRTimerInfoTag &xbmcTimer, PVR_TIMER &addonTimer); + + /*! + * @brief Copy over channel info from xbmcChannel to addonClient. + * @param xbmcChannel The channel on XBMC's side. + * @param addonChannel The channel on the addon's side. + */ + static void WriteClientChannelInfo(const CPVRChannel &xbmcChannel, PVR_CHANNEL &addonChannel); + + /*! + * @brief Whether a channel can be played by this add-on + * @param channel The channel to check. + * @return True when it can be played, false otherwise. + */ + bool CanPlayChannel(const CPVRChannel &channel) const; + + bool LogError(const PVR_ERROR error, const char *strMethod) const; + void LogException(const std::exception &e, const char *strFunctionName) const; + + bool m_bReadyToUse; /*!< true if this add-on is connected to the backend, false otherwise */ + CStdString m_strHostName; /*!< the host name */ + PVR_MENUHOOKS m_menuhooks; /*!< the menu hooks for this add-on */ + int m_iClientId; /*!< database ID of the client */ + + /* cached data */ + CStdString m_strBackendName; /*!< the cached backend version */ + bool m_bGotBackendName; /*!< true if the backend name has already been fetched */ + CStdString m_strBackendVersion; /*!< the cached backend version */ + bool m_bGotBackendVersion; /*!< true if the backend version has already been fetched */ + CStdString m_strConnectionString; /*!< the cached connection string */ + bool m_bGotConnectionString; /*!< true if the connection string has already been fetched */ + CStdString m_strFriendlyName; /*!< the cached friendly name */ + bool m_bGotFriendlyName; /*!< true if the friendly name has already been fetched */ + PVR_ADDON_CAPABILITIES m_addonCapabilities; /*!< the cached add-on capabilities */ + bool m_bGotAddonCapabilities; /*!< true if the add-on capabilities have already been fetched */ + PVR_SIGNAL_STATUS m_qualityInfo; /*!< stream quality information */ + + /* stored strings to make sure const char* members in PVR_PROPERTIES stay valid */ + std::string m_strUserPath; /*!< @brief translated path to the user profile */ + std::string m_strClientPath; /*!< @brief translated path to this add-on */ + + CCriticalSection m_critSection; + + bool m_bIsPlayingTV; + CPVRChannelPtr m_playingChannel; + bool m_bIsPlayingRecording; + CPVRRecording m_playingRecording; + ADDON::AddonVersion m_apiVersion; + }; +} diff --git a/xbmc/pvr/addons/PVRClients.cpp b/xbmc/pvr/addons/PVRClients.cpp new file mode 100644 index 0000000000..33f093a68d --- /dev/null +++ b/xbmc/pvr/addons/PVRClients.cpp @@ -0,0 +1,1303 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "PVRClients.h" + +#include "Application.h" +#include "ApplicationMessenger.h" +#include "settings/GUISettings.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogSelect.h" +#include "pvr/PVRManager.h" +#include "pvr/PVRDatabase.h" +#include "guilib/GUIWindowManager.h" +#include "settings/Settings.h" +#include "pvr/channels/PVRChannelGroups.h" +#include "pvr/channels/PVRChannelGroupInternal.h" +#include "pvr/recordings/PVRRecordings.h" +#include "pvr/timers/PVRTimers.h" + +#ifdef HAS_VIDEO_PLAYBACK +#include "cores/VideoRenderers/RenderManager.h" +#endif + +using namespace std; +using namespace ADDON; +using namespace PVR; +using namespace EPG; + +CPVRClients::CPVRClients(void) : + CThread("PVR add-on updater"), + m_bChannelScanRunning(false), + m_bIsSwitchingChannels(false), + m_bIsValidChannelSettings(false), + m_playingClientId(-EINVAL), + m_bIsPlayingLiveTV(false), + m_bIsPlayingRecording(false), + m_scanStart(0), + m_bNoAddonWarningDisplayed(false) +{ +} + +CPVRClients::~CPVRClients(void) +{ + Unload(); +} + +void CPVRClients::Start(void) +{ + Stop(); + + m_addonDb.Open(); + Create(); + SetPriority(-1); +} + +void CPVRClients::Stop(void) +{ + StopThread(); + m_addonDb.Close(); +} + +bool CPVRClients::IsConnectedClient(int iClientId) const +{ + PVR_CLIENT client; + return GetConnectedClient(iClientId, client); +} + +bool CPVRClients::IsConnectedClient(const AddonPtr addon) +{ + for (PVR_CLIENTMAP_CITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++) + if (itr->second->ID() == addon->ID()) + return itr->second->ReadyToUse(); + return false; +} + +int CPVRClients::GetClientId(const AddonPtr client) const +{ + CSingleLock lock(m_critSection); + + for (PVR_CLIENTMAP_CITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++) + if (itr->second->ID() == client->ID()) + return itr->first; + + return -1; +} + +bool CPVRClients::GetClient(int iClientId, PVR_CLIENT &addon) const +{ + bool bReturn(false); + if (iClientId <= PVR_INVALID_CLIENT_ID || iClientId == PVR_VIRTUAL_CLIENT_ID) + return bReturn; + + CSingleLock lock(m_critSection); + + PVR_CLIENTMAP_CITR itr = m_clientMap.find(iClientId); + if (itr != m_clientMap.end()) + { + addon = itr->second; + bReturn = true; + } + + return bReturn; +} + +bool CPVRClients::GetConnectedClient(int iClientId, PVR_CLIENT &addon) const +{ + if (GetClient(iClientId, addon)) + return addon->ReadyToUse(); + return false; +} + +bool CPVRClients::RequestRestart(AddonPtr addon, bool bDataChanged) +{ + return StopClient(addon, true); +} + +bool CPVRClients::RequestRemoval(AddonPtr addon) +{ + return StopClient(addon, false); +} + +void CPVRClients::Unload(void) +{ + Stop(); + + CSingleLock lock(m_critSection); + + /* destroy all clients */ + for (PVR_CLIENTMAP_ITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++) + itr->second->Destroy(); + + /* reset class properties */ + m_bChannelScanRunning = false; + m_bIsPlayingLiveTV = false; + m_bIsPlayingRecording = false; + m_strPlayingClientName = ""; + + m_clientMap.clear(); +} + +int CPVRClients::GetFirstConnectedClientID(void) +{ + CSingleLock lock(m_critSection); + + for (PVR_CLIENTMAP_ITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++) + if (itr->second->ReadyToUse()) + return itr->second->GetID(); + + return -1; +} + +int CPVRClients::EnabledClientAmount(void) const +{ + int iReturn(0); + CSingleLock lock(m_critSection); + + for (PVR_CLIENTMAP_CITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++) + if (itr->second->Enabled()) + ++iReturn; + + return iReturn; +} + +bool CPVRClients::HasEnabledClients(void) const +{ + return EnabledClientAmount() > 0; +} + +bool CPVRClients::StopClient(AddonPtr client, bool bRestart) +{ + int iId = GetClientId(client); + PVR_CLIENT mappedClient; + if (GetConnectedClient(iId, mappedClient)) + { + g_PVRManager.StopUpdateThreads(); + if (bRestart) + mappedClient->ReCreate(); + else + mappedClient->Destroy(); + g_PVRManager.StartUpdateThreads(); + + return true; + } + + return false; +} + +int CPVRClients::ConnectedClientAmount(void) const +{ + int iReturn(0); + CSingleLock lock(m_critSection); + + for (PVR_CLIENTMAP_CITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++) + if (itr->second->ReadyToUse()) + ++iReturn; + + return iReturn; +} + +bool CPVRClients::HasConnectedClients(void) const +{ + CSingleLock lock(m_critSection); + + for (PVR_CLIENTMAP_CITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++) + if (itr->second->ReadyToUse()) + return true; + + return false; +} + +bool CPVRClients::GetClientName(int iClientId, CStdString &strName) const +{ + bool bReturn(false); + PVR_CLIENT client; + if ((bReturn = GetConnectedClient(iClientId, client)) == true) + strName = client->GetFriendlyName(); + + return bReturn; +} + +int CPVRClients::GetConnectedClients(PVR_CLIENTMAP &clients) const +{ + int iReturn(0); + CSingleLock lock(m_critSection); + + for (PVR_CLIENTMAP_CITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++) + { + if (itr->second->ReadyToUse()) + { + clients.insert(std::make_pair(itr->second->GetID(), itr->second)); + ++iReturn; + } + } + + return iReturn; +} + +int CPVRClients::GetPlayingClientID(void) const +{ + CSingleLock lock(m_critSection); + + if (m_bIsPlayingLiveTV || m_bIsPlayingRecording) + return m_playingClientId; + return -EINVAL; +} + +const CStdString CPVRClients::GetPlayingClientName(void) const +{ + CSingleLock lock(m_critSection); + return m_strPlayingClientName; +} + +CStdString CPVRClients::GetStreamURL(const CPVRChannel &tag) +{ + CStdString strReturn; + PVR_CLIENT client; + if (GetConnectedClient(tag.ClientID(), client)) + strReturn = client->GetLiveStreamURL(tag); + else + CLog::Log(LOGERROR, "PVR - %s - cannot find client %d",__FUNCTION__, tag.ClientID()); + + return strReturn; +} + +bool CPVRClients::SwitchChannel(const CPVRChannel &channel) +{ + { + CSingleLock lock(m_critSection); + if (m_bIsSwitchingChannels) + { + CLog::Log(LOGDEBUG, "PVRClients - %s - can't switch to channel '%s'. waiting for the previous switch to complete", __FUNCTION__, channel.ChannelName().c_str()); + return false; + } + m_bIsSwitchingChannels = true; + } + + bool bSwitchSuccessful(false); + CPVRChannelPtr currentChannel; + if (// no channel is currently playing + !GetPlayingChannel(currentChannel) || + // different backend + currentChannel->ClientID() != channel.ClientID() || + // different type + currentChannel->IsRadio() != channel.IsRadio() || + // stream URL should always be opened as a new file + !channel.StreamURL().IsEmpty() || !currentChannel->StreamURL().IsEmpty()) + { + CloseStream(); + if (channel.StreamURL().IsEmpty()) + { + bSwitchSuccessful = OpenStream(channel, true); + } + else + { + CFileItem m_currentFile(channel); + CApplicationMessenger::Get().PlayFile(m_currentFile, false); + bSwitchSuccessful = true; + } + } + // same channel + else if (currentChannel.get() && *currentChannel == channel) + { + bSwitchSuccessful = true; + } + else + { + PVR_CLIENT client; + if (GetConnectedClient(channel.ClientID(), client)) + bSwitchSuccessful = client->SwitchChannel(channel); + } + + { + CSingleLock lock(m_critSection); + m_bIsSwitchingChannels = false; + if (bSwitchSuccessful) + m_bIsValidChannelSettings = false; + } + + if (!bSwitchSuccessful) + CLog::Log(LOGERROR, "PVR - %s - cannot switch to channel '%s' on client '%d'",__FUNCTION__, channel.ChannelName().c_str(), channel.ClientID()); + + return bSwitchSuccessful; +} + +bool CPVRClients::GetPlayingChannel(CPVRChannelPtr &channel) const +{ + PVR_CLIENT client; + if (GetPlayingClient(client)) + return client->GetPlayingChannel(channel); + return false; +} + +bool CPVRClients::GetPlayingRecording(CPVRRecording &recording) const +{ + PVR_CLIENT client; + if (GetPlayingClient(client)) + return client->GetPlayingRecording(recording); + return false; +} + +bool CPVRClients::HasTimerSupport(int iClientId) +{ + CSingleLock lock(m_critSection); + + return IsConnectedClient(iClientId) && m_clientMap[iClientId]->SupportsTimers(); +} + +PVR_ERROR CPVRClients::GetTimers(CPVRTimers *timers) +{ + PVR_ERROR error(PVR_ERROR_NO_ERROR); + PVR_CLIENTMAP clients; + GetConnectedClients(clients); + + /* get the timer list from each client */ + for (PVR_CLIENTMAP_ITR itrClients = clients.begin(); itrClients != clients.end(); itrClients++) + { + PVR_ERROR currentError = (*itrClients).second->GetTimers(timers); + if (currentError != PVR_ERROR_NOT_IMPLEMENTED && + currentError != PVR_ERROR_NO_ERROR) + { + CLog::Log(LOGERROR, "PVR - %s - cannot get timers from client '%d': %s",__FUNCTION__, (*itrClients).first, CPVRClient::ToString(currentError)); + error = currentError; + } + } + + return error; +} + +PVR_ERROR CPVRClients::AddTimer(const CPVRTimerInfoTag &timer) +{ + PVR_ERROR error(PVR_ERROR_UNKNOWN); + + PVR_CLIENT client; + if (GetConnectedClient(timer.m_iClientId, client)) + error = client->AddTimer(timer); + + if (error != PVR_ERROR_NO_ERROR) + CLog::Log(LOGERROR, "PVR - %s - cannot add timer to client '%d': %s",__FUNCTION__, timer.m_iClientId, CPVRClient::ToString(error)); + + return error; +} + +PVR_ERROR CPVRClients::UpdateTimer(const CPVRTimerInfoTag &timer) +{ + PVR_ERROR error(PVR_ERROR_UNKNOWN); + + PVR_CLIENT client; + if (GetConnectedClient(timer.m_iClientId, client)) + error = client->UpdateTimer(timer); + + if (error != PVR_ERROR_NO_ERROR) + CLog::Log(LOGERROR, "PVR - %s - cannot update timer on client '%d': %s",__FUNCTION__, timer.m_iClientId, CPVRClient::ToString(error)); + + return error; +} + +PVR_ERROR CPVRClients::DeleteTimer(const CPVRTimerInfoTag &timer, bool bForce) +{ + PVR_ERROR error(PVR_ERROR_UNKNOWN); + PVR_CLIENT client; + + if (GetConnectedClient(timer.m_iClientId, client)) + error = client->DeleteTimer(timer, bForce); + + return error; +} + +PVR_ERROR CPVRClients::RenameTimer(const CPVRTimerInfoTag &timer, const CStdString &strNewName) +{ + PVR_ERROR error(PVR_ERROR_UNKNOWN); + + PVR_CLIENT client; + if (GetConnectedClient(timer.m_iClientId, client)) + error = client->RenameTimer(timer, strNewName); + + if (error != PVR_ERROR_NO_ERROR) + CLog::Log(LOGERROR, "PVR - %s - cannot rename timer on client '%d': %s",__FUNCTION__, timer.m_iClientId, CPVRClient::ToString(error)); + + return error; +} + +PVR_ERROR CPVRClients::GetRecordings(CPVRRecordings *recordings) +{ + PVR_ERROR error(PVR_ERROR_NO_ERROR); + PVR_CLIENTMAP clients; + GetConnectedClients(clients); + + for (PVR_CLIENTMAP_ITR itrClients = clients.begin(); itrClients != clients.end(); itrClients++) + { + PVR_ERROR currentError = (*itrClients).second->GetRecordings(recordings); + if (currentError != PVR_ERROR_NOT_IMPLEMENTED && + currentError != PVR_ERROR_NO_ERROR) + { + CLog::Log(LOGERROR, "PVR - %s - cannot get recordings from client '%d': %s",__FUNCTION__, (*itrClients).first, CPVRClient::ToString(currentError)); + error = currentError; + } + } + + return error; +} + +PVR_ERROR CPVRClients::RenameRecording(const CPVRRecording &recording) +{ + PVR_ERROR error(PVR_ERROR_UNKNOWN); + + PVR_CLIENT client; + if (GetConnectedClient(recording.m_iClientId, client)) + error = client->RenameRecording(recording); + + if (error != PVR_ERROR_NO_ERROR) + CLog::Log(LOGERROR, "PVR - %s - cannot rename recording on client '%d': %s",__FUNCTION__, recording.m_iClientId, CPVRClient::ToString(error)); + + return error; +} + +PVR_ERROR CPVRClients::DeleteRecording(const CPVRRecording &recording) +{ + PVR_ERROR error(PVR_ERROR_UNKNOWN); + + PVR_CLIENT client; + if (GetConnectedClient(recording.m_iClientId, client)) + error = client->DeleteRecording(recording); + + if (error != PVR_ERROR_NO_ERROR) + CLog::Log(LOGERROR, "PVR - %s - cannot delete recording from client '%d': %s",__FUNCTION__, recording.m_iClientId, CPVRClient::ToString(error)); + + return error; +} + +bool CPVRClients::SetRecordingLastPlayedPosition(const CPVRRecording &recording, int lastplayedposition, PVR_ERROR *error) +{ + *error = PVR_ERROR_UNKNOWN; + PVR_CLIENT client; + if (GetConnectedClient(recording.m_iClientId, client) && client->SupportsRecordings()) + *error = client->SetRecordingLastPlayedPosition(recording, lastplayedposition); + else + CLog::Log(LOGERROR, "PVR - %s - client %d does not support recordings",__FUNCTION__, recording.m_iClientId); + + return *error == PVR_ERROR_NO_ERROR; +} + +int CPVRClients::GetRecordingLastPlayedPosition(const CPVRRecording &recording) +{ + int rc = 0; + + PVR_CLIENT client; + if (GetConnectedClient(recording.m_iClientId, client) && client->SupportsRecordings()) + rc = client->GetRecordingLastPlayedPosition(recording); + else + CLog::Log(LOGERROR, "PVR - %s - client %d does not support recordings",__FUNCTION__, recording.m_iClientId); + + return rc; +} + +bool CPVRClients::SetRecordingPlayCount(const CPVRRecording &recording, int count, PVR_ERROR *error) +{ + *error = PVR_ERROR_UNKNOWN; + PVR_CLIENT client; + if (GetConnectedClient(recording.m_iClientId, client) && client->SupportsRecordingPlayCount()) + *error = client->SetRecordingPlayCount(recording, count); + else + CLog::Log(LOGERROR, "PVR - %s - client %d does not support setting recording's play count",__FUNCTION__, recording.m_iClientId); + + return *error == PVR_ERROR_NO_ERROR; +} + +bool CPVRClients::IsRecordingOnPlayingChannel(void) const +{ + CPVRChannelPtr currentChannel; + return GetPlayingChannel(currentChannel) && + currentChannel->IsRecording(); +} + +bool CPVRClients::CanRecordInstantly(void) +{ + CPVRChannelPtr currentChannel; + return GetPlayingChannel(currentChannel) && + currentChannel->CanRecord(); +} + +PVR_ERROR CPVRClients::GetEPGForChannel(const CPVRChannel &channel, CEpg *epg, time_t start, time_t end) +{ + PVR_ERROR error(PVR_ERROR_UNKNOWN); + PVR_CLIENT client; + if (GetConnectedClient(channel.ClientID(), client)) + error = client->GetEPGForChannel(channel, epg, start, end); + + if (error != PVR_ERROR_NO_ERROR) + CLog::Log(LOGERROR, "PVR - %s - cannot get EPG for channel '%s' from client '%d': %s",__FUNCTION__, channel.ChannelName().c_str(), channel.ClientID(), CPVRClient::ToString(error)); + + return error; +} + +PVR_ERROR CPVRClients::GetChannels(CPVRChannelGroupInternal *group) +{ + PVR_ERROR error(PVR_ERROR_NO_ERROR); + PVR_CLIENTMAP clients; + GetConnectedClients(clients); + + /* get the channel list from each client */ + for (PVR_CLIENTMAP_ITR itrClients = clients.begin(); itrClients != clients.end(); itrClients++) + { + PVR_ERROR currentError = (*itrClients).second->GetChannels(*group, group->IsRadio()); + if (currentError != PVR_ERROR_NOT_IMPLEMENTED && + currentError != PVR_ERROR_NO_ERROR) + { + error = currentError; + CLog::Log(LOGERROR, "PVR - %s - cannot get channels from client '%d': %s",__FUNCTION__, (*itrClients).first, CPVRClient::ToString(error)); + } + } + + return error; +} + +PVR_ERROR CPVRClients::GetChannelGroups(CPVRChannelGroups *groups) +{ + PVR_ERROR error(PVR_ERROR_NO_ERROR); + PVR_CLIENTMAP clients; + GetConnectedClients(clients); + + for (PVR_CLIENTMAP_ITR itrClients = clients.begin(); itrClients != clients.end(); itrClients++) + { + PVR_ERROR currentError = (*itrClients).second->GetChannelGroups(groups); + if (currentError != PVR_ERROR_NOT_IMPLEMENTED && + currentError != PVR_ERROR_NO_ERROR) + { + error = currentError; + CLog::Log(LOGERROR, "PVR - %s - cannot get groups from client '%d': %s",__FUNCTION__, (*itrClients).first, CPVRClient::ToString(error)); + } + } + + return error; +} + +PVR_ERROR CPVRClients::GetChannelGroupMembers(CPVRChannelGroup *group) +{ + PVR_ERROR error(PVR_ERROR_NO_ERROR); + PVR_CLIENTMAP clients; + GetConnectedClients(clients); + + /* get the member list from each client */ + for (PVR_CLIENTMAP_ITR itrClients = clients.begin(); itrClients != clients.end(); itrClients++) + { + PVR_ERROR currentError = (*itrClients).second->GetChannelGroupMembers(group); + if (currentError != PVR_ERROR_NOT_IMPLEMENTED && + currentError != PVR_ERROR_NO_ERROR) + { + error = currentError; + CLog::Log(LOGERROR, "PVR - %s - cannot get group members from client '%d': %s",__FUNCTION__, (*itrClients).first, CPVRClient::ToString(error)); + } + } + + return error; +} + +bool CPVRClients::HasMenuHooks(int iClientID) +{ + if (iClientID < 0) + iClientID = GetPlayingClientID(); + + PVR_CLIENT client; + return (GetConnectedClient(iClientID, client) && + client->HaveMenuHooks()); +} + +bool CPVRClients::GetMenuHooks(int iClientID, PVR_MENUHOOKS *hooks) +{ + bool bReturn(false); + + if (iClientID < 0) + iClientID = GetPlayingClientID(); + + PVR_CLIENT client; + if (GetConnectedClient(iClientID, client) && client->HaveMenuHooks()) + { + hooks = client->GetMenuHooks(); + bReturn = true; + } + + return bReturn; +} + +void CPVRClients::ProcessMenuHooks(int iClientID) +{ + PVR_MENUHOOKS *hooks = NULL; + + if (iClientID < 0) + iClientID = GetPlayingClientID(); + + PVR_CLIENT client; + if (GetConnectedClient(iClientID, client) && client->HaveMenuHooks()) + { + hooks = client->GetMenuHooks(); + std::vector<int> hookIDs; + + CGUIDialogSelect* pDialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT); + pDialog->Reset(); + pDialog->SetHeading(19196); + for (unsigned int i = 0; i < hooks->size(); i++) + pDialog->Add(client->GetString(hooks->at(i).iLocalizedStringId)); + pDialog->DoModal(); + + int selection = pDialog->GetSelectedLabel(); + if (selection >= 0) + client->CallMenuHook(hooks->at(selection)); + } +} + +bool CPVRClients::IsRunningChannelScan(void) const +{ + CSingleLock lock(m_critSection); + return m_bChannelScanRunning; +} + +vector<PVR_CLIENT> CPVRClients::GetClientsSupportingChannelScan(void) const +{ + vector<PVR_CLIENT> possibleScanClients; + CSingleLock lock(m_critSection); + + /* get clients that support channel scanning */ + for (PVR_CLIENTMAP_CITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++) + { + if (itr->second->ReadyToUse() && itr->second->SupportsChannelScan()) + possibleScanClients.push_back(itr->second); + } + + return possibleScanClients; +} + +void CPVRClients::StartChannelScan(void) +{ + PVR_CLIENT scanClient; + CSingleLock lock(m_critSection); + vector<PVR_CLIENT> possibleScanClients = GetClientsSupportingChannelScan(); + m_bChannelScanRunning = true; + + /* multiple clients found */ + if (possibleScanClients.size() > 1) + { + CGUIDialogSelect* pDialog= (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT); + + pDialog->Reset(); + pDialog->SetHeading(19119); + + for (unsigned int i = 0; i < possibleScanClients.size(); i++) + pDialog->Add(possibleScanClients[i]->GetFriendlyName()); + + pDialog->DoModal(); + + int selection = pDialog->GetSelectedLabel(); + if (selection >= 0) + scanClient = possibleScanClients[selection]; + } + /* one client found */ + else if (possibleScanClients.size() == 1) + { + scanClient = possibleScanClients[0]; + } + /* no clients found */ + else if (!scanClient) + { + CGUIDialogOK::ShowAndGetInput(19033,0,19192,0); + return; + } + + /* start the channel scan */ + CLog::Log(LOGNOTICE,"PVR - %s - starting to scan for channels on client %s", + __FUNCTION__, scanClient->GetFriendlyName().c_str()); + long perfCnt = XbmcThreads::SystemClockMillis(); + + /* stop the supervisor thread */ + g_PVRManager.StopUpdateThreads(); + + /* do the scan */ + if (scanClient->StartChannelScan() != PVR_ERROR_NO_ERROR) + /* an error occured */ + CGUIDialogOK::ShowAndGetInput(19111,0,19193,0); + + /* restart the supervisor thread */ + g_PVRManager.StartUpdateThreads(); + + CLog::Log(LOGNOTICE, "PVRManager - %s - channel scan finished after %li.%li seconds", + __FUNCTION__, (XbmcThreads::SystemClockMillis()-perfCnt)/1000, (XbmcThreads::SystemClockMillis()-perfCnt)%1000); + m_bChannelScanRunning = false; +} + +bool CPVRClients::IsKnownClient(const AddonPtr client) const +{ + // database IDs start at 1 + return GetClientId(client) > 0; +} + +int CPVRClients::RegisterClient(AddonPtr client) +{ + int iClientId(-1); + if (!client->Enabled()) + return -1; + + CLog::Log(LOGDEBUG, "%s - registering add-on '%s'", __FUNCTION__, client->Name().c_str()); + + CPVRDatabase *database = GetPVRDatabase(); + if (!database) + return -1; + + // check whether we already know this client + iClientId = database->GetClientId(client->ID()); + + // try to register the new client in the db + if (iClientId < 0 && (iClientId = database->Persist(client)) < 0) + { + CLog::Log(LOGERROR, "PVR - %s - can't add client '%s' to the database", __FUNCTION__, client->Name().c_str()); + return -1; + } + + PVR_CLIENT addon; + // load and initialise the client libraries + { + CSingleLock lock(m_critSection); + PVR_CLIENTMAP_ITR existingClient = m_clientMap.find(iClientId); + if (existingClient != m_clientMap.end()) + { + // return existing client + addon = existingClient->second; + } + else + { + // create a new client instance + addon = boost::dynamic_pointer_cast<CPVRClient>(client); + m_clientMap.insert(std::make_pair(iClientId, addon)); + } + } + + if (iClientId < 0) + CLog::Log(LOGERROR, "PVR - %s - can't register add-on '%s'", __FUNCTION__, client->Name().c_str()); + + return iClientId; +} + +bool CPVRClients::UpdateAndInitialiseClients(bool bInitialiseAllClients /* = false */) +{ + bool bReturn(true); + ADDON::VECADDONS map; + ADDON::VECADDONS disableAddons; + { + CSingleLock lock(m_critSection); + map = m_addons; + } + + if (map.size() == 0) + return false; + + for (unsigned iClientPtr = 0; iClientPtr < map.size(); iClientPtr++) + { + const AddonPtr clientAddon = map.at(iClientPtr); + bool bEnabled = clientAddon->Enabled() && + !m_addonDb.IsAddonDisabled(clientAddon->ID()); + + if (!bEnabled && IsKnownClient(clientAddon)) + { + /* stop the client and remove it from the db */ + StopClient(clientAddon, false); + } + else if (bEnabled && (bInitialiseAllClients || !IsKnownClient(clientAddon) || !IsConnectedClient(clientAddon))) + { + bool bDisabled(false); + + // register the add-on in the pvr db, and create the CPVRClient instance + int iClientId = RegisterClient(clientAddon); + if (iClientId < 0) + { + // failed to register or create the add-on, disable it + CLog::Log(LOGWARNING, "%s - failed to register add-on %s, disabling it", __FUNCTION__, clientAddon->Name().c_str()); + disableAddons.push_back(clientAddon); + bDisabled = true; + } + else + { + PVR_CLIENT addon; + if (!GetClient(iClientId, addon)) + { + CLog::Log(LOGWARNING, "%s - failed to find add-on %s, disabling it", __FUNCTION__, clientAddon->Name().c_str()); + disableAddons.push_back(clientAddon); + bDisabled = true; + } + // re-check the enabled status. newly installed clients get disabled when they're added to the db + else if (addon->Enabled() && !addon->Create(iClientId)) + { + CLog::Log(LOGWARNING, "%s - failed to create add-on %s", __FUNCTION__, clientAddon->Name().c_str()); + if (!addon.get() || !addon->DllLoaded()) + { + // failed to load the dll of this add-on, disable it + CLog::Log(LOGWARNING, "%s - failed to load the dll for add-on %s, disabling it", __FUNCTION__, clientAddon->Name().c_str()); + disableAddons.push_back(clientAddon); + bDisabled = true; + } + } + } + + if (bDisabled && (g_PVRManager.GetState() == ManagerStateStarted || g_PVRManager.GetState() == ManagerStateStarting)) + CGUIDialogOK::ShowAndGetInput(24070, 24071, 16029, 0); + } + } + + // disable add-ons that failed to initialise + if (disableAddons.size() > 0) + { + CSingleLock lock(m_critSection); + for (ADDON::VECADDONS::iterator it = disableAddons.begin(); it != disableAddons.end(); it++) + { + // disable in the add-on db + m_addonDb.DisableAddon((*it)->ID(), true); + + // remove from the pvr add-on list + ADDON::VECADDONS::iterator addonPtr = std::find(m_addons.begin(), m_addons.end(), *it); + if (addonPtr != m_addons.end()) + m_addons.erase(addonPtr); + } + } + + return bReturn; +} + +void CPVRClients::Process(void) +{ + bool bCheckedEnabledClientsOnStartup(false); + + CAddonMgr::Get().RegisterAddonMgrCallback(ADDON_PVRDLL, this); + CAddonMgr::Get().RegisterObserver(this); + + UpdateAddons(); + + while (!g_application.m_bStop && !m_bStop) + { + UpdateAndInitialiseClients(); + + if (!bCheckedEnabledClientsOnStartup) + { + bCheckedEnabledClientsOnStartup = true; + if (!HasEnabledClients() && !m_bNoAddonWarningDisplayed) + ShowDialogNoClientsEnabled(); + } + + PVR_CLIENT client; + if (GetPlayingClient(client)) + client->UpdateCharInfoSignalStatus(); + Sleep(1000); + } +} + +void CPVRClients::ShowDialogNoClientsEnabled(void) +{ + if (g_PVRManager.GetState() != ManagerStateStarted && !g_PVRManager.GetState() == ManagerStateStarting) + return; + + CGUIDialogOK::ShowAndGetInput(19240, 19241, 19242, 19243); + + vector<CStdString> params; + params.push_back("addons://disabled/xbmc.pvrclient"); + params.push_back("return"); + g_windowManager.ActivateWindow(WINDOW_ADDON_BROWSER, params); +} + +void CPVRClients::SaveCurrentChannelSettings(void) +{ + CPVRChannelPtr channel; + { + CSingleLock lock(m_critSection); + if (!GetPlayingChannel(channel) || !m_bIsValidChannelSettings) + return; + } + + CPVRDatabase *database = GetPVRDatabase(); + if (!database) + return; + + if (g_settings.m_currentVideoSettings != g_settings.m_defaultVideoSettings) + { + CLog::Log(LOGDEBUG, "PVR - %s - persisting custom channel settings for channel '%s'", + __FUNCTION__, channel->ChannelName().c_str()); + database->PersistChannelSettings(*channel, g_settings.m_currentVideoSettings); + } + else + { + CLog::Log(LOGDEBUG, "PVR - %s - no custom channel settings for channel '%s'", + __FUNCTION__, channel->ChannelName().c_str()); + database->DeleteChannelSettings(*channel); + } +} + +void CPVRClients::LoadCurrentChannelSettings(void) +{ + CPVRChannelPtr channel; + { + CSingleLock lock(m_critSection); + if (!GetPlayingChannel(channel)) + return; + } + + CPVRDatabase *database = GetPVRDatabase(); + if (!database) + return; + + if (g_application.m_pPlayer) + { + /* set the default settings first */ + CVideoSettings loadedChannelSettings = g_settings.m_defaultVideoSettings; + + /* try to load the settings from the database */ + database->GetChannelSettings(*channel, loadedChannelSettings); + + g_settings.m_currentVideoSettings = g_settings.m_defaultVideoSettings; + g_settings.m_currentVideoSettings.m_Brightness = loadedChannelSettings.m_Brightness; + g_settings.m_currentVideoSettings.m_Contrast = loadedChannelSettings.m_Contrast; + g_settings.m_currentVideoSettings.m_Gamma = loadedChannelSettings.m_Gamma; + g_settings.m_currentVideoSettings.m_Crop = loadedChannelSettings.m_Crop; + g_settings.m_currentVideoSettings.m_CropLeft = loadedChannelSettings.m_CropLeft; + g_settings.m_currentVideoSettings.m_CropRight = loadedChannelSettings.m_CropRight; + g_settings.m_currentVideoSettings.m_CropTop = loadedChannelSettings.m_CropTop; + g_settings.m_currentVideoSettings.m_CropBottom = loadedChannelSettings.m_CropBottom; + g_settings.m_currentVideoSettings.m_CustomPixelRatio = loadedChannelSettings.m_CustomPixelRatio; + g_settings.m_currentVideoSettings.m_CustomZoomAmount = loadedChannelSettings.m_CustomZoomAmount; + g_settings.m_currentVideoSettings.m_CustomVerticalShift = loadedChannelSettings.m_CustomVerticalShift; + g_settings.m_currentVideoSettings.m_NoiseReduction = loadedChannelSettings.m_NoiseReduction; + g_settings.m_currentVideoSettings.m_Sharpness = loadedChannelSettings.m_Sharpness; + g_settings.m_currentVideoSettings.m_InterlaceMethod = loadedChannelSettings.m_InterlaceMethod; + g_settings.m_currentVideoSettings.m_OutputToAllSpeakers = loadedChannelSettings.m_OutputToAllSpeakers; + g_settings.m_currentVideoSettings.m_AudioDelay = loadedChannelSettings.m_AudioDelay; + g_settings.m_currentVideoSettings.m_AudioStream = loadedChannelSettings.m_AudioStream; + g_settings.m_currentVideoSettings.m_SubtitleOn = loadedChannelSettings.m_SubtitleOn; + g_settings.m_currentVideoSettings.m_SubtitleDelay = loadedChannelSettings.m_SubtitleDelay; + g_settings.m_currentVideoSettings.m_CustomNonLinStretch = loadedChannelSettings.m_CustomNonLinStretch; + g_settings.m_currentVideoSettings.m_ScalingMethod = loadedChannelSettings.m_ScalingMethod; + g_settings.m_currentVideoSettings.m_PostProcess = loadedChannelSettings.m_PostProcess; + g_settings.m_currentVideoSettings.m_DeinterlaceMode = loadedChannelSettings.m_DeinterlaceMode; + + /* only change the view mode if it's different */ + if (g_settings.m_currentVideoSettings.m_ViewMode != loadedChannelSettings.m_ViewMode) + { + g_settings.m_currentVideoSettings.m_ViewMode = loadedChannelSettings.m_ViewMode; + + g_renderManager.SetViewMode(g_settings.m_currentVideoSettings.m_ViewMode); + g_settings.m_currentVideoSettings.m_CustomZoomAmount = g_settings.m_fZoomAmount; + g_settings.m_currentVideoSettings.m_CustomPixelRatio = g_settings.m_fPixelRatio; + } + + /* only change the subtitle stream, if it's different */ + if (g_settings.m_currentVideoSettings.m_SubtitleStream != loadedChannelSettings.m_SubtitleStream) + { + g_settings.m_currentVideoSettings.m_SubtitleStream = loadedChannelSettings.m_SubtitleStream; + + g_application.m_pPlayer->SetSubtitle(g_settings.m_currentVideoSettings.m_SubtitleStream); + } + + /* only change the audio stream if it's different */ + if (g_application.m_pPlayer->GetAudioStream() != g_settings.m_currentVideoSettings.m_AudioStream) + g_application.m_pPlayer->SetAudioStream(g_settings.m_currentVideoSettings.m_AudioStream); + + g_application.m_pPlayer->SetAVDelay(g_settings.m_currentVideoSettings.m_AudioDelay); + g_application.m_pPlayer->SetDynamicRangeCompression((long)(g_settings.m_currentVideoSettings.m_VolumeAmplification * 100)); + g_application.m_pPlayer->SetSubtitleVisible(g_settings.m_currentVideoSettings.m_SubtitleOn); + g_application.m_pPlayer->SetSubTitleDelay(g_settings.m_currentVideoSettings.m_SubtitleDelay); + + /* settings can be saved on next channel switch */ + m_bIsValidChannelSettings = true; + } +} + +bool CPVRClients::UpdateAddons(void) +{ + ADDON::VECADDONS addons; + bool bReturn(CAddonMgr::Get().GetAddons(ADDON_PVRDLL, addons, true)); + + if (bReturn) + { + CSingleLock lock(m_critSection); + m_addons = addons; + } + + if ((!bReturn || addons.size() == 0) && !m_bNoAddonWarningDisplayed && + !CAddonMgr::Get().HasAddons(ADDON_PVRDLL, false) && + (g_PVRManager.GetState() == ManagerStateStarted || g_PVRManager.GetState() == ManagerStateStarting)) + { + // No PVR add-ons could be found + // You need a tuner, backend software, and an add-on for the backend to be able to use PVR. + // Please visit xbmc.org/PVR to learn more. + m_bNoAddonWarningDisplayed = true; + CGUIDialogOK::ShowAndGetInput(19271, 19272, 19273, 19274); + } + + return bReturn; +} + +void CPVRClients::Notify(const Observable &obs, const ObservableMessage msg) +{ + if (msg == ObservableMessageAddons) + { + UpdateAddons(); + UpdateAndInitialiseClients(); + } +} + +bool CPVRClients::GetClient(const CStdString &strId, ADDON::AddonPtr &addon) const +{ + CSingleLock lock(m_critSection); + for (PVR_CLIENTMAP_CITR itr = m_clientMap.begin(); itr != m_clientMap.end(); itr++) + { + if (itr->second->ID() == strId) + { + addon = itr->second; + return true; + } + } + return false; +} + +bool CPVRClients::SupportsChannelGroups(int iClientId) const +{ + PVR_CLIENT client; + return GetConnectedClient(iClientId, client) && client->SupportsChannelGroups(); +} + +bool CPVRClients::SupportsChannelScan(int iClientId) const +{ + PVR_CLIENT client; + return GetConnectedClient(iClientId, client) && client->SupportsChannelScan(); +} + +bool CPVRClients::SupportsEPG(int iClientId) const +{ + PVR_CLIENT client; + return GetConnectedClient(iClientId, client) && client->SupportsEPG(); +} + +bool CPVRClients::SupportsLastPlayedPosition(int iClientId) const +{ + PVR_CLIENT client; + return GetConnectedClient(iClientId, client) && client->SupportsLastPlayedPosition(); +} + +bool CPVRClients::SupportsRadio(int iClientId) const +{ + PVR_CLIENT client; + return GetConnectedClient(iClientId, client) && client->SupportsRadio(); +} + +bool CPVRClients::SupportsRecordings(int iClientId) const +{ + PVR_CLIENT client; + return GetConnectedClient(iClientId, client) && client->SupportsRecordings(); +} + +bool CPVRClients::SupportsRecordingFolders(int iClientId) const +{ + PVR_CLIENT client; + return GetConnectedClient(iClientId, client) && client->SupportsRecordingFolders(); +} + +bool CPVRClients::SupportsRecordingPlayCount(int iClientId) const +{ + PVR_CLIENT client; + return GetConnectedClient(iClientId, client) && client->SupportsRecordingPlayCount(); +} + +bool CPVRClients::SupportsTimers(int iClientId) const +{ + PVR_CLIENT client; + return GetConnectedClient(iClientId, client) && client->SupportsTimers(); +} + +bool CPVRClients::SupportsTV(int iClientId) const +{ + PVR_CLIENT client; + return GetConnectedClient(iClientId, client) && client->SupportsTV(); +} + +bool CPVRClients::HandlesDemuxing(int iClientId) const +{ + PVR_CLIENT client; + return GetConnectedClient(iClientId, client) && client->HandlesDemuxing(); +} + +bool CPVRClients::HandlesInputStream(int iClientId) const +{ + PVR_CLIENT client; + return GetConnectedClient(iClientId, client) && client->HandlesInputStream(); +} + +bool CPVRClients::GetPlayingClient(PVR_CLIENT &client) const +{ + return GetConnectedClient(GetPlayingClientID(), client); +} + +bool CPVRClients::OpenStream(const CPVRChannel &tag, bool bIsSwitchingChannel) +{ + bool bReturn(false); + CloseStream(); + + /* try to open the stream on the client */ + PVR_CLIENT client; + if (GetConnectedClient(tag.ClientID(), client) && + client->OpenStream(tag, bIsSwitchingChannel)) + { + CSingleLock lock(m_critSection); + m_playingClientId = tag.ClientID(); + m_bIsPlayingLiveTV = true; + + if (tag.ClientID() == PVR_VIRTUAL_CLIENT_ID) + m_strPlayingClientName = g_localizeStrings.Get(19209); + else if (!tag.IsVirtual() && client.get()) + m_strPlayingClientName = client->GetFriendlyName(); + else + m_strPlayingClientName = g_localizeStrings.Get(13205); + + bReturn = true; + } + + return bReturn; +} + +bool CPVRClients::OpenStream(const CPVRRecording &tag) +{ + bool bReturn(false); + CloseStream(); + + /* try to open the recording stream on the client */ + PVR_CLIENT client; + if (GetConnectedClient(tag.m_iClientId, client) && + client->OpenStream(tag)) + { + CSingleLock lock(m_critSection); + m_playingClientId = tag.m_iClientId; + m_bIsPlayingRecording = true; + m_strPlayingClientName = client->GetFriendlyName(); + bReturn = true; + } + + return bReturn; +} + +void CPVRClients::CloseStream(void) +{ + PVR_CLIENT playingClient; + if (GetPlayingClient(playingClient)) + playingClient->CloseStream(); + + CSingleLock lock(m_critSection); + m_bIsPlayingLiveTV = false; + m_bIsPlayingRecording = false; + m_playingClientId = PVR_INVALID_CLIENT_ID; + m_strPlayingClientName = ""; +} + +int CPVRClients::ReadStream(void* lpBuf, int64_t uiBufSize) +{ + PVR_CLIENT client; + if (GetPlayingClient(client)) + return client->ReadStream(lpBuf, uiBufSize); + return -EINVAL; +} + +int64_t CPVRClients::GetStreamLength(void) +{ + PVR_CLIENT client; + if (GetPlayingClient(client)) + return client->GetStreamLength(); + return -EINVAL; +} + +int64_t CPVRClients::SeekStream(int64_t iFilePosition, int iWhence/* = SEEK_SET*/) +{ + PVR_CLIENT client; + if (GetPlayingClient(client)) + return client->SeekStream(iFilePosition, iWhence); + return -EINVAL; +} + +int64_t CPVRClients::GetStreamPosition(void) +{ + PVR_CLIENT client; + if (GetPlayingClient(client)) + return client->GetStreamPosition(); + return -EINVAL; +} + +CStdString CPVRClients::GetCurrentInputFormat(void) const +{ + CStdString strReturn; + CPVRChannelPtr currentChannel; + if (GetPlayingChannel(currentChannel)) + strReturn = currentChannel->InputFormat(); + + return strReturn; +} + +PVR_STREAM_PROPERTIES CPVRClients::GetCurrentStreamProperties(void) +{ + PVR_STREAM_PROPERTIES props; + PVR_CLIENT client; + if (GetPlayingClient(client)) + client->GetStreamProperties(&props); + + return props; +} + +bool CPVRClients::IsPlaying(void) const +{ + CSingleLock lock(m_critSection); + return m_bIsPlayingRecording || m_bIsPlayingLiveTV; +} + +bool CPVRClients::IsPlayingRadio(void) const +{ + PVR_CLIENT client; + if (GetPlayingClient(client)) + return client->IsPlayingLiveRadio(); + return false; +} + +bool CPVRClients::IsPlayingTV(void) const +{ + PVR_CLIENT client; + if (GetPlayingClient(client)) + return client->IsPlayingLiveTV(); + return false; +} + +bool CPVRClients::IsPlayingRecording(void) const +{ + CSingleLock lock(m_critSection); + return m_bIsPlayingRecording; +} + +bool CPVRClients::IsReadingLiveStream(void) const +{ + CSingleLock lock(m_critSection); + return m_bIsPlayingLiveTV; +} + +bool CPVRClients::IsEncrypted(void) const +{ + PVR_CLIENT client; + if (GetPlayingClient(client)) + return client->IsPlayingEncryptedChannel(); + return false; +} diff --git a/xbmc/pvr/addons/PVRClients.h b/xbmc/pvr/addons/PVRClients.h new file mode 100644 index 0000000000..06773f509b --- /dev/null +++ b/xbmc/pvr/addons/PVRClients.h @@ -0,0 +1,614 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "threads/CriticalSection.h" +#include "threads/Thread.h" +#include "utils/Observer.h" +#include "PVRClient.h" +#include "pvr/channels/PVRChannel.h" +#include "pvr/recordings/PVRRecording.h" +#include "addons/AddonDatabase.h" + +#include <vector> +#include <deque> + +namespace EPG +{ + class CEpg; +} + +namespace PVR +{ + class CPVRGUIInfo; + + typedef std::map< int, boost::shared_ptr<CPVRClient> > PVR_CLIENTMAP; + typedef std::map< int, boost::shared_ptr<CPVRClient> >::iterator PVR_CLIENTMAP_ITR; + typedef std::map< int, boost::shared_ptr<CPVRClient> >::const_iterator PVR_CLIENTMAP_CITR; + typedef std::map< int, PVR_STREAM_PROPERTIES > STREAMPROPS; + typedef boost::shared_ptr<CPVRClient> PVR_CLIENT; + + class CPVRClients : public ADDON::IAddonMgrCallback, + public Observer, + private CThread + { + friend class CPVRGUIInfo; + + public: + CPVRClients(void); + virtual ~CPVRClients(void); + + /*! + * @brief Start the backend info updater thread. + */ + void Start(void); + + /*! + * @brief Stop the backend info updater thread. + */ + void Stop(void); + + /*! + * @brief Load the settings for the current channel from the database. + */ + void LoadCurrentChannelSettings(void); + + /*! + * @brief Persist the current channel settings in the database. + */ + void SaveCurrentChannelSettings(void); + + /*! @name Backend methods */ + //@{ + + /*! + * @brief Check whether a client ID points to a valid and connected add-on. + * @param iClientId The client ID. + * @return True when the client ID is valid and connected, false otherwise. + */ + bool IsConnectedClient(int iClientId) const; + + bool IsConnectedClient(const ADDON::AddonPtr addon); + + /*! + * @brief Restart a single client add-on. + * @param addon The add-on to restart. + * @param bDataChanged True if the client's data changed, false otherwise (unused). + * @return True if the client was found and restarted, false otherwise. + */ + bool RequestRestart(ADDON::AddonPtr addon, bool bDataChanged); + + /*! + * @brief Remove a single client add-on. + * @param addon The add-on to remove. + * @return True if the client was found and removed, false otherwise. + */ + bool RequestRemoval(ADDON::AddonPtr addon); + + /*! + * @brief Unload all loaded add-ons and reset all class properties. + */ + void Unload(void); + + /*! + * @brief The ID of the first active client or -1 if no clients are active; + */ + int GetFirstConnectedClientID(void); + + /*! + * @return True when at least one client is known and enabled, false otherwise. + */ + bool HasEnabledClients(void) const; + + /*! + * @return The amount of enabled clients. + */ + int EnabledClientAmount(void) const; + + /*! + * @brief Stop a client. + * @param addon The client to stop. + * @param bRestart If true, restart the client. + * @return True if the client was found, false otherwise. + */ + bool StopClient(ADDON::AddonPtr client, bool bRestart); + + /*! + * @return The amount of connected clients. + */ + int ConnectedClientAmount(void) const; + + /*! + * @brief Check whether there are any connected clients. + * @return True if at least one client is connected. + */ + bool HasConnectedClients(void) const; + + /*! + * @brief Get the friendly name for the client with the given id. + * @param iClientId The id of the client. + * @param strName The friendly name of the client or an empty string when it wasn't found. + * @return True if the client was found, false otherwise. + */ + bool GetClientName(int iClientId, CStdString &strName) const; + + /*! + * @bried Get all connected clients. + * @param clients Store the active clients in this map. + * @return The amount of added clients. + */ + int GetConnectedClients(PVR_CLIENTMAP &clients) const; + + /*! + * @return The client ID of the client that is currently playing a stream or -1 if no client is playing. + */ + int GetPlayingClientID(void) const; + + //@} + + /*! @name Stream methods */ + //@{ + + /*! + * @return True if a stream is playing, false otherwise. + */ + bool IsPlaying(void) const; + + /*! + * @return The friendly name of the client that is currently playing or an empty string if nothing is playing. + */ + const CStdString GetPlayingClientName(void) const; + + /*! + * @brief Read from an open stream. + * @param lpBuf Target buffer. + * @param uiBufSize The size of the buffer. + * @return The amount of bytes that was added. + */ + int ReadStream(void* lpBuf, int64_t uiBufSize); + + /*! + * @brief Return the filesize of the currently running stream. + * Limited to recordings playback at the moment. + * @return The size of the stream. + */ + int64_t GetStreamLength(void); + + /*! + * @brief Seek to a position in a stream. + * Limited to recordings playback at the moment. + * @param iFilePosition The position to seek to. + * @param iWhence Specify how to seek ("new position=pos", "new position=pos+actual postion" or "new position=filesize-pos") + * @return The new stream position. + */ + int64_t SeekStream(int64_t iFilePosition, int iWhence = SEEK_SET); + + /*! + * @brief Get the currently playing position in a stream. + * @return The current position. + */ + int64_t GetStreamPosition(void); + + /*! + * @brief Close a PVR stream. + */ + void CloseStream(void); + + /*! + * @brief Get the properties of the current playing stream content. + * @return A pointer to the properties or NULL if no stream is playing. + */ + PVR_STREAM_PROPERTIES GetCurrentStreamProperties(void); + + /*! + * @brief Get the input format name of the current playing stream content. + * @return A pointer to the properties or NULL if no stream is playing. + */ + CStdString GetCurrentInputFormat(void) const; + + /*! + * @return True if a live stream is playing, false otherwise. + */ + bool IsReadingLiveStream(void) const; + + /*! + * @return True if a TV channel is playing, false otherwise. + */ + bool IsPlayingTV(void) const; + + /*! + * @return True if a radio channel playing, false otherwise. + */ + bool IsPlayingRadio(void) const; + + /*! + * @return True if the currently playing channel is encrypted, false otherwise. + */ + bool IsEncrypted(void) const; + + /*! + * @brief Open a stream on the given channel. + * @param tag The channel to start playing. + * @param bIsSwitchingChannel True when switching channels, false otherwise. + * @return True if the stream was opened successfully, false otherwise. + */ + bool OpenStream(const CPVRChannel &tag, bool bIsSwitchingChannel); + + /*! + * @brief Get the URL for the stream to the given channel. + * @param tag The channel to get the stream url for. + * @return The requested stream url or an empty string if it wasn't found. + */ + CStdString GetStreamURL(const CPVRChannel &tag); + + /*! + * @brief Switch an opened live tv stream to another channel. + * @param channel The channel to switch to. + * @return True if the switch was successfull, false otherwise. + */ + bool SwitchChannel(const CPVRChannel &channel); + + /*! + * @brief Get the channel that is currently playing. + * @param channel A copy of the channel that is currently playing. + * @return True if a channel is playing, false otherwise. + */ + bool GetPlayingChannel(CPVRChannelPtr &channel) const; + + /*! + * @return True if a recording is playing, false otherwise. + */ + bool IsPlayingRecording(void) const; + + /*! + * @brief Open a stream from the given recording. + * @param tag The recording to start playing. + * @return True if the stream was opened successfully, false otherwise. + */ + bool OpenStream(const CPVRRecording &tag); + + /*! + * @brief Get the recordings that is currently playing. + * @param recording A copy of the recording that is currently playing. + * @return True if a recording is playing, false otherwise. + */ + bool GetPlayingRecording(CPVRRecording &recording) const; + + //@} + + /*! @name Timer methods */ + //@{ + + /*! + * @brief Check whether a client supports timers. + * @param iClientId The id of the client to check. + * @return True if the supports timers, false otherwise. + */ + bool HasTimerSupport(int iClientId); + + /*! + * @brief Get all timers from clients + * @param timers Store the timers in this container. + * @return The amount of timers that were added. + */ + PVR_ERROR GetTimers(CPVRTimers *timers); + + /*! + * @brief Add a new timer to a backend. + * @param timer The timer to add. + * @param error An error if it occured. + * @return True if the timer was added successfully, false otherwise. + */ + PVR_ERROR AddTimer(const CPVRTimerInfoTag &timer); + + /*! + * @brief Update a timer on the backend. + * @param timer The timer to update. + * @param error An error if it occured. + * @return True if the timer was updated successfully, false otherwise. + */ + PVR_ERROR UpdateTimer(const CPVRTimerInfoTag &timer); + + /*! + * @brief Delete a timer from the backend. + * @param timer The timer to delete. + * @param bForce Also delete when currently recording if true. + * @param error An error if it occured. + * @return True if the timer was deleted successfully, false otherwise. + */ + PVR_ERROR DeleteTimer(const CPVRTimerInfoTag &timer, bool bForce); + + /*! + * @brief Rename a timer on the backend. + * @param timer The timer to rename. + * @param strNewName The new name. + * @param error An error if it occured. + * @return True if the timer was renamed successfully, false otherwise. + */ + PVR_ERROR RenameTimer(const CPVRTimerInfoTag &timer, const CStdString &strNewName); + + //@} + + /*! @name Recording methods */ + //@{ + + /*! + * @brief Check whether a client supports recordings. + * @param iClientId The id of the client to check. + * @return True if the supports recordings, false otherwise. + */ + bool SupportsRecordings(int iClientId) const; + + /*! + * @brief Get all recordings from clients + * @param recordings Store the recordings in this container. + * @return The amount of recordings that were added. + */ + PVR_ERROR GetRecordings(CPVRRecordings *recordings); + + /*! + * @brief Rename a recordings on the backend. + * @param recording The recordings to rename. + * @param error An error if it occured. + * @return True if the recording was renamed successfully, false otherwise. + */ + PVR_ERROR RenameRecording(const CPVRRecording &recording); + + /*! + * @brief Delete a recording from the backend. + * @param recording The recording to delete. + * @param error An error if it occured. + * @return True if the recordings was deleted successfully, false otherwise. + */ + PVR_ERROR DeleteRecording(const CPVRRecording &recording); + + /*! + * @brief Set play count of a recording on the backend. + * @param recording The recording to set the play count. + * @param count Play count. + * @param error An error if it occured. + * @return True if the recording's play count was set successfully, false otherwise. + */ + bool SetRecordingPlayCount(const CPVRRecording &recording, int count, PVR_ERROR *error); + + /*! + * @brief Set the last watched position of a recording on the backend. + * @param recording The recording. + * @param position The last watched position in seconds + * @param error An error if it occured. + * @return True if the last played position was updated successfully, false otherwise + */ + bool SetRecordingLastPlayedPosition(const CPVRRecording &recording, int lastplayedposition, PVR_ERROR *error); + + /*! + * @brief Retrieve the last watched position of a recording on the backend. + * @param recording The recording. + * @return The last watched position in seconds + */ + int GetRecordingLastPlayedPosition(const CPVRRecording &recording); + + /*! + * @brief Check whether there is an active recording on the current channel. + * @return True if there is, false otherwise. + */ + bool IsRecordingOnPlayingChannel(void) const; + + /*! + * @brief Check whether the current channel can be recorded instantly. + * @return True if it can, false otherwise. + */ + bool CanRecordInstantly(void); + + //@} + + /*! @name EPG methods */ + //@{ + + /*! + * @brief Check whether a client supports EPG transfer. + * @param iClientId The id of the client to check. + * @return True if the supports EPG transfer, false otherwise. + */ + bool SupportsEPG(int iClientId) const; + + /*! + * @brief Get the EPG table for a channel. + * @param channel The channel to get the EPG table for. + * @param epg Store the EPG in this container. + * @param start Get entries after this start time. + * @param end Get entries before this end time. + * @param error An error if it occured. + * @return True if the EPG was transfered successfully, false otherwise. + */ + PVR_ERROR GetEPGForChannel(const CPVRChannel &channel, EPG::CEpg *epg, time_t start, time_t end); + + //@} + + /*! @name Channel methods */ + //@{ + + /*! + * @brief Get all channels from backends. + * @param group The container to store the channels in. + * @param error An error if it occured. + * @return The amount of channels that were added. + */ + PVR_ERROR GetChannels(CPVRChannelGroupInternal *group); + + /*! + * @brief Check whether a client supports channel groups. + * @param iClientId The id of the client to check. + * @return True if the supports channel groups, false otherwise. + */ + bool SupportsChannelGroups(int iClientId) const; + + /*! + * @brief Get all channel groups from backends. + * @param groups Store the channel groups in this container. + * @param error An error if it occured. + * @return The amount of groups that were added. + */ + PVR_ERROR GetChannelGroups(CPVRChannelGroups *groups); + + /*! + * @brief Get all group members of a channel group. + * @param group The group to get the member for. + * @param error An error if it occured. + * @return The amount of channels that were added. + */ + PVR_ERROR GetChannelGroupMembers(CPVRChannelGroup *group); + + //@} + + /*! @name Menu hook methods */ + //@{ + + /*! + * @brief Check whether a client has any PVR specific menu entries. + * @param iClientId The ID of the client to get the menu entries for. Get the menu for the active channel if iClientId < 0. + * @return True if the client has any menu hooks, false otherwise. + */ + bool HasMenuHooks(int iClientId); + + /*! + * @brief Open selection and progress PVR actions. + * @param iClientId The ID of the client to process the menu entries for. Process the menu entries for the active channel if iClientId < 0. + */ + void ProcessMenuHooks(int iClientID); + + //@} + + /*! @name Channel scan methods */ + //@{ + + /*! + * @return True when a channel scan is currently running, false otherwise. + */ + bool IsRunningChannelScan(void) const; + + /*! + * @brief Open a selection dialog and start a channel scan on the selected client. + */ + void StartChannelScan(void); + + /*! + * @return All clients that support channel scanning. + */ + std::vector< boost::shared_ptr<CPVRClient> > GetClientsSupportingChannelScan(void) const; + + //@} + + void Notify(const Observable &obs, const ObservableMessage msg); + + bool GetClient(const CStdString &strId, ADDON::AddonPtr &addon) const; + + bool SupportsChannelScan(int iClientId) const; + bool SupportsLastPlayedPosition(int iClientId) const; + bool SupportsRadio(int iClientId) const; + bool SupportsRecordingFolders(int iClientId) const; + bool SupportsRecordingPlayCount(int iClientId) const; + bool SupportsTimers(int iClientId) const; + bool SupportsTV(int iClientId) const; + bool HandlesDemuxing(int iClientId) const; + bool HandlesInputStream(int iClientId) const; + + bool GetPlayingClient(PVR_CLIENT &client) const; + + private: + /*! + * @brief Update add-ons from the AddonManager + * @return True when updated, false otherwise + */ + bool UpdateAddons(void); + + /*! + * @brief Get the menu hooks for a client. + * @param iClientID The client to get the hooks for. + * @param hooks The container to add the hooks to. + * @return True if the hooks were added successfully (if any), false otherwise. + */ + bool GetMenuHooks(int iClientID, PVR_MENUHOOKS *hooks); + + /*! + * @brief Updates the backend information + */ + void Process(void); + + /*! + * @brief Show a dialog to guide new users who have no clients enabled. + */ + void ShowDialogNoClientsEnabled(void); + + /*! + * @brief Get the instance of the client. + * @param iClientId The id of the client to get. + * @param addon The client. + * @return True if the client was found, false otherwise. + */ + bool GetClient(int iClientId, boost::shared_ptr<CPVRClient> &addon) const; + + /*! + * @brief Get the instance of the client, if it's connected. + * @param iClientId The id of the client to get. + * @param addon The client. + * @return True if the client is connected, false otherwise. + */ + bool GetConnectedClient(int iClientId, boost::shared_ptr<CPVRClient> &addon) const; + + /*! + * @brief Check whether a client is registered. + * @param client The client to check. + * @return True if this client is registered, false otherwise. + */ + bool IsKnownClient(const ADDON::AddonPtr client) const; + + /*! + * @brief Check whether there are any new pvr add-ons enabled or whether any of the known clients has been disabled. + * @param bInitialiseAllClients True to initialise all clients, false to only initialise new clients. + * @return True if all clients were updated successfully, false otherwise. + */ + bool UpdateAndInitialiseClients(bool bInitialiseAllClients = false); + + /*! + * @brief Initialise and connect a client. + * @param client The client to initialise. + * @return The id of the client if it was created or found in the existing client map, -1 otherwise. + */ + int RegisterClient(ADDON::AddonPtr client); + + int GetClientId(const ADDON::AddonPtr client) const; + + bool m_bChannelScanRunning; /*!< true when a channel scan is currently running, false otherwise */ + bool m_bIsSwitchingChannels; /*!< true while switching channels */ + bool m_bIsValidChannelSettings; /*!< true if current channel settings are valid and can be saved */ + int m_playingClientId; /*!< the ID of the client that is currently playing */ + bool m_bIsPlayingLiveTV; + bool m_bIsPlayingRecording; + DWORD m_scanStart; /*!< scan start time to check for non present streams */ + CStdString m_strPlayingClientName; /*!< the name client that is currenty playing a stream or an empty string if nothing is playing */ + ADDON::VECADDONS m_addons; + PVR_CLIENTMAP m_clientMap; /*!< a map of all known clients */ + STREAMPROPS m_streamProps; /*!< the current stream's properties */ + bool m_bNoAddonWarningDisplayed; /*!< true when a warning was displayed that no add-ons were found, false otherwise */ + CCriticalSection m_critSection; + CAddonDatabase m_addonDb; + }; +} diff --git a/xbmc/pvr/channels/Makefile b/xbmc/pvr/channels/Makefile new file mode 100644 index 0000000000..cb8566321f --- /dev/null +++ b/xbmc/pvr/channels/Makefile @@ -0,0 +1,10 @@ +SRCS=PVRChannel.cpp \ + PVRChannelGroup.cpp \ + PVRChannelGroupInternal.cpp \ + PVRChannelGroups.cpp \ + PVRChannelGroupsContainer.cpp + +LIB=pvrchannels.a + +include ../../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/xbmc/pvr/channels/PVRChannel.cpp b/xbmc/pvr/channels/PVRChannel.cpp new file mode 100644 index 0000000000..097891aa39 --- /dev/null +++ b/xbmc/pvr/channels/PVRChannel.cpp @@ -0,0 +1,866 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "FileItem.h" +#include "guilib/LocalizeStrings.h" +#include "utils/log.h" +#include "Util.h" +#include "filesystem/File.h" +#include "settings/GUISettings.h" +#include "utils/StringUtils.h" +#include "threads/SingleLock.h" + +#include "PVRChannelGroupsContainer.h" +#include "epg/EpgContainer.h" +#include "pvr/timers/PVRTimers.h" +#include "pvr/PVRDatabase.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClients.h" + +using namespace PVR; +using namespace EPG; + +bool CPVRChannel::operator==(const CPVRChannel &right) const +{ + return (m_bIsRadio == right.m_bIsRadio && + m_iUniqueId == right.m_iUniqueId && + m_iClientId == right.m_iClientId); +} + +bool CPVRChannel::operator!=(const CPVRChannel &right) const +{ + return !(*this == right); +} + +CPVRChannel::CPVRChannel(bool bRadio /* = false */) +{ + m_iChannelId = -1; + m_bIsRadio = bRadio; + m_bIsHidden = false; + m_bIsUserSetIcon = false; + m_bIsLocked = false; + m_strIconPath = StringUtils::EmptyString; + m_strChannelName = StringUtils::EmptyString; + m_bIsVirtual = false; + m_iLastWatched = 0; + m_bChanged = false; + m_iCachedChannelNumber = 0; + + m_iEpgId = -1; + m_bEPGCreated = false; + m_bEPGEnabled = true; + m_strEPGScraper = "client"; + + m_iUniqueId = -1; + m_iClientId = -1; + m_iClientChannelNumber = -1; + m_strClientChannelName = StringUtils::EmptyString; + m_strInputFormat = StringUtils::EmptyString; + m_strStreamURL = StringUtils::EmptyString; + m_strFileNameAndPath = StringUtils::EmptyString; + m_iClientEncryptionSystem = -1; +} + +CPVRChannel::CPVRChannel(const PVR_CHANNEL &channel, unsigned int iClientId) +{ + m_iChannelId = -1; + m_bIsRadio = channel.bIsRadio; + m_bIsHidden = channel.bIsHidden; + m_bIsUserSetIcon = false; + m_bIsLocked = false; + m_strIconPath = channel.strIconPath; + m_strChannelName = channel.strChannelName; + m_iUniqueId = channel.iUniqueId; + m_iClientChannelNumber = channel.iChannelNumber; + m_strClientChannelName = channel.strChannelName; + m_strInputFormat = channel.strInputFormat; + m_strStreamURL = channel.strStreamURL; + m_iClientEncryptionSystem = channel.iEncryptionSystem; + m_iCachedChannelNumber = 0; + m_iClientId = iClientId; + m_strFileNameAndPath = StringUtils::EmptyString; + m_bIsVirtual = false; + m_iLastWatched = 0; + m_bEPGEnabled = true; + m_strEPGScraper = "client"; + m_iEpgId = -1; + m_bEPGCreated = false; + m_bChanged = false; + + if (m_strChannelName.IsEmpty()) + m_strChannelName.Format("%s %d", g_localizeStrings.Get(19029), m_iUniqueId); + + UpdateEncryptionName(); +} + +CPVRChannel::CPVRChannel(const CPVRChannel &channel) +{ + *this = channel; +} + +CPVRChannel &CPVRChannel::operator=(const CPVRChannel &channel) +{ + m_iChannelId = channel.m_iChannelId; + m_bIsRadio = channel.m_bIsRadio; + m_bIsHidden = channel.m_bIsHidden; + m_bIsUserSetIcon = channel.m_bIsUserSetIcon; + m_bIsLocked = channel.m_bIsLocked; + m_strIconPath = channel.m_strIconPath; + m_strChannelName = channel.m_strChannelName; + m_bIsVirtual = channel.m_bIsVirtual; + m_iLastWatched = channel.m_iLastWatched; + m_bEPGEnabled = channel.m_bEPGEnabled; + m_strEPGScraper = channel.m_strEPGScraper; + m_iUniqueId = channel.m_iUniqueId; + m_iClientId = channel.m_iClientId; + m_iClientChannelNumber = channel.m_iClientChannelNumber; + m_strClientChannelName = channel.m_strClientChannelName; + m_strInputFormat = channel.m_strInputFormat; + m_strStreamURL = channel.m_strStreamURL; + m_strFileNameAndPath = channel.m_strFileNameAndPath; + m_iClientEncryptionSystem = channel.m_iClientEncryptionSystem; + m_iCachedChannelNumber = channel.m_iCachedChannelNumber; + m_iEpgId = channel.m_iEpgId; + m_bEPGCreated = channel.m_bEPGCreated; + m_bChanged = channel.m_bChanged; + + UpdateEncryptionName(); + + return *this; +} + +/********** XBMC related channel methods **********/ + +bool CPVRChannel::Delete(void) +{ + bool bReturn = false; + CPVRDatabase *database = GetPVRDatabase(); + if (!database) + return bReturn; + + /* delete the EPG table */ + CEpg *epg = GetEPG(); + if (epg) + { + CPVRChannelPtr empty; + epg->SetChannel(empty); + g_EpgContainer.DeleteEpg(*epg, true); + CSingleLock lock(m_critSection); + m_bEPGCreated = false; + } + + bReturn = database->Delete(*this); + return bReturn; +} + +CEpg *CPVRChannel::GetEPG(void) const +{ + int iEpgId(-1); + { + CSingleLock lock(m_critSection); + if (!m_bIsHidden && m_bEPGEnabled && m_iEpgId > 0) + iEpgId = m_iEpgId; + } + + return iEpgId > 0 ? g_EpgContainer.GetById(iEpgId) : NULL; +} + +bool CPVRChannel::UpdateFromClient(const CPVRChannel &channel) +{ + SetClientID(channel.ClientID()); + SetClientChannelNumber(channel.ClientChannelNumber()); + SetInputFormat(channel.InputFormat()); + SetStreamURL(channel.StreamURL()); + SetEncryptionSystem(channel.EncryptionSystem()); + SetClientChannelName(channel.ClientChannelName()); + + CSingleLock lock(m_critSection); + if (m_strChannelName.IsEmpty()) + SetChannelName(channel.ClientChannelName()); + if (m_strIconPath.IsEmpty()||(!m_strIconPath.Equals(channel.IconPath()) && !IsUserSetIcon())) + SetIconPath(channel.IconPath()); + + return m_bChanged; +} + +bool CPVRChannel::Persist(bool bQueueWrite /* = false */) +{ + { + // not changed + CSingleLock lock(m_critSection); + if (!m_bChanged && m_iChannelId > 0) + return true; + } + + if (CPVRDatabase *database = GetPVRDatabase()) + { + if (!bQueueWrite) + { + bool bReturn = database->Persist(*this, false); + CSingleLock lock(m_critSection); + m_bChanged = !bReturn; + } + else + { + return database->Persist(*this, true); + } + } + + return false; +} + +bool CPVRChannel::SetChannelID(int iChannelId) +{ + CSingleLock lock(m_critSection); + if (m_iChannelId != iChannelId) + { + /* update the id */ + m_iChannelId = iChannelId; + SetChanged(); + m_bChanged = true; + + return true; + } + + return false; +} + +int CPVRChannel::ChannelNumber(void) const +{ + CSingleLock lock(m_critSection); + return m_iCachedChannelNumber; +} + +bool CPVRChannel::SetHidden(bool bIsHidden) +{ + CSingleLock lock(m_critSection); + + if (m_bIsHidden != bIsHidden) + { + /* update the hidden flag */ + m_bIsHidden = bIsHidden; + SetChanged(); + m_bChanged = true; + + return true; + } + + return false; +} + +bool CPVRChannel::SetLocked(bool bIsLocked) +{ + CSingleLock lock(m_critSection); + + if (m_bIsLocked != bIsLocked) + { + /* update the locked flag */ + m_bIsLocked = bIsLocked; + SetChanged(); + m_bChanged = true; + + return true; + } + + return false; +} + +bool CPVRChannel::IsRecording(void) const +{ + return g_PVRTimers->IsRecordingOnChannel(*this); +} + +bool CPVRChannel::SetIconPath(const CStdString &strIconPath, bool bIsUserSetIcon /* = false */) +{ + CSingleLock lock(m_critSection); + + /* check if the path is valid */ + if (!CFile::Exists(strIconPath) && !strIconPath.IsEmpty()) + return false; + + if (m_strIconPath != strIconPath) + { + /* update the path */ + m_strIconPath.Format("%s", strIconPath); + SetChanged(); + m_bChanged = true; + + /* did the user change the icon? */ + if (bIsUserSetIcon) + m_bIsUserSetIcon = !m_strIconPath.IsEmpty(); + + return true; + } + + return false; +} + +bool CPVRChannel::SetChannelName(const CStdString &strChannelName) +{ + CStdString strName(strChannelName); + + if (strName.IsEmpty()) + strName.Format(g_localizeStrings.Get(19085), ClientChannelNumber()); + + CSingleLock lock(m_critSection); + if (m_strChannelName != strName) + { + /* update the channel name */ + m_strChannelName = strName; + SetChanged(); + m_bChanged = true; + + return true; + } + + return false; +} + +bool CPVRChannel::SetVirtual(bool bIsVirtual) +{ + CSingleLock lock(m_critSection); + + if (m_bIsVirtual != bIsVirtual) + { + /* update the virtual flag */ + m_bIsVirtual = bIsVirtual; + SetChanged(); + m_bChanged = true; + + return true; + } + + return false; +} + +bool CPVRChannel::SetLastWatched(time_t iLastWatched) +{ + CSingleLock lock(m_critSection); + + if (m_iLastWatched != iLastWatched) + { + /* update last watched */ + m_iLastWatched = iLastWatched; + SetChanged(); + m_bChanged = true; + + return true; + } + + return false; +} + +bool CPVRChannel::IsEmpty() const +{ + CSingleLock lock(m_critSection); + return (m_strFileNameAndPath.IsEmpty() || + m_strStreamURL.IsEmpty()); +} + +/********** Client related channel methods **********/ + +bool CPVRChannel::SetUniqueID(int iUniqueId) +{ + CSingleLock lock(m_critSection); + + if (m_iUniqueId != iUniqueId) + { + /* update the unique ID */ + m_iUniqueId = iUniqueId; + SetChanged(); + m_bChanged = true; + + return true; + } + + return false; +} + +bool CPVRChannel::SetClientID(int iClientId) +{ + CSingleLock lock(m_critSection); + + if (m_iClientId != iClientId) + { + /* update the client ID */ + m_iClientId = iClientId; + SetChanged(); + m_bChanged = true; + + return true; + } + + return false; +} + +bool CPVRChannel::SetClientChannelNumber(int iClientChannelNumber) +{ + CSingleLock lock(m_critSection); + + if (m_iClientChannelNumber != iClientChannelNumber && iClientChannelNumber > 0) + { + /* update the client channel number */ + m_iClientChannelNumber = iClientChannelNumber; + SetChanged(); + m_bChanged = true; + + return true; + } + + return false; +} + +bool CPVRChannel::SetClientChannelName(const CStdString &strClientChannelName) +{ + CSingleLock lock(m_critSection); + + if (m_strClientChannelName != strClientChannelName) + { + /* update the client channel name */ + m_strClientChannelName.Format("%s", strClientChannelName); + SetChanged(); + // this is not persisted, so don't update m_bChanged + + return true; + } + + return false; +} + +bool CPVRChannel::SetInputFormat(const CStdString &strInputFormat) +{ + CSingleLock lock(m_critSection); + + if (m_strInputFormat != strInputFormat) + { + /* update the input format */ + m_strInputFormat.Format("%s", strInputFormat); + SetChanged(); + m_bChanged = true; + + return true; + } + + return false; +} + +bool CPVRChannel::SetStreamURL(const CStdString &strStreamURL) +{ + CSingleLock lock(m_critSection); + + if (m_strStreamURL != strStreamURL) + { + /* update the stream url */ + m_strStreamURL.Format("%s", strStreamURL); + SetChanged(); + m_bChanged = true; + + return true; + } + + return false; +} + +void CPVRChannel::UpdatePath(unsigned int iNewChannelNumber) +{ + CStdString strFileNameAndPath; + CSingleLock lock(m_critSection); + CPVRChannelGroupPtr group = g_PVRChannelGroups->GetGroupAll(m_bIsRadio); + + if (group) + { + strFileNameAndPath.Format("pvr://channels/%s/%s/%i.pvr", (m_bIsRadio ? "radio" : "tv"), group->GroupName().c_str(), iNewChannelNumber); + if (m_strFileNameAndPath != strFileNameAndPath) + { + m_strFileNameAndPath = strFileNameAndPath; + SetChanged(); + } + } +} + +bool CPVRChannel::SetEncryptionSystem(int iClientEncryptionSystem) +{ + CSingleLock lock(m_critSection); + + if (m_iClientEncryptionSystem != iClientEncryptionSystem) + { + /* update the client encryption system */ + m_iClientEncryptionSystem = iClientEncryptionSystem; + UpdateEncryptionName(); + SetChanged(); + m_bChanged = true; + + return true; + } + + return false; +} + +void CPVRChannel::UpdateEncryptionName(void) +{ + // http://www.dvb.org/index.php?id=174 + // http://en.wikipedia.org/wiki/Conditional_access_system + CStdString strName; + CSingleLock lock(m_critSection); + + if ( m_iClientEncryptionSystem == 0x0000) + strName = g_localizeStrings.Get(19013); /* Free To Air */ + else if (m_iClientEncryptionSystem < 0x0000) + strName = g_localizeStrings.Get(13205); /* Unknown */ + else + { + if ( m_iClientEncryptionSystem >= 0x0001 && + m_iClientEncryptionSystem <= 0x009F) + strName = g_localizeStrings.Get(19014); /* Fixed */ + else if (m_iClientEncryptionSystem >= 0x00A0 && + m_iClientEncryptionSystem <= 0x00A1) + strName = g_localizeStrings.Get(338); /* Analog */ + else if (m_iClientEncryptionSystem >= 0x00A2 && + m_iClientEncryptionSystem <= 0x00FF) + strName = g_localizeStrings.Get(19014); /* Fixed */ + else if (m_iClientEncryptionSystem >= 0x0100 && + m_iClientEncryptionSystem <= 0x01FF) + strName = "SECA Mediaguard"; + else if (m_iClientEncryptionSystem == 0x0464) + strName = "EuroDec"; + else if (m_iClientEncryptionSystem >= 0x0500 && + m_iClientEncryptionSystem <= 0x05FF) + strName = "Viaccess"; + else if (m_iClientEncryptionSystem >= 0x0600 && + m_iClientEncryptionSystem <= 0x06FF) + strName = "Irdeto"; + else if (m_iClientEncryptionSystem >= 0x0900 && + m_iClientEncryptionSystem <= 0x09FF) + strName = "NDS Videoguard"; + else if (m_iClientEncryptionSystem >= 0x0B00 && + m_iClientEncryptionSystem <= 0x0BFF) + strName = "Conax"; + else if (m_iClientEncryptionSystem >= 0x0D00 && + m_iClientEncryptionSystem <= 0x0DFF) + strName = "CryptoWorks"; + else if (m_iClientEncryptionSystem >= 0x0E00 && + m_iClientEncryptionSystem <= 0x0EFF) + strName = "PowerVu"; + else if (m_iClientEncryptionSystem == 0x1000) + strName = "RAS"; + else if (m_iClientEncryptionSystem >= 0x1200 && + m_iClientEncryptionSystem <= 0x12FF) + strName = "NagraVision"; + else if (m_iClientEncryptionSystem >= 0x1700 && + m_iClientEncryptionSystem <= 0x17FF) + strName = "BetaCrypt"; + else if (m_iClientEncryptionSystem >= 0x1800 && + m_iClientEncryptionSystem <= 0x18FF) + strName = "NagraVision"; + else if (m_iClientEncryptionSystem == 0x22F0) + strName = "Codicrypt"; + else if (m_iClientEncryptionSystem == 0x2600) + strName = "BISS"; + else if (m_iClientEncryptionSystem == 0x4347) + strName = "CryptOn"; + else if (m_iClientEncryptionSystem == 0x4800) + strName = "Accessgate"; + else if (m_iClientEncryptionSystem == 0x4900) + strName = "China Crypt"; + else if (m_iClientEncryptionSystem == 0x4A10) + strName = "EasyCas"; + else if (m_iClientEncryptionSystem == 0x4A20) + strName = "AlphaCrypt"; + else if (m_iClientEncryptionSystem == 0x4A70) + strName = "DreamCrypt"; + else if (m_iClientEncryptionSystem == 0x4A60) + strName = "SkyCrypt"; + else if (m_iClientEncryptionSystem == 0x4A61) + strName = "Neotioncrypt"; + else if (m_iClientEncryptionSystem == 0x4A62) + strName = "SkyCrypt"; + else if (m_iClientEncryptionSystem == 0x4A63) + strName = "Neotion SHL"; + else if (m_iClientEncryptionSystem >= 0x4A64 && + m_iClientEncryptionSystem <= 0x4A6F) + strName = "SkyCrypt"; + else if (m_iClientEncryptionSystem == 0x4A80) + strName = "ThalesCrypt"; + else if (m_iClientEncryptionSystem == 0x4AA1) + strName = "KeyFly"; + else if (m_iClientEncryptionSystem == 0x4ABF) + strName = "DG-Crypt"; + else if (m_iClientEncryptionSystem >= 0x4AD0 && + m_iClientEncryptionSystem <= 0x4AD1) + strName = "X-Crypt"; + else if (m_iClientEncryptionSystem == 0x4AD4) + strName = "OmniCrypt"; + else if (m_iClientEncryptionSystem == 0x4AE0) + strName = "RossCrypt"; + else if (m_iClientEncryptionSystem == 0x5500) + strName = "Z-Crypt"; + else if (m_iClientEncryptionSystem == 0x5501) + strName = "Griffin"; + else + strName = g_localizeStrings.Get(19499); /* Unknown */ + + strName.AppendFormat(" (%04X)", m_iClientEncryptionSystem); + } + + m_strClientEncryptionName = strName; +} + +/********** EPG methods **********/ + +int CPVRChannel::GetEPG(CFileItemList &results) const +{ + CEpg *epg = GetEPG(); + if (!epg) + { + CLog::Log(LOGDEBUG, "PVR - %s - cannot get EPG for channel '%s'", + __FUNCTION__, m_strChannelName.c_str()); + return -1; + } + + return epg->Get(results); +} + +bool CPVRChannel::ClearEPG() const +{ + CEpg *epg = GetEPG(); + if (epg) + epg->Clear(); + + return true; +} + +bool CPVRChannel::GetEPGNow(CEpgInfoTag &tag) const +{ + CEpg *epg = GetEPG(); + return epg ? epg->InfoTagNow(tag) : false; +} + +bool CPVRChannel::GetEPGNext(CEpgInfoTag &tag) const +{ + CEpg *epg = GetEPG(); + return epg ? epg->InfoTagNext(tag) : false; +} + +bool CPVRChannel::SetEPGEnabled(bool bEPGEnabled) +{ + CSingleLock lock(m_critSection); + + if (m_bEPGEnabled != bEPGEnabled) + { + /* update the EPG flag */ + m_bEPGEnabled = bEPGEnabled; + SetChanged(); + m_bChanged = true; + + /* clear the previous EPG entries if needed */ + if (!m_bEPGEnabled && m_bEPGCreated) + ClearEPG(); + + return true; + } + + return false; +} + +bool CPVRChannel::SetEPGScraper(const CStdString &strScraper) +{ + CSingleLock lock(m_critSection); + + if (m_strEPGScraper != strScraper) + { + bool bCleanEPG = !m_strEPGScraper.IsEmpty() || strScraper.IsEmpty(); + + /* update the scraper name */ + m_strEPGScraper.Format("%s", strScraper); + SetChanged(); + m_bChanged = true; + + /* clear the previous EPG entries if needed */ + if (bCleanEPG && m_bEPGEnabled && m_bEPGCreated) + ClearEPG(); + + return true; + } + + return false; +} + +void CPVRChannel::SetCachedChannelNumber(unsigned int iChannelNumber) +{ + CSingleLock lock(m_critSection); + m_iCachedChannelNumber = iChannelNumber; +} + +void CPVRChannel::ToSortable(SortItem& sortable) const +{ + CSingleLock lock(m_critSection); + sortable[FieldChannelName] = m_strChannelName; +} + +int CPVRChannel::ChannelID(void) const +{ + CSingleLock lock(m_critSection); + return m_iChannelId; +} + +bool CPVRChannel::IsNew(void) const +{ + CSingleLock lock(m_critSection); + return m_iChannelId <= 0; +} + +bool CPVRChannel::IsHidden(void) const +{ + CSingleLock lock(m_critSection); + return m_bIsHidden; +} + +bool CPVRChannel::IsLocked(void) const +{ + CSingleLock lock(m_critSection); + return m_bIsLocked; +} + +CStdString CPVRChannel::IconPath(void) const +{ + CSingleLock lock(m_critSection); + CStdString strReturn(m_strIconPath); + return strReturn; +} + +bool CPVRChannel::IsUserSetIcon(void) const +{ + CSingleLock lock(m_critSection); + return m_bIsUserSetIcon; +} + +CStdString CPVRChannel::ChannelName(void) const +{ + CSingleLock lock(m_critSection); + return m_strChannelName; +} + +bool CPVRChannel::IsVirtual(void) const +{ + CSingleLock lock(m_critSection); + return m_bIsVirtual; +} + +time_t CPVRChannel::LastWatched(void) const +{ + CSingleLock lock(m_critSection); + return m_iLastWatched; +} + +bool CPVRChannel::IsChanged() const +{ + CSingleLock lock(m_critSection); + return m_bChanged; +} + +int CPVRChannel::UniqueID(void) const +{ + CSingleLock lock(m_critSection); + return m_iUniqueId; +} + +int CPVRChannel::ClientID(void) const +{ + CSingleLock lock(m_critSection); + return m_iClientId; +} + +int CPVRChannel::ClientChannelNumber(void) const +{ + CSingleLock lock(m_critSection); + return m_iClientChannelNumber; +} + +CStdString CPVRChannel::ClientChannelName(void) const +{ + CSingleLock lock(m_critSection); + CStdString strReturn(m_strClientChannelName); + return strReturn; +} + +CStdString CPVRChannel::InputFormat(void) const +{ + CSingleLock lock(m_critSection); + CStdString strReturn(m_strInputFormat); + return strReturn; +} + +CStdString CPVRChannel::StreamURL(void) const +{ + CSingleLock lock(m_critSection); + CStdString strReturn(m_strStreamURL); + return strReturn; +} + +CStdString CPVRChannel::Path(void) const +{ + CSingleLock lock(m_critSection); + CStdString strReturn(m_strFileNameAndPath); + return strReturn; +} + +bool CPVRChannel::IsEncrypted(void) const +{ + CSingleLock lock(m_critSection); + return m_iClientEncryptionSystem > 0; +} + +int CPVRChannel::EncryptionSystem(void) const +{ + CSingleLock lock(m_critSection); + return m_iClientEncryptionSystem; +} + +CStdString CPVRChannel::EncryptionName(void) const +{ + CSingleLock lock(m_critSection); + CStdString strReturn(m_strClientEncryptionName); + return strReturn; +} + +int CPVRChannel::EpgID(void) const +{ + CSingleLock lock(m_critSection); + return m_iEpgId; +} + +bool CPVRChannel::EPGEnabled(void) const +{ + CSingleLock lock(m_critSection); + return m_bEPGEnabled; +} + +CStdString CPVRChannel::EPGScraper(void) const +{ + CSingleLock lock(m_critSection); + CStdString strReturn(m_strEPGScraper); + return strReturn; +} + +bool CPVRChannel::CanRecord(void) const +{ + return g_PVRClients->SupportsRecordings(m_iClientId); +} diff --git a/xbmc/pvr/channels/PVRChannel.h b/xbmc/pvr/channels/PVRChannel.h new file mode 100644 index 0000000000..69986c163e --- /dev/null +++ b/xbmc/pvr/channels/PVRChannel.h @@ -0,0 +1,494 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "XBDateTime.h" +#include "FileItem.h" +#include "addons/include/xbmc_pvr_types.h" +#include "utils/Observer.h" +#include "threads/CriticalSection.h" + +#include <boost/shared_ptr.hpp> + +namespace EPG +{ + class CEpg; +} + +namespace PVR +{ + class CPVRDatabase; + class CPVRChannelGroupInternal; + + class CPVRChannel; + typedef boost::shared_ptr<PVR::CPVRChannel> CPVRChannelPtr; + + /** PVR Channel class */ + class CPVRChannel : public Observable + { + friend class CPVRDatabase; + friend class CPVRChannelGroupInternal; + + public: + /*! @brief Create a new channel */ + CPVRChannel(bool bRadio = false); + CPVRChannel(const PVR_CHANNEL &channel, unsigned int iClientId); + CPVRChannel(const CPVRChannel &channel); + + bool operator ==(const CPVRChannel &right) const; + bool operator !=(const CPVRChannel &right) const; + CPVRChannel &operator=(const CPVRChannel &channel); + + /*! @name XBMC related channel methods + */ + //@{ + + /*! + * @brief Delete this channel from the database and delete the corresponding EPG table if it exists. + * @return True if it was deleted successfully, false otherwise. + */ + bool Delete(void); + + /*! + * @brief Update this channel tag with the data of the given channel tag. + * @param channel The new channel data. + * @return True if something changed, false otherwise. + */ + bool UpdateFromClient(const CPVRChannel &channel); + + /*! + * @brief Persists the changes in the database. + * @param bQueueWrite Queue the change and write changes later. + * @return True if the changes were saved succesfully, false otherwise. + */ + bool Persist(bool bQueueWrite = false); + + /*! + * @return The identifier given to this channel by the TV database. + */ + int ChannelID(void) const; + + /*! + * @return True when not persisted yet, false otherwise. + */ + bool IsNew(void) const; + + /*! + * @brief Set the identifier for this channel. + * @param iDatabaseId The new channel ID + * @return True if the something changed, false otherwise. + */ + bool SetChannelID(int iDatabaseId); + + /*! + * @return The channel number used by XBMC by the currently active group. + */ + int ChannelNumber(void) const; + + /*! + * @return True if this channel is a radio channel, false if not. + */ + bool IsRadio(void) const { return m_bIsRadio; } + + /*! + * @return True if this channel is hidden. False if not. + */ + bool IsHidden(void) const; + + /*! + * @brief Set to true to hide this channel. Set to false to unhide it. + * + * Set to true to hide this channel. Set to false to unhide it. + * The EPG of hidden channels won't be updated. + * @param bIsHidden The new setting. + * @return True if the something changed, false otherwise. + */ + bool SetHidden(bool bIsHidden); + + /*! + * @return True if this channel is locked. False if not. + */ + bool IsLocked(void) const; + + /*! + * @brief Set to true to lock this channel. Set to false to unlock it. + * + * Set to true to lock this channel. Set to false to unlock it. + * Locked channels need can only be viewed if parental PIN entered. + * @param bIsLocked The new setting. + * @return True if the something changed, false otherwise. + */ + bool SetLocked(bool bIsLocked); + + /*! + * @return True if a recording is currently running on this channel. False if not. + */ + bool IsRecording(void) const; + + /*! + * @return The path to the icon for this channel. + */ + CStdString IconPath(void) const; + + /*! + * @return True if this user changed icon via GUI. False if not. + */ + bool IsUserSetIcon(void) const; + + /*! + * @brief Set the path to the icon for this channel. + * @param strIconPath The new path. + * @param bIsUserSetIcon true if user changed the icon via GUI, false otherwise. + * @return True if the something changed, false otherwise. + */ + bool SetIconPath(const CStdString &strIconPath, bool bIsUserSetIcon = false); + + /*! + * @return The name for this channel used by XBMC. + */ + CStdString ChannelName(void) const; + + /*! + * @brief Set the name for this channel used by XBMC. + * @param strChannelName The new channel name. + * @return True if the something changed, false otherwise. + */ + bool SetChannelName(const CStdString &strChannelName); + + /*! + * @return True if this channel is marked as virtual. False if not. + */ + bool IsVirtual(void) const; + + /*! + * @brief True if this channel is marked as virtual. False if not. + * @param bIsVirtual The new value. + * @return True if the something changed, false otherwise. + */ + bool SetVirtual(bool bIsVirtual); + + /*! + * @return Time channel has been watched last. + */ + time_t LastWatched() const; + + /*! + * @brief Last time channel has been watched + * @param iLastWatched The new value. + * @return True if the something changed, false otherwise. + */ + bool SetLastWatched(time_t iLastWatched); + + /*! + * @brief True if this channel has no file or stream name + * @return True if this channel has no file or stream name + */ + bool IsEmpty() const; + + bool IsChanged() const; + //@} + + /*! @name Client related channel methods + */ + //@{ + + /*! + * @brief A unique identifier for this channel. + * + * A unique identifier for this channel. + * It can be used to find the same channel on different providers + * + * @return The Unique ID. + */ + int UniqueID(void) const; + + /*! + * @brief Change the unique identifier for this channel. + * @param iUniqueId The new unique ID. + * @return True if the something changed, false otherwise. + */ + bool SetUniqueID(int iUniqueId); + + /*! + * @return The identifier of the client that serves this channel. + */ + int ClientID(void) const; + + /*! + * @brief Set the identifier of the client that serves this channel. + * @param iClientId The new ID. + * @return True if the something changed, false otherwise. + */ + bool SetClientID(int iClientId); + + /*! + * @return The channel number on the client. + */ + int ClientChannelNumber(void) const; + + /*! + * @brief Set the channel number on the client. + * + * Set the channel number on the client. + * It will only be changed in this tag and won't change anything on the client. + * + * @param iClientChannelNumber The new channel number + * @return True if the something changed, false otherwise. + */ + bool SetClientChannelNumber(int iClientChannelNumber); + + /*! + * @return The name of this channel on the client. + */ + CStdString ClientChannelName(void) const; + + /*! + * @brief Set the name of this channel on the client. + * + * Set the name of this channel on the client. + * It will only be changed in this tag and won't change anything on the client. + * + * @param strClientChannelName The new channel name + * @return True if the something changed, false otherwise. + */ + bool SetClientChannelName(const CStdString &strClientChannelName); + + /*! + * @brief The stream input type + * + * The stream input type + * If it is empty, ffmpeg will try to scan the stream to find the right input format. + * See "xbmc/cores/dvdplayer/Codecs/ffmpeg/libavformat/allformats.c" for a + * list of the input formats. + * + * @return The stream input type + */ + CStdString InputFormat(void) const; + + /*! + * @brief Set the stream input type + * @param strInputFormat The new input format. + * @return True if the something changed, false otherwise. + */ + bool SetInputFormat(const CStdString &strInputFormat); + + /*! + * @brief The stream URL to access this channel. + * + * The stream URL to access this channel. + * If this is empty, then the client should be used to read from the channel. + * + * @return The stream URL to access this channel. + */ + CStdString StreamURL(void) const; + + /*! + * @brief Set the stream URL to access this channel. + * + * Set the stream URL to access this channel. + * If this is empty, then the client should be used to read from the channel. + * + * @param strStreamURL The new stream URL. + * @return True if the something changed, false otherwise. + */ + bool SetStreamURL(const CStdString &strStreamURL); + + /*! + * @brief The path in the XBMC VFS to be used by PVRManager to open and read the stream. + * @return The path in the XBMC VFS to be used by PVRManager to open and read the stream. + */ + CStdString Path(void) const; + + void ToSortable(SortItem& sortable) const; + + /*! + * @brief Update the path after the channel number in the internal group changed. + */ + void UpdatePath(unsigned int iNewChannelNumber); + + /*! + * @brief Return true if this channel is encrypted. + * + * Return true if this channel is encrypted. Does not inform whether XBMC can play the file. + * Decryption should be done by the client. + * + * @return Return true if this channel is encrypted. + */ + bool IsEncrypted(void) const; + + + /*! + * @brief Return the encryption system ID for this channel. 0 for FTA. + * + * Return the encryption system ID for this channel. 0 for FTA. + * The values are documented on: http://www.dvb.org/index.php?id=174. + * + * @return Return the encryption system ID for this channel. + */ + int EncryptionSystem(void) const; + + /*! + * @brief Set the encryption ID (CAID) for this channel. + * @param iClientEncryptionSystem The new CAID. + * @return True if the something changed, false otherwise. + */ + bool SetEncryptionSystem(int iClientEncryptionSystem); + + /*! + * @return A friendly name for the used encryption system. + */ + CStdString EncryptionName(void) const; + //@} + + /*! @name EPG methods + */ + //@{ + + /*! + * @return The ID of the EPG table to use for this channel or -1 if it isn't set. + */ + int EpgID(void) const; + + /*! + * @brief Get the EPG table for this channel. + * @return The EPG for this channel. + */ + EPG::CEpg *GetEPG(void) const; + + /*! + * @brief Get the EPG table for this channel. + * @param results The file list to store the results in. + * @return The number of tables that were added. + */ + int GetEPG(CFileItemList &results) const; + + /*! + * @brief Clear the EPG for this channel. + * @return True if it was cleared, false if not. + */ + bool ClearEPG(void) const; + + /*! + * @brief Get the EPG tag that is active on this channel now. + * + * Get the EPG tag that is active on this channel now. + * Will return an empty tag if there is none. + * + * @return The EPG tag that is active on this channel now. + */ + bool GetEPGNow(EPG::CEpgInfoTag &tag) const; + + /*! + * @brief Get the EPG tag that is active on this channel next. + * + * Get the EPG tag that is active on this channel next. + * Will return an empty tag if there is none. + * + * @return The EPG tag that is active on this channel next. + */ + bool GetEPGNext(EPG::CEpgInfoTag &tag) const; + + /*! + * @return Don't use an EPG for this channel if set to false. + */ + bool EPGEnabled(void) const; + + /*! + * @brief Set to true if an EPG should be used for this channel. Set to false otherwise. + * @param bEPGEnabled The new value. + * @return True if the something changed, false otherwise. + */ + bool SetEPGEnabled(bool bEPGEnabled); + + /*! + * @brief Get the name of the scraper to be used for this channel. + * + * Get the name of the scraper to be used for this channel. + * The default is 'client', which means the EPG should be loaded from the backend. + * + * @return The name of the scraper to be used for this channel. + */ + CStdString EPGScraper(void) const; + + /*! + * @brief Set the name of the scraper to be used for this channel. + * + * Set the name of the scraper to be used for this channel. + * Set to "client" to load the EPG from the backend + * + * @param strScraper The new scraper name. + * @return True if the something changed, false otherwise. + */ + bool SetEPGScraper(const CStdString &strScraper); + + void SetCachedChannelNumber(unsigned int iChannelNumber); + + bool CanRecord(void) const; + //@} + private: + /*! + * @brief Update the encryption name after SetEncryptionSystem() has been called. + */ + void UpdateEncryptionName(void); + + /*! @name XBMC related channel data + */ + //@{ + int m_iChannelId; /*!< the identifier given to this channel by the TV database */ + bool m_bIsRadio; /*!< true if this channel is a radio channel, false if not */ + bool m_bIsHidden; /*!< true if this channel is hidden, false if not */ + bool m_bIsUserSetIcon; /*!< true if user set the icon via GUI, false if not */ + bool m_bIsLocked; /*!< true if channel is locked, false if not */ + CStdString m_strIconPath; /*!< the path to the icon for this channel */ + CStdString m_strChannelName; /*!< the name for this channel used by XBMC */ + bool m_bIsVirtual; /*!< true if this channel is marked as virtual, false if not */ + time_t m_iLastWatched; /*!< last time channel has been watched */ + bool m_bChanged; /*!< true if anything in this entry was changed that needs to be persisted */ + unsigned int m_iCachedChannelNumber; /*!< the cached channel number in the selected group */ + //@} + + /*! @name EPG related channel data + */ + //@{ + int m_iEpgId; /*!< the id of the EPG for this channel */ + bool m_bEPGCreated; /*!< true if an EPG has been created for this channel */ + bool m_bEPGEnabled; /*!< don't use an EPG for this channel if set to false */ + CStdString m_strEPGScraper; /*!< the name of the scraper to be used for this channel */ + //@} + + /*! @name Client related channel data + */ + //@{ + int m_iUniqueId; /*!< the unique identifier for this channel */ + int m_iClientId; /*!< the identifier of the client that serves this channel */ + int m_iClientChannelNumber; /*!< the channel number on the client */ + CStdString m_strClientChannelName; /*!< the name of this channel on the client */ + CStdString m_strInputFormat; /*!< the stream input type based on ffmpeg/libavformat/allformats.c */ + CStdString m_strStreamURL; /*!< URL of the stream. Use the client to read stream if this is empty */ + CStdString m_strFileNameAndPath; /*!< the filename to be used by PVRManager to open and read the stream */ + int m_iClientEncryptionSystem; /*!< the encryption system used by this channel. 0 for FreeToAir, -1 for unknown */ + CStdString m_strClientEncryptionName; /*!< the name of the encryption system used by this channel */ + //@} + + CCriticalSection m_critSection; + }; +} diff --git a/xbmc/pvr/channels/PVRChannelGroup.cpp b/xbmc/pvr/channels/PVRChannelGroup.cpp new file mode 100644 index 0000000000..36084509d4 --- /dev/null +++ b/xbmc/pvr/channels/PVRChannelGroup.cpp @@ -0,0 +1,1153 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +/** + * TODO: + * - use Observable here, so we can use event driven operations later + */ + +#include "settings/GUISettings.h" +#include "guilib/GUIWindowManager.h" +#include "dialogs/GUIDialogYesNo.h" +#include "dialogs/GUIDialogOK.h" +#include "music/tags/MusicInfoTag.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "threads/SingleLock.h" + +#include "PVRChannelGroupsContainer.h" +#include "pvr/PVRDatabase.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClients.h" +#include "epg/EpgContainer.h" + +using namespace PVR; +using namespace EPG; + +CPVRChannelGroup::CPVRChannelGroup(void) : + m_bRadio(false), + m_iGroupType(PVR_GROUP_TYPE_DEFAULT), + m_iGroupId(-1), + m_bLoaded(false), + m_bChanged(false), + m_bUsingBackendChannelOrder(false) +{ +} + +CPVRChannelGroup::CPVRChannelGroup(bool bRadio, unsigned int iGroupId, const CStdString &strGroupName) : + m_bRadio(bRadio), + m_iGroupType(PVR_GROUP_TYPE_DEFAULT), + m_iGroupId(iGroupId), + m_strGroupName(strGroupName), + m_bLoaded(false), + m_bChanged(false), + m_bUsingBackendChannelOrder(false) +{ +} + +CPVRChannelGroup::CPVRChannelGroup(const PVR_CHANNEL_GROUP &group) : + m_bRadio(group.bIsRadio), + m_iGroupType(PVR_GROUP_TYPE_DEFAULT), + m_iGroupId(-1), + m_strGroupName(group.strGroupName), + m_bLoaded(false), + m_bChanged(false), + m_bUsingBackendChannelOrder(false) +{ +} + +CPVRChannelGroup::~CPVRChannelGroup(void) +{ + Unload(); +} + +bool CPVRChannelGroup::operator==(const CPVRChannelGroup& right) const +{ + return (m_bRadio == right.m_bRadio && + m_iGroupType == right.m_iGroupType && + m_iGroupId == right.m_iGroupId && + m_strGroupName.Equals(right.m_strGroupName)); +} + +bool CPVRChannelGroup::operator!=(const CPVRChannelGroup &right) const +{ + return !(*this == right); +} + +CPVRChannelGroup::CPVRChannelGroup(const CPVRChannelGroup &group) +{ + m_bRadio = group.m_bRadio; + m_iGroupType = group.m_iGroupType; + m_iGroupId = group.m_iGroupId; + m_strGroupName = group.m_strGroupName; + m_bLoaded = group.m_bLoaded; + m_bChanged = group.m_bChanged; + m_bUsingBackendChannelOrder = group.m_bUsingBackendChannelOrder; + m_bUsingBackendChannelNumbers = group.m_bUsingBackendChannelNumbers; + + for (int iPtr = 0; iPtr < group.Size(); iPtr++) + m_members.push_back(group.m_members.at(iPtr)); +} + +int CPVRChannelGroup::Load(void) +{ + /* make sure this container is empty before loading */ + Unload(); + + m_bUsingBackendChannelOrder = g_guiSettings.GetBool("pvrmanager.backendchannelorder"); + m_bUsingBackendChannelNumbers = g_guiSettings.GetBool("pvrmanager.usebackendchannelnumbers"); + + int iChannelCount = m_iGroupId > 0 ? LoadFromDb() : 0; + CLog::Log(LOGDEBUG, "PVRChannelGroup - %s - %d channels loaded from the database for group '%s'", + __FUNCTION__, iChannelCount, m_strGroupName.c_str()); + + Update(); + if (Size() - iChannelCount > 0) + { + CLog::Log(LOGDEBUG, "PVRChannelGroup - %s - %d channels added from clients to group '%s'", + __FUNCTION__, Size() - iChannelCount, m_strGroupName.c_str()); + } + + SortByChannelNumber(); + Renumber(); + + g_guiSettings.RegisterObserver(this); + m_bLoaded = true; + + return Size(); +} + +void CPVRChannelGroup::Unload(void) +{ + CSingleLock lock(m_critSection); + g_guiSettings.UnregisterObserver(this); + m_members.clear(); +} + +bool CPVRChannelGroup::Update(void) +{ + if (GroupType() == PVR_GROUP_TYPE_USER_DEFINED || + !g_guiSettings.GetBool("pvrmanager.syncchannelgroups")) + return false; + + CPVRChannelGroup PVRChannels_tmp(m_bRadio, m_iGroupId, m_strGroupName); + PVRChannels_tmp.LoadFromClients(); + + return UpdateGroupEntries(PVRChannels_tmp); +} + +bool CPVRChannelGroup::SetChannelNumber(const CPVRChannel &channel, unsigned int iChannelNumber) +{ + bool bReturn(false); + CSingleLock lock(m_critSection); + + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + { + if (*m_members.at(iChannelPtr).channel == channel) + { + if (m_members.at(iChannelPtr).iChannelNumber != iChannelNumber) + { + m_bChanged = true; + bReturn = true; + m_members.at(iChannelPtr).iChannelNumber = iChannelNumber; + } + break; + } + } + + return bReturn; +} + +bool CPVRChannelGroup::MoveChannel(unsigned int iOldChannelNumber, unsigned int iNewChannelNumber, bool bSaveInDb /* = true */) +{ + if (iOldChannelNumber == iNewChannelNumber) + return true; + + bool bReturn(false); + CSingleLock lock(m_critSection); + + /* make sure the list is sorted by channel number */ + SortByChannelNumber(); + + /* old channel number out of range */ + if (iOldChannelNumber > m_members.size()) + return bReturn; + + /* new channel number out of range */ + if (iNewChannelNumber > m_members.size()) + iNewChannelNumber = m_members.size(); + + /* move the channel in the list */ + PVRChannelGroupMember entry = m_members.at(iOldChannelNumber - 1); + m_members.erase(m_members.begin() + iOldChannelNumber - 1); + m_members.insert(m_members.begin() + iNewChannelNumber - 1, entry); + + /* renumber the list */ + Renumber(); + + m_bChanged = true; + + if (bSaveInDb) + bReturn = Persist(); + else + bReturn = true; + + CLog::Log(LOGNOTICE, "CPVRChannelGroup - %s - %s channel '%s' moved to channel number '%d'", + __FUNCTION__, (m_bRadio ? "radio" : "tv"), entry.channel->ChannelName().c_str(), iNewChannelNumber); + + return true; +} + +void CPVRChannelGroup::SearchAndSetChannelIcons(bool bUpdateDb /* = false */) +{ + if (g_guiSettings.GetString("pvrmenu.iconpath").IsEmpty()) + return; + + CPVRDatabase *database = GetPVRDatabase(); + if (!database) + return; + + CSingleLock lock(m_critSection); + + for (unsigned int ptr = 0; ptr < m_members.size(); ptr++) + { + PVRChannelGroupMember groupMember = m_members.at(ptr); + + /* skip if an icon is already set */ + if (!groupMember.channel->IconPath().IsEmpty()) + continue; + + CStdString strBasePath = g_guiSettings.GetString("pvrmenu.iconpath"); + CStdString strChannelName = groupMember.channel->ClientChannelName(); + + CStdString strIconPath = strBasePath + groupMember.channel->ClientChannelName(); + CStdString strIconPathLower = strBasePath + strChannelName.ToLower(); + CStdString strIconPathUid; + strIconPathUid.Format("%s/%08d", strBasePath, groupMember.channel->UniqueID()); + + groupMember.channel->SetIconPath(strIconPath + ".tbn") || + groupMember.channel->SetIconPath(strIconPath + ".jpg") || + groupMember.channel->SetIconPath(strIconPath + ".png") || + + groupMember.channel->SetIconPath(strIconPathLower + ".tbn") || + groupMember.channel->SetIconPath(strIconPathLower + ".jpg") || + groupMember.channel->SetIconPath(strIconPathLower + ".png") || + + groupMember.channel->SetIconPath(strIconPathUid + ".tbn") || + groupMember.channel->SetIconPath(strIconPathUid + ".jpg") || + groupMember.channel->SetIconPath(strIconPathUid + ".png"); + + if (bUpdateDb) + groupMember.channel->Persist(); + + /* TODO: start channel icon scraper here if nothing was found */ + } +} + +/********** sort methods **********/ + +struct sortByClientChannelNumber +{ + bool operator()(const PVRChannelGroupMember &channel1, const PVRChannelGroupMember &channel2) + { + return channel1.channel->ClientChannelNumber() < channel2.channel->ClientChannelNumber(); + } +}; + +struct sortByChannelNumber +{ + bool operator()(const PVRChannelGroupMember &channel1, const PVRChannelGroupMember &channel2) + { + return channel1.iChannelNumber < channel2.iChannelNumber; + } +}; + +void CPVRChannelGroup::SortByClientChannelNumber(void) +{ + CSingleLock lock(m_critSection); + sort(m_members.begin(), m_members.end(), sortByClientChannelNumber()); +} + +void CPVRChannelGroup::SortByChannelNumber(void) +{ + CSingleLock lock(m_critSection); + sort(m_members.begin(), m_members.end(), sortByChannelNumber()); +} + +/********** getters **********/ + +CPVRChannelPtr CPVRChannelGroup::GetByClient(int iUniqueChannelId, int iClientID) const +{ + CSingleLock lock(m_critSection); + + for (unsigned int ptr = 0; ptr < m_members.size(); ptr++) + { + PVRChannelGroupMember groupMember = m_members.at(ptr); + if (groupMember.channel->UniqueID() == iUniqueChannelId && + groupMember.channel->ClientID() == iClientID) + return groupMember.channel; + } + + CPVRChannelPtr empty; + return empty; +} + +CPVRChannelPtr CPVRChannelGroup::GetByChannelID(int iChannelID) const +{ + CSingleLock lock(m_critSection); + + for (unsigned int ptr = 0; ptr < m_members.size(); ptr++) + { + PVRChannelGroupMember groupMember = m_members.at(ptr); + if (groupMember.channel->ChannelID() == iChannelID) + return groupMember.channel; + } + + CPVRChannelPtr empty; + return empty; +} + +CPVRChannelPtr CPVRChannelGroup::GetByChannelEpgID(int iEpgID) const +{ + CSingleLock lock(m_critSection); + + for (unsigned int ptr = 0; ptr < m_members.size(); ptr++) + { + PVRChannelGroupMember groupMember = m_members.at(ptr); + if (groupMember.channel->EpgID() == iEpgID) + return groupMember.channel; + } + + CPVRChannelPtr empty; + return empty; +} + +CPVRChannelPtr CPVRChannelGroup::GetByUniqueID(int iUniqueID) const +{ + CSingleLock lock(m_critSection); + + for (unsigned int ptr = 0; ptr < m_members.size(); ptr++) + { + PVRChannelGroupMember groupMember = m_members.at(ptr); + if (groupMember.channel->UniqueID() == iUniqueID) + return groupMember.channel; + } + + CPVRChannelPtr empty; + return empty; +} + +CFileItemPtr CPVRChannelGroup::GetLastPlayedChannel(unsigned int iCurrentChannel /* = -1 */) const +{ + CSingleLock lock(m_critSection); + + time_t tCurrentLastWatched(0), tMaxLastWatched(0); + if (iCurrentChannel > 0) + { + CPVRChannelPtr channel = GetByChannelID(iCurrentChannel); + if (channel.get()) + { + CDateTime::GetCurrentDateTime().GetAsTime(tMaxLastWatched); + channel->SetLastWatched(tMaxLastWatched); + channel->Persist(); + } + } + + CPVRChannelPtr returnChannel; + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + { + PVRChannelGroupMember groupMember = m_members.at(iChannelPtr); + + if (g_PVRClients->IsConnectedClient(groupMember.channel->ClientID()) && + groupMember.channel->LastWatched() > 0 && + (tMaxLastWatched == 0 || groupMember.channel->LastWatched() < tMaxLastWatched) && + (tCurrentLastWatched == 0 || groupMember.channel->LastWatched() > tCurrentLastWatched)) + { + returnChannel = groupMember.channel; + tCurrentLastWatched = returnChannel->LastWatched(); + } + } + + if (returnChannel) + { + CFileItemPtr retVal = CFileItemPtr(new CFileItem(*returnChannel)); + return retVal; + } + + CFileItemPtr retVal = CFileItemPtr(new CFileItem); + return retVal; +} + + +unsigned int CPVRChannelGroup::GetChannelNumber(const CPVRChannel &channel) const +{ + unsigned int iReturn = 0; + CSingleLock lock(m_critSection); + unsigned int iSize = m_members.size(); + + for (unsigned int iChannelPtr = 0; iChannelPtr < iSize; iChannelPtr++) + { + PVRChannelGroupMember member = m_members.at(iChannelPtr); + if (member.channel->ChannelID() == channel.ChannelID()) + { + iReturn = member.iChannelNumber; + break; + } + } + + return iReturn; +} + +CFileItemPtr CPVRChannelGroup::GetByChannelNumber(unsigned int iChannelNumber) const +{ + CSingleLock lock(m_critSection); + + for (unsigned int ptr = 0; ptr < m_members.size(); ptr++) + { + PVRChannelGroupMember groupMember = m_members.at(ptr); + if (groupMember.iChannelNumber == iChannelNumber) + { + CFileItemPtr retVal = CFileItemPtr(new CFileItem(*groupMember.channel)); + return retVal; + } + } + + CFileItemPtr retVal = CFileItemPtr(new CFileItem); + return retVal; +} + +CFileItemPtr CPVRChannelGroup::GetByChannelUpDown(const CFileItem &channel, bool bChannelUp) const +{ + if (channel.HasPVRChannelInfoTag()) + { + CSingleLock lock(m_critSection); + int iChannelIndex = GetIndex(*channel.GetPVRChannelInfoTag()); + + bool bGotChannel(false); + while (!bGotChannel) + { + if (bChannelUp) + iChannelIndex++; + else + iChannelIndex--; + + if (iChannelIndex >= (int)m_members.size()) + iChannelIndex = 0; + else if (iChannelIndex < 0) + iChannelIndex = m_members.size() - 1; + + CFileItemPtr current = GetByIndex(iChannelIndex); + if (!current || *current->GetPVRChannelInfoTag() == *channel.GetPVRChannelInfoTag()) + break; + + if (!current->GetPVRChannelInfoTag()->IsHidden()) + return current; + } + } + + CFileItemPtr retVal(new CFileItem); + return retVal; +} + +CFileItemPtr CPVRChannelGroup::GetByIndex(unsigned int iIndex) const +{ + CSingleLock lock(m_critSection); + if (iIndex < m_members.size()) + { + CFileItemPtr retVal = CFileItemPtr(new CFileItem(*m_members.at(iIndex).channel)); + return retVal; + } + + CFileItemPtr retVal = CFileItemPtr(new CFileItem); + return retVal; +} + +int CPVRChannelGroup::GetIndex(const CPVRChannel &channel) const +{ + int iIndex(-1); + CSingleLock lock(m_critSection); + + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + { + if (*m_members.at(iChannelPtr).channel == channel) + { + iIndex = iChannelPtr; + break; + } + } + + return iIndex; +} + +int CPVRChannelGroup::GetMembers(CFileItemList &results, bool bGroupMembers /* = true */) const +{ + int iOrigSize = results.Size(); + CSingleLock lock(m_critSection); + + const CPVRChannelGroup* channels = bGroupMembers ? this : g_PVRChannelGroups->GetGroupAll(m_bRadio).get(); + for (unsigned int iChannelPtr = 0; iChannelPtr < channels->m_members.size(); iChannelPtr++) + { + CPVRChannelPtr channel = channels->m_members.at(iChannelPtr).channel; + if (!channel) + continue; + + if (bGroupMembers || !IsGroupMember(*channel)) + { + CFileItemPtr pFileItem(new CFileItem(*channel)); + results.Add(pFileItem); + } + } + + return results.Size() - iOrigSize; +} + +CPVRChannelGroupPtr CPVRChannelGroup::GetNextGroup(void) const +{ + return g_PVRChannelGroups->Get(m_bRadio)->GetNextGroup(*this); +} + +CPVRChannelGroupPtr CPVRChannelGroup::GetPreviousGroup(void) const +{ + return g_PVRChannelGroups->Get(m_bRadio)->GetPreviousGroup(*this); +} + +/********** private methods **********/ + +int CPVRChannelGroup::LoadFromDb(bool bCompress /* = false */) +{ + CPVRDatabase *database = GetPVRDatabase(); + if (!database) + return -1; + + int iChannelCount = Size(); + + database->Get(*this); + + return Size() - iChannelCount; +} + +int CPVRChannelGroup::LoadFromClients(void) +{ + int iCurSize = Size(); + + /* get the channels from the backends */ + g_PVRClients->GetChannelGroupMembers(this); + + return Size() - iCurSize; +} + +bool CPVRChannelGroup::AddAndUpdateChannels(const CPVRChannelGroup &channels, bool bUseBackendChannelNumbers) +{ + bool bReturn(false); + CSingleLock lock(m_critSection); + + /* go through the channel list and check for new channels. + channels will only by updated in CPVRChannelGroupInternal to prevent dupe updates */ + for (unsigned int iChannelPtr = 0; iChannelPtr < channels.m_members.size(); iChannelPtr++) + { + PVRChannelGroupMember member = channels.m_members.at(iChannelPtr); + if (!member.channel) + continue; + + /* check whether this channel is known in the internal group */ + CPVRChannelPtr existingChannel = g_PVRChannelGroups->GetGroupAll(m_bRadio)->GetByClient(member.channel->UniqueID(), member.channel->ClientID()); + if (!existingChannel) + continue; + + /* if it's found, add the channel to this group */ + if (!IsGroupMember(*existingChannel)) + { + int iChannelNumber = bUseBackendChannelNumbers ? member.channel->ClientChannelNumber() : 0; + AddToGroup(*existingChannel, iChannelNumber, false); + + bReturn = true; + CLog::Log(LOGINFO,"PVRChannelGroup - %s - added %s channel '%s' at position %d in group '%s'", + __FUNCTION__, m_bRadio ? "radio" : "TV", existingChannel->ChannelName().c_str(), iChannelNumber, GroupName().c_str()); + } + } + + return bReturn; +} + +bool CPVRChannelGroup::RemoveDeletedChannels(const CPVRChannelGroup &channels) +{ + bool bReturn(false); + CSingleLock lock(m_critSection); + + /* check for deleted channels */ + for (int iChannelPtr = m_members.size() - 1; iChannelPtr >= 0; iChannelPtr--) + { + CPVRChannelPtr channel = m_members.at(iChannelPtr).channel; + if (!channel) + continue; + + if (channels.GetByClient(channel->UniqueID(), channel->ClientID()) == NULL) + { + /* channel was not found */ + CLog::Log(LOGINFO,"PVRChannelGroup - %s - deleted %s channel '%s' from group '%s'", + __FUNCTION__, m_bRadio ? "radio" : "TV", channel->ChannelName().c_str(), GroupName().c_str()); + + /* remove this channel from all non-system groups if this is the internal group */ + if (IsInternalGroup()) + { + g_PVRChannelGroups->Get(m_bRadio)->RemoveFromAllGroups(*channel); + + /* since it was not found in the internal group, it was deleted from the backend */ + channel->Delete(); + } + + m_members.erase(m_members.begin() + iChannelPtr); + m_bChanged = true; + bReturn = true; + } + } + + return bReturn; +} + +bool CPVRChannelGroup::UpdateGroupEntries(const CPVRChannelGroup &channels) +{ + bool bReturn(false); + bool bChanged(false); + bool bRemoved(false); + + CSingleLock lock(m_critSection); + /* sort by client channel number if this is the first time or if pvrmanager.backendchannelorder is true */ + bool bUseBackendChannelNumbers(m_members.size() == 0 || m_bUsingBackendChannelOrder); + + CPVRDatabase *database = GetPVRDatabase(); + if (!database) + return bReturn; + + bRemoved = RemoveDeletedChannels(channels); + bChanged = AddAndUpdateChannels(channels, bUseBackendChannelNumbers) || bRemoved; + + if (bChanged) + { + if (bUseBackendChannelNumbers) + SortByClientChannelNumber(); + + /* renumber to make sure all channels have a channel number. + new channels were added at the back, so they'll get the highest numbers */ + bool bRenumbered = Renumber(); + + SetChanged(); + lock.Leave(); + + NotifyObservers(HasNewChannels() || bRemoved || bRenumbered ? ObservableMessageChannelGroup : ObservableMessageChannelGroupReset); + + bReturn = Persist(); + } + else + { + bReturn = true; + } + + return bReturn; +} + +void CPVRChannelGroup::RemoveInvalidChannels(void) +{ + bool bDelete(false); + CSingleLock lock(m_critSection); + for (unsigned int ptr = 0; ptr < m_members.size(); ptr--) + { + bDelete = false; + CPVRChannelPtr channel = m_members.at(ptr).channel; + if (channel->IsVirtual()) + continue; + + if (m_members.at(ptr).channel->ClientChannelNumber() <= 0) + { + CLog::Log(LOGERROR, "PVRChannelGroup - %s - removing invalid channel '%s' from client '%i': no valid client channel number", + __FUNCTION__, channel->ChannelName().c_str(), channel->ClientID()); + bDelete = true; + } + + if (!bDelete && channel->UniqueID() <= 0) + { + CLog::Log(LOGERROR, "PVRChannelGroup - %s - removing invalid channel '%s' from client '%i': no valid unique ID", + __FUNCTION__, channel->ChannelName().c_str(), channel->ClientID()); + bDelete = true; + } + + /* remove this channel from all non-system groups if this is the internal group */ + if (bDelete) + { + if (IsInternalGroup()) + { + g_PVRChannelGroups->Get(m_bRadio)->RemoveFromAllGroups(*channel); + channel->Delete(); + } + else + { + m_members.erase(m_members.begin() + ptr); + } + m_bChanged = true; + } + } +} + +bool CPVRChannelGroup::RemoveFromGroup(const CPVRChannel &channel) +{ + bool bReturn(false); + CSingleLock lock(m_critSection); + + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + { + if (channel == *m_members.at(iChannelPtr).channel) + { + // TODO notify observers + m_members.erase(m_members.begin() + iChannelPtr); + bReturn = true; + m_bChanged = true; + break; + } + } + + Renumber(); + + return bReturn; +} + +bool CPVRChannelGroup::AddToGroup(CPVRChannel &channel, int iChannelNumber /* = 0 */, bool bSortAndRenumber /* = true */) +{ + CSingleLock lock(m_critSection); + + bool bReturn(false); + + if (!CPVRChannelGroup::IsGroupMember(channel)) + { + if (iChannelNumber <= 0 || iChannelNumber > (int) m_members.size() + 1) + iChannelNumber = m_members.size() + 1; + + CPVRChannelPtr realChannel = (IsInternalGroup()) ? + GetByClient(channel.UniqueID(), channel.ClientID()) : + g_PVRChannelGroups->GetGroupAll(m_bRadio)->GetByClient(channel.UniqueID(), channel.ClientID()); + + if (realChannel) + { + PVRChannelGroupMember newMember = { realChannel, iChannelNumber }; + m_members.push_back(newMember); + m_bChanged = true; + + if (bSortAndRenumber) + { + if (m_bUsingBackendChannelOrder) + SortByClientChannelNumber(); + else + SortByChannelNumber(); + Renumber(); + } + + // TODO notify observers + bReturn = true; + } + } + + return bReturn; +} + +bool CPVRChannelGroup::IsGroupMember(const CPVRChannel &channel) const +{ + bool bReturn(false); + CSingleLock lock(m_critSection); + + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + { + if (channel == *m_members.at(iChannelPtr).channel) + { + bReturn = true; + break; + } + } + + return bReturn; +} + +bool CPVRChannelGroup::IsGroupMember(int iChannelId) const +{ + bool bReturn(false); + CSingleLock lock(m_critSection); + + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + { + if (iChannelId == m_members.at(iChannelPtr).channel->ChannelID()) + { + bReturn = true; + break; + } + } + + return bReturn; +} + +bool CPVRChannelGroup::SetGroupName(const CStdString &strGroupName, bool bSaveInDb /* = false */) +{ + bool bReturn(false); + CSingleLock lock(m_critSection); + + if (m_strGroupName != strGroupName) + { + /* update the name */ + m_strGroupName = strGroupName; + m_bChanged = true; +// SetChanged(); + + /* persist the changes */ + if (bSaveInDb) + Persist(); + + bReturn = true; + } + + return bReturn; +} + +bool CPVRChannelGroup::Persist(void) +{ + bool bReturn(true); + CSingleLock lock(m_critSection); + + if (!HasChanges()) + return bReturn; + + if (CPVRDatabase *database = GetPVRDatabase()) + { + CLog::Log(LOGDEBUG, "CPVRChannelGroup - %s - persisting channel group '%s' with %d channels", + __FUNCTION__, GroupName().c_str(), (int) m_members.size()); + m_bChanged = false; + lock.Leave(); + + bReturn = database->Persist(*this); + } + else + { + bReturn = false; + } + + return bReturn; +} + +bool CPVRChannelGroup::Renumber(void) +{ + bool bReturn(false); + unsigned int iChannelNumber(0); + bool bUseBackendChannelNumbers(g_guiSettings.GetBool("pvrmanager.usebackendchannelnumbers") && g_PVRClients->EnabledClientAmount() == 1); + CSingleLock lock(m_critSection); + + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + { + unsigned int iCurrentChannelNumber; + if (m_members.at(iChannelPtr).channel->IsHidden()) + iCurrentChannelNumber = 0; + else if (bUseBackendChannelNumbers) + iCurrentChannelNumber = m_members.at(iChannelPtr).channel->ClientChannelNumber(); + else + iCurrentChannelNumber = ++iChannelNumber; + + if (m_members.at(iChannelPtr).iChannelNumber != iCurrentChannelNumber) + { + bReturn = true; + m_bChanged = true; + } + + m_members.at(iChannelPtr).iChannelNumber = iCurrentChannelNumber; + } + + SortByChannelNumber(); + ResetChannelNumberCache(); + + return bReturn; +} + +void CPVRChannelGroup::ResetChannelNumberCache(void) +{ + CPVRChannelGroupPtr playingGroup = g_PVRManager.GetPlayingGroup(m_bRadio); + if (!playingGroup || *this != *playingGroup) + return; + + CSingleLock lock(m_critSection); + + /* reset the channel number cache */ + if (!IsInternalGroup()) + g_PVRChannelGroups->GetGroupAll(m_bRadio)->ResetChannelNumbers(); + + /* set all channel numbers on members of this group */ + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + m_members.at(iChannelPtr).channel->SetCachedChannelNumber(m_members.at(iChannelPtr).iChannelNumber); +} + +bool CPVRChannelGroup::HasChangedChannels(void) const +{ + bool bReturn(false); + CSingleLock lock(m_critSection); + + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + { + if (m_members.at(iChannelPtr).channel->IsChanged()) + { + bReturn = true; + break; + } + } + + return bReturn; +} + +bool CPVRChannelGroup::HasNewChannels(void) const +{ + bool bReturn(false); + CSingleLock lock(m_critSection); + + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + { + if (m_members.at(iChannelPtr).channel->ChannelID() <= 0) + { + bReturn = true; + break; + } + } + + return bReturn; +} + +bool CPVRChannelGroup::HasChanges(void) const +{ + CSingleLock lock(m_critSection); + return m_bChanged || HasNewChannels() || HasChangedChannels(); +} + +void CPVRChannelGroup::ResetChannelNumbers(void) +{ + CSingleLock lock(m_critSection); + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + m_members.at(iChannelPtr).channel->SetCachedChannelNumber(0); +} + +void CPVRChannelGroup::Notify(const Observable &obs, const ObservableMessage msg) +{ + if (msg == ObservableMessageGuiSettings) + { + CSingleLock lock(m_critSection); + bool bUsingBackendChannelOrder = g_guiSettings.GetBool("pvrmanager.backendchannelorder"); + bool bUsingBackendChannelNumbers = g_guiSettings.GetBool("pvrmanager.usebackendchannelnumbers"); + bool bChannelNumbersChanged = m_bUsingBackendChannelNumbers != bUsingBackendChannelNumbers; + bool bChannelOrderChanged = m_bUsingBackendChannelOrder != bUsingBackendChannelOrder; + + m_bUsingBackendChannelOrder = bUsingBackendChannelOrder; + m_bUsingBackendChannelNumbers = bUsingBackendChannelNumbers; + + /* check whether this channel group has to be renumbered */ + if (bChannelOrderChanged || bChannelNumbersChanged) + { + CLog::Log(LOGDEBUG, "CPVRChannelGroup - %s - renumbering group '%s' to use the backend channel order and/or numbers", + __FUNCTION__, m_strGroupName.c_str()); + SortByClientChannelNumber(); + Renumber(); + Persist(); + } + } +} + +bool CPVRPersistGroupJob::DoWork(void) +{ + return m_group->Persist(); +} + +int CPVRChannelGroup::GetEPGSearch(CFileItemList &results, const EpgSearchFilter &filter) +{ + int iInitialSize = results.Size(); + + /* get filtered results from all tables */ + g_EpgContainer.GetEPGSearch(results, filter); + + /* remove duplicate entries */ + if (filter.m_bPreventRepeats) + EpgSearchFilter::RemoveDuplicates(results); + + /* filter recordings */ + if (filter.m_bIgnorePresentRecordings) + EpgSearchFilter::FilterRecordings(results); + + /* filter timers */ + if (filter.m_bIgnorePresentTimers) + EpgSearchFilter::FilterTimers(results); + + return results.Size() - iInitialSize; +} + +int CPVRChannelGroup::GetEPGNow(CFileItemList &results) +{ + int iInitialSize = results.Size(); + CSingleLock lock(m_critSection); + + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + { + CPVRChannelPtr channel = m_members.at(iChannelPtr).channel; + CEpg *epg = channel->GetEPG(); + if (!epg || !epg->HasValidEntries() || m_members.at(iChannelPtr).channel->IsHidden()) + continue; + + CEpgInfoTag epgNow; + if (!epg->InfoTagNow(epgNow)) + continue; + + CFileItemPtr entry(new CFileItem(epgNow)); + entry->SetLabel2(epgNow.StartAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false)); + entry->SetPath(channel->ChannelName()); + entry->SetThumbnailImage(channel->IconPath()); + results.Add(entry); + } + + return results.Size() - iInitialSize; +} + +int CPVRChannelGroup::GetEPGNext(CFileItemList &results) +{ + int iInitialSize = results.Size(); + CSingleLock lock(m_critSection); + + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + { + CPVRChannelPtr channel = m_members.at(iChannelPtr).channel; + CEpg *epg = channel->GetEPG(); + if (!epg || !epg->HasValidEntries() || m_members.at(iChannelPtr).channel->IsHidden()) + continue; + + CEpgInfoTag epgNow; + if (!epg->InfoTagNext(epgNow)) + continue; + + CFileItemPtr entry(new CFileItem(epgNow)); + entry->SetLabel2(epgNow.StartAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false)); + entry->SetPath(channel->ChannelName()); + entry->SetThumbnailImage(channel->IconPath()); + results.Add(entry); + } + + return results.Size() - iInitialSize; +} + +int CPVRChannelGroup::GetEPGAll(CFileItemList &results) +{ + int iInitialSize = results.Size(); + CSingleLock lock(m_critSection); + + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + { + if (!m_members.at(iChannelPtr).channel || m_members.at(iChannelPtr).channel->IsHidden()) + continue; + + m_members.at(iChannelPtr).channel->GetEPG(results); + } + + return results.Size() - iInitialSize; +} + +int CPVRChannelGroup::Size(void) const +{ + return m_members.size(); +} + +int CPVRChannelGroup::GroupID(void) const +{ + return m_iGroupId; +} + +void CPVRChannelGroup::SetGroupID(int iGroupId) +{ + if (iGroupId >= 0) + m_iGroupId = iGroupId; +} + +void CPVRChannelGroup::SetGroupType(int iGroupType) +{ + m_iGroupType = iGroupType; +} + +int CPVRChannelGroup::GroupType(void) const +{ + return m_iGroupType; +} + +CStdString CPVRChannelGroup::GroupName(void) const +{ + CSingleLock lock(m_critSection); + CStdString strReturn(m_strGroupName); + return strReturn; +} + +bool CPVRChannelGroup::UpdateChannel(const CFileItem &item, bool bHidden, bool bVirtual, bool bEPGEnabled, bool bParentalLocked, int iEPGSource, int iChannelNumber, const CStdString &strChannelName, const CStdString &strIconPath, const CStdString &strStreamURL) +{ + if (!item.HasPVRChannelInfoTag()) + return false; + + CSingleLock lock(m_critSection); + + /* get the real channel from the group */ + CPVRChannelPtr channel = GetByUniqueID(item.GetPVRChannelInfoTag()->UniqueID()); + if (!channel) + return false; + + channel->SetChannelName(strChannelName); + channel->SetHidden(bHidden); + channel->SetLocked(bParentalLocked); + channel->SetIconPath(strIconPath); + + if (bVirtual) + channel->SetStreamURL(strStreamURL); + if (iEPGSource == 0) + channel->SetEPGScraper("client"); + + // TODO add other scrapers + channel->SetEPGEnabled(bEPGEnabled); + + /* set new values in the channel tag */ + if (bHidden) + { + SortByChannelNumber(); // or previous changes will be overwritten + RemoveFromGroup(*channel); + } + else + { + SetChannelNumber(*channel, iChannelNumber); + } + + return true; +} + +bool CPVRChannelGroup::ToggleChannelLocked(const CFileItem &item) +{ + if (!item.HasPVRChannelInfoTag()) + return false; + + CSingleLock lock(m_critSection); + + /* get the real channel from the group */ + CPVRChannelPtr channel = GetByUniqueID(item.GetPVRChannelInfoTag()->UniqueID()); + if (!channel) + return false; + + channel->SetLocked(!channel->IsLocked()); + + return true; +} diff --git a/xbmc/pvr/channels/PVRChannelGroup.h b/xbmc/pvr/channels/PVRChannelGroup.h new file mode 100644 index 0000000000..dc137f4c97 --- /dev/null +++ b/xbmc/pvr/channels/PVRChannelGroup.h @@ -0,0 +1,475 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "FileItem.h" +#include "PVRChannel.h" +#include "utils/JobManager.h" + +#include <boost/shared_ptr.hpp> + +namespace EPG +{ + struct EpgSearchFilter; +} + +namespace PVR +{ +#define XBMC_INTERNAL_GROUP_RADIO 1 +#define XBMC_INTERNAL_GROUP_TV 2 + +#define PVR_GROUP_TYPE_DEFAULT 0 +#define PVR_GROUP_TYPE_INTERNAL 1 +#define PVR_GROUP_TYPE_USER_DEFINED 2 + + class CPVRChannelGroups; + class CPVRChannelGroupInternal; + class CPVRChannelGroupsContainer; + + typedef struct + { + CPVRChannelPtr channel; + unsigned int iChannelNumber; + } PVRChannelGroupMember; + + class CPVRChannelGroup; + typedef boost::shared_ptr<PVR::CPVRChannelGroup> CPVRChannelGroupPtr; + + /** A group of channels */ + class CPVRChannelGroup : private Observer, + public Observable, + public IJobCallback + + { + friend class CPVRChannelGroups; + friend class CPVRChannelGroupInternal; + friend class CPVRChannelGroupsContainer; + friend class CPVRDatabase; + + public: + CPVRChannelGroup(void); + + /*! + * @brief Create a new channel group instance. + * @param bRadio True if this group holds radio channels. + * @param iGroupId The database ID of this group. + * @param strGroupName The name of this group. + */ + CPVRChannelGroup(bool bRadio, unsigned int iGroupId, const CStdString &strGroupName); + + /*! + * @brief Create a new channel group instance from a channel group provided by an add-on. + * @param group The channel group provided by the add-on. + */ + CPVRChannelGroup(const PVR_CHANNEL_GROUP &group); + + /*! + * @brief Copy constructor + * @param group Source group + */ + CPVRChannelGroup(const CPVRChannelGroup &group); + + virtual ~CPVRChannelGroup(void); + + bool operator ==(const CPVRChannelGroup &right) const; + bool operator !=(const CPVRChannelGroup &right) const; + + /*! + * @return The amount of group members + */ + int Size(void) const; + + /*! + * @brief Refresh the channel list from the clients. + */ + virtual bool Update(void); + + /*! + * @brief Change the channelnumber of a group. Used by CGUIDialogPVRChannelManager. Call SortByChannelNumber() and Renumber() after all changes are done. + * @param channel The channel to change the channel number for. + * @param iChannelNumber The new channel number. + */ + bool SetChannelNumber(const CPVRChannel &channel, unsigned int iChannelNumber); + + /*! + * @brief Move a channel from position iOldIndex to iNewIndex. + * @param iOldChannelNumber The channel number of the channel to move. + * @param iNewChannelNumber The new channel number. + * @param bSaveInDb If true, save this change in the database. + * @return True if the channel was moved successfully, false otherwise. + */ + virtual bool MoveChannel(unsigned int iOldChannelNumber, unsigned int iNewChannelNumber, bool bSaveInDb = true); + + /*! + * @brief Search missing channel icons for all known channels. + * @param bUpdateDb If true, update the changed values in the database. + */ + void SearchAndSetChannelIcons(bool bUpdateDb = false); + + /*! + * @brief Remove a channel from this container. + * @param channel The channel to remove. + * @return True if the channel was found and removed, false otherwise. + */ + virtual bool RemoveFromGroup(const CPVRChannel &channel); + + /*! + * @brief Add a channel to this container. + * @param channel The channel to add. + * @param iChannelNumber The channel number of the channel number to add. Use -1 to add it at the end. + * @param bSortAndRenumber Set to false to keep the channel list unsorted after adding a new channel. + * @return True if the channel was added, false otherwise. + */ + virtual bool AddToGroup(CPVRChannel &channel, int iChannelNumber = 0, bool bSortAndRenumber = true); + + /*! + * @brief Change the name of this group. + * @param strGroupName The new group name. + * @param bSaveInDb Save in the database or not. + * @return True if the something changed, false otherwise. + */ + bool SetGroupName(const CStdString &strGroupName, bool bSaveInDb = false); + + /*! + * @brief Persist changed or new data. + * @return True if the channel was persisted, false otherwise. + */ + bool Persist(void); + + /*! + * @brief Check whether a channel is in this container. + * @param channel The channel to find. + * @return True if the channel was found, false otherwise. + */ + virtual bool IsGroupMember(const CPVRChannel &channel) const; + + /*! + * @brief Check whether a channel is in this container. + * @param iChannelId The db id of the channel to find. + * @return True if the channel was found, false otherwise. + */ + virtual bool IsGroupMember(int iChannelId) const; + + /*! + * @brief Check if this group is the internal group containing all channels. + * @return True if it's the internal group, false otherwise. + */ + virtual bool IsInternalGroup(void) const { return false; } + + /*! + * @brief True if this group holds radio channels, false if it holds TV channels. + * @return True if this group holds radio channels, false if it holds TV channels. + */ + bool IsRadio(void) const { return m_bRadio; } + + /*! + * @brief The database ID of this group. + * @return The database ID of this group. + */ + int GroupID(void) const; + + /*! + * @brief Set the database ID of this group. + * @param iGroupId The new database ID. + */ + void SetGroupID(int iGroupId); + + /*! + * @brief Set the type of this group. + * @param the new type for this group. + */ + void SetGroupType(int iGroupType); + + /*! + * @brief Return the type of this group. + */ + int GroupType(void) const; + + /*! + * @brief The name of this group. + * @return The name of this group. + */ + CStdString GroupName(void) const; + + /*! @name Sort methods + */ + //@{ + + /*! + * @brief Sort the current channel list by client channel number. + */ + void SortByClientChannelNumber(void); + + /*! + * @brief Sort the current channel list by channel number. + */ + void SortByChannelNumber(void); + + //@} + + void Notify(const Observable &obs, const ObservableMessage msg); + + /*! + * @brief Get a channel given it's EPG ID. + * @param iEpgID The channel EPG ID. + * @return The channel or NULL if it wasn't found. + */ + CPVRChannelPtr GetByChannelEpgID(int iEpgID) const; + + /*! + * @brief The channel that was played last that has a valid client or NULL if there was none. + * @param iCurrentChannel The channelid of the current channel that is playing, or -1 if none + * @return The requested channel. + */ + CFileItemPtr GetLastPlayedChannel(unsigned int iCurrentChannel = -1) const; + + /*! + * @brief Get a channel given it's channel number. + * @param iChannelNumber The channel number. + * @return The channel or NULL if it wasn't found. + */ + CFileItemPtr GetByChannelNumber(unsigned int iChannelNumber) const; + + /*! + * @brief Get the channel number in this group of the given channel. + * @param channel The channel to get the channel number for. + * @return The channel number in this group or 0 if the channel isn't a member of this group. + */ + unsigned int GetChannelNumber(const CPVRChannel &channel) const; + + /*! + * @brief Get the next channel in this group. + * @param channel The current channel. + * @return The channel or NULL if it wasn't found. + */ + CFileItemPtr GetByChannelUp(const CFileItem &channel) const { return GetByChannelUpDown(channel, true); } + + /*! + * @brief Get the previous channel in this group. + * @param channel The current channel. + * @return The channel or NULL if it wasn't found. + */ + CFileItemPtr GetByChannelDown(const CFileItem &channel) const { return GetByChannelUpDown(channel, false); } + + /*! + * @brief Get a channel given it's index in this container. + * @param index The index in this container. + * @return The channel or NULL if it wasn't found. + */ + CFileItemPtr GetByIndex(unsigned int index) const; + + /*! + * @brief Get the current index in this group of a channel. + * @param channel The channel to get the index for. + * @return The index or -1 if it wasn't found. + */ + int GetIndex(const CPVRChannel &channel) const; + + /*! + * @brief Get the list of channels in a group. + * @param results The file list to store the results in. + * @param bGroupMembers If true, get the channels that are in this group. Get the channels that are not in this group otherwise. + * @return The amount of channels that were added to the list. + */ + virtual int GetMembers(CFileItemList &results, bool bGroupMembers = true) const; + + /*! + * @return The next channel group. + */ + CPVRChannelGroupPtr GetNextGroup(void) const; + + /*! + * @return The previous channel group. + */ + CPVRChannelGroupPtr GetPreviousGroup(void) const; + + /*! + * @brief The amount of hidden channels in this container. + * @return The amount of hidden channels in this container. + */ + virtual int GetNumHiddenChannels(void) const { return 0; } + + /*! + * @return True if there is at least one channel in this group with changes that haven't been persisted, false otherwise. + */ + bool HasChangedChannels(void) const; + + /*! + * @return True if there is at least one new channel in this group that hasn't been persisted, false otherwise. + */ + bool HasNewChannels(void) const; + + /*! + * @return True if anything changed in this group that hasn't been persisted, false otherwise. + */ + bool HasChanges(void) const; + + /*! + * @brief Reset the channel number cache if this is the selected group in the UI. + */ + void ResetChannelNumberCache(void); + + void OnJobComplete(unsigned int jobID, bool success, CJob* job) {} + + /*! + * @brief Get all EPG tables and apply a filter. + * @param results The fileitem list to store the results in. + * @param filter The filter to apply. + * @return The amount of entries that were added. + */ + int GetEPGSearch(CFileItemList &results, const EPG::EpgSearchFilter &filter); + + /*! + * @brief Get all EPG tables. + * @param results The fileitem list to store the results in. + * @return The amount of entries that were added. + */ + int GetEPGAll(CFileItemList &results); + + /*! + * @brief Get all entries that are active now. + * @param results The fileitem list to store the results in. + * @return The amount of entries that were added. + */ + int GetEPGNow(CFileItemList &results); + + /*! + * @brief Get all entries that will be active next. + * @param results The fileitem list to store the results in. + * @return The amount of entries that were added. + */ + int GetEPGNext(CFileItemList &results); + + bool UpdateChannel(const CFileItem &channel, bool bHidden, bool bVirtual, bool bEPGEnabled, bool bParentalLocked, int iEPGSource, int iChannelNumber, const CStdString &strChannelName, const CStdString &strIconPath, const CStdString &strStreamURL); + + bool ToggleChannelLocked(const CFileItem &channel); + + virtual bool AddNewChannel(const CPVRChannel &channel, unsigned int iChannelNumber = 0) { return false; } + + /*! + * @brief Get a channel given the channel number on the client. + * @param iUniqueChannelId The unique channel id on the client. + * @param iClientID The ID of the client. + * @return The channel or NULL if it wasn't found. + */ + CPVRChannelPtr GetByClient(int iUniqueChannelId, int iClientID) const; + + protected: + /*! + * @brief Load the channels stored in the database. + * @param bCompress If true, compress the database after storing the channels. + * @return The amount of channels that were added. + */ + virtual int LoadFromDb(bool bCompress = false); + + /*! + * @brief Update the current channel list with the given list. + * + * Update the current channel list with the given list. + * Only the new channels will be present in the passed list after this call. + * + * @param channels The channels to use to update this list. + * @return True if everything went well, false otherwise. + */ + virtual bool UpdateGroupEntries(const CPVRChannelGroup &channels); + + virtual bool AddAndUpdateChannels(const CPVRChannelGroup &channels, bool bUseBackendChannelNumbers); + + bool RemoveDeletedChannels(const CPVRChannelGroup &channels); + + /*! + * @brief Remove invalid channels from this container. + */ + void RemoveInvalidChannels(void); + + /*! + * @brief Load the channels from the database. + * @return The amount of channels that were added or -1 if an error occured. + */ + virtual int Load(void); + + /*! + * @brief Clear this channel list. + */ + virtual void Unload(void); + + /*! + * @brief Load the channels from the clients. + * @return The amount of channels that were added. + */ + virtual int LoadFromClients(void); + + /*! + * @brief Remove invalid channels and updates the channel numbers. + * @return True if something changed, false otherwise. + */ + virtual bool Renumber(void); + + /*! + * @brief Get the previous or next channel in this group. + * @param channel The current channel. + * @param bChannelUp True to get the next channel, false to get the previous one. + * @return The requested channel or NULL if there is none. + */ + CFileItemPtr GetByChannelUpDown(const CFileItem &channel, bool bChannelUp) const; + + void ResetChannelNumbers(void); + + /*! + * @brief Get a channel given it's unique ID. + * @param iUniqueID The unique ID. + * @return The channel or NULL if it wasn't found. + */ + CPVRChannelPtr GetByUniqueID(int iUniqueID) const; + + /*! + * @brief Get a channel given it's channel ID. + * @param iChannelID The channel ID. + * @return The channel or NULL if it wasn't found. + */ + CPVRChannelPtr GetByChannelID(int iChannelID) const; + + bool m_bRadio; /*!< true if this container holds radio channels, false if it holds TV channels */ + int m_iGroupType; /*!< The type of this group */ + int m_iGroupId; /*!< The ID of this group in the database */ + CStdString m_strGroupName; /*!< The name of this group */ + bool m_bLoaded; /*!< True if this container is loaded, false otherwise */ + bool m_bChanged; /*!< true if anything changed in this group that hasn't been persisted, false otherwise */ + bool m_bUsingBackendChannelOrder; /*!< true to use the channel order from backends, false otherwise */ + bool m_bUsingBackendChannelNumbers; /*!< true to use the channel numbers from 1 backend, false otherwise */ + std::vector<PVRChannelGroupMember> m_members; + CCriticalSection m_critSection; + }; + + class CPVRPersistGroupJob : public CJob + { + public: + CPVRPersistGroupJob(CPVRChannelGroupPtr group) { m_group = group; } + virtual ~CPVRPersistGroupJob() {} + const char *GetType() const { return "pvr-channelgroup-persist"; } + + virtual bool DoWork(); + + private: + CPVRChannelGroupPtr m_group; + }; +} diff --git a/xbmc/pvr/channels/PVRChannelGroupInternal.cpp b/xbmc/pvr/channels/PVRChannelGroupInternal.cpp new file mode 100644 index 0000000000..f2651c6a3a --- /dev/null +++ b/xbmc/pvr/channels/PVRChannelGroupInternal.cpp @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "PVRChannelGroupInternal.h" + +#include "settings/GUISettings.h" +#include "guilib/GUIWindowManager.h" +#include "dialogs/GUIDialogYesNo.h" +#include "dialogs/GUIDialogOK.h" +#include "utils/log.h" + +#include "PVRChannelGroupsContainer.h" +#include "pvr/PVRDatabase.h" +#include "pvr/PVRManager.h" +#include "epg/EpgContainer.h" +#include "pvr/timers/PVRTimers.h" +#include "pvr/addons/PVRClients.h" + +using namespace PVR; +using namespace EPG; +using namespace std; + +CPVRChannelGroupInternal::CPVRChannelGroupInternal(bool bRadio) : + CPVRChannelGroup(bRadio, bRadio ? XBMC_INTERNAL_GROUP_RADIO : XBMC_INTERNAL_GROUP_TV, g_localizeStrings.Get(bRadio ? 19216 : 19217)) +{ + m_iHiddenChannels = 0; + m_iGroupType = PVR_GROUP_TYPE_INTERNAL; +} + +CPVRChannelGroupInternal::CPVRChannelGroupInternal(const CPVRChannelGroup &group) : + CPVRChannelGroup(group) +{ + m_iHiddenChannels = group.GetNumHiddenChannels(); +} + +CPVRChannelGroupInternal::~CPVRChannelGroupInternal(void) +{ + Unload(); +} + +int CPVRChannelGroupInternal::Load(void) +{ + int iChannelCount = CPVRChannelGroup::Load(); + UpdateChannelPaths(); + CreateChannelEpgs(); + + return iChannelCount; +} + +void CPVRChannelGroupInternal::CheckGroupName(void) +{ + CSingleLock lock(m_critSection); + + /* check whether the group name is still correct, or channels will fail to load after the language setting changed */ + CStdString strNewGroupName = g_localizeStrings.Get(m_bRadio ? 19216 : 19217); + if (!m_strGroupName.Equals(strNewGroupName)) + { + SetGroupName(strNewGroupName, true); + UpdateChannelPaths(); + } +} + +void CPVRChannelGroupInternal::UpdateChannelPaths(void) +{ + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + { + PVRChannelGroupMember member = m_members.at(iChannelPtr); + member.channel->UpdatePath(iChannelPtr); + } +} + +void CPVRChannelGroupInternal::UpdateFromClient(const CPVRChannel &channel, unsigned int iChannelNumber /* = 0 */) +{ + CSingleLock lock(m_critSection); + CPVRChannelPtr realChannel = GetByClient(channel.UniqueID(), channel.ClientID()); + if (realChannel) + realChannel->UpdateFromClient(channel); + else + { + PVRChannelGroupMember newMember = { CPVRChannelPtr(new CPVRChannel(channel)), iChannelNumber > 0 ? iChannelNumber : m_members.size() + 1 }; + m_members.push_back(newMember); + m_bChanged = true; + + if (m_bUsingBackendChannelOrder) + SortByClientChannelNumber(); + else + SortByChannelNumber(); + Renumber(); + } +} + +bool CPVRChannelGroupInternal::InsertInGroup(CPVRChannel &channel, int iChannelNumber /* = 0 */, bool bSortAndRenumber /* = true */) +{ + CSingleLock lock(m_critSection); + return CPVRChannelGroup::AddToGroup(channel, iChannelNumber, bSortAndRenumber); +} + +bool CPVRChannelGroupInternal::Update(void) +{ + CPVRChannelGroupInternal PVRChannels_tmp(m_bRadio); + PVRChannels_tmp.LoadFromClients(); + + return UpdateGroupEntries(PVRChannels_tmp); +} + +bool CPVRChannelGroupInternal::AddToGroup(CPVRChannel &channel, int iChannelNumber /* = 0 */, bool bSortAndRenumber /* = true */) +{ + CSingleLock lock(m_critSection); + + bool bReturn(false); + + /* get the actual channel since this is called from a fileitemlist copy */ + CPVRChannelPtr realChannel = GetByChannelID(channel.ChannelID()); + if (!realChannel) + return bReturn; + + /* switch the hidden flag */ + if (realChannel->IsHidden()) + { + realChannel->SetHidden(false); + m_iHiddenChannels--; + + if (bSortAndRenumber) + Renumber(); + } + + /* move this channel and persist */ + bReturn = (iChannelNumber > 0) ? + MoveChannel(realChannel->ChannelNumber(), iChannelNumber, true) : + MoveChannel(realChannel->ChannelNumber(), m_members.size() - m_iHiddenChannels, true); + + if (m_bLoaded) + realChannel->Persist(); + return bReturn; +} + +bool CPVRChannelGroupInternal::RemoveFromGroup(const CPVRChannel &channel) +{ + CSingleLock lock(m_critSection); + + /* check if this channel is currently playing if we are hiding it */ + CPVRChannelPtr currentChannel; + if (g_PVRManager.GetCurrentChannel(currentChannel) && *currentChannel == channel) + { + CGUIDialogOK::ShowAndGetInput(19098,19101,0,19102); + return false; + } + + /* get the actual channel since this is called from a fileitemlist copy */ + CPVRChannelPtr realChannel = GetByChannelID(channel.ChannelID()); + if (!realChannel) + return false; + + /* switch the hidden flag */ + if (!realChannel->IsHidden()) + { + realChannel->SetHidden(true); + ++m_iHiddenChannels; + } + else + { + realChannel->SetHidden(false); + --m_iHiddenChannels; + } + + /* renumber this list */ + Renumber(); + + /* and persist */ + return realChannel->Persist() && + Persist(); +} + +bool CPVRChannelGroupInternal::MoveChannel(unsigned int iOldChannelNumber, unsigned int iNewChannelNumber, bool bSaveInDb /* = true */) +{ + CSingleLock lock(m_critSection); + /* new channel number out of range */ + if (iNewChannelNumber > m_members.size() - m_iHiddenChannels) + iNewChannelNumber = m_members.size() - m_iHiddenChannels; + + return CPVRChannelGroup::MoveChannel(iOldChannelNumber, iNewChannelNumber, bSaveInDb); +} + +int CPVRChannelGroupInternal::GetMembers(CFileItemList &results, bool bGroupMembers /* = true */) const +{ + int iOrigSize = results.Size(); + CSingleLock lock(m_critSection); + + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + { + CPVRChannelPtr channel = m_members.at(iChannelPtr).channel; + if (!channel) + continue; + + if (bGroupMembers != channel->IsHidden()) + { + CFileItemPtr pFileItem(new CFileItem(*channel)); + results.Add(pFileItem); + } + } + + return results.Size() - iOrigSize; +} + +int CPVRChannelGroupInternal::LoadFromDb(bool bCompress /* = false */) +{ + CPVRDatabase *database = GetPVRDatabase(); + if (!database) + return -1; + + int iChannelCount = Size(); + + if (database->Get(*this) > 0) + { + if (bCompress) + database->Compress(true); + } + else + { + CLog::Log(LOGINFO, "PVRChannelGroupInternal - %s - no channels in the database", + __FUNCTION__); + } + + SortByChannelNumber(); + + return Size() - iChannelCount; +} + +int CPVRChannelGroupInternal::LoadFromClients(void) +{ + int iCurSize = Size(); + + /* get the channels from the backends */ + g_PVRClients->GetChannels(this); + + return Size() - iCurSize; +} + +bool CPVRChannelGroupInternal::Renumber(void) +{ + CSingleLock lock(m_critSection); + bool bReturn(CPVRChannelGroup::Renumber()); + + m_iHiddenChannels = 0; + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + { + if (m_members.at(iChannelPtr).channel->IsHidden()) + m_iHiddenChannels++; + else + m_members.at(iChannelPtr).channel->UpdatePath(iChannelPtr); + } + + return bReturn; +} + +bool CPVRChannelGroupInternal::IsGroupMember(const CPVRChannel &channel) const +{ + return !channel.IsHidden(); +} + +bool CPVRChannelGroupInternal::UpdateChannel(const CPVRChannel &channel) +{ + CSingleLock lock(m_critSection); + CPVRChannelPtr updateChannel = GetByUniqueID(channel.UniqueID()); + + if (!updateChannel) + { + updateChannel = CPVRChannelPtr(new CPVRChannel(channel.IsRadio())); + PVRChannelGroupMember newMember = { updateChannel, 0 }; + m_members.push_back(newMember); + updateChannel->SetUniqueID(channel.UniqueID()); + } + updateChannel->UpdateFromClient(channel); + + return updateChannel->Persist(!m_bLoaded); +} + +bool CPVRChannelGroupInternal::AddAndUpdateChannels(const CPVRChannelGroup &channels, bool bUseBackendChannelNumbers) +{ + bool bReturn(false); + CSingleLock lock(m_critSection); + + /* go through the channel list and check for updated or new channels */ + for (unsigned int iChannelPtr = 0; iChannelPtr < channels.m_members.size(); iChannelPtr++) + { + PVRChannelGroupMember member = channels.m_members.at(iChannelPtr); + if (!member.channel) + continue; + + /* check whether this channel is present in this container */ + CPVRChannelPtr existingChannel = GetByClient(member.channel->UniqueID(), member.channel->ClientID()); + if (existingChannel) + { + /* if it's present, update the current tag */ + if (existingChannel->UpdateFromClient(*member.channel)) + { + bReturn = true; + CLog::Log(LOGINFO,"PVRChannelGroupInternal - %s - updated %s channel '%s'", __FUNCTION__, m_bRadio ? "radio" : "TV", member.channel->ChannelName().c_str()); + } + } + else + { + /* new channel */ + UpdateFromClient(*member.channel, bUseBackendChannelNumbers ? member.channel->ClientChannelNumber() : 0); + bReturn = true; + CLog::Log(LOGINFO,"PVRChannelGroupInternal - %s - added %s channel '%s'", __FUNCTION__, m_bRadio ? "radio" : "TV", member.channel->ChannelName().c_str()); + } + } + + return bReturn; +} + +bool CPVRChannelGroupInternal::UpdateGroupEntries(const CPVRChannelGroup &channels) +{ + bool bReturn(false); + + if (CPVRChannelGroup::UpdateGroupEntries(channels)) + { + /* try to find channel icons */ + SearchAndSetChannelIcons(); + g_PVRTimers->UpdateChannels(); + Persist(); + + bReturn = true; + } + + return bReturn; +} + +void CPVRChannelGroupInternal::CreateChannelEpg(CPVRChannelPtr channel, bool bForce /* = false */) +{ + if (!channel) + return; + + CSingleLock lock(channel->m_critSection); + if (!channel->m_bEPGCreated || bForce) + { + CEpg *epg = g_EpgContainer.CreateChannelEpg(channel); + if (epg) + { + channel->m_bEPGCreated = true; + if (epg->EpgID() != channel->m_iEpgId) + { + channel->m_iEpgId = epg->EpgID(); + channel->m_bChanged = true; + } + } + } +} + +bool CPVRChannelGroupInternal::CreateChannelEpgs(bool bForce /* = false */) +{ + { + CSingleLock lock(m_critSection); + for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++) + CreateChannelEpg(m_members.at(iChannelPtr).channel); + } + + if (HasChangedChannels()) + { + g_EpgContainer.PersistTables(); + return Persist(); + } + + return true; +} diff --git a/xbmc/pvr/channels/PVRChannelGroupInternal.h b/xbmc/pvr/channels/PVRChannelGroupInternal.h new file mode 100644 index 0000000000..62b78a6a94 --- /dev/null +++ b/xbmc/pvr/channels/PVRChannelGroupInternal.h @@ -0,0 +1,177 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "PVRChannelGroup.h" + +namespace PVR +{ + class CPVRChannelGroups; + class CPVRDatabase; + + /** XBMC's internal group, the group containing all channels */ + + class CPVRChannelGroupInternal : public CPVRChannelGroup + { + friend class CPVRChannelGroups; + friend class CPVRDatabase; + + public: + /*! + * @brief Create a new internal channel group. + * @param bRadio True if this group holds radio channels. + */ + CPVRChannelGroupInternal(bool bRadio); + + CPVRChannelGroupInternal(const CPVRChannelGroup &group); + + virtual ~CPVRChannelGroupInternal(void); + + /** + * @brief The amount of channels in this container. + * @return The amount of channels in this container. + */ + int GetNumHiddenChannels() const { return m_iHiddenChannels; } + + /*! + * @brief Add or update a channel in this table. + * @param channel The channel to update. + * @return True if the channel was updated and persisted. + */ + bool UpdateChannel(const CPVRChannel &channel); + + /*! + * @brief Add a channel to this internal group. + * @param iChannelNumber The channel number to use for this channel or 0 to add it to the back. + * @param bSortAndRenumber Set to false to not to sort the group after adding a channel + */ + bool InsertInGroup(CPVRChannel &channel, int iChannelNumber = 0, bool bSortAndRenumber = true); + + /*! + * @brief Callback for add-ons to update a channel. + * @param channel The updated channel. + * @return True if the channel has been updated succesfully, false otherwise. + */ + void UpdateFromClient(const CPVRChannel &channel, unsigned int iChannelNumber = 0); + + /*! + * @see CPVRChannelGroup::IsGroupMember + */ + bool IsGroupMember(const CPVRChannel &channel) const; + + /*! + * @see CPVRChannelGroup::AddToGroup + */ + bool AddToGroup(CPVRChannel &channel, int iChannelNumber = 0, bool bSortAndRenumber = true); + + /*! + * @see CPVRChannelGroup::RemoveFromGroup + */ + bool RemoveFromGroup(const CPVRChannel &channel); + + /*! + * @see CPVRChannelGroup::MoveChannel + */ + bool MoveChannel(unsigned int iOldChannelNumber, unsigned int iNewChannelNumber, bool bSaveInDb = true); + + /*! + * @see CPVRChannelGroup::GetMembers + */ + int GetMembers(CFileItemList &results, bool bGroupMembers = true) const; + + /*! + * @brief Check whether the group name is still correct after the language setting changed. + */ + void CheckGroupName(void); + + /*! + * @brief Create an EPG table for each channel. + * @brief bForce Create the tables, even if they already have been created before. + * @return True if all tables were created successfully, false otherwise. + */ + bool CreateChannelEpgs(bool bForce = false); + + bool AddNewChannel(const CPVRChannel &channel, unsigned int iChannelNumber = 0) { UpdateFromClient(channel, iChannelNumber); return true; } + + protected: + /*! + * @brief Load all channels from the database. + * @param bCompress Compress the database after changing anything. + * @return The amount of channels that were loaded. + */ + int LoadFromDb(bool bCompress = false); + + /*! + * @brief Load all channels from the clients. + * @return The amount of channels that were loaded. + */ + int LoadFromClients(void); + + /*! + * @brief Check if this group is the internal group containing all channels. + * @return True if it's the internal group, false otherwise. + */ + bool IsInternalGroup(void) const { return true; } + + /*! + * @brief Update the current channel list with the given list. + * + * Update the current channel list with the given list. + * Only the new channels will be present in the passed list after this call. + * + * @param channels The channels to use to update this list. + * @return True if everything went well, false otherwise. + */ + bool UpdateGroupEntries(const CPVRChannelGroup &channels); + + bool AddAndUpdateChannels(const CPVRChannelGroup &channels, bool bUseBackendChannelNumbers); + + /*! + * @brief Refresh the channel list from the clients. + */ + bool Update(void); + + /*! + * @brief Remove invalid channels and updates the channel numbers. + */ + bool Renumber(void); + + /*! + * @brief Load the channels from the database. + * + * Load the channels from the database. + * If no channels are stored in the database, then the channels will be loaded from the clients. + * + * @return The amount of channels that were added. + */ + int Load(void); + + /*! + * @brief Update the vfs paths of all channels. + */ + void UpdateChannelPaths(void); + + void CreateChannelEpg(CPVRChannelPtr channel, bool bForce = false); + + int m_iHiddenChannels; /*!< the amount of hidden channels in this container */ + }; +} diff --git a/xbmc/pvr/channels/PVRChannelGroups.cpp b/xbmc/pvr/channels/PVRChannelGroups.cpp new file mode 100644 index 0000000000..77f098951c --- /dev/null +++ b/xbmc/pvr/channels/PVRChannelGroups.cpp @@ -0,0 +1,462 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "PVRChannelGroups.h" + +#include "FileItem.h" +#include "settings/GUISettings.h" +#include "guilib/GUIWindowManager.h" +#include "utils/log.h" +#include "URL.h" +#include "filesystem/File.h" + +#include "PVRChannelGroupInternal.h" +#include "pvr/PVRDatabase.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClients.h" + +using namespace PVR; + +CPVRChannelGroups::CPVRChannelGroups(bool bRadio) : + m_bRadio(bRadio) +{ +} + +CPVRChannelGroups::~CPVRChannelGroups(void) +{ + Clear(); +} + +void CPVRChannelGroups::Clear(void) +{ + CSingleLock lock(m_critSection); + m_groups.clear(); +} + +bool CPVRChannelGroups::GetGroupsFromClients(void) +{ + if (! g_guiSettings.GetBool("pvrmanager.syncchannelgroups")) + return true; + + return g_PVRClients->GetChannelGroups(this) == PVR_ERROR_NO_ERROR; +} + +bool CPVRChannelGroups::Update(const CPVRChannelGroup &group, bool bSaveInDb) +{ + if (group.GroupName().IsEmpty() && group.GroupID() <= 0) + return true; + + CPVRChannelGroupPtr updateGroup; + { + CSingleLock lock(m_critSection); + // try to find the group by id + if (group.GroupID() > 0) + updateGroup = GetById(group.GroupID()); + + // try to find the group by name if we didn't find it yet + if (!updateGroup) + updateGroup = GetByName(group.GroupName()); + + if (!updateGroup) + { + // create a new group if none was found + updateGroup = CPVRChannelGroupPtr(new CPVRChannelGroup(m_bRadio, group.GroupID(), group.GroupName())); + updateGroup->SetGroupType(group.GroupType()); + m_groups.push_back(updateGroup); + } + else + { + // update existing group + updateGroup->SetGroupID(group.GroupID()); + updateGroup->SetGroupName(group.GroupName()); + updateGroup->SetGroupType(group.GroupType()); + } + } + + // persist changes + if (bSaveInDb && updateGroup) + return updateGroup->Persist(); + + return true; +} + +CFileItemPtr CPVRChannelGroups::GetByPath(const CStdString &strPath) const +{ + // get the filename from curl + CURL url(strPath); + CStdString strFileName = url.GetFileName(); + URIUtils::RemoveSlashAtEnd(strFileName); + + CStdString strCheckPath; + for (std::vector<CPVRChannelGroupPtr>::const_iterator it = m_groups.begin(); it != m_groups.end(); it++) + { + // check if the path matches + strCheckPath.Format("channels/%s/%s/", (*it)->IsRadio() ? "radio" : "tv", (*it)->GroupName().c_str()); + if (strFileName.Left(strCheckPath.length()) == strCheckPath) + { + strFileName.erase(0, strCheckPath.length()); + return (*it)->GetByIndex(atoi(strFileName.c_str())); + } + } + + // no match + CFileItemPtr retVal(new CFileItem); + return retVal; +} + +CPVRChannelGroupPtr CPVRChannelGroups::GetById(int iGroupId) const +{ + CSingleLock lock(m_critSection); + for (std::vector<CPVRChannelGroupPtr>::const_iterator it = m_groups.begin(); it != m_groups.end(); it++) + { + if ((*it)->GroupID() == iGroupId) + return *it; + } + + CPVRChannelGroupPtr empty; + return empty; +} + +CPVRChannelGroupPtr CPVRChannelGroups::GetByName(const CStdString &strName) const +{ + CSingleLock lock(m_critSection); + for (std::vector<CPVRChannelGroupPtr>::const_iterator it = m_groups.begin(); it != m_groups.end(); it++) + { + if ((*it)->GroupName().Equals(strName)) + return *it; + } + + CPVRChannelGroupPtr empty; + return empty; +} + +void CPVRChannelGroups::RemoveFromAllGroups(const CPVRChannel &channel) +{ + CSingleLock lock(m_critSection); + for (std::vector<CPVRChannelGroupPtr>::const_iterator it = m_groups.begin(); it != m_groups.end(); it++) + { + // only delete the channel from non-system groups + if (!(*it)->IsInternalGroup()) + (*it)->RemoveFromGroup(channel); + } +} + +bool CPVRChannelGroups::Update(bool bChannelsOnly /* = false */) +{ + bool bUpdateAllGroups = !bChannelsOnly && g_guiSettings.GetBool("pvrmanager.syncchannelgroups"); + bool bReturn(true); + + // sync groups + if (bUpdateAllGroups) + GetGroupsFromClients(); + + // sync channels in groups + { + CSingleLock lock(m_critSection); + for (std::vector<CPVRChannelGroupPtr>::iterator it = m_groups.begin(); it != m_groups.end(); it++) + { + if (bUpdateAllGroups || (*it)->IsInternalGroup()) + bReturn = (*it)->Update() && bReturn; + } + } + + // persist changes + return PersistAll() && bReturn; +} + +bool CPVRChannelGroups::UpdateGroupsEntries(const CPVRChannelGroups &groups) +{ + CSingleLock lock(m_critSection); + + // go through groups list and check for deleted groups + for (int iGroupPtr = m_groups.size() - 1; iGroupPtr > 0; iGroupPtr--) + { + CPVRChannelGroup existingGroup(*m_groups.at(iGroupPtr)); + CPVRChannelGroupPtr group = groups.GetByName(existingGroup.GroupName()); + // user defined group wasn't found + if (existingGroup.GroupType() == PVR_GROUP_TYPE_DEFAULT && !group) + { + CLog::Log(LOGDEBUG, "PVR - %s - user defined group %s with id '%u' does not exist on the client anymore; deleting it", __FUNCTION__, existingGroup.GroupName().c_str(), existingGroup.GroupID()); + DeleteGroup(*m_groups.at(iGroupPtr)); + } + } + + // go through the groups list and check for new groups + for (std::vector<CPVRChannelGroupPtr>::const_iterator it = groups.m_groups.begin(); it != m_groups.end(); it++) + { + // check if this group is present in this container + CPVRChannelGroupPtr existingGroup = GetByName((*it)->GroupName()); + + // add it if not + if (!existingGroup) + m_groups.push_back(CPVRChannelGroupPtr(new CPVRChannelGroup(m_bRadio, -1, (*it)->GroupName()))); + } + + return true; +} + +bool CPVRChannelGroups::LoadUserDefinedChannelGroups(void) +{ + CPVRDatabase *database = GetPVRDatabase(); + if (!database) + return false; + + bool bSyncWithBackends = g_guiSettings.GetBool("pvrmanager.syncchannelgroups"); + + CSingleLock lock(m_critSection); + + // load the other groups from the database + int iSize = m_groups.size(); + database->Get(*this); + CLog::Log(LOGDEBUG, "PVR - %s - %d user defined %s channel groups fetched from the database", __FUNCTION__, (int) (m_groups.size() - iSize), m_bRadio ? "radio" : "TV"); + + // load groups from the backends if the option is enabled + iSize = m_groups.size(); + if (bSyncWithBackends) + { + GetGroupsFromClients(); + CLog::Log(LOGDEBUG, "PVR - %s - %d new user defined %s channel groups fetched from clients", __FUNCTION__, (int) (m_groups.size() - iSize), m_bRadio ? "radio" : "TV"); + } + else + CLog::Log(LOGDEBUG, "PVR - %s - 'synchannelgroups' is disabled; skipping groups from clients", __FUNCTION__); + + // load group members + for (std::vector<CPVRChannelGroupPtr>::iterator it = m_groups.begin(); it != m_groups.end(); it++) + (*it)->Load(); + + // persist changes if we fetched groups from the backends + return bSyncWithBackends ? PersistAll() : true; +} + +bool CPVRChannelGroups::Load(void) +{ + CSingleLock lock(m_critSection); + + // remove previous contents + Clear(); + + CLog::Log(LOGDEBUG, "PVR - %s - loading all %s channel groups", __FUNCTION__, m_bRadio ? "radio" : "TV"); + + // create and load the internal channel group + CPVRChannelGroupPtr internalChannels = CPVRChannelGroupPtr(new CPVRChannelGroupInternal(m_bRadio)); + m_groups.push_back(internalChannels); + internalChannels->Load(); + + // load the other groups from the database + LoadUserDefinedChannelGroups(); + + // set the internal group as selected at startup + SetSelectedGroup(internalChannels); + + CLog::Log(LOGDEBUG, "PVR - %s - %d %s channel groups loaded", __FUNCTION__, (int) m_groups.size(), m_bRadio ? "radio" : "TV"); + + // need at least 1 group + return m_groups.size() > 0; +} + +bool CPVRChannelGroups::PersistAll(void) +{ + bool bReturn(true); + CLog::Log(LOGDEBUG, "PVR - %s - persisting all changes in channel groups", __FUNCTION__); + + CSingleLock lock(m_critSection); + for (std::vector<CPVRChannelGroupPtr>::iterator it = m_groups.begin(); it != m_groups.end(); it++) + bReturn &= (*it)->Persist(); + + return bReturn; +} + +CPVRChannelGroupPtr CPVRChannelGroups::GetGroupAll(void) const +{ + CSingleLock lock(m_critSection); + if (m_groups.size() > 0) + return m_groups.at(0); + + CPVRChannelGroupPtr empty; + return empty; +} + +int CPVRChannelGroups::GetGroupList(CFileItemList* results) const +{ + int iReturn(0); + CSingleLock lock(m_critSection); + + CStdString strPath; + for (std::vector<CPVRChannelGroupPtr>::const_iterator it = m_groups.begin(); it != m_groups.end(); it++) + { + strPath.Format("channels/%s/%i", m_bRadio ? "radio" : "tv", (*it)->GroupID()); + CFileItemPtr group(new CFileItem(strPath, true)); + group->m_strTitle = (*it)->GroupName(); + results->Add(group); + ++iReturn; + } + + return iReturn; +} + +CPVRChannelGroupPtr CPVRChannelGroups::GetPreviousGroup(const CPVRChannelGroup &group) const +{ + bool bReturnNext(false); + + { + CSingleLock lock(m_critSection); + for (std::vector<CPVRChannelGroupPtr>::const_reverse_iterator it = m_groups.rbegin(); it != m_groups.rend(); it++) + { + // return this entry + if (bReturnNext) + return *it; + + // return the next entry + if ((*it)->GroupID() == group.GroupID()) + bReturnNext = true; + } + } + + // no match + return GetGroupAll(); +} + +CPVRChannelGroupPtr CPVRChannelGroups::GetNextGroup(const CPVRChannelGroup &group) const +{ + bool bReturnNext(false); + + { + CSingleLock lock(m_critSection); + for (std::vector<CPVRChannelGroupPtr>::const_iterator it = m_groups.begin(); it != m_groups.end(); it++) + { + // return this entry + if (bReturnNext) + return *it; + + // return the next entry + if ((*it)->GroupID() == group.GroupID()) + bReturnNext = true; + } + } + + // no match + return GetGroupAll(); +} + +CPVRChannelGroupPtr CPVRChannelGroups::GetSelectedGroup(void) const +{ + CSingleLock lock(m_critSection); + return m_selectedGroup; +} + +void CPVRChannelGroups::SetSelectedGroup(CPVRChannelGroupPtr group) +{ + // update the selected group + { + CSingleLock lock(m_critSection); + m_selectedGroup = group; + } + + // update the channel number cache + group->Renumber(); +} + +bool CPVRChannelGroups::AddGroup(const CStdString &strName) +{ + bool bPersist(false); + CPVRChannelGroupPtr group; + + { + CSingleLock lock(m_critSection); + + // check if there's no group with the same name yet + group = GetByName(strName); + if (!group) + { + // create a new group + group = CPVRChannelGroupPtr(new CPVRChannelGroup(m_bRadio, -1, strName)); + m_groups.push_back(group); + bPersist = true; + } + } + + // persist in the db if a new group was added + return bPersist ? group->Persist() : true; +} + +bool CPVRChannelGroups::DeleteGroup(const CPVRChannelGroup &group) +{ + // don't delete internal groups + if (group.IsInternalGroup()) + { + CLog::Log(LOGERROR, "PVR - %s - cannot delete internal group '%s'", __FUNCTION__, group.GroupName().c_str()); + return false; + } + + // delete the group in this container + CSingleLock lock(m_critSection); + for (std::vector<CPVRChannelGroupPtr>::iterator it = m_groups.begin(); it != m_groups.end(); it++) + { + if ((*it)->GroupID() == group.GroupID()) + { + // update the selected group in the gui if it's deleted + CPVRChannelGroupPtr selectedGroup = GetSelectedGroup(); + if (selectedGroup && *selectedGroup == group) + g_PVRManager.SetPlayingGroup(GetGroupAll()); + + m_groups.erase(it); + break; + } + } + + // delete the group from the database + CPVRDatabase *database = GetPVRDatabase(); + return database ? database->Delete(group) : false; +} + +void CPVRChannelGroups::FillGroupsGUI(int iWindowId, int iControlId) const +{ + int iListGroupPtr(0); + int iSelectedGroupPtr(0); + CPVRChannelGroupPtr selectedGroup = g_PVRManager.GetPlayingGroup(false); + std::vector<CGUIMessage> messages; + + // fetch all groups + { + CSingleLock lock(m_critSection); + for (std::vector<CPVRChannelGroupPtr>::const_iterator it = m_groups.begin(); it != m_groups.end(); it++) + { + // skip empty groups + if ((*it)->Size() == 0) + continue; + + if ((*it)->GroupID() == selectedGroup->GroupID()) + iSelectedGroupPtr = iListGroupPtr; + + CGUIMessage msg(GUI_MSG_LABEL_ADD, iWindowId, iControlId, iListGroupPtr++); + msg.SetLabel((*it)->GroupName()); + messages.push_back(msg); + } + } + + // send updates + for (std::vector<CGUIMessage>::iterator it = messages.begin(); it != messages.end(); it++) + g_windowManager.SendMessage(*it); + + // selected group + CGUIMessage msgSel(GUI_MSG_ITEM_SELECT, iWindowId, iControlId, iSelectedGroupPtr); + g_windowManager.SendMessage(msgSel); +} diff --git a/xbmc/pvr/channels/PVRChannelGroups.h b/xbmc/pvr/channels/PVRChannelGroups.h new file mode 100644 index 0000000000..04d63071dd --- /dev/null +++ b/xbmc/pvr/channels/PVRChannelGroups.h @@ -0,0 +1,188 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "FileItem.h" +#include "PVRChannelGroup.h" +#include "threads/CriticalSection.h" + +namespace PVR +{ + /** A container class for channel groups */ + + class CPVRChannelGroups + { + public: + /*! + * @brief Create a new group container. + * @param bRadio True if this is a container for radio channels, false if it is for tv channels. + */ + CPVRChannelGroups(bool bRadio); + virtual ~CPVRChannelGroups(void); + + /*! + * @brief Remove all channels from this group. + */ + void Clear(void); + + /*! + * @brief Load this container's contents from the database or PVR clients. + * @return True if it was loaded successfully, false if not. + */ + bool Load(void); + + /*! + * @return Amount of groups in this container + */ + int Size(void) const { CSingleLock lock(m_critSection); return m_groups.size(); } + + /*! + * @brief Update a group or add it if it's not in here yet. + * @param group The group to update. + * @param bSaveInDb True to save the changes in the db. + * @return True if the group was added or update successfully, false otherwise. + */ + bool Update(const CPVRChannelGroup &group, bool bSaveInDb = false); + + /*! + * @brief Called by the add-on callback to add a new group + * @param group The group to add + * @return True when updated, false otherwise + */ + bool UpdateFromClient(const CPVRChannelGroup &group) { return Update(group, false); } + + /*! + * @brief Get a channel given it's path + * @param strPath The path to the channel + * @return The channel, or an empty fileitem when not found + */ + CFileItemPtr GetByPath(const CStdString &strPath) const; + + /*! + * @brief Get a pointer to a channel group given it's ID. + * @param iGroupId The ID of the group. + * @return The group or NULL if it wasn't found. + */ + CPVRChannelGroupPtr GetById(int iGroupId) const; + + /*! + * @brief Get a group given it's name. + * @param strName The name. + * @return The group or NULL if it wan't found. + */ + CPVRChannelGroupPtr GetByName(const CStdString &strName) const; + + /*! + * @brief Get the group that contains all channels. + * @return The group that contains all channels. + */ + CPVRChannelGroupPtr GetGroupAll(void) const; + + /*! + * @brief Get the list of groups. + * @param results The file list to store the results in. + * @return The amount of items that were added. + */ + int GetGroupList(CFileItemList* results) const; + + /*! + * @brief Get the previous group in this container. + * @param group The current group. + * @return The previous group or the group containing all channels if it wasn't found. + */ + CPVRChannelGroupPtr GetPreviousGroup(const CPVRChannelGroup &group) const; + + /*! + * @brief Get the next group in this container. + * @param group The current group. + * @return The next group or the group containing all channels if it wasn't found. + */ + CPVRChannelGroupPtr GetNextGroup(const CPVRChannelGroup &group) const; + + /*! + * @brief Get the group that is currently selected in the UI. + * @return The selected group. + */ + CPVRChannelGroupPtr GetSelectedGroup(void) const; + + /*! + * @brief Change the selected group. + * @param group The group to select. + */ + void SetSelectedGroup(CPVRChannelGroupPtr group); + + /*! + * @brief Add a group to this container. + * @param strName The name of the group. + * @return True if the group was added, false otherwise. + */ + bool AddGroup(const CStdString &strName); + + /*! + * @brief Delete a group in this container. + * @param group The group to delete. + * @return True if it was deleted successfully, false if not. + */ + bool DeleteGroup(const CPVRChannelGroup &group); + + /*! + * @brief Remove a channel from all non-system groups. + * @param channel The channel to remove. + */ + void RemoveFromAllGroups(const CPVRChannel &channel); + + /*! + * @brief Persist all changes in channel groups. + * @return True if everything was persisted, false otherwise. + */ + bool PersistAll(void); + + /*! + * @return True when this container contains radio groups, false otherwise + */ + bool IsRadio(void) const { return m_bRadio; } + + /*! + * @brief Call by a guiwindow/dialog to add the groups to a control + * @param iWindowId The window to add the groups to. + * @param iControlId The control to add the groups to + */ + void FillGroupsGUI(int iWindowId, int iControlId) const; + + /*! + * @brief Update the contents of the groups in this container. + * @param bChannelsOnly Set to true to only update channels, not the groups themselves. + * @return True if the update was successful, false otherwise. + */ + bool Update(bool bChannelsOnly = false); + + private: + bool UpdateGroupsEntries(const CPVRChannelGroups &groups); + bool LoadUserDefinedChannelGroups(void); + bool GetGroupsFromClients(void); + + bool m_bRadio; /*!< true if this is a container for radio channels, false if it is for tv channels */ + CPVRChannelGroupPtr m_selectedGroup; /*!< the group that's currently selected in the UI */ + std::vector<CPVRChannelGroupPtr> m_groups; /*!< the groups in this container */ + CCriticalSection m_critSection; + }; +} diff --git a/xbmc/pvr/channels/PVRChannelGroupsContainer.cpp b/xbmc/pvr/channels/PVRChannelGroupsContainer.cpp new file mode 100644 index 0000000000..51e5be72e0 --- /dev/null +++ b/xbmc/pvr/channels/PVRChannelGroupsContainer.cpp @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "PVRChannelGroupsContainer.h" +#include "URL.h" +#include "dialogs/GUIDialogOK.h" +#include "guilib/LocalizeStrings.h" +#include "utils/URIUtils.h" +#include "utils/log.h" +#include "pvr/PVRManager.h" + +using namespace PVR; + +CPVRChannelGroupsContainer::CPVRChannelGroupsContainer(void) : + m_groupsRadio(new CPVRChannelGroups(true)), + m_groupsTV(new CPVRChannelGroups(false)), + m_bUpdateChannelsOnly(false), + m_bIsUpdating(false) +{ +} + +CPVRChannelGroupsContainer::~CPVRChannelGroupsContainer(void) +{ + delete m_groupsRadio; + delete m_groupsTV; +} + +bool CPVRChannelGroupsContainer::Update(bool bChannelsOnly /* = false */) +{ + CSingleLock lock(m_critSection); + if (m_bIsUpdating) + return false; + m_bIsUpdating = true; + m_bUpdateChannelsOnly = bChannelsOnly; + lock.Leave(); + + CLog::Log(LOGDEBUG, "CPVRChannelGroupsContainer - %s - updating %s", __FUNCTION__, bChannelsOnly ? "channels" : "channel groups"); + bool bReturn = m_groupsRadio->Update(bChannelsOnly) && + m_groupsTV->Update(bChannelsOnly); + + lock.Enter(); + m_bIsUpdating = false; + lock.Leave(); + + return bReturn; +} + +bool CPVRChannelGroupsContainer::Load(void) +{ + Unload(); + + return m_groupsRadio->Load() && + m_groupsTV->Load(); +} + +void CPVRChannelGroupsContainer::Unload(void) +{ + m_groupsRadio->Clear(); + m_groupsTV->Clear(); +} + +CPVRChannelGroups *CPVRChannelGroupsContainer::Get(bool bRadio) const +{ + return bRadio ? m_groupsRadio : m_groupsTV; +} + +CPVRChannelGroupPtr CPVRChannelGroupsContainer::GetGroupAll(bool bRadio) const +{ + return Get(bRadio)->GetGroupAll(); +} + +CPVRChannelGroupPtr CPVRChannelGroupsContainer::GetByIdFromAll(int iGroupId) const +{ + CPVRChannelGroupPtr group = m_groupsTV->GetById(iGroupId); + if (!group) + group = m_groupsRadio->GetById(iGroupId); + + return group; +} + +CPVRChannelPtr CPVRChannelGroupsContainer::GetChannelById(int iChannelId) const +{ + CPVRChannelPtr channel = m_groupsTV->GetGroupAll()->GetByChannelID(iChannelId); + if (!channel) + channel = m_groupsRadio->GetGroupAll()->GetByChannelID(iChannelId); + + return channel; +} + +CPVRChannelPtr CPVRChannelGroupsContainer::GetChannelByEpgId(int iEpgId) const +{ + CPVRChannelPtr channel = m_groupsTV->GetGroupAll()->GetByChannelEpgID(iEpgId); + if (!channel) + channel = m_groupsRadio->GetGroupAll()->GetByChannelEpgID(iEpgId); + + return channel; +} + +bool CPVRChannelGroupsContainer::GetGroupsDirectory(CFileItemList *results, bool bRadio) +{ + const CPVRChannelGroups *channelGroups = Get(bRadio); + if (channelGroups) + { + channelGroups->GetGroupList(results); + return true; + } + return false; +} + +CFileItemPtr CPVRChannelGroupsContainer::GetByPath(const CStdString &strPath) const +{ + for (unsigned int bRadio = 0; bRadio <= 1; bRadio++) + { + const CPVRChannelGroups *groups = Get(bRadio == 1); + CFileItemPtr retVal = groups->GetByPath(strPath); + if (retVal && retVal->HasPVRChannelInfoTag()) + return retVal; + } + + CFileItemPtr retVal(new CFileItem); + return retVal; +} + +bool CPVRChannelGroupsContainer::GetDirectory(const CStdString& strPath, CFileItemList &results) +{ + CStdString strBase(strPath); + + /* get the filename from curl */ + CURL url(strPath); + CStdString fileName = url.GetFileName(); + URIUtils::RemoveSlashAtEnd(fileName); + + if (fileName == "channels") + { + CFileItemPtr item; + + /* all tv channels */ + item.reset(new CFileItem(strBase + "/tv/", true)); + item->SetLabel(g_localizeStrings.Get(19020)); + item->SetLabelPreformated(true); + results.Add(item); + + /* all radio channels */ + item.reset(new CFileItem(strBase + "/radio/", true)); + item->SetLabel(g_localizeStrings.Get(19021)); + item->SetLabelPreformated(true); + results.Add(item); + + return true; + } + else if (fileName == "channels/tv") + { + return GetGroupsDirectory(&results, false); + } + else if (fileName == "channels/radio") + { + return GetGroupsDirectory(&results, true); + } + else if (fileName.Left(12) == "channels/tv/") + { + CStdString strGroupName(fileName.substr(12)); + URIUtils::RemoveSlashAtEnd(strGroupName); + CPVRChannelGroupPtr group = GetTV()->GetByName(strGroupName); + if (!group) + group = GetGroupAllTV(); + if (group) + group->GetMembers(results, !fileName.Right(7).Equals(".hidden")); + return true; + } + else if (fileName.Left(15) == "channels/radio/") + { + CStdString strGroupName(fileName.substr(15)); + URIUtils::RemoveSlashAtEnd(strGroupName); + CPVRChannelGroupPtr group = GetRadio()->GetByName(strGroupName); + if (!group) + group = GetGroupAllRadio(); + if (group) + group->GetMembers(results, !fileName.Right(7).Equals(".hidden")); + return true; + } + + return false; +} + +int CPVRChannelGroupsContainer::GetNumChannelsFromAll() +{ + return GetGroupAllTV()->Size() + GetGroupAllRadio()->Size(); +} + +CPVRChannelGroupPtr CPVRChannelGroupsContainer::GetSelectedGroup(bool bRadio) const +{ + return Get(bRadio)->GetSelectedGroup(); +} + +CPVRChannelPtr CPVRChannelGroupsContainer::GetByUniqueID(int iClientChannelNumber, int iClientID) +{ + CPVRChannelPtr channel; + CPVRChannelGroupPtr channelgroup = GetGroupAllTV(); + if (channelgroup) + channel = channelgroup->GetByClient(iClientChannelNumber, iClientID); + + if (!channelgroup || !channel) + channelgroup = GetGroupAllRadio(); + if (channelgroup) + channel = channelgroup->GetByClient(iClientChannelNumber, iClientID); + + return channel; +} + +CFileItemPtr CPVRChannelGroupsContainer::GetByChannelIDFromAll(int iChannelID) +{ + CPVRChannelPtr channel; + CPVRChannelGroupPtr channelgroup = GetGroupAllTV(); + if (channelgroup) + channel = channelgroup->GetByChannelID(iChannelID); + + if (!channel) + { + channelgroup = GetGroupAllRadio(); + if (channelgroup) + channel = channelgroup->GetByChannelID(iChannelID); + } + + if (channel) + { + CFileItemPtr retVal = CFileItemPtr(new CFileItem(*channel)); + return retVal; + } + + CFileItemPtr retVal = CFileItemPtr(new CFileItem); + return retVal; +} + +void CPVRChannelGroupsContainer::SearchMissingChannelIcons(void) +{ + CLog::Log(LOGINFO, "PVRChannelGroupsContainer - %s - starting channel icon search", __FUNCTION__); + + // TODO: Add Process dialog here + CPVRChannelGroupPtr channelgrouptv = GetGroupAllTV(); + CPVRChannelGroupPtr channelgroupradio = GetGroupAllRadio(); + + if (channelgrouptv) + channelgrouptv->SearchAndSetChannelIcons(true); + if (channelgroupradio) + channelgroupradio->SearchAndSetChannelIcons(true); + + CGUIDialogOK::ShowAndGetInput(19103,0,20177,0); +} + +CFileItemPtr CPVRChannelGroupsContainer::GetLastPlayedChannel(void) const +{ + CFileItemPtr lastChannel = GetGroupAllTV()->GetLastPlayedChannel(); + bool bHasTVChannel(lastChannel && lastChannel->HasPVRChannelInfoTag()); + + CFileItemPtr lastRadioChannel = GetGroupAllRadio()->GetLastPlayedChannel(); + bool bHasRadioChannel(lastRadioChannel && lastRadioChannel->HasPVRChannelInfoTag()); + + if (!bHasTVChannel || (bHasRadioChannel && lastChannel->GetPVRChannelInfoTag()->LastWatched() < lastRadioChannel->GetPVRChannelInfoTag()->LastWatched())) + return lastRadioChannel; + + return lastChannel; +} + +bool CPVRChannelGroupsContainer::CreateChannel(const CPVRChannel &channel) +{ + return GetGroupAll(channel.IsRadio())->AddNewChannel(channel); +} diff --git a/xbmc/pvr/channels/PVRChannelGroupsContainer.h b/xbmc/pvr/channels/PVRChannelGroupsContainer.h new file mode 100644 index 0000000000..4b449e8728 --- /dev/null +++ b/xbmc/pvr/channels/PVRChannelGroupsContainer.h @@ -0,0 +1,200 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "PVRChannelGroups.h" +#include "threads/Thread.h" +#include "threads/CriticalSection.h" + +namespace PVR +{ + class CPVRManager; + class CPVRChannelsUpdateJob; + class CPVRChannelGroupsUpdateJob; + + class CPVRChannelGroupsContainer + { + friend class CPVRManager; + friend class CPVRChannelsUpdateJob; + friend class CPVRChannelGroupsUpdateJob; + + public: + /*! + * @brief Create a new container for all channel groups + */ + CPVRChannelGroupsContainer(void); + + /*! + * @brief Destroy this container. + */ + virtual ~CPVRChannelGroupsContainer(void); + + /*! + * @brief Load all channel groups and all channels in those channel groups. + * @return True if all groups were loaded, false otherwise. + */ + bool Load(void); + + /*! + * @brief Unload and destruct all channel groups and all channels in them. + */ + void Unload(void); + + /*! + * @brief Get the TV channel groups. + * @return The TV channel groups. + */ + CPVRChannelGroups *GetTV(void) const { return Get(false); } + + /*! + * @brief Get the radio channel groups. + * @return The radio channel groups. + */ + CPVRChannelGroups *GetRadio(void) const { return Get(true); } + + /*! + * @brief Get the radio or TV channel groups. + * @param bRadio If true, get the radio channel groups. Get the TV channel groups otherwise. + * @return The requested groups. + */ + CPVRChannelGroups *Get(bool bRadio) const; + + /*! + * @brief Get the group containing all TV channels. + * @return The group containing all TV channels. + */ + CPVRChannelGroupPtr GetGroupAllTV(void) const{ return GetGroupAll(false); } + + /*! + * @brief Get the group containing all radio channels. + * @return The group containing all radio channels. + */ + CPVRChannelGroupPtr GetGroupAllRadio(void) const{ return GetGroupAll(true); } + + /*! + * @brief Get the group containing all TV or radio channels. + * @param bRadio If true, get the group containing all radio channels. Get the group containing all TV channels otherwise. + * @return The requested group. + */ + CPVRChannelGroupPtr GetGroupAll(bool bRadio) const; + + /*! + * @brief Get a group given it's ID. + * @param iGroupId The ID of the group. + * @return The requested group or NULL if it wasn't found. + */ + CPVRChannelGroupPtr GetByIdFromAll(int iGroupId) const; + + /*! + * @brief Get a channel given it's database ID. + * @param iChannelId The ID of the channel. + * @return The channel or NULL if it wasn't found. + */ + CPVRChannelPtr GetChannelById(int iChannelId) const; + + /*! + * @brief Get a channel given it's EPG ID. + * @param iEpgId The EPG ID of the channel. + * @return The channel or NULL if it wasn't found. + */ + CPVRChannelPtr GetChannelByEpgId(int iEpgId) const; + + /*! + * @brief Get the groups list for a directory. + * @param strBase The directory path. + * @param results The file list to store the results in. + * @param bRadio Get radio channels or tv channels. + * @return True if the list was filled succesfully. + */ + bool GetGroupsDirectory(CFileItemList *results, bool bRadio); + + /*! + * @brief Get a channel given it's path. + * @param strPath The path. + * @return The channel or NULL if it wasn't found. + */ + CFileItemPtr GetByPath(const CStdString &strPath) const; + + /*! + * @brief Get the directory for a path. + * @param strPath The path. + * @param results The file list to store the results in. + * @return True if the directory was found, false if not. + */ + bool GetDirectory(const CStdString& strPath, CFileItemList &results); + + /*! + * @brief The total amount of unique channels in all containers. + * @return The total amount of unique channels in all containers. + */ + int GetNumChannelsFromAll(void); + + /*! + * @brief Get the group that is currently selected in the UI. + * @param bRadio True to get the selected radio group, false to get the selected TV group. + * @return The selected group. + */ + CPVRChannelGroupPtr GetSelectedGroup(bool bRadio) const; + + /*! + * @brief Get a channel given it's channel ID from all containers. + * @param iClientChannelNumber The channel number on the client. + * @param iClientID The ID of the client. + * @return The channel or NULL if it wasn't found. + */ + CPVRChannelPtr GetByUniqueID(int iClientChannelNumber, int iClientID); + + /*! + * @brief Get a channel given it's channel ID from all containers. + * @param iChannelID The channel ID. + * @return The channel or NULL if it wasn't found. + */ + CFileItemPtr GetByChannelIDFromAll(int iChannelID); + + /*! + * @brief Try to find missing channel icons automatically + */ + void SearchMissingChannelIcons(void); + + /*! + * @brief The channel that was played last that has a valid client or NULL if there was none. + * @return The requested channel. + */ + CFileItemPtr GetLastPlayedChannel(void) const; + + bool CreateChannel(const CPVRChannel &channel); + + protected: + /*! + * @brief Update the contents of all the groups in this container. + * @param bChannelsOnly Set to true to only update channels, not the groups themselves. + * @return True if the update was successful, false otherwise. + */ + bool Update(bool bChannelsOnly = false); + + CPVRChannelGroups *m_groupsRadio; /*!< all radio channel groups */ + CPVRChannelGroups *m_groupsTV; /*!< all TV channel groups */ + CCriticalSection m_critSection; + bool m_bUpdateChannelsOnly; + bool m_bIsUpdating; + }; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.cpp b/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.cpp new file mode 100644 index 0000000000..cf7108e38b --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.cpp @@ -0,0 +1,854 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIDialogPVRChannelManager.h" + +#include "FileItem.h" +#include "GUIDialogPVRGroupManager.h" +#include "dialogs/GUIDialogFileBrowser.h" +#include "guilib/GUIKeyboardFactory.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogProgress.h" +#include "dialogs/GUIDialogSelect.h" +#include "dialogs/GUIDialogYesNo.h" +#include "guilib/GUIEditControl.h" +#include "guilib/GUIRadioButtonControl.h" +#include "guilib/GUISpinControlEx.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/addons/PVRClients.h" +#include "settings/GUISettings.h" +#include "settings/Settings.h" +#include "storage/MediaManager.h" + +#define BUTTON_OK 4 +#define BUTTON_APPLY 5 +#define BUTTON_CANCEL 6 +#define RADIOBUTTON_ACTIVE 7 +#define EDIT_NAME 8 +#define BUTTON_CHANNEL_LOGO 9 +#define IMAGE_CHANNEL_LOGO 10 +#define RADIOBUTTON_USEEPG 12 +#define SPIN_EPGSOURCE_SELECTION 13 +#define RADIOBUTTON_PARENTAL_LOCK 14 +#define CONTROL_LIST_CHANNELS 20 +#define BUTTON_GROUP_MANAGER 30 +#define BUTTON_EDIT_CHANNEL 31 +#define BUTTON_DELETE_CHANNEL 32 +#define BUTTON_NEW_CHANNEL 33 +#define BUTTON_RADIO_TV 34 + +using namespace std; +using namespace PVR; + +CGUIDialogPVRChannelManager::CGUIDialogPVRChannelManager(void) : + CGUIDialog(WINDOW_DIALOG_PVR_CHANNEL_MANAGER, "DialogPVRChannelManager.xml"), + m_bIsRadio(false), + m_channelItems(new CFileItemList) +{ +} + +CGUIDialogPVRChannelManager::~CGUIDialogPVRChannelManager(void) +{ + delete m_channelItems; +} + +bool CGUIDialogPVRChannelManager::OnActionClose(const CAction &action) +{ + bool bReturn(false); + int iActionId = action.GetID(); + if (iActionId == ACTION_PREVIOUS_MENU || iActionId == ACTION_PARENT_DIR) + { + Close(); + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRChannelManager::OnActionMove(const CAction &action) +{ + bool bReturn(false); + int iActionId = action.GetID(); + if (GetFocusedControlID() == CONTROL_LIST_CHANNELS && + (iActionId == ACTION_MOVE_DOWN || iActionId == ACTION_MOVE_UP || + iActionId == ACTION_PAGE_DOWN || iActionId == ACTION_PAGE_UP)) + { + bReturn = true; + if (!m_bMovingMode) + { + CGUIDialog::OnAction(action); + int iSelected = m_viewControl.GetSelectedItem(); + if (iSelected != m_iSelected) + { + m_iSelected = iSelected; + SetData(m_iSelected); + } + } + else + { + CStdString strNumber; + CGUIDialog::OnAction(action); + + bool bMoveUp = iActionId == ACTION_PAGE_UP || iActionId == ACTION_MOVE_UP; + unsigned int iLines = bMoveUp ? abs(m_iSelected - m_viewControl.GetSelectedItem()) : 1; + bool bOutOfBounds = bMoveUp ? m_iSelected <= 0 : m_iSelected >= m_channelItems->Size() - 1; + if (bOutOfBounds) + { + bMoveUp = !bMoveUp; + iLines = m_channelItems->Size() - 1; + } + + for (unsigned int iLine = 0; iLine < iLines; iLine++) + { + unsigned int iNewSelect = bMoveUp ? m_iSelected - 1 : m_iSelected + 1; + if (m_channelItems->Get(iNewSelect)->GetProperty("Number").asString() != "-") + { + strNumber.Format("%i", m_iSelected+1); + m_channelItems->Get(iNewSelect)->SetProperty("Number", strNumber); + strNumber.Format("%i", iNewSelect+1); + m_channelItems->Get(m_iSelected)->SetProperty("Number", strNumber); + } + m_channelItems->Swap(iNewSelect, m_iSelected); + m_iSelected = iNewSelect; + } + + m_viewControl.SetItems(*m_channelItems); + m_viewControl.SetSelectedItem(m_iSelected); + } + } + + return bReturn; +} + +bool CGUIDialogPVRChannelManager::OnAction(const CAction& action) +{ + return OnActionClose(action) || + OnActionMove(action) || + CGUIDialog::OnAction(action); +} + +bool CGUIDialogPVRChannelManager::OnMessageInit(CGUIMessage &message) +{ + CGUIWindow::OnMessage(message); + m_iSelected = 0; + m_bIsRadio = false; + m_bMovingMode = false; + m_bContainsChanges = false; + SetProperty("IsRadio", ""); + Update(); + SetData(m_iSelected); + + return true; +} + +bool CGUIDialogPVRChannelManager::OnClickListChannels(CGUIMessage &message) +{ + if (!m_bMovingMode) + { + int iAction = message.GetParam1(); + int iItem = m_viewControl.GetSelectedItem(); + + /* Check file item is in list range and get his pointer */ + if (iItem < 0 || iItem >= (int)m_channelItems->Size()) return true; + + /* Process actions */ + if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK) + { + /* Show Contextmenu */ + OnPopupMenu(iItem); + + return true; + } + } + else + { + CFileItemPtr pItem = m_channelItems->Get(m_iSelected); + if (pItem) + { + pItem->SetProperty("Changed", true); + pItem->Select(false); + m_bMovingMode = false; + m_bContainsChanges = true; + return true; + } + } + + return false; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonOK(CGUIMessage &message) +{ + SaveList(); + Close(); + return true; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonApply(CGUIMessage &message) +{ + SaveList(); + return true; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonCancel(CGUIMessage &message) +{ + Close(); + return true; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonRadioTV(CGUIMessage &message) +{ + if (m_bContainsChanges) + { + CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + if (!pDialog) + return true; + + pDialog->SetHeading(20052); + pDialog->SetLine(0, ""); + pDialog->SetLine(1, 19212); + pDialog->SetLine(2, 20103); + pDialog->DoModal(); + + if (pDialog->IsConfirmed()) + SaveList(); + } + + m_iSelected = 0; + m_bMovingMode = false; + m_bContainsChanges = false; + m_bIsRadio = !m_bIsRadio; + SetProperty("IsRadio", m_bIsRadio ? "true" : ""); + Update(); + SetData(m_iSelected); + return true; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonRadioActive(CGUIMessage &message) +{ + CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl *)GetControl(RADIOBUTTON_ACTIVE); + if (pRadioButton) + { + CFileItemPtr pItem = m_channelItems->Get(m_iSelected); + if (pItem) + { + pItem->SetProperty("Changed", true); + pItem->SetProperty("ActiveChannel", pRadioButton->IsSelected()); + m_bContainsChanges = true; + Renumber(); + return true; + } + } + + return false; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonRadioParentalLocked(CGUIMessage &message) +{ + CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl *)GetControl(RADIOBUTTON_PARENTAL_LOCK); + + // ask for PIN first + if (!g_PVRManager.CheckParentalPIN(g_localizeStrings.Get(19262).c_str())) + { + pRadioButton->SetSelected(!pRadioButton->IsSelected()); + return false; + } + + if (pRadioButton) + { + CFileItemPtr pItem = m_channelItems->Get(m_iSelected); + if (pItem) + { + pItem->SetProperty("Changed", true); + pItem->SetProperty("ParentalLocked", pRadioButton->IsSelected()); + m_bContainsChanges = true; + Renumber(); + return true; + } + } + + return false; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonEditName(CGUIMessage &message) +{ + CGUIEditControl *pEdit = (CGUIEditControl *)GetControl(EDIT_NAME); + if (pEdit) + { + CFileItemPtr pItem = m_channelItems->Get(m_iSelected); + if (pItem) + { + pItem->SetProperty("Changed", true); + pItem->SetProperty("Name", pEdit->GetLabel2()); + m_bContainsChanges = true; + + return true; + } + } + + return false; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonChannelLogo(CGUIMessage &message) +{ + CFileItemPtr pItem = m_channelItems->Get(m_iSelected); + if (!pItem) + return false; + if (g_settings.GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked()) + return false; + else if (!g_passwordManager.IsMasterLockUnlocked(true)) + return false; + + // setup our thumb list + CFileItemList items; + + // add the current thumb, if available + if (!pItem->GetProperty("Icon").asString().empty()) + { + CFileItemPtr current(new CFileItem("thumb://Current", false)); + current->SetThumbnailImage(pItem->GetPVRChannelInfoTag()->IconPath()); + current->SetLabel(g_localizeStrings.Get(20016)); + items.Add(current); + } + else if (pItem->HasThumbnail()) + { // already have a thumb that the share doesn't know about - must be a local one, so we mayaswell reuse it. + CFileItemPtr current(new CFileItem("thumb://Current", false)); + current->SetThumbnailImage(pItem->GetThumbnailImage()); + current->SetLabel(g_localizeStrings.Get(20016)); + items.Add(current); + } + + // and add a "no thumb" entry as well + CFileItemPtr nothumb(new CFileItem("thumb://None", false)); + nothumb->SetIconImage(pItem->GetIconImage()); + nothumb->SetLabel(g_localizeStrings.Get(20018)); + items.Add(nothumb); + + CStdString strThumb; + VECSOURCES shares; + if (g_guiSettings.GetString("pvrmenu.iconpath") != "") + { + CMediaSource share1; + share1.strPath = g_guiSettings.GetString("pvrmenu.iconpath"); + share1.strName = g_localizeStrings.Get(19018); + shares.push_back(share1); + } + g_mediaManager.GetLocalDrives(shares); + if (!CGUIDialogFileBrowser::ShowAndGetImage(items, shares, g_localizeStrings.Get(1030), strThumb)) + return false; + + if (strThumb == "thumb://Current") + return true; + + if (strThumb == "thumb://None") + strThumb = ""; + + pItem->SetProperty("Icon", strThumb); + pItem->SetProperty("Changed", true); + m_bContainsChanges = true; + return true; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonUseEPG(CGUIMessage &message) +{ + CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl *)GetControl(RADIOBUTTON_USEEPG); + if (pRadioButton) + { + CFileItemPtr pItem = m_channelItems->Get(m_iSelected); + if (pItem) + { + pItem->SetProperty("Changed", true); + pItem->SetProperty("UseEPG", pRadioButton->IsSelected()); + m_bContainsChanges = true; + + return true; + } + } + + return false; +} + +bool CGUIDialogPVRChannelManager::OnClickEPGSourceSpin(CGUIMessage &message) +{ + // TODO: Add EPG scraper support + return true; +// CGUISpinControlEx *pSpin = (CGUISpinControlEx *)GetControl(SPIN_EPGSOURCE_SELECTION); +// if (pSpin) +// { +// CFileItemPtr pItem = m_channelItems->Get(m_iSelected); +// if (pItem) +// { +// pItem->SetProperty("EPGSource", (int)0); +// pItem->SetProperty("Changed", true); +// m_bContainsChanges = true; +// return true; +// } +// } +} + +bool CGUIDialogPVRChannelManager::OnClickButtonGroupManager(CGUIMessage &message) +{ + /* Load group manager dialog */ + CGUIDialogPVRGroupManager* pDlgInfo = (CGUIDialogPVRGroupManager*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GROUP_MANAGER); + if (!pDlgInfo) + return false; + + pDlgInfo->SetRadio(m_bIsRadio); + + /* Open dialog window */ + pDlgInfo->DoModal(); + + Update(); + return true; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonEditChannel(CGUIMessage &message) +{ + CFileItemPtr pItem = m_channelItems->Get(m_iSelected); + if (!pItem) + return false; + + if (pItem->GetProperty("Virtual").asBoolean()) + { + CStdString strURL = pItem->GetProperty("StreamURL").asString(); + if (CGUIKeyboardFactory::ShowAndGetInput(strURL, g_localizeStrings.Get(19214), false)) + pItem->SetProperty("StreamURL", strURL); + return true; + } + + CGUIDialogOK::ShowAndGetInput(19033,19038,0,0); + return true; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonDeleteChannel(CGUIMessage &message) +{ + CFileItemPtr pItem = m_channelItems->Get(m_iSelected); + if (!pItem) + return false; + + CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + if (!pDialog) + return true; + + pDialog->SetHeading(19211); + pDialog->SetLine(0, ""); + pDialog->SetLine(1, 750); + pDialog->SetLine(2, ""); + pDialog->DoModal(); + + if (pDialog->IsConfirmed()) + { + if (pItem->GetProperty("Virtual").asBoolean()) + { + pItem->GetPVRChannelInfoTag()->SetVirtual(true); + m_channelItems->Remove(m_iSelected); + m_viewControl.SetItems(*m_channelItems); + Renumber(); + return true; + } + CGUIDialogOK::ShowAndGetInput(19033,19038,0,0); + } + return true; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonNewChannel(CGUIMessage &message) +{ + std::vector<long> clients; + + CGUIDialogSelect* pDlgSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT); + if (!pDlgSelect) + return false; + + pDlgSelect->SetHeading(19213); // Select Client + pDlgSelect->Add(g_localizeStrings.Get(19209)); + clients.push_back(PVR_VIRTUAL_CLIENT_ID); + + PVR_CLIENTMAP clientMap; + if (g_PVRClients->GetConnectedClients(clientMap) > 0) + { + PVR_CLIENTMAP_ITR itr; + for (itr = clientMap.begin() ; itr != clientMap.end(); itr++) + { + clients.push_back((*itr).first); + pDlgSelect->Add((*itr).second->Name()); + } + } + pDlgSelect->DoModal(); + + int selection = pDlgSelect->GetSelectedLabel(); + if (selection >= 0 && selection <= (int) clients.size()) + { + int clientID = clients[selection]; + if (clientID == PVR_VIRTUAL_CLIENT_ID) + { + CStdString strURL = ""; + if (CGUIKeyboardFactory::ShowAndGetInput(strURL, g_localizeStrings.Get(19214), false)) + { + if (!strURL.IsEmpty()) + { + CPVRChannel *newchannel = new CPVRChannel(m_bIsRadio); + newchannel->SetChannelName(g_localizeStrings.Get(19204)); + newchannel->SetEPGEnabled(false); + newchannel->SetVirtual(true); + newchannel->SetStreamURL(strURL); + newchannel->SetClientID(PVR_VIRTUAL_CLIENT_ID); + if (g_PVRChannelGroups->CreateChannel(*newchannel)) + g_PVRChannelGroups->GetGroupAll(m_bIsRadio)->Persist(); + + CFileItemPtr channel(new CFileItem(newchannel)); + if (channel) + { + channel->SetProperty("ActiveChannel", true); + channel->SetProperty("Name", g_localizeStrings.Get(19204)); + channel->SetProperty("UseEPG", false); + channel->SetProperty("Icon", newchannel->IconPath()); + channel->SetProperty("EPGSource", (int)0); + channel->SetProperty("ClientName", g_localizeStrings.Get(19209)); + channel->SetProperty("ParentalLocked", false); + + m_channelItems->AddFront(channel, m_iSelected); + m_viewControl.SetItems(*m_channelItems); + Renumber(); + } + } + } + } + else + { + CGUIDialogOK::ShowAndGetInput(19033,19038,0,0); + } + } + return true; +} + +bool CGUIDialogPVRChannelManager::OnMessageClick(CGUIMessage &message) +{ + int iControl = message.GetSenderId(); + switch(iControl) + { + case CONTROL_LIST_CHANNELS: + return OnClickListChannels(message); + case BUTTON_OK: + return OnClickButtonOK(message); + case BUTTON_APPLY: + return OnClickButtonApply(message); + case BUTTON_CANCEL: + return OnClickButtonCancel(message); + case BUTTON_RADIO_TV: + return OnClickButtonRadioTV(message); + case RADIOBUTTON_ACTIVE: + return OnClickButtonRadioActive(message); + case RADIOBUTTON_PARENTAL_LOCK: + return OnClickButtonRadioParentalLocked(message); + case EDIT_NAME: + return OnClickButtonEditName(message); + case BUTTON_CHANNEL_LOGO: + return OnClickButtonChannelLogo(message); + case RADIOBUTTON_USEEPG: + return OnClickButtonUseEPG(message); + case SPIN_EPGSOURCE_SELECTION: + return OnClickEPGSourceSpin(message); + case BUTTON_GROUP_MANAGER: + return OnClickButtonGroupManager(message); + case BUTTON_EDIT_CHANNEL: + return OnClickButtonEditChannel(message); + case BUTTON_DELETE_CHANNEL: + return OnClickButtonDeleteChannel(message); + case BUTTON_NEW_CHANNEL: + return OnClickButtonNewChannel(message); + default: + return false; + } +} + +bool CGUIDialogPVRChannelManager::OnMessage(CGUIMessage& message) +{ + unsigned int iMessage = message.GetMessage(); + + switch (iMessage) + { + case GUI_MSG_WINDOW_DEINIT: + Clear(); + break; + case GUI_MSG_WINDOW_INIT: + return OnMessageInit(message); + case GUI_MSG_CLICKED: + return OnMessageClick(message); + } + + return CGUIDialog::OnMessage(message); +} + +void CGUIDialogPVRChannelManager::OnWindowLoaded(void) +{ + CGUIDialog::OnWindowLoaded(); + + m_viewControl.Reset(); + m_viewControl.SetParentWindow(GetID()); + m_viewControl.AddView(GetControl(CONTROL_LIST_CHANNELS)); +} + +void CGUIDialogPVRChannelManager::OnWindowUnload(void) +{ + CGUIDialog::OnWindowUnload(); + m_viewControl.Reset(); +} + +CFileItemPtr CGUIDialogPVRChannelManager::GetCurrentListItem(int offset) +{ + return m_channelItems->Get(m_iSelected); +} + +bool CGUIDialogPVRChannelManager::OnPopupMenu(int iItem) +{ + // popup the context menu + // grab our context menu + CContextButtons buttons; + + // mark the item + if (iItem >= 0 && iItem < m_channelItems->Size()) + m_channelItems->Get(iItem)->Select(true); + else + return false; + + CFileItemPtr pItem = m_channelItems->Get(iItem); + if (!pItem) + return false; + + buttons.Add(CONTEXT_BUTTON_MOVE, 116); /* Move channel up or down */ + if (pItem->GetProperty("Virtual").asBoolean()) + buttons.Add(CONTEXT_BUTTON_EDIT_SOURCE, 1027); /* Edit virtual channel URL */ + + int choice = CGUIDialogContextMenu::ShowAndGetChoice(buttons); + + // deselect our item + if (iItem >= 0 && iItem < m_channelItems->Size()) + m_channelItems->Get(iItem)->Select(false); + + if (choice < 0) + return false; + + return OnContextButton(iItem, (CONTEXT_BUTTON)choice); +} + +bool CGUIDialogPVRChannelManager::OnContextButton(int itemNumber, CONTEXT_BUTTON button) +{ + /* Check file item is in list range and get his pointer */ + if (itemNumber < 0 || itemNumber >= (int)m_channelItems->Size()) return false; + + CFileItemPtr pItem = m_channelItems->Get(itemNumber); + if (!pItem) + return false; + + if (button == CONTEXT_BUTTON_MOVE) + { + m_bMovingMode = true; + pItem->Select(true); + } + else if (button == CONTEXT_BUTTON_EDIT_SOURCE) + { + CStdString strURL = pItem->GetProperty("StreamURL").asString(); + if (CGUIKeyboardFactory::ShowAndGetInput(strURL, g_localizeStrings.Get(19214), false)) + pItem->SetProperty("StreamURL", strURL); + } + return true; +} + +void CGUIDialogPVRChannelManager::SetData(int iItem) +{ + CGUIEditControl *pEdit; + CGUIRadioButtonControl *pRadioButton; + + /* Check file item is in list range and get his pointer */ + if (iItem < 0 || iItem >= (int)m_channelItems->Size()) return; + + CFileItemPtr pItem = m_channelItems->Get(iItem); + if (!pItem) + return; + + pEdit = (CGUIEditControl *)GetControl(EDIT_NAME); + if (pEdit) + { + pEdit->SetLabel2(pItem->GetProperty("Name").asString()); + pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_TEXT, 19208); + } + + pRadioButton = (CGUIRadioButtonControl *)GetControl(RADIOBUTTON_ACTIVE); + if (pRadioButton) pRadioButton->SetSelected(pItem->GetProperty("ActiveChannel").asBoolean()); + + pRadioButton = (CGUIRadioButtonControl *)GetControl(RADIOBUTTON_USEEPG); + if (pRadioButton) pRadioButton->SetSelected(pItem->GetProperty("UseEPG").asBoolean()); + + pRadioButton = (CGUIRadioButtonControl *)GetControl(RADIOBUTTON_PARENTAL_LOCK); + if (pRadioButton) pRadioButton->SetSelected(pItem->GetProperty("ParentalLocked").asBoolean()); +} + +void CGUIDialogPVRChannelManager::Update() +{ + // lock our display, as this window is rendered from the player thread + g_graphicsContext.Lock(); + m_viewControl.SetCurrentView(CONTROL_LIST_CHANNELS); + + // empty the lists ready for population + Clear(); + + CPVRChannelGroupPtr channels = g_PVRChannelGroups->GetGroupAll(m_bIsRadio); + + // No channels available, nothing to do. + if(!channels) + return; + + for (int iChannelPtr = 0; iChannelPtr < channels->Size(); iChannelPtr++) + { + CFileItemPtr channelFile = channels->GetByIndex(iChannelPtr); + if (!channelFile || !channelFile->HasPVRChannelInfoTag()) + continue; + const CPVRChannel *channel = channelFile->GetPVRChannelInfoTag(); + + channelFile->SetProperty("ActiveChannel", !channel->IsHidden()); + channelFile->SetProperty("Name", channel->ChannelName()); + channelFile->SetProperty("UseEPG", channel->EPGEnabled()); + channelFile->SetProperty("Icon", channel->IconPath()); + channelFile->SetProperty("EPGSource", (int)0); + channelFile->SetProperty("ParentalLocked", channel->IsLocked()); + CStdString number; number.Format("%i", channel->ChannelNumber()); + channelFile->SetProperty("Number", number); + + if (channel->IsVirtual()) + { + channelFile->SetProperty("Virtual", true); + channelFile->SetProperty("StreamURL", channel->StreamURL()); + } + + CStdString clientName; + if (channel->ClientID() == PVR_VIRTUAL_CLIENT_ID) /* XBMC internal */ + clientName = g_localizeStrings.Get(19209); + else + g_PVRClients->GetClientName(channel->ClientID(), clientName); + channelFile->SetProperty("ClientName", clientName); + + m_channelItems->Add(channelFile); + } + + CGUISpinControlEx *pSpin = (CGUISpinControlEx *)GetControl(SPIN_EPGSOURCE_SELECTION); + if (pSpin) + { + pSpin->Clear(); + pSpin->AddLabel(g_localizeStrings.Get(19210), 0); + /// TODO: Add Labels for EPG scrapers here + } + + Renumber(); + m_viewControl.SetItems(*m_channelItems); + m_viewControl.SetSelectedItem(m_iSelected); + + g_graphicsContext.Unlock(); +} + +void CGUIDialogPVRChannelManager::Clear(void) +{ + m_viewControl.Clear(); + m_channelItems->Clear(); +} + +bool CGUIDialogPVRChannelManager::PersistChannel(CFileItemPtr pItem, CPVRChannelGroupPtr group, unsigned int *iChannelNumber) +{ + if (!pItem || !pItem->HasPVRChannelInfoTag() || !group) + return false; + + /* get values from the form */ + bool bHidden = !pItem->GetProperty("ActiveChannel").asBoolean(); + bool bVirtual = pItem->GetProperty("Virtual").asBoolean(); + bool bEPGEnabled = pItem->GetProperty("UseEPG").asBoolean(); + bool bParentalLocked = pItem->GetProperty("ParentalLocked").asBoolean(); + int iEPGSource = pItem->GetProperty("EPGSource").asInteger(); + CStdString strChannelName = pItem->GetProperty("Name").asString(); + CStdString strIconPath = pItem->GetProperty("Icon").asString(); + CStdString strStreamURL = pItem->GetProperty("StreamURL").asString(); + + return group->UpdateChannel(*pItem, bHidden, bVirtual, bEPGEnabled, bParentalLocked, iEPGSource, ++(*iChannelNumber), strChannelName, strIconPath, strStreamURL); +} + +void CGUIDialogPVRChannelManager::SaveList(void) +{ + if (!m_bContainsChanges) + return; + + /* display the progress dialog */ + CGUIDialogProgress* pDlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS); + pDlgProgress->SetHeading(190); + pDlgProgress->SetLine(0, ""); + pDlgProgress->SetLine(1, 328); + pDlgProgress->SetLine(2, ""); + pDlgProgress->StartModal(); + pDlgProgress->Progress(); + pDlgProgress->SetPercentage(0); + + /* persist all channels */ + unsigned int iNextChannelNumber(0); + CPVRChannelGroupPtr group = g_PVRChannelGroups->GetGroupAll(m_bIsRadio); + if (!group) + return; + for (int iListPtr = 0; iListPtr < m_channelItems->Size(); iListPtr++) + { + CFileItemPtr pItem = m_channelItems->Get(iListPtr); + PersistChannel(pItem, group, &iNextChannelNumber); + + pDlgProgress->SetPercentage(iListPtr * 100 / m_channelItems->Size()); + } + + group->SortByChannelNumber(); + group->Persist(); + group->ResetChannelNumberCache(); + m_bContainsChanges = false; + SetItemsUnchanged(); + pDlgProgress->Close(); +} + +void CGUIDialogPVRChannelManager::SetItemsUnchanged(void) +{ + for (int iItemPtr = 0; iItemPtr < m_channelItems->Size(); iItemPtr++) + { + CFileItemPtr pItem = m_channelItems->Get(iItemPtr); + if (pItem) + pItem->SetProperty("Changed", false); + } +} + +void CGUIDialogPVRChannelManager::Renumber(void) +{ + int iNextChannelNumber(0); + CStdString strNumber; + CFileItemPtr pItem; + for (int iChannelPtr = 0; iChannelPtr < m_channelItems->Size(); iChannelPtr++) + { + pItem = m_channelItems->Get(iChannelPtr); + if (pItem->GetProperty("ActiveChannel").asBoolean()) + { + strNumber.Format("%i", ++iNextChannelNumber); + pItem->SetProperty("Number", strNumber); + } + else + pItem->SetProperty("Number", "-"); + } +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.h b/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.h new file mode 100644 index 0000000000..0a7205c1c5 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.h @@ -0,0 +1,85 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "guilib/GUIDialog.h" +#include "dialogs/GUIDialogContextMenu.h" +#include "GUIViewControl.h" +#include "../channels/PVRChannelGroup.h" + +namespace PVR +{ + class CGUIDialogPVRChannelManager : public CGUIDialog + { + public: + CGUIDialogPVRChannelManager(void); + virtual ~CGUIDialogPVRChannelManager(void); + virtual bool OnMessage(CGUIMessage& message); + virtual bool OnAction(const CAction& action); + virtual void OnWindowLoaded(void); + virtual void OnWindowUnload(void); + virtual bool HasListItems() const { return true; }; + virtual CFileItemPtr GetCurrentListItem(int offset = 0); + + protected: + virtual bool OnPopupMenu(int iItem); + virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button); + + virtual bool OnActionClose(const CAction &action); + virtual bool OnActionMove(const CAction &action); + + virtual bool OnMessageInit(CGUIMessage &message); + virtual bool OnMessageClick(CGUIMessage &message); + + virtual bool OnClickListChannels(CGUIMessage &message); + virtual bool OnClickButtonOK(CGUIMessage &message); + virtual bool OnClickButtonApply(CGUIMessage &message); + virtual bool OnClickButtonCancel(CGUIMessage &message); + virtual bool OnClickButtonRadioTV(CGUIMessage &message); + virtual bool OnClickButtonRadioActive(CGUIMessage &message); + virtual bool OnClickButtonRadioParentalLocked(CGUIMessage &message); + virtual bool OnClickButtonEditName(CGUIMessage &message); + virtual bool OnClickButtonChannelLogo(CGUIMessage &message); + virtual bool OnClickButtonUseEPG(CGUIMessage &message); + virtual bool OnClickEPGSourceSpin(CGUIMessage &message); + virtual bool OnClickButtonGroupManager(CGUIMessage &message); + virtual bool OnClickButtonEditChannel(CGUIMessage &message); + virtual bool OnClickButtonDeleteChannel(CGUIMessage &message); + virtual bool OnClickButtonNewChannel(CGUIMessage &message); + + virtual bool PersistChannel(CFileItemPtr pItem, CPVRChannelGroupPtr group, unsigned int *iChannelNumber); + virtual void SetItemsUnchanged(void); + + private: + void Clear(void); + void Update(void); + void SaveList(void); + void Renumber(void); + void SetData(int iItem); + bool m_bIsRadio; + bool m_bMovingMode; + bool m_bContainsChanges; + + int m_iSelected; + CFileItemList* m_channelItems; + CGUIViewControl m_viewControl; + }; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp b/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp new file mode 100644 index 0000000000..8c312e4773 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIDialogPVRChannelsOSD.h" +#include "Application.h" +#include "ApplicationMessenger.h" +#include "FileItem.h" +#include "guilib/GUIWindowManager.h" +#include "dialogs/GUIDialogOK.h" +#include "GUIDialogPVRGuideInfo.h" +#include "ViewState.h" +#include "settings/GUISettings.h" +#include "GUIInfoManager.h" + +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "epg/Epg.h" +#include "pvr/timers/PVRTimerInfoTag.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" + +using namespace std; +using namespace PVR; +using namespace EPG; + +#define CONTROL_LIST 11 + +CGUIDialogPVRChannelsOSD::CGUIDialogPVRChannelsOSD() : + CGUIDialog(WINDOW_DIALOG_PVR_OSD_CHANNELS, "DialogPVRChannelsOSD.xml"), + Observer() +{ + m_vecItems = new CFileItemList; +} + +CGUIDialogPVRChannelsOSD::~CGUIDialogPVRChannelsOSD() +{ + delete m_vecItems; + + if (IsObserving(g_infoManager)) + g_infoManager.UnregisterObserver(this); +} + +bool CGUIDialogPVRChannelsOSD::OnMessage(CGUIMessage& message) +{ + switch (message.GetMessage()) + { + case GUI_MSG_WINDOW_DEINIT: + { + Clear(); + } + break; + + case GUI_MSG_WINDOW_INIT: + { + /* Close dialog immediately if now TV or radio channel is playing */ + if (!g_PVRManager.IsPlaying()) + { + Close(); + return true; + } + CGUIWindow::OnMessage(message); + Update(); + return true; + } + break; + + case GUI_MSG_CLICKED: + { + int iControl = message.GetSenderId(); + + if (m_viewControl.HasControl(iControl)) // list/thumb control + { + int iItem = m_viewControl.GetSelectedItem(); + int iAction = message.GetParam1(); + + if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK) + { + /* Switch to channel */ + GotoChannel(iItem); + return true; + } + else if (iAction == ACTION_SHOW_INFO || iAction == ACTION_MOUSE_RIGHT_CLICK) + { + /* Show information Dialog */ + ShowInfo(iItem); + return true; + } + } + } + break; + + case GUI_MSG_MOVE: + { + int iAction = message.GetParam1(); + + if (iAction == ACTION_MOVE_RIGHT || iAction == ACTION_MOVE_LEFT) + { + CPVRChannelPtr channel; + g_PVRManager.GetCurrentChannel(channel); + + CPVRChannelGroupPtr group = g_PVRManager.GetPlayingGroup(channel->IsRadio()); + CPVRChannelGroupPtr nextGroup = iAction == ACTION_MOVE_RIGHT ? group->GetNextGroup() : group->GetPreviousGroup(); + + g_PVRManager.SetPlayingGroup(nextGroup); + + Clear(); + Update(); + + return true; + } + } + break; + } + + return CGUIDialog::OnMessage(message); +} + +void CGUIDialogPVRChannelsOSD::Update() +{ + // lock our display, as this window is rendered from the player thread + g_graphicsContext.Lock(); + + if (!IsObserving(g_infoManager)) + g_infoManager.RegisterObserver(this); + + m_viewControl.SetCurrentView(DEFAULT_VIEW_LIST); + + // empty the list ready for population + Clear(); + + CPVRChannelPtr channel; + g_PVRManager.GetCurrentChannel(channel); + CPVRChannelGroupPtr group = g_PVRManager.GetPlayingGroup(channel->IsRadio()); + + if (group) + { + group->GetMembers(*m_vecItems); + m_viewControl.SetItems(*m_vecItems); + m_viewControl.SetSelectedItem(group->GetIndex(*channel)); + } + + g_graphicsContext.Unlock(); +} + +void CGUIDialogPVRChannelsOSD::Clear() +{ + m_viewControl.Clear(); + m_vecItems->Clear(); +} + +void CGUIDialogPVRChannelsOSD::CloseOrSelect(unsigned int iItem) +{ + if (g_guiSettings.GetBool("pvrmenu.closechannelosdonswitch")) + Close(); + else + m_viewControl.SetSelectedItem(iItem); +} + +void CGUIDialogPVRChannelsOSD::GotoChannel(int item) +{ + /* Check file item is in list range and get his pointer */ + if (item < 0 || item >= (int)m_vecItems->Size()) return; + CFileItemPtr pItem = m_vecItems->Get(item); + + if (pItem->GetPath() == g_application.CurrentFile()) + { + CloseOrSelect(item); + return; + } + + if (g_PVRManager.IsPlaying() && pItem->HasPVRChannelInfoTag() && g_application.m_pPlayer) + { + CPVRChannel *channel = pItem->GetPVRChannelInfoTag(); + if (!g_PVRManager.CheckParentalLock(*channel) || + !g_application.m_pPlayer->SwitchChannel(*channel)) + { + Close(true); + return; + } + } + else + CApplicationMessenger::Get().PlayFile(*pItem); + + CloseOrSelect(item); +} + +void CGUIDialogPVRChannelsOSD::ShowInfo(int item) +{ + /* Check file item is in list range and get his pointer */ + if (item < 0 || item >= (int)m_vecItems->Size()) return; + + CFileItemPtr pItem = m_vecItems->Get(item); + if (pItem && pItem->IsPVRChannel()) + { + CPVRChannel *channel = pItem->GetPVRChannelInfoTag(); + if (!g_PVRManager.CheckParentalLock(*channel)) + return; + + /* Get the current running show on this channel from the EPG storage */ + CEpgInfoTag epgnow; + if (!channel->GetEPGNow(epgnow)) + return; + CFileItem *itemNow = new CFileItem(epgnow); + + /* Load programme info dialog */ + CGUIDialogPVRGuideInfo* pDlgInfo = (CGUIDialogPVRGuideInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GUIDE_INFO); + if (!pDlgInfo) + return; + + /* inform dialog about the file item and open dialog window */ + pDlgInfo->SetProgInfo(itemNow); + pDlgInfo->DoModal(); + delete itemNow; /* delete previuosly created FileItem */ + } + + return; +} + +void CGUIDialogPVRChannelsOSD::OnWindowLoaded() +{ + CGUIDialog::OnWindowLoaded(); + m_viewControl.Reset(); + m_viewControl.SetParentWindow(GetID()); + m_viewControl.AddView(GetControl(CONTROL_LIST)); +} + +void CGUIDialogPVRChannelsOSD::OnWindowUnload() +{ + CGUIDialog::OnWindowUnload(); + m_viewControl.Reset(); +} + +CGUIControl *CGUIDialogPVRChannelsOSD::GetFirstFocusableControl(int id) +{ + if (m_viewControl.HasControl(id)) + id = m_viewControl.GetCurrentControl(); + + return CGUIWindow::GetFirstFocusableControl(id); +} + +void CGUIDialogPVRChannelsOSD::Notify(const Observable &obs, const ObservableMessage msg) +{ + if (msg == ObservableMessageCurrentItem) + { + g_graphicsContext.Lock(); + m_viewControl.SetItems(*m_vecItems); + g_graphicsContext.Unlock(); + } +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.h b/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.h new file mode 100644 index 0000000000..9bd8a5dcc4 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.h @@ -0,0 +1,53 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "guilib/GUIDialog.h" +#include "GUIViewControl.h" +#include "utils/Observer.h" + +class CFileItemList; + +namespace PVR +{ + class CGUIDialogPVRChannelsOSD : public CGUIDialog, public Observer + { + public: + CGUIDialogPVRChannelsOSD(void); + virtual ~CGUIDialogPVRChannelsOSD(void); + virtual bool OnMessage(CGUIMessage& message); + virtual void OnWindowLoaded(); + virtual void OnWindowUnload(); + virtual void Notify(const Observable &obs, const ObservableMessage msg); + + protected: + void CloseOrSelect(unsigned int iItem); + void GotoChannel(int iItem); + void ShowInfo(int item); + void Clear(); + void Update(); + CGUIControl *GetFirstFocusableControl(int id); + + CFileItemList *m_vecItems; + CGUIViewControl m_viewControl; + }; +} + diff --git a/xbmc/pvr/dialogs/GUIDialogPVRCutterOSD.cpp b/xbmc/pvr/dialogs/GUIDialogPVRCutterOSD.cpp new file mode 100644 index 0000000000..ba4f79768b --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRCutterOSD.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIDialogPVRCutterOSD.h" +#include "utils/log.h" +#include "Application.h" + +using namespace std; +using namespace PVR; + +CGUIDialogPVRCutterOSD::CGUIDialogPVRCutterOSD() + : CGUIDialog(WINDOW_DIALOG_PVR_OSD_CUTTER, "DialogPVRCutterOSD.xml") +{ +} + +CGUIDialogPVRCutterOSD::~CGUIDialogPVRCutterOSD() +{ +} + +bool CGUIDialogPVRCutterOSD::OnAction(const CAction& action) +{ + if (action.GetID() == ACTION_PREVIOUS_MENU || action.GetID() == ACTION_PARENT_DIR) + { + Close(); + return true; + } + + return CGUIDialog::OnAction(action); +} + +bool CGUIDialogPVRCutterOSD::OnMessage(CGUIMessage& message) +{ + return CGUIDialog::OnMessage(message); +} + +void CGUIDialogPVRCutterOSD::OnInitWindow() +{ + CGUIDialog::OnInitWindow(); +} + +void CGUIDialogPVRCutterOSD::OnDeinitWindow(int nextWindowID) +{ + CGUIDialog::OnDeinitWindow(nextWindowID); +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRCutterOSD.h b/xbmc/pvr/dialogs/GUIDialogPVRCutterOSD.h new file mode 100644 index 0000000000..43e241cc54 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRCutterOSD.h @@ -0,0 +1,37 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "guilib/GUIDialog.h" + +namespace PVR +{ + class CGUIDialogPVRCutterOSD : public CGUIDialog + { + public: + CGUIDialogPVRCutterOSD(void); + virtual ~CGUIDialogPVRCutterOSD(void); + virtual bool OnMessage(CGUIMessage& message); + virtual bool OnAction(const CAction& action); + virtual void OnInitWindow(); + virtual void OnDeinitWindow(int nextWindowID); + }; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRDirectorOSD.cpp b/xbmc/pvr/dialogs/GUIDialogPVRDirectorOSD.cpp new file mode 100644 index 0000000000..4e510224f1 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRDirectorOSD.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +/* + * DESCRIPTION: + * + * Used in Fullscreen view to control, multifeed channel groups. + * + */ + +#include "GUIDialogPVRDirectorOSD.h" +#include "utils/log.h" +#include "Application.h" + +using namespace PVR; + +CGUIDialogPVRDirectorOSD::CGUIDialogPVRDirectorOSD() + : CGUIDialog(WINDOW_DIALOG_PVR_OSD_DIRECTOR, "DialogPVRDirectorOSD.xml") +{ +} + +CGUIDialogPVRDirectorOSD::~CGUIDialogPVRDirectorOSD() +{ +} + +bool CGUIDialogPVRDirectorOSD::OnAction(const CAction& action) +{ + if (action.GetID() == ACTION_PREVIOUS_MENU || action.GetID() == ACTION_PARENT_DIR) + { + Close(); + return true; + } + + return CGUIDialog::OnAction(action); +} + +bool CGUIDialogPVRDirectorOSD::OnMessage(CGUIMessage& message) +{ + return CGUIDialog::OnMessage(message); +} + +void CGUIDialogPVRDirectorOSD::OnInitWindow() +{ + CGUIDialog::OnInitWindow(); +} + +void CGUIDialogPVRDirectorOSD::OnDeinitWindow(int nextWindowID) +{ + CGUIDialog::OnDeinitWindow(nextWindowID); +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRDirectorOSD.h b/xbmc/pvr/dialogs/GUIDialogPVRDirectorOSD.h new file mode 100644 index 0000000000..b815ced3d6 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRDirectorOSD.h @@ -0,0 +1,37 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "guilib/GUIDialog.h" + +namespace PVR +{ + class CGUIDialogPVRDirectorOSD : public CGUIDialog + { + public: + CGUIDialogPVRDirectorOSD(void); + virtual ~CGUIDialogPVRDirectorOSD(void); + virtual bool OnMessage(CGUIMessage& message); + virtual bool OnAction(const CAction& action); + virtual void OnInitWindow(); + virtual void OnDeinitWindow(int nextWindowID); + }; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.cpp new file mode 100644 index 0000000000..b173c1b746 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.cpp @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIDialogPVRGroupManager.h" +#include "Application.h" +#include "FileItem.h" +#include "guilib/GUIKeyboardFactory.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogYesNo.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" + +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" + +using namespace std; +using namespace PVR; + +#define CONTROL_LIST_CHANNELS_LEFT 11 +#define CONTROL_LIST_CHANNELS_RIGHT 12 +#define CONTROL_LIST_CHANNEL_GROUPS 13 +#define CONTROL_CURRENT_GROUP_LABEL 20 +#define CONTROL_UNGROUPED_LABEL 21 +#define CONTROL_IN_GROUP_LABEL 22 +#define BUTTON_NEWGROUP 26 +#define BUTTON_RENAMEGROUP 27 +#define BUTTON_DELGROUP 28 +#define BUTTON_OK 29 + +CGUIDialogPVRGroupManager::CGUIDialogPVRGroupManager() : + CGUIDialog(WINDOW_DIALOG_PVR_GROUP_MANAGER, "DialogPVRGroupManager.xml") +{ + m_ungroupedChannels = new CFileItemList; + m_groupMembers = new CFileItemList; + m_channelGroups = new CFileItemList; +} + +CGUIDialogPVRGroupManager::~CGUIDialogPVRGroupManager() +{ + delete m_ungroupedChannels; + delete m_groupMembers; + delete m_channelGroups; +} + +bool CGUIDialogPVRGroupManager::PersistChanges(void) +{ + return g_PVRChannelGroups->Get(m_bIsRadio)->PersistAll(); +} + +bool CGUIDialogPVRGroupManager::CancelChanges(void) +{ + // TODO + return false; +} + +bool CGUIDialogPVRGroupManager::ActionButtonOk(CGUIMessage &message) +{ + bool bReturn = false; + unsigned int iControl = message.GetSenderId(); + + if (iControl == BUTTON_OK) + { + PersistChanges(); + Close(); + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::ActionButtonNewGroup(CGUIMessage &message) +{ + bool bReturn = false; + unsigned int iControl = message.GetSenderId(); + + if (iControl == BUTTON_NEWGROUP) + { + CStdString strGroupName = ""; + /* prompt for a group name */ + if (CGUIKeyboardFactory::ShowAndGetInput(strGroupName, g_localizeStrings.Get(19139), false)) + { + if (strGroupName != "") + { + /* add the group if it doesn't already exist */ + CPVRChannelGroups *groups = ((CPVRChannelGroups *) g_PVRChannelGroups->Get(m_bIsRadio)); + if (groups->AddGroup(strGroupName)) + { + g_PVRChannelGroups->Get(m_bIsRadio)->GetByName(strGroupName)->SetGroupType(PVR_GROUP_TYPE_USER_DEFINED); + m_iSelectedChannelGroup = groups->Size() - 1; + Update(); + } + } + } + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::ActionButtonDeleteGroup(CGUIMessage &message) +{ + bool bReturn = false; + unsigned int iControl = message.GetSenderId(); + + if (iControl == BUTTON_DELGROUP) + { + if (!m_selectedGroup) + return bReturn; + + CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + if (!pDialog) + return bReturn; + + pDialog->SetHeading(117); + pDialog->SetLine(0, ""); + pDialog->SetLine(1, m_selectedGroup->GroupName()); + pDialog->SetLine(2, ""); + pDialog->DoModal(); + + if (pDialog->IsConfirmed()) + { + if (((CPVRChannelGroups *) g_PVRChannelGroups->Get(m_bIsRadio))->DeleteGroup(*m_selectedGroup)) + Update(); + } + + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::ActionButtonRenameGroup(CGUIMessage &message) +{ + bool bReturn = false; + unsigned int iControl = message.GetSenderId(); + + if (iControl == BUTTON_RENAMEGROUP) + { + if (!m_selectedGroup) + return bReturn; + + CStdString strGroupName(m_selectedGroup->GroupName()); + if (CGUIKeyboardFactory::ShowAndGetInput(strGroupName, g_localizeStrings.Get(19139), false)) + { + if (strGroupName != "") + { + m_selectedGroup->SetGroupName(strGroupName, true); + Update(); + } + } + + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::ActionButtonUngroupedChannels(CGUIMessage &message) +{ + bool bReturn = false; + unsigned int iControl = message.GetSenderId(); + + if (m_viewUngroupedChannels.HasControl(iControl)) // list/thumb control + { + m_iSelectedUngroupedChannel = m_viewUngroupedChannels.GetSelectedItem(); + int iAction = message.GetParam1(); + + if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK) + { + if (m_channelGroups->GetFileCount() == 0) + { + CGUIDialogOK::ShowAndGetInput(19033,19137,0,19138); + } + else if (m_ungroupedChannels->GetFileCount() > 0) + { + CFileItemPtr pItemChannel = m_ungroupedChannels->Get(m_iSelectedUngroupedChannel); + if (m_selectedGroup->AddToGroup(*pItemChannel->GetPVRChannelInfoTag())) + Update(); + } + } + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::ActionButtonGroupMembers(CGUIMessage &message) +{ + bool bReturn = false; + unsigned int iControl = message.GetSenderId(); + + if (m_viewGroupMembers.HasControl(iControl)) // list/thumb control + { + m_iSelectedGroupMember = m_viewGroupMembers.GetSelectedItem(); + int iAction = message.GetParam1(); + + if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK) + { + if (m_selectedGroup && m_groupMembers->GetFileCount() > 0) + { + CFileItemPtr pItemChannel = m_groupMembers->Get(m_iSelectedGroupMember); + m_selectedGroup->RemoveFromGroup(*pItemChannel->GetPVRChannelInfoTag()); + Update(); + } + } + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::ActionButtonChannelGroups(CGUIMessage &message) +{ + bool bReturn = false; + unsigned int iControl = message.GetSenderId(); + + if (m_viewChannelGroups.HasControl(iControl)) // list/thumb control + { + int iAction = message.GetParam1(); + + if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK) + { + m_iSelectedChannelGroup = m_viewChannelGroups.GetSelectedItem(); + Update(); + } + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::OnMessageClick(CGUIMessage &message) +{ + return ActionButtonOk(message) || + ActionButtonNewGroup(message) || + ActionButtonDeleteGroup(message) || + ActionButtonRenameGroup(message) || + ActionButtonUngroupedChannels(message) || + ActionButtonGroupMembers(message) || + ActionButtonChannelGroups(message); +} + +bool CGUIDialogPVRGroupManager::OnMessage(CGUIMessage& message) +{ + unsigned int iMessage = message.GetMessage(); + + switch (iMessage) + { + case GUI_MSG_WINDOW_DEINIT: + { + Clear(); + } + break; + + case GUI_MSG_WINDOW_INIT: + { + CGUIWindow::OnMessage(message); + m_iSelectedUngroupedChannel = 0; + m_iSelectedGroupMember = 0; + m_iSelectedChannelGroup = 0; + Update(); + return true; + } + break; + + case GUI_MSG_CLICKED: + { + OnMessageClick(message); + } + break; + } + + return CGUIDialog::OnMessage(message); +} + +void CGUIDialogPVRGroupManager::OnWindowLoaded() +{ + CGUIDialog::OnWindowLoaded(); + + m_viewUngroupedChannels.Reset(); + m_viewUngroupedChannels.SetParentWindow(GetID()); + m_viewUngroupedChannels.AddView(GetControl(CONTROL_LIST_CHANNELS_LEFT)); + + m_viewGroupMembers.Reset(); + m_viewGroupMembers.SetParentWindow(GetID()); + m_viewGroupMembers.AddView(GetControl(CONTROL_LIST_CHANNELS_RIGHT)); + + m_viewChannelGroups.Reset(); + m_viewChannelGroups.SetParentWindow(GetID()); + m_viewChannelGroups.AddView(GetControl(CONTROL_LIST_CHANNEL_GROUPS)); +} + +void CGUIDialogPVRGroupManager::OnWindowUnload() +{ + CGUIDialog::OnWindowUnload(); + m_viewUngroupedChannels.Reset(); + m_viewGroupMembers.Reset(); + m_viewChannelGroups.Reset(); +} + +void CGUIDialogPVRGroupManager::Update() +{ + /* lock our display, as this window is rendered from the player thread */ + g_graphicsContext.Lock(); + m_viewUngroupedChannels.SetCurrentView(CONTROL_LIST_CHANNELS_LEFT); + m_viewGroupMembers.SetCurrentView(CONTROL_LIST_CHANNELS_RIGHT); + m_viewChannelGroups.SetCurrentView(CONTROL_LIST_CHANNEL_GROUPS); + + Clear(); + + /* get the groups list */ + g_PVRChannelGroups->Get(m_bIsRadio)->GetGroupList(m_channelGroups); + m_viewChannelGroups.SetItems(*m_channelGroups); + m_viewChannelGroups.SetSelectedItem(m_iSelectedChannelGroup); + + /* select a group or select the default group if no group was selected */ + CFileItemPtr pItem = m_channelGroups->Get(m_viewChannelGroups.GetSelectedItem()); + m_selectedGroup = g_PVRChannelGroups->Get(m_bIsRadio)->GetByName(pItem->m_strTitle); + if (m_selectedGroup) + { + /* set this group in the pvrmanager, so it becomes the selected group in other dialogs too */ + g_PVRManager.SetPlayingGroup(m_selectedGroup); + SET_CONTROL_LABEL(CONTROL_CURRENT_GROUP_LABEL, m_selectedGroup->GroupName()); + + if (m_selectedGroup->IsInternalGroup()) + { + CStdString strNewLabel; + strNewLabel.Format("%s %s", g_localizeStrings.Get(19022), m_bIsRadio ? g_localizeStrings.Get(19024) : g_localizeStrings.Get(19023)); + SET_CONTROL_LABEL(CONTROL_UNGROUPED_LABEL, strNewLabel); + + strNewLabel.Format("%s %s", g_localizeStrings.Get(19218), m_bIsRadio ? g_localizeStrings.Get(19024) : g_localizeStrings.Get(19023)); + SET_CONTROL_LABEL(CONTROL_IN_GROUP_LABEL, strNewLabel); + } + else + { + CStdString strNewLabel; + strNewLabel.Format("%s", g_localizeStrings.Get(19219)); + SET_CONTROL_LABEL(CONTROL_UNGROUPED_LABEL, strNewLabel); + + strNewLabel.Format("%s %s", g_localizeStrings.Get(19220), m_selectedGroup->GroupName()); + SET_CONTROL_LABEL(CONTROL_IN_GROUP_LABEL, strNewLabel); + } + + /* get all channels that are not in this group for the center part */ + m_selectedGroup->GetMembers(*m_ungroupedChannels, false); + m_viewUngroupedChannels.SetItems(*m_ungroupedChannels); + m_viewUngroupedChannels.SetSelectedItem(m_iSelectedUngroupedChannel); + + /* get all channels in this group for the right side part */ + m_selectedGroup->GetMembers(*m_groupMembers, true); + m_viewGroupMembers.SetItems(*m_groupMembers); + m_viewGroupMembers.SetSelectedItem(m_iSelectedGroupMember); + } + + g_graphicsContext.Unlock(); +} + +void CGUIDialogPVRGroupManager::Clear() +{ + m_viewUngroupedChannels.Clear(); + m_viewGroupMembers.Clear(); + m_viewChannelGroups.Clear(); + + m_ungroupedChannels->Clear(); + m_groupMembers->Clear(); + m_channelGroups->Clear(); +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.h b/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.h new file mode 100644 index 0000000000..600ab07341 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.h @@ -0,0 +1,72 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "guilib/GUIDialog.h" +#include "GUIViewControl.h" +#include "../channels/PVRChannelGroup.h" + +class CFileItemList; + +namespace PVR +{ + class CGUIDialogPVRGroupManager : public CGUIDialog + { + public: + CGUIDialogPVRGroupManager(void); + virtual ~CGUIDialogPVRGroupManager(void); + virtual bool OnMessage(CGUIMessage& message); + virtual void OnWindowLoaded(); + virtual void OnWindowUnload(); + void SetRadio(bool IsRadio) { m_bIsRadio = IsRadio; } + + protected: + void Clear(); + void Update(); + + private: + bool PersistChanges(void); + bool CancelChanges(void); + bool ActionButtonOk(CGUIMessage &message); + bool ActionButtonNewGroup(CGUIMessage &message); + bool ActionButtonDeleteGroup(CGUIMessage &message); + bool ActionButtonRenameGroup(CGUIMessage &message); + bool ActionButtonUngroupedChannels(CGUIMessage &message); + bool ActionButtonGroupMembers(CGUIMessage &message); + bool ActionButtonChannelGroups(CGUIMessage &message); + bool OnMessageClick(CGUIMessage &message); + + CPVRChannelGroupPtr m_selectedGroup; + bool m_bIsRadio; + + unsigned int m_iSelectedUngroupedChannel; + unsigned int m_iSelectedGroupMember; + unsigned int m_iSelectedChannelGroup; + + CFileItemList * m_ungroupedChannels; + CFileItemList * m_groupMembers; + CFileItemList * m_channelGroups; + + CGUIViewControl m_viewUngroupedChannels; + CGUIViewControl m_viewGroupMembers; + CGUIViewControl m_viewChannelGroups; + }; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp new file mode 100644 index 0000000000..c8ef0a9e9c --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIDialogPVRGuideInfo.h" +#include "Application.h" +#include "guilib/GUIWindowManager.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogYesNo.h" + +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "epg/EpgInfoTag.h" +#include "pvr/timers/PVRTimers.h" +#include "pvr/timers/PVRTimerInfoTag.h" + +using namespace std; +using namespace PVR; +using namespace EPG; + +#define CONTROL_BTN_SWITCH 5 +#define CONTROL_BTN_RECORD 6 +#define CONTROL_BTN_OK 7 + +CGUIDialogPVRGuideInfo::CGUIDialogPVRGuideInfo(void) + : CGUIDialog(WINDOW_DIALOG_PVR_GUIDE_INFO, "DialogPVRGuideInfo.xml") + , m_progItem(new CFileItem) +{ +} + +CGUIDialogPVRGuideInfo::~CGUIDialogPVRGuideInfo(void) +{ +} + +bool CGUIDialogPVRGuideInfo::ActionStartTimer(const CEpgInfoTag *tag) +{ + bool bReturn = false; + + CPVRChannelPtr channel; + if (tag) + channel = tag->ChannelTag(); + if (!channel || !g_PVRManager.CheckParentalLock(*channel)) + return false; + + // prompt user for confirmation of channel record + CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + + if (pDialog) + { + pDialog->SetHeading(264); + pDialog->SetLine(0, ""); + pDialog->SetLine(1, tag->Title()); + pDialog->SetLine(2, ""); + pDialog->DoModal(); + + if (pDialog->IsConfirmed()) + { + Close(); + CPVRTimerInfoTag *newTimer = CPVRTimerInfoTag::CreateFromEpg(*tag); + if (newTimer) + { + bReturn = CPVRTimers::AddTimer(*newTimer); + delete newTimer; + } + else + { + bReturn = false; + } + } + } + + return bReturn; +} + +bool CGUIDialogPVRGuideInfo::ActionCancelTimer(CFileItemPtr timer) +{ + bool bReturn = false; + if (!timer || !timer->HasPVRTimerInfoTag()) + { + return bReturn; + } + + // prompt user for confirmation of timer deletion + CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + + if (pDialog) + { + pDialog->SetHeading(265); + pDialog->SetLine(0, ""); + pDialog->SetLine(1, timer->GetPVRTimerInfoTag()->m_strTitle); + pDialog->SetLine(2, ""); + pDialog->DoModal(); + + if (pDialog->IsConfirmed()) + { + Close(); + bReturn = CPVRTimers::DeleteTimer(*timer); + } + } + + return bReturn; +} + +bool CGUIDialogPVRGuideInfo::OnClickButtonOK(CGUIMessage &message) +{ + bool bReturn = false; + + if (message.GetSenderId() == CONTROL_BTN_OK) + { + Close(); + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGuideInfo::OnClickButtonRecord(CGUIMessage &message) +{ + bool bReturn = false; + + if (message.GetSenderId() == CONTROL_BTN_RECORD) + { + bReturn = true; + + const CEpgInfoTag *tag = m_progItem->GetEPGInfoTag(); + if (!tag || !tag->HasPVRChannel()) + { + /* invalid channel */ + CGUIDialogOK::ShowAndGetInput(19033,19067,0,0); + Close(); + return bReturn; + } + + CFileItemPtr timerTag = g_PVRTimers->GetTimerForEpgTag(m_progItem.get()); + bool bHasTimer = timerTag != NULL && timerTag->HasPVRTimerInfoTag(); + + if (!bHasTimer) + ActionStartTimer(tag); + else + ActionCancelTimer(timerTag); + } + + return bReturn; +} + +bool CGUIDialogPVRGuideInfo::OnClickButtonSwitch(CGUIMessage &message) +{ + bool bReturn = false; + + if (message.GetSenderId() == CONTROL_BTN_SWITCH) + { + Close(); + + if (!m_progItem->GetEPGInfoTag()->HasPVRChannel() || + !g_application.PlayFile(CFileItem(*m_progItem->GetEPGInfoTag()->ChannelTag()))) + CGUIDialogOK::ShowAndGetInput(19033,0,19035,0); + else + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGuideInfo::OnMessage(CGUIMessage& message) +{ + switch (message.GetMessage()) + { + case GUI_MSG_WINDOW_INIT: + CGUIDialog::OnMessage(message); + Update(); + break; + case GUI_MSG_CLICKED: + return OnClickButtonOK(message) || + OnClickButtonRecord(message) || + OnClickButtonSwitch(message); + } + + return CGUIDialog::OnMessage(message); +} + +void CGUIDialogPVRGuideInfo::SetProgInfo(const CFileItem *item) +{ + *m_progItem = *item; +} + +CFileItemPtr CGUIDialogPVRGuideInfo::GetCurrentListItem(int offset) +{ + return m_progItem; +} + +void CGUIDialogPVRGuideInfo::Update() +{ + const CEpgInfoTag *tag = m_progItem->GetEPGInfoTag(); + if (!tag) + { + /* no epg event selected */ + return; + } + + if (tag->EndAsLocalTime() <= CDateTime::GetCurrentDateTime()) + { + /* event has passed. hide the record button */ + SET_CONTROL_HIDDEN(CONTROL_BTN_RECORD); + return; + } + + CFileItemPtr match = g_PVRTimers->GetTimerForEpgTag(m_progItem.get()); + if (!match || !match->HasPVRTimerInfoTag()) + { + /* no timer present on this tag */ + if (tag->StartAsLocalTime() < CDateTime::GetCurrentDateTime()) + SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 264); + else + SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 19061); + } + else + { + /* timer present on this tag */ + if (tag->StartAsLocalTime() < CDateTime::GetCurrentDateTime()) + SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 19059); + else + SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 19060); + } +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.h b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.h new file mode 100644 index 0000000000..59d49e8dbf --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.h @@ -0,0 +1,56 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "guilib/GUIDialog.h" + +namespace EPG +{ + class CEpgInfoTag; +} + +namespace PVR +{ + class CPVRTimerInfoTag; + + class CGUIDialogPVRGuideInfo : public CGUIDialog + { + public: + CGUIDialogPVRGuideInfo(void); + virtual ~CGUIDialogPVRGuideInfo(void); + virtual bool OnMessage(CGUIMessage& message); + virtual bool HasListItems() const { return true; }; + virtual CFileItemPtr GetCurrentListItem(int offset = 0); + + void SetProgInfo(const CFileItem *item); + + protected: + void Update(); + bool ActionStartTimer(const EPG::CEpgInfoTag *tag); + bool ActionCancelTimer(CFileItemPtr timer); + + bool OnClickButtonOK(CGUIMessage &message); + bool OnClickButtonRecord(CGUIMessage &message); + bool OnClickButtonSwitch(CGUIMessage &message); + + CFileItemPtr m_progItem; + }; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideOSD.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGuideOSD.cpp new file mode 100644 index 0000000000..49dcdf0d11 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideOSD.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIDialogPVRGuideOSD.h" +#include "Application.h" +#include "FileItem.h" +#include "GUIDialogPVRGuideInfo.h" +#include "guilib/GUIWindowManager.h" +#include "ViewState.h" +#include "epg/Epg.h" + +#include "pvr/PVRManager.h" + +using namespace std; +using namespace PVR; + +#define CONTROL_LIST 11 + +CGUIDialogPVRGuideOSD::CGUIDialogPVRGuideOSD() + : CGUIDialog(WINDOW_DIALOG_PVR_OSD_GUIDE, "DialogPVRGuideOSD.xml") +{ + m_vecItems = new CFileItemList; +} + +CGUIDialogPVRGuideOSD::~CGUIDialogPVRGuideOSD() +{ + delete m_vecItems; +} + +bool CGUIDialogPVRGuideOSD::OnMessage(CGUIMessage& message) +{ + switch (message.GetMessage()) + { + case GUI_MSG_WINDOW_DEINIT: + { + Clear(); + } + break; + + case GUI_MSG_WINDOW_INIT: + { + /* Close dialog immediately if now TV or radio channel is playing */ + if (!g_PVRManager.IsPlaying()) + { + Close(); + return true; + } + CGUIWindow::OnMessage(message); + Update(); + return true; + } + break; + + case GUI_MSG_CLICKED: + { + int iControl = message.GetSenderId(); + + if (m_viewControl.HasControl(iControl)) // list/thumb control + { + int iItem = m_viewControl.GetSelectedItem(); + int iAction = message.GetParam1(); + + if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK) + { + ShowInfo(iItem); + return true; + } + } + } + break; + } + + return CGUIDialog::OnMessage(message); +} + +void CGUIDialogPVRGuideOSD::Update() +{ + // lock our display, as this window is rendered from the player thread + g_graphicsContext.Lock(); + m_viewControl.SetCurrentView(DEFAULT_VIEW_LIST); + + // empty the list ready for population + Clear(); + + g_PVRManager.GetCurrentEpg(*m_vecItems); + m_viewControl.SetItems(*m_vecItems); + + /* select the active entry */ + unsigned int iSelectedItem = 0; + for (int iEpgPtr = 0; iEpgPtr < m_vecItems->Size(); iEpgPtr++) + { + CFileItemPtr entry = m_vecItems->Get(iEpgPtr); + if (entry->GetEPGInfoTag()->IsActive()) + { + iSelectedItem = iEpgPtr; + break; + } + } + m_viewControl.SetSelectedItem(iSelectedItem); + + g_graphicsContext.Unlock(); +} + +void CGUIDialogPVRGuideOSD::Clear() +{ + m_viewControl.Clear(); + m_vecItems->Clear(); +} + +void CGUIDialogPVRGuideOSD::ShowInfo(int item) +{ + /* Check file item is in list range and get his pointer */ + if (item < 0 || item >= (int)m_vecItems->Size()) return; + + CFileItemPtr pItem = m_vecItems->Get(item); + + /* Load programme info dialog */ + CGUIDialogPVRGuideInfo* pDlgInfo = (CGUIDialogPVRGuideInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GUIDE_INFO); + if (!pDlgInfo) + return; + + /* inform dialog about the file item and open dialog window */ + pDlgInfo->SetProgInfo(pItem.get()); + pDlgInfo->DoModal(); +} + +void CGUIDialogPVRGuideOSD::OnWindowLoaded() +{ + CGUIDialog::OnWindowLoaded(); + m_viewControl.Reset(); + m_viewControl.SetParentWindow(GetID()); + m_viewControl.AddView(GetControl(CONTROL_LIST)); +} + +void CGUIDialogPVRGuideOSD::OnWindowUnload() +{ + CGUIDialog::OnWindowUnload(); + m_viewControl.Reset(); +} + +CGUIControl *CGUIDialogPVRGuideOSD::GetFirstFocusableControl(int id) +{ + if (m_viewControl.HasControl(id)) + id = m_viewControl.GetCurrentControl(); + + return CGUIWindow::GetFirstFocusableControl(id); +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideOSD.h b/xbmc/pvr/dialogs/GUIDialogPVRGuideOSD.h new file mode 100644 index 0000000000..4b6f8a4b8b --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideOSD.h @@ -0,0 +1,49 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "guilib/GUIDialog.h" +#include "GUIViewControl.h" + +class CFileItemList; + +namespace PVR +{ + class CGUIDialogPVRGuideOSD : public CGUIDialog + { + public: + CGUIDialogPVRGuideOSD(void); + virtual ~CGUIDialogPVRGuideOSD(void); + virtual bool OnMessage(CGUIMessage& message); + virtual void OnWindowLoaded(); + virtual void OnWindowUnload(); + + protected: + void ShowInfo(int iItem); + void Clear(); + void Update(); + + CGUIControl *GetFirstFocusableControl(int id); + + CFileItemList *m_vecItems; + CGUIViewControl m_viewControl; + }; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp new file mode 100644 index 0000000000..5632586808 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIDialogPVRGuideSearch.h" +#include "Application.h" +#include "guilib/LocalizeStrings.h" +#include "guilib/GUIEditControl.h" +#include "guilib/GUIRadioButtonControl.h" +#include "guilib/GUISpinControlEx.h" +#include "guilib/GUIWindowManager.h" + +#include "addons/include/xbmc_pvr_types.h" +#include "pvr/PVRManager.h" +#include "epg/EpgSearchFilter.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" + +using namespace std; +using namespace PVR; + +#define CONTROL_EDIT_SEARCH 9 +#define CONTROL_BTN_INC_DESC 10 +#define CONTROL_BTN_CASE_SENS 11 +#define CONTROL_SPIN_MIN_DURATION 12 +#define CONTROL_SPIN_MAX_DURATION 13 +#define CONTROL_EDIT_START_DATE 14 +#define CONTROL_EDIT_STOP_DATE 15 +#define CONTROL_EDIT_START_TIME 16 +#define CONTROL_EDIT_STOP_TIME 17 +#define CONTROL_SPIN_GENRE 18 +#define CONTROL_SPIN_NO_REPEATS 19 +#define CONTROL_BTN_UNK_GENRE 20 +#define CONTROL_SPIN_GROUPS 21 +#define CONTROL_BTN_FTA_ONLY 22 +#define CONTROL_SPIN_CHANNELS 23 +#define CONTROL_BTN_IGNORE_TMR 24 +#define CONTROL_BTN_CANCEL 25 +#define CONTROL_BTN_SEARCH 26 +#define CONTROL_BTN_IGNORE_REC 27 +#define CONTROL_BTN_DEFAULTS 28 + +CGUIDialogPVRGuideSearch::CGUIDialogPVRGuideSearch(void) : + CGUIDialog(WINDOW_DIALOG_PVR_GUIDE_SEARCH, "DialogPVRGuideSearch.xml"), + m_bConfirmed(false), + m_bCanceled(false), + m_searchFilter(NULL) +{ +} + +void CGUIDialogPVRGuideSearch::UpdateChannelSpin(void) +{ + CGUISpinControlEx *pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_CHANNELS); + CGUISpinControlEx *pSpinGroups = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_GROUPS); + if (!pSpin || !pSpinGroups) + return; + + int iChannelGroup = pSpin->GetValue(); + + pSpin->Clear(); + pSpin->AddLabel(g_localizeStrings.Get(19217), EPG_SEARCH_UNSET); + + int iGroupId = (iChannelGroup == EPG_SEARCH_UNSET) ? + XBMC_INTERNAL_GROUP_TV : + iChannelGroup; + CPVRChannelGroupPtr group = g_PVRChannelGroups->GetByIdFromAll(iGroupId); + if (!group) + group = g_PVRChannelGroups->GetGroupAllTV(); + + for (int iChannelPtr = 0; iChannelPtr < group->Size(); iChannelPtr++) + { + CFileItemPtr channel = group->GetByIndex(iChannelPtr); + if (!channel || !channel->HasPVRChannelInfoTag()) + continue; + + int iChannelNumber = group->GetChannelNumber(*channel->GetPVRChannelInfoTag()); + pSpin->AddLabel(channel->GetPVRChannelInfoTag()->ChannelName().c_str(), iChannelNumber); + } +} + +void CGUIDialogPVRGuideSearch::UpdateGroupsSpin(void) +{ + CFileItemList groups; + CGUISpinControlEx *pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_GROUPS); + if (!pSpin) + return; + + /* tv groups */ + g_PVRChannelGroups->GetTV()->GetGroupList(&groups); + for (int iGroupPtr = 0; iGroupPtr < groups.Size(); iGroupPtr++) + pSpin->AddLabel(groups[iGroupPtr]->GetLabel(), atoi(groups[iGroupPtr]->GetPath())); + + /* radio groups */ + groups.ClearItems(); + g_PVRChannelGroups->GetRadio()->GetGroupList(&groups); + for (int iGroupPtr = 0; iGroupPtr < groups.Size(); iGroupPtr++) + pSpin->AddLabel(groups[iGroupPtr]->GetLabel(), atoi(groups[iGroupPtr]->GetPath())); + + pSpin->SetValue(m_searchFilter->m_iChannelGroup); +} + +void CGUIDialogPVRGuideSearch::UpdateGenreSpin(void) +{ + CGUISpinControlEx *pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_GENRE); + if (!pSpin) + return; + + pSpin->Clear(); + pSpin->AddLabel(g_localizeStrings.Get(593), EPG_SEARCH_UNSET); + pSpin->AddLabel(g_localizeStrings.Get(19500), EPG_EVENT_CONTENTMASK_MOVIEDRAMA); + pSpin->AddLabel(g_localizeStrings.Get(19516), EPG_EVENT_CONTENTMASK_NEWSCURRENTAFFAIRS); + pSpin->AddLabel(g_localizeStrings.Get(19532), EPG_EVENT_CONTENTMASK_SHOW); + pSpin->AddLabel(g_localizeStrings.Get(19548), EPG_EVENT_CONTENTMASK_SPORTS); + pSpin->AddLabel(g_localizeStrings.Get(19564), EPG_EVENT_CONTENTMASK_CHILDRENYOUTH); + pSpin->AddLabel(g_localizeStrings.Get(19580), EPG_EVENT_CONTENTMASK_MUSICBALLETDANCE); + pSpin->AddLabel(g_localizeStrings.Get(19596), EPG_EVENT_CONTENTMASK_ARTSCULTURE); + pSpin->AddLabel(g_localizeStrings.Get(19612), EPG_EVENT_CONTENTMASK_SOCIALPOLITICALECONOMICS); + pSpin->AddLabel(g_localizeStrings.Get(19628), EPG_EVENT_CONTENTMASK_EDUCATIONALSCIENCE); + pSpin->AddLabel(g_localizeStrings.Get(19644), EPG_EVENT_CONTENTMASK_LEISUREHOBBIES); + pSpin->AddLabel(g_localizeStrings.Get(19660), EPG_EVENT_CONTENTMASK_SPECIAL); + pSpin->AddLabel(g_localizeStrings.Get(19499), EPG_EVENT_CONTENTMASK_USERDEFINED); + pSpin->SetValue(m_searchFilter->m_iGenreType); +} + +void CGUIDialogPVRGuideSearch::UpdateDurationSpin(void) +{ + /* minimum duration */ + CGUISpinControlEx *pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_MIN_DURATION); + if (!pSpin) + return; + + pSpin->Clear(); + pSpin->AddLabel("-", EPG_SEARCH_UNSET); + for (int i = 1; i < 12*60/5; i++) + { + CStdString string; + string.Format(g_localizeStrings.Get(14044), i*5); + pSpin->AddLabel(string, i*5); + } + pSpin->SetValue(m_searchFilter->m_iMinimumDuration); + + /* maximum duration */ + pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_MAX_DURATION); + if (!pSpin) + return; + + pSpin->Clear(); + pSpin->AddLabel("-", EPG_SEARCH_UNSET); + for (int i = 1; i < 12*60/5; i++) + { + CStdString string; + string.Format(g_localizeStrings.Get(14044),i*5); + pSpin->AddLabel(string, i*5); + } + pSpin->SetValue(m_searchFilter->m_iMaximumDuration); +} + +bool CGUIDialogPVRGuideSearch::OnMessage(CGUIMessage& message) +{ + CGUIDialog::OnMessage(message); + + switch (message.GetMessage()) + { + case GUI_MSG_WINDOW_INIT: + { + m_bConfirmed = false; + m_bCanceled = false; + } + break; + + case GUI_MSG_CLICKED: + { + int iControl = message.GetSenderId(); + if (iControl == CONTROL_BTN_SEARCH) + { + OnSearch(); + m_bConfirmed = true; + m_bCanceled = false; + Close(); + return true; + } + else if (iControl == CONTROL_BTN_CANCEL) + { + Close(); + m_bCanceled = true; + return true; + } + else if (iControl == CONTROL_BTN_DEFAULTS) + { + if (m_searchFilter) + { + m_searchFilter->Reset(); + Update(); + } + + return true; + } + else if (iControl == CONTROL_SPIN_GROUPS) + { + UpdateChannelSpin(); + return true; + } + } + break; + } + + return false; +} + +void CGUIDialogPVRGuideSearch::OnWindowLoaded() +{ + Update(); + return CGUIDialog::OnWindowLoaded(); +} + +void CGUIDialogPVRGuideSearch::ReadDateTime(const CStdString &strDate, const CStdString &strTime, CDateTime &dateTime) const +{ + int iHours, iMinutes; + sscanf(strTime, "%d:%d", &iHours, &iMinutes); + dateTime.SetFromDBDate(strDate); + dateTime.SetDateTime(dateTime.GetYear(), dateTime.GetMonth(), dateTime.GetDay(), iHours, iMinutes, 0); +} + +void CGUIDialogPVRGuideSearch::OnSearch() +{ + CStdString strTmp; + CGUISpinControlEx *pSpin; + CGUIEditControl *pEdit; + CGUIRadioButtonControl *pRadioButton; + + if (!m_searchFilter) + return; + + pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_SEARCH); + if (pEdit) m_searchFilter->m_strSearchTerm = pEdit->GetLabel2(); + + pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_INC_DESC); + if (pRadioButton) m_searchFilter->m_bSearchInDescription = pRadioButton->IsSelected(); + + pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_CASE_SENS); + if (pRadioButton) m_searchFilter->m_bIsCaseSensitive = pRadioButton->IsSelected(); + + pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_FTA_ONLY); + if (pRadioButton) m_searchFilter->m_bFTAOnly = pRadioButton->IsSelected(); + + pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_UNK_GENRE); + if (pRadioButton) m_searchFilter->m_bIncludeUnknownGenres = pRadioButton->IsSelected(); + + pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_IGNORE_REC); + if (pRadioButton) m_searchFilter->m_bIgnorePresentRecordings = pRadioButton->IsSelected(); + + pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_IGNORE_TMR); + if (pRadioButton) m_searchFilter->m_bIgnorePresentTimers = pRadioButton->IsSelected(); + + pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_SPIN_NO_REPEATS); + if (pRadioButton) m_searchFilter->m_bPreventRepeats = pRadioButton->IsSelected(); + + pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_GENRE); + if (pSpin) m_searchFilter->m_iGenreType = pSpin->GetValue(); + + pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_MIN_DURATION); + if (pSpin) m_searchFilter->m_iMinimumDuration = pSpin->GetValue(); + + pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_MAX_DURATION); + if (pSpin) m_searchFilter->m_iMaximumDuration = pSpin->GetValue(); + + pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_CHANNELS); + if (pSpin) m_searchFilter->m_iChannelNumber = pSpin->GetValue(); + + pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_GROUPS); + if (pSpin) m_searchFilter->m_iChannelGroup = pSpin->GetValue(); + + pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_START_TIME); + if (pEdit) strTmp = pEdit->GetLabel2(); + + pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_START_DATE); + if (pEdit) ReadDateTime(pEdit->GetLabel2(), strTmp, m_searchFilter->m_startDateTime); + strTmp.clear(); + + pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_STOP_TIME); + if (pEdit) strTmp = pEdit->GetLabel2(); + + pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_STOP_DATE); + if (pEdit) ReadDateTime(pEdit->GetLabel2(), strTmp, m_searchFilter->m_endDateTime); +} + +void CGUIDialogPVRGuideSearch::Update() +{ + CGUIEditControl *pEdit; + CGUIRadioButtonControl *pRadioButton; + + if (!m_searchFilter) + return; + + pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_SEARCH); + if (pEdit) + { + pEdit->SetLabel2(m_searchFilter->m_strSearchTerm); + pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_TEXT, 16017); + } + + pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_CASE_SENS); + if (pRadioButton) pRadioButton->SetSelected(m_searchFilter->m_bIsCaseSensitive); + + pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_INC_DESC); + if (pRadioButton) pRadioButton->SetSelected(m_searchFilter->m_bSearchInDescription); + + pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_FTA_ONLY); + if (pRadioButton) pRadioButton->SetSelected(m_searchFilter->m_bFTAOnly); + + pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_UNK_GENRE); + if (pRadioButton) pRadioButton->SetSelected(m_searchFilter->m_bIncludeUnknownGenres); + + pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_IGNORE_REC); + if (pRadioButton) pRadioButton->SetSelected(m_searchFilter->m_bIgnorePresentRecordings); + + pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_IGNORE_TMR); + if (pRadioButton) pRadioButton->SetSelected(m_searchFilter->m_bIgnorePresentTimers); + + pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_SPIN_NO_REPEATS); + if (pRadioButton) pRadioButton->SetSelected(m_searchFilter->m_bPreventRepeats); + + /* Set time fields */ + pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_START_TIME); + if (pEdit) + { + pEdit->SetLabel2(m_searchFilter->m_endDateTime.GetAsLocalizedTime("", false)); + pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_TIME, 14066); + } + pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_STOP_TIME); + if (pEdit) + { + pEdit->SetLabel2(m_searchFilter->m_startDateTime.GetAsLocalizedTime("", false)); + pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_TIME, 14066); + } + pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_START_DATE); + if (pEdit) + { + pEdit->SetLabel2(m_searchFilter->m_startDateTime.GetAsDBDate()); + pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_DATE, 14067); + } + pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_STOP_DATE); + if (pEdit) + { + pEdit->SetLabel2(m_searchFilter->m_endDateTime.GetAsDBDate()); + pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_DATE, 14067); + } + + UpdateDurationSpin(); + UpdateGroupsSpin(); + UpdateChannelSpin(); + UpdateGenreSpin(); +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.h b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.h new file mode 100644 index 0000000000..92feaaa887 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.h @@ -0,0 +1,58 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "guilib/GUIDialog.h" +#include "XBDateTime.h" + +namespace EPG +{ + struct EpgSearchFilter; +} + +namespace PVR +{ + class CGUIDialogPVRGuideSearch : public CGUIDialog + { + public: + CGUIDialogPVRGuideSearch(void); + virtual ~CGUIDialogPVRGuideSearch(void) {} + virtual bool OnMessage(CGUIMessage& message); + virtual void OnWindowLoaded(); + + void SetFilterData(EPG::EpgSearchFilter *searchFilter) { m_searchFilter = searchFilter; } + bool IsConfirmed() const { return m_bConfirmed; } + bool IsCanceled() const { return m_bCanceled; } + void OnSearch(); + + protected: + void UpdateChannelSpin(void); + void UpdateGroupsSpin(void); + void UpdateGenreSpin(void); + void UpdateDurationSpin(void); + void ReadDateTime(const CStdString &strDate, const CStdString &strTime, CDateTime &dateTime) const; + void Update(); + + bool m_bConfirmed; + bool m_bCanceled; + EPG::EpgSearchFilter *m_searchFilter; + }; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.cpp b/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.cpp new file mode 100644 index 0000000000..99947ba33c --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIDialogPVRRecordingInfo.h" +#include "guilib/GUIWindowManager.h" +#include "FileItem.h" + +using namespace std; +using namespace PVR; + +#define CONTROL_BTN_OK 10 + +CGUIDialogPVRRecordingInfo::CGUIDialogPVRRecordingInfo(void) + : CGUIDialog(WINDOW_DIALOG_PVR_RECORDING_INFO, "DialogPVRRecordingInfo.xml") + , m_recordItem(new CFileItem) +{ +} + +bool CGUIDialogPVRRecordingInfo::OnMessage(CGUIMessage& message) +{ + if (message.GetMessage() == GUI_MSG_CLICKED) + { + int iControl = message.GetSenderId(); + + if (iControl == CONTROL_BTN_OK) + { + Close(); + return true; + } + } + + return CGUIDialog::OnMessage(message); +} + +void CGUIDialogPVRRecordingInfo::SetRecording(const CFileItem *item) +{ + *m_recordItem = *item; +} + +CFileItemPtr CGUIDialogPVRRecordingInfo::GetCurrentListItem(int offset) +{ + return m_recordItem; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.h b/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.h new file mode 100644 index 0000000000..a0c0e30198 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.h @@ -0,0 +1,41 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "guilib/GUIDialog.h" + +namespace PVR +{ + class CGUIDialogPVRRecordingInfo : public CGUIDialog + { + public: + CGUIDialogPVRRecordingInfo(void); + virtual ~CGUIDialogPVRRecordingInfo(void) {} + virtual bool OnMessage(CGUIMessage& message); + virtual bool HasListItems() const { return true; }; + virtual CFileItemPtr GetCurrentListItem(int offset = 0); + + void SetRecording(const CFileItem *item); + + protected: + CFileItemPtr m_recordItem; + }; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp new file mode 100644 index 0000000000..6a82a7c959 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIDialogPVRTimerSettings.h" +#include "guilib/GUIKeyboardFactory.h" +#include "dialogs/GUIDialogNumeric.h" +#include "settings/GUISettings.h" +#include "guilib/LocalizeStrings.h" + +#include "pvr/PVRManager.h" +#include "pvr/timers/PVRTimerInfoTag.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" + +using namespace std; +using namespace PVR; + +#define CONTROL_TMR_ACTIVE 20 +#define CONTROL_TMR_CHNAME_TV 21 +#define CONTROL_TMR_DAY 22 +#define CONTROL_TMR_BEGIN 23 +#define CONTROL_TMR_END 24 +#define CONTROL_TMR_PRIORITY 26 +#define CONTROL_TMR_LIFETIME 27 +#define CONTROL_TMR_FIRST_DAY 28 +#define CONTROL_TMR_NAME 29 +#define CONTROL_TMR_DIR 30 +#define CONTROL_TMR_RADIO 50 +#define CONTROL_TMR_CHNAME_RADIO 51 + +CGUIDialogPVRTimerSettings::CGUIDialogPVRTimerSettings(void) + : CGUIDialogSettings(WINDOW_DIALOG_PVR_TIMER_SETTING, "DialogPVRTimerSettings.xml") +{ + m_cancelled = true; + m_tmp_day = 11; +} + +void CGUIDialogPVRTimerSettings::AddChannelNames(CFileItemList &channelsList, SETTINGSTRINGS &channelNames, bool bRadio) +{ + g_PVRChannelGroups->GetGroupAll(bRadio)->GetMembers(channelsList); + + channelNames.push_back("0 dummy"); + for (int i = 0; i < channelsList.Size(); i++) + { + CStdString string; + CFileItemPtr item = channelsList[i]; + const CPVRChannel *channel = item->GetPVRChannelInfoTag(); + string.Format("%i %s", channel->ChannelNumber(), channel->ChannelName().c_str()); + channelNames.push_back(string); + } + + int iControl = bRadio ? CONTROL_TMR_CHNAME_RADIO : CONTROL_TMR_CHNAME_TV; + AddSpin(iControl, 19078, &m_timerItem->GetPVRTimerInfoTag()->m_iChannelNumber, channelNames.size(), channelNames); + EnableSettings(iControl, m_timerItem->GetPVRTimerInfoTag()->m_bIsRadio == bRadio); +} + +void CGUIDialogPVRTimerSettings::SetWeekdaySettingFromTimer(const CPVRTimerInfoTag &timer) +{ + if (timer.m_bIsRepeating) + { + if (timer.m_iWeekdays == 0x01) + m_tmp_day = 0; + else if (timer.m_iWeekdays == 0x02) + m_tmp_day = 1; + else if (timer.m_iWeekdays == 0x04) + m_tmp_day = 2; + else if (timer.m_iWeekdays == 0x08) + m_tmp_day = 3; + else if (timer.m_iWeekdays == 0x10) + m_tmp_day = 4; + else if (timer.m_iWeekdays == 0x20) + m_tmp_day = 5; + else if (timer.m_iWeekdays == 0x40) + m_tmp_day = 6; + else if (timer.m_iWeekdays == 0x1F) + m_tmp_day = 7; + else if (timer.m_iWeekdays == 0x3F) + m_tmp_day = 8; + else if (timer.m_iWeekdays == 0x7F) + m_tmp_day = 9; + else if (timer.m_iWeekdays == 0x60) + m_tmp_day = 10; + } +} + +void CGUIDialogPVRTimerSettings::SetTimerFromWeekdaySetting(CPVRTimerInfoTag &timer) +{ + timer.m_bIsRepeating = true; + + if (m_tmp_day == 0) + timer.m_iWeekdays = 0x01; + else if (m_tmp_day == 1) + timer.m_iWeekdays = 0x02; + else if (m_tmp_day == 2) + timer.m_iWeekdays = 0x04; + else if (m_tmp_day == 3) + timer.m_iWeekdays = 0x08; + else if (m_tmp_day == 4) + timer.m_iWeekdays = 0x10; + else if (m_tmp_day == 5) + timer.m_iWeekdays = 0x20; + else if (m_tmp_day == 6) + timer.m_iWeekdays = 0x40; + else if (m_tmp_day == 7) + timer.m_iWeekdays = 0x1F; + else if (m_tmp_day == 8) + timer.m_iWeekdays = 0x3F; + else if (m_tmp_day == 9) + timer.m_iWeekdays = 0x7F; + else if (m_tmp_day == 10) + timer.m_iWeekdays = 0x60; + else + timer.m_iWeekdays = 0; +} + +void CGUIDialogPVRTimerSettings::CreateSettings() +{ + CPVRTimerInfoTag* tag = m_timerItem->GetPVRTimerInfoTag(); + + // clear out any old settings + m_settings.clear(); + + // create our settings controls + m_bTimerActive = tag->IsActive(); + AddBool(CONTROL_TMR_ACTIVE, 19074, &m_bTimerActive); + AddButton(CONTROL_TMR_NAME, 19075, &tag->m_strTitle, true); + + if (tag->SupportsFolders()) + AddButton(CONTROL_TMR_DIR, 19076, &tag->m_strDirectory, true); + + AddBool(CONTROL_TMR_RADIO, 19077, &tag->m_bIsRadio); + + /// Channel names + { + // For TV + CFileItemList channelslist_tv; + SETTINGSTRINGS channelstrings_tv; + AddChannelNames(channelslist_tv, channelstrings_tv, false); + + // For Radio + CFileItemList channelslist_radio; + SETTINGSTRINGS channelstrings_radio; + AddChannelNames(channelslist_radio, channelstrings_radio, true); + } + + /// Day + { + SETTINGSTRINGS daystrings; + tm time_cur; + tm time_tmr; + + for (unsigned int iDayPtr = 19086; iDayPtr <= 19096; iDayPtr++) + daystrings.push_back(g_localizeStrings.Get(iDayPtr)); + CDateTime time = CDateTime::GetCurrentDateTime(); + CDateTime timestart = tag->StartAsLocalTime(); + + /* get diffence of timer in days between today and timer start date */ + time.GetAsTm(time_cur); + timestart.GetAsTm(time_tmr); + + m_tmp_day += time_tmr.tm_yday - time_cur.tm_yday; + if (time_tmr.tm_yday - time_cur.tm_yday < 0) + m_tmp_day += 365; + + for (int i = 1; i < 365; ++i) + { + CStdString string = time.GetAsLocalizedDate(); + daystrings.push_back(string); + time += CDateTimeSpan(1, 0, 0, 0); + } + + SetWeekdaySettingFromTimer(*tag); + + AddSpin(CONTROL_TMR_DAY, 19079, &m_tmp_day, daystrings.size(), daystrings); + } + + AddButton(CONTROL_TMR_BEGIN, 19080, &timerStartTimeStr, true); + AddButton(CONTROL_TMR_END, 19081, &timerEndTimeStr, true); + AddSpin(CONTROL_TMR_PRIORITY, 19082, &tag->m_iPriority, 0, 99); + AddSpin(CONTROL_TMR_LIFETIME, 19083, &tag->m_iLifetime, 0, 365); + + /// First day + { + SETTINGSTRINGS daystrings; + tm time_cur; + tm time_tmr; + + CDateTime time = CDateTime::GetCurrentDateTime(); + CDateTime timestart = tag->FirstDayAsLocalTime(); + + /* get diffence of timer in days between today and timer start date */ + if (time < timestart) + { + time.GetAsTm(time_cur); + timestart.GetAsTm(time_tmr); + + m_tmp_iFirstDay += time_tmr.tm_yday - time_cur.tm_yday + 1; + if (time_tmr.tm_yday - time_cur.tm_yday < 0) + m_tmp_iFirstDay += 365; + } + + daystrings.push_back(g_localizeStrings.Get(19030)); + + for (int i = 1; i < 365; ++i) + { + CStdString string = time.GetAsLocalizedDate(); + daystrings.push_back(string); + time += CDateTimeSpan(1, 0, 0, 0); + } + + AddSpin(CONTROL_TMR_FIRST_DAY, 19084, &m_tmp_iFirstDay, daystrings.size(), daystrings); + + if (tag->m_bIsRepeating) + EnableSettings(CONTROL_TMR_FIRST_DAY, true); + else + EnableSettings(CONTROL_TMR_FIRST_DAY, false); + } +} + +void CGUIDialogPVRTimerSettings::OnSettingChanged(SettingInfo &setting) +{ + CPVRTimerInfoTag* tag = m_timerItem->GetPVRTimerInfoTag(); + + if (setting.id == CONTROL_TMR_NAME) + { + if (CGUIKeyboardFactory::ShowAndGetInput(tag->m_strTitle, g_localizeStrings.Get(19097), false)) + { + UpdateSetting(CONTROL_TMR_NAME); + } + } + if (setting.id == CONTROL_TMR_DIR && CGUIKeyboardFactory::ShowAndGetInput(tag->m_strDirectory, g_localizeStrings.Get(19104), false)) + UpdateSetting(CONTROL_TMR_DIR); + else if (setting.id == CONTROL_TMR_RADIO || setting.id == CONTROL_TMR_CHNAME_TV || setting.id == CONTROL_TMR_CHNAME_RADIO) + { + if (setting.id == CONTROL_TMR_RADIO) + { + EnableSettings(CONTROL_TMR_CHNAME_TV, !tag->m_bIsRadio); + EnableSettings(CONTROL_TMR_CHNAME_RADIO, tag->m_bIsRadio); + } + + CFileItemPtr channel = g_PVRChannelGroups->GetGroupAll(tag->m_bIsRadio)->GetByChannelNumber(tag->m_iChannelNumber); + if (channel && channel->HasPVRChannelInfoTag()) + { + tag->m_iClientChannelUid = channel->GetPVRChannelInfoTag()->UniqueID(); + tag->m_iClientId = channel->GetPVRChannelInfoTag()->ClientID(); + tag->m_bIsRadio = channel->GetPVRChannelInfoTag()->IsRadio(); + tag->m_iChannelNumber = channel->GetPVRChannelInfoTag()->ChannelNumber(); + } + } + else if (setting.id == CONTROL_TMR_DAY && m_tmp_day > 10) + { + CDateTime time = CDateTime::GetCurrentDateTime(); + CDateTime timestart = timerStartTime; + CDateTime timestop = timerEndTime; + int m_tmp_diff; + tm time_cur; + tm time_tmr; + + /* get diffence of timer in days between today and timer start date */ + time.GetAsTm(time_cur); + timestart.GetAsTm(time_tmr); + + m_tmp_diff = time_tmr.tm_yday - time_cur.tm_yday; + if (time_tmr.tm_yday - time_cur.tm_yday < 0) + m_tmp_diff = 365; + + CDateTime newStart = timestart + CDateTimeSpan(m_tmp_day-11-m_tmp_diff, 0, 0, 0); + CDateTime newEnd = timestop + CDateTimeSpan(m_tmp_day-11-m_tmp_diff, 0, 0, 0); + tag->SetStartFromLocalTime(newStart); + tag->SetEndFromLocalTime(newEnd); + + EnableSettings(CONTROL_TMR_FIRST_DAY, false); + + tag->m_bIsRepeating = false; + tag->m_iWeekdays = 0; + } + else if (setting.id == CONTROL_TMR_DAY && m_tmp_day <= 10) + { + EnableSettings(CONTROL_TMR_FIRST_DAY, true); + SetTimerFromWeekdaySetting(*tag); + } + else if (setting.id == CONTROL_TMR_BEGIN) + { + if (CGUIDialogNumeric::ShowAndGetTime(timerStartTime, g_localizeStrings.Get(14066))) + { + CDateTime timestart = timerStartTime; + int start_day = tag->StartAsLocalTime().GetDay(); + int start_month = tag->StartAsLocalTime().GetMonth(); + int start_year = tag->StartAsLocalTime().GetYear(); + int start_hour = timestart.GetHour(); + int start_minute = timestart.GetMinute(); + CDateTime newStart(start_year, start_month, start_day, start_hour, start_minute, 0); + tag->SetStartFromLocalTime(newStart); + + timerStartTimeStr = tag->StartAsLocalTime().GetAsLocalizedTime("", false); + UpdateSetting(CONTROL_TMR_BEGIN); + } + } + else if (setting.id == CONTROL_TMR_END) + { + if (CGUIDialogNumeric::ShowAndGetTime(timerEndTime, g_localizeStrings.Get(14066))) + { + CDateTime timestop = timerEndTime; + int start_day = tag->EndAsLocalTime().GetDay(); + int start_month = tag->EndAsLocalTime().GetMonth(); + int start_year = tag->EndAsLocalTime().GetYear(); + int start_hour = timestop.GetHour(); + int start_minute = timestop.GetMinute(); + CDateTime newEnd(start_year, start_month, start_day, start_hour, start_minute, 0); + tag->SetEndFromLocalTime(newEnd); + + timerEndTimeStr = tag->EndAsLocalTime().GetAsLocalizedTime("", false); + UpdateSetting(CONTROL_TMR_END); + } + } + else if (setting.id == CONTROL_TMR_FIRST_DAY && m_tmp_day <= 10) + { + CDateTime newFirstDay; + if (m_tmp_iFirstDay > 0) + newFirstDay = CDateTime::GetCurrentDateTime() + CDateTimeSpan(m_tmp_iFirstDay-1, 0, 0, 0); + + tag->SetFirstDayFromLocalTime(newFirstDay); + } + + tag->UpdateSummary(); +} + +void CGUIDialogPVRTimerSettings::SetTimer(CFileItem *item) +{ + m_timerItem = item; + m_cancelled = true; + + m_timerItem->GetPVRTimerInfoTag()->StartAsLocalTime().GetAsSystemTime(timerStartTime); + m_timerItem->GetPVRTimerInfoTag()->EndAsLocalTime().GetAsSystemTime(timerEndTime); + timerStartTimeStr = m_timerItem->GetPVRTimerInfoTag()->StartAsLocalTime().GetAsLocalizedTime("", false); + timerEndTimeStr = m_timerItem->GetPVRTimerInfoTag()->EndAsLocalTime().GetAsLocalizedTime("", false); + + m_tmp_iFirstDay = 0; + m_tmp_day = 11; +} + +void CGUIDialogPVRTimerSettings::OnOkay() +{ + m_cancelled = false; + CPVRTimerInfoTag* tag = m_timerItem->GetPVRTimerInfoTag(); + if (tag->m_strTitle == g_localizeStrings.Get(19056)) + tag->m_strTitle = g_PVRChannelGroups->GetByUniqueID(tag->m_iClientChannelUid, tag->m_iClientId)->ChannelName(); + + if (m_bTimerActive) + tag->m_state = PVR_TIMER_STATE_SCHEDULED; + else + tag->m_state = PVR_TIMER_STATE_CANCELLED; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h new file mode 100644 index 0000000000..e1ec01b24a --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h @@ -0,0 +1,61 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "XBDateTime.h" +#include "settings/GUIDialogSettings.h" +#include "guilib/GUIListItem.h" + +class CFileItem; + +namespace PVR +{ + class CPVRTimerInfoTag; + + class CGUIDialogPVRTimerSettings : public CGUIDialogSettings + { + public: + CGUIDialogPVRTimerSettings(void); + virtual ~CGUIDialogPVRTimerSettings(void) {} + void SetTimer(CFileItem *item); + bool GetOK() { return !m_cancelled; } + + protected: + virtual void CreateSettings(); + virtual void OnSettingChanged(SettingInfo &setting); + virtual void OnOkay(); + virtual void OnCancel() { m_cancelled = true; } + virtual void AddChannelNames(CFileItemList &channelsList, SETTINGSTRINGS &channelNames, bool bRadio); + virtual void SetWeekdaySettingFromTimer(const CPVRTimerInfoTag &timer); + virtual void SetTimerFromWeekdaySetting(CPVRTimerInfoTag &timer); + + SYSTEMTIME timerStartTime; + SYSTEMTIME timerEndTime; + CStdString timerStartTimeStr; + CStdString timerEndTimeStr; + int m_tmp_iFirstDay;; + int m_tmp_day; + bool m_bTimerActive; + + CFileItem *m_timerItem; + bool m_cancelled; + }; +} diff --git a/xbmc/pvr/dialogs/Makefile b/xbmc/pvr/dialogs/Makefile new file mode 100644 index 0000000000..675cdbb8d2 --- /dev/null +++ b/xbmc/pvr/dialogs/Makefile @@ -0,0 +1,15 @@ +SRCS=GUIDialogPVRChannelManager.cpp \ + GUIDialogPVRChannelsOSD.cpp \ + GUIDialogPVRCutterOSD.cpp \ + GUIDialogPVRDirectorOSD.cpp \ + GUIDialogPVRGroupManager.cpp \ + GUIDialogPVRGuideInfo.cpp \ + GUIDialogPVRGuideOSD.cpp \ + GUIDialogPVRGuideSearch.cpp \ + GUIDialogPVRRecordingInfo.cpp \ + GUIDialogPVRTimerSettings.cpp + +LIB=pvrdialogs.a + +include ../../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/xbmc/pvr/recordings/Makefile b/xbmc/pvr/recordings/Makefile new file mode 100644 index 0000000000..465f1fbc13 --- /dev/null +++ b/xbmc/pvr/recordings/Makefile @@ -0,0 +1,7 @@ +SRCS=PVRRecording.cpp \ + PVRRecordings.cpp + +LIB=pvrrecordings.a + +include ../../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/xbmc/pvr/recordings/PVRRecording.cpp b/xbmc/pvr/recordings/PVRRecording.cpp new file mode 100644 index 0000000000..bee615556f --- /dev/null +++ b/xbmc/pvr/recordings/PVRRecording.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "dialogs/GUIDialogOK.h" +#include "pvr/PVRManager.h" +#include "settings/AdvancedSettings.h" +#include "PVRRecordings.h" +#include "pvr/addons/PVRClients.h" +#include "utils/StringUtils.h" + +#include "epg/Epg.h" + +using namespace PVR; +using namespace EPG; + +CPVRRecording::CPVRRecording() +{ + Reset(); +} + +CPVRRecording::CPVRRecording(const PVR_RECORDING &recording, unsigned int iClientId) +{ + Reset(); + + m_strRecordingId = recording.strRecordingId; + m_strTitle = recording.strTitle; + m_iClientId = iClientId; + m_recordingTime = recording.recordingTime + g_advancedSettings.m_iPVRTimeCorrection; + m_duration = CDateTimeSpan(0, 0, recording.iDuration / 60, recording.iDuration % 60); + m_iPriority = recording.iPriority; + m_iLifetime = recording.iLifetime; + m_strDirectory = recording.strDirectory; + m_strPlot = recording.strPlot; + m_strPlotOutline = recording.strPlotOutline; + m_strStreamURL = recording.strStreamURL; + m_strChannelName = recording.strChannelName; + m_genre = StringUtils::Split(CEpg::ConvertGenreIdToString(recording.iGenreType, recording.iGenreSubType), g_advancedSettings.m_videoItemSeparator); + m_iRecPlayCount = recording.iPlayCount; +} + +bool CPVRRecording::operator ==(const CPVRRecording& right) const +{ + return (this == &right) || + (m_strRecordingId == right.m_strRecordingId && + m_iClientId == right.m_iClientId && + m_strChannelName == right.m_strChannelName && + m_recordingTime == right.m_recordingTime && + m_duration == right.m_duration && + m_strPlotOutline == right.m_strPlotOutline && + m_strPlot == right.m_strPlot && + m_strStreamURL == right.m_strStreamURL && + m_iPriority == right.m_iPriority && + m_iLifetime == right.m_iLifetime && + m_strDirectory == right.m_strDirectory && + m_strFileNameAndPath == right.m_strFileNameAndPath && + m_strTitle == right.m_strTitle && + m_iRecPlayCount == right.m_iRecPlayCount); +} + +bool CPVRRecording::operator !=(const CPVRRecording& right) const +{ + return !(*this == right); +} + +void CPVRRecording::Reset(void) +{ + m_strRecordingId = StringUtils::EmptyString; + m_iClientId = 0; + m_strChannelName = StringUtils::EmptyString; + m_strDirectory = StringUtils::EmptyString; + m_strStreamURL = StringUtils::EmptyString; + m_iPriority = -1; + m_iLifetime = -1; + m_strFileNameAndPath = StringUtils::EmptyString; + m_iRecPlayCount = 0; + + m_recordingTime.Reset(); + CVideoInfoTag::Reset(); +} + +int CPVRRecording::GetDuration() const +{ + return (m_duration.GetDays() * 60*60*24 + + m_duration.GetHours() * 60*60 + + m_duration.GetMinutes() * 60 + + m_duration.GetSeconds()); +} + +bool CPVRRecording::Delete(void) +{ + PVR_ERROR error = g_PVRClients->DeleteRecording(*this); + if (error != PVR_ERROR_NO_ERROR) + { + DisplayError(error); + return false; + } + + return true; +} + +bool CPVRRecording::Rename(const CStdString &strNewName) +{ + m_strTitle.Format("%s", strNewName); + PVR_ERROR error = g_PVRClients->RenameRecording(*this); + if (error != PVR_ERROR_NO_ERROR) + { + DisplayError(error); + return false; + } + + return true; +} + +bool CPVRRecording::SetPlayCount(int count) +{ + PVR_ERROR error; + m_iRecPlayCount = count; + if (g_PVRClients->SupportsRecordingPlayCount(m_iClientId) && + !g_PVRClients->SetRecordingPlayCount(*this, count, &error)) + { + DisplayError(error); + return false; + } + + return true; +} + +void CPVRRecording::DisplayError(PVR_ERROR err) const +{ + if (err == PVR_ERROR_SERVER_ERROR) + CGUIDialogOK::ShowAndGetInput(19033,19111,19110,0); /* print info dialog "Server error!" */ + else if (err == PVR_ERROR_REJECTED) + CGUIDialogOK::ShowAndGetInput(19033,19068,19110,0); /* print info dialog "Couldn't delete recording!" */ + else + CGUIDialogOK::ShowAndGetInput(19033,19147,19110,0); /* print info dialog "Unknown error!" */ + + return; +} + +void CPVRRecording::Update(const CPVRRecording &tag) +{ + m_strRecordingId = tag.m_strRecordingId; + m_iClientId = tag.m_iClientId; + m_strTitle = tag.m_strTitle; + m_recordingTime = tag.m_recordingTime; + m_duration = tag.m_duration; + m_iPriority = tag.m_iPriority; + m_iLifetime = tag.m_iLifetime; + m_strDirectory = tag.m_strDirectory; + m_strPlot = tag.m_strPlot; + m_strPlotOutline = tag.m_strPlotOutline; + m_strStreamURL = tag.m_strStreamURL; + m_strChannelName = tag.m_strChannelName; + m_genre = tag.m_genre; + m_iRecPlayCount = tag.m_iRecPlayCount; + + CStdString strShow; + strShow.Format("%s - ", g_localizeStrings.Get(20364).c_str()); + if (m_strPlotOutline.Left(strShow.size()).Equals(strShow)) + { + CStdString strEpisode = m_strPlotOutline; + CStdString strTitle = m_strDirectory; + + int pos = strTitle.ReverseFind('/'); + strTitle.erase(0, pos + 1); + strEpisode.erase(0, strShow.size()); + m_strTitle.Format("%s - %s", strTitle.c_str(), strEpisode); + pos = strEpisode.Find('-'); + strEpisode.erase(0, pos + 2); + m_strPlotOutline = strEpisode; + } + UpdatePath(); +} + +void CPVRRecording::UpdatePath(void) +{ + if (!m_strStreamURL.IsEmpty()) + { + m_strFileNameAndPath = m_strStreamURL; + } + else + { + CStdString strTitle(m_strTitle); + CStdString strDatetime(m_recordingTime.GetAsSaveString()); + strTitle.Replace('/','-'); + strTitle.Remove('?'); + + if (m_strDirectory != StringUtils::EmptyString) + m_strFileNameAndPath.Format("pvr://recordings/%s/%s/%s.pvr", m_strDirectory.c_str(), strDatetime.c_str(), strTitle.c_str()); + else + m_strFileNameAndPath.Format("pvr://recordings/%s/%s.pvr", strDatetime.c_str(), strTitle.c_str()); + } +} + +const CDateTime &CPVRRecording::RecordingTimeAsLocalTime(void) const +{ + static CDateTime tmp; + tmp.SetFromUTCDateTime(m_recordingTime); + + return tmp; +} diff --git a/xbmc/pvr/recordings/PVRRecording.h b/xbmc/pvr/recordings/PVRRecording.h new file mode 100644 index 0000000000..302d94d2e2 --- /dev/null +++ b/xbmc/pvr/recordings/PVRRecording.h @@ -0,0 +1,113 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +/* + * DESCRIPTION: + * + * CPVRRecordingInfoTag is part of the XBMC PVR system to support recording entrys, + * stored on a other Backend like VDR or MythTV. + * + * The recording information tag holds data about name, length, recording time + * and so on of recorded stream stored on the backend. + * + * The filename string is used to by the PVRManager and passed to DVDPlayer + * to stream data from the backend to XBMC. + * + * It is a also CVideoInfoTag and some of his variables must be set! + * + */ + +#include "addons/include/xbmc_pvr_types.h" +#include "video/VideoInfoTag.h" +#include "XBDateTime.h" + +namespace PVR +{ + class CPVRRecording : public CVideoInfoTag + { + public: + int m_iClientId; /*!< ID of the backend */ + CStdString m_strRecordingId; /*!< unique id of the recording on the client */ + CStdString m_strChannelName; /*!< name of the channel this was recorded from */ + CDateTimeSpan m_duration; /*!< duration of this recording */ + int m_iPriority; /*!< priority of this recording */ + int m_iLifetime; /*!< lifetime of this recording */ + CStdString m_strStreamURL; /*!< stream URL. if empty use pvr client */ + CStdString m_strDirectory; /*!< directory of this recording on the client */ + int m_iRecPlayCount; /*!< play count of this recording on the client */ + + CPVRRecording(void); + CPVRRecording(const PVR_RECORDING &recording, unsigned int iClientId); + virtual ~CPVRRecording() {}; + + bool operator ==(const CPVRRecording& right) const; + bool operator !=(const CPVRRecording& right) const; + + /*! + * @brief Reset this tag to it's initial state. + */ + void Reset(void); + + /*! + * @brief The duration of this recording in seconds. + * @return The duration. + */ + int GetDuration() const; + + /*! + * @brief Delete this recording on the client (if supported). + * @return True if it was deleted successfully, false otherwise. + */ + bool Delete(void); + + /*! + * @brief Rename this recording on the client (if supported). + * @param strNewName The new name. + * @return True if it was renamed successfully, false otherwise. + */ + bool Rename(const CStdString &strNewName); + + /*! + * @brief Set this recording's play count on the client (if supported). + * @param count play count. + * @return True if play count was set successfully, false otherwise. + */ + bool SetPlayCount(int count); + + /*! + * @brief Update this tag with the contents of the given tag. + * @param tag The new tag info. + */ + void Update(const CPVRRecording &tag); + + const CDateTime &RecordingTimeAsUTC(void) const { return m_recordingTime; } + const CDateTime &RecordingTimeAsLocalTime(void) const; + void SetRecordingTimeFromUTC(CDateTime &recordingTime) { m_recordingTime = recordingTime; } + void SetRecordingTimeFromLocalTime(CDateTime &recordingTime) { m_recordingTime = recordingTime.GetAsUTCDateTime(); } + + private: + CDateTime m_recordingTime; /*!< start time of the recording */ + + void UpdatePath(void); + void DisplayError(PVR_ERROR err) const; + }; +} diff --git a/xbmc/pvr/recordings/PVRRecordings.cpp b/xbmc/pvr/recordings/PVRRecordings.cpp new file mode 100644 index 0000000000..d7c57255d6 --- /dev/null +++ b/xbmc/pvr/recordings/PVRRecordings.cpp @@ -0,0 +1,544 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "FileItem.h" +#include "dialogs/GUIDialogOK.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "Util.h" +#include "URL.h" +#include "utils/log.h" +#include "threads/SingleLock.h" +#include "video/VideoDatabase.h" + +#include "utils/URIUtils.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClients.h" +#include "PVRRecordings.h" + +using namespace PVR; + +CPVRRecordings::CPVRRecordings(void) : + m_bIsUpdating(false), + m_strDirectoryHistory("pvr://recordings/") +{ + m_thumbLoader.SetNumOfWorkers(1); +} + +void CPVRRecordings::UpdateFromClients(void) +{ + CSingleLock lock(m_critSection); + Clear(); + g_PVRClients->GetRecordings(this); +} + +CStdString CPVRRecordings::TrimSlashes(const CStdString &strOrig) const +{ + CStdString strReturn(strOrig); + while (strReturn.Left(1) == "/") + strReturn.erase(0, 1); + + URIUtils::RemoveSlashAtEnd(strReturn); + + return strReturn; +} + +const CStdString CPVRRecordings::GetDirectoryFromPath(const CStdString &strPath, const CStdString &strBase) const +{ + CStdString strReturn; + CStdString strUsePath = TrimSlashes(strPath); + CStdString strUseBase = TrimSlashes(strBase); + + /* strip the base or return an empty value if it doesn't fit or match */ + if (!strUseBase.IsEmpty()) + { + /* adding "/" to make sure that base matches the complete folder name and not only parts of it */ + if (strUsePath.GetLength() <= strUseBase.GetLength() || strUsePath.Left(strUseBase.GetLength() + 1) != strUseBase + "/") + return strReturn; + strUsePath.erase(0, strUseBase.GetLength()); + } + + /* check for more occurences */ + int iDelimiter = strUsePath.Find('/'); + if (iDelimiter > 0) + strReturn = strUsePath.Left(iDelimiter); + else + strReturn = strUsePath; + + return TrimSlashes(strReturn); +} + +bool CPVRRecordings::IsDirectoryMember(const CStdString &strDirectory, const CStdString &strEntryDirectory, bool bDirectMember /* = true */) const +{ + CStdString strUseDirectory = TrimSlashes(strDirectory); + CStdString strUseEntryDirectory = TrimSlashes(strEntryDirectory); + + return strUseEntryDirectory.Left(strUseDirectory.GetLength()).Equals(strUseDirectory) && + (!bDirectMember || strUseEntryDirectory.Equals(strUseDirectory)); +} + +void CPVRRecordings::GetContents(const CStdString &strDirectory, CFileItemList *results) +{ + for (unsigned int iRecordingPtr = 0; iRecordingPtr < m_recordings.size(); iRecordingPtr++) + { + CPVRRecording *current = m_recordings.at(iRecordingPtr); + bool directMember = !HasAllRecordingsPathExtension(strDirectory); + if (!IsDirectoryMember(RemoveAllRecordingsPathExtension(strDirectory), current->m_strDirectory, directMember)) + continue; + + CFileItemPtr pFileItem(new CFileItem(*current)); + pFileItem->SetLabel2(current->RecordingTimeAsLocalTime().GetAsLocalizedDateTime(true, false)); + pFileItem->m_dateTime = current->RecordingTimeAsLocalTime(); + pFileItem->SetPath(current->m_strFileNameAndPath); + + // Set the play count either directly from client (if supported) or from video db + if (g_PVRClients->SupportsRecordingPlayCount(pFileItem->GetPVRRecordingInfoTag()->m_iClientId)) + { + pFileItem->GetPVRRecordingInfoTag()->m_playCount=pFileItem->GetPVRRecordingInfoTag()->m_iRecPlayCount; + } + else + { + CVideoDatabase db; + if (db.Open()) + pFileItem->GetPVRRecordingInfoTag()->m_playCount=db.GetPlayCount(*pFileItem); + } + pFileItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, pFileItem->GetPVRRecordingInfoTag()->m_playCount > 0); + + results->Add(pFileItem); + } +} + +void CPVRRecordings::GetSubDirectories(const CStdString &strBase, CFileItemList *results, bool bAutoSkip /* = true */) +{ + CStdString strUseBase = TrimSlashes(strBase); + + std::set<CStdString> unwatchedFolders; + + for (unsigned int iRecordingPtr = 0; iRecordingPtr < m_recordings.size(); iRecordingPtr++) + { + CPVRRecording *current = m_recordings.at(iRecordingPtr); + const CStdString strCurrent = GetDirectoryFromPath(current->m_strDirectory, strUseBase); + if (strCurrent.IsEmpty()) + continue; + + CStdString strFilePath; + if(strUseBase.empty()) + strFilePath.Format("pvr://recordings/%s/", strCurrent.c_str()); + else + strFilePath.Format("pvr://recordings/%s/%s/", strUseBase.c_str(), strCurrent.c_str()); + + if (!results->Contains(strFilePath)) + { + CFileItemPtr pFileItem; + pFileItem.reset(new CFileItem(strCurrent, true)); + pFileItem->SetPath(strFilePath); + pFileItem->SetLabel(strCurrent); + pFileItem->SetLabelPreformated(true); + pFileItem->m_dateTime = current->RecordingTimeAsLocalTime(); + + // Initialize folder overlay from play count (either directly from client or from video database) + CVideoDatabase db; + bool supportsPlayCount = g_PVRClients->SupportsRecordingPlayCount(current->m_iClientId); + if ((supportsPlayCount && current->m_iRecPlayCount > 0) || + (!supportsPlayCount && db.Open() && db.GetPlayCount(*pFileItem) > 0)) + { + pFileItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_WATCHED, false); + } + else + { + unwatchedFolders.insert(strFilePath); + } + + results->Add(pFileItem); + } + else + { + CFileItemPtr pFileItem; + pFileItem=results->Get(strFilePath); + if (pFileItem->m_dateTime<current->RecordingTimeAsLocalTime()) + pFileItem->m_dateTime = current->RecordingTimeAsLocalTime(); + + // Unset folder overlay if recording is unwatched + if (unwatchedFolders.find(strFilePath) == unwatchedFolders.end()) { + CVideoDatabase db; + bool supportsPlayCount = g_PVRClients->SupportsRecordingPlayCount(current->m_iClientId); + if ((supportsPlayCount && current->m_iRecPlayCount == 0) || (!supportsPlayCount && db.Open() && db.GetPlayCount(*pFileItem) == 0)) + { + pFileItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, false); + unwatchedFolders.insert(strFilePath); + } + } + } + } + + int subDirectories = results->Size(); + CFileItemList files; + GetContents(strBase, &files); + + if (bAutoSkip && results->Size() == 1 && files.Size() == 0) + { + CStdString strGetPath; + strGetPath.Format("%s/%s/", strUseBase.c_str(), results->Get(0)->GetLabel()); + + results->Clear(); + + CLog::Log(LOGDEBUG, "CPVRRecordings - %s - '%s' only has 1 subdirectory, selecting that directory ('%s')", + __FUNCTION__, strUseBase.c_str(), strGetPath.c_str()); + GetSubDirectories(strGetPath, results, true); + return; + } + + results->Append(files); + + // Add 'All Recordings' item (if we have at least one subdirectory in the list) + if (subDirectories > 0) + { + CStdString strLabel(g_localizeStrings.Get(19270)); // "* All recordings" + CFileItemPtr pItem(new CFileItem(strLabel)); + CStdString strAllPath; + if(strUseBase.empty()) + strAllPath = "pvr://recordings"; + else + strAllPath.Format("pvr://recordings/%s", strUseBase.c_str()); + pItem->SetPath(AddAllRecordingsPathExtension(strAllPath)); + pItem->SetSpecialSort(SortSpecialOnTop); + pItem->SetLabelPreformated(true); + pItem->m_bIsFolder = true; + pItem->m_bIsShareOrDrive = false; + for(int i=0; i<results->Size(); ++i) + { + if(pItem->m_dateTime < results->Get(i)->m_dateTime) + pItem->m_dateTime = results->Get(i)->m_dateTime; + } + results->AddFront(pItem, 0); + } + + if (!strUseBase.IsEmpty()) + { + CStdString strLabel(".."); + CFileItemPtr pItem(new CFileItem(strLabel)); + pItem->SetPath(m_strDirectoryHistory); + pItem->m_bIsFolder = true; + pItem->m_bIsShareOrDrive = false; + results->AddFront(pItem, 0); + } + m_strDirectoryHistory.Format("pvr://recordings/%s", strUseBase.c_str()); +} + +bool CPVRRecordings::HasAllRecordingsPathExtension(const CStdString &strDirectory) +{ + CStdString strUseDir = TrimSlashes(strDirectory); + CStdString strAllRecordingsPathExtension(PVR_ALL_RECORDINGS_PATH_EXTENSION); + + if (strUseDir.GetLength() < strAllRecordingsPathExtension.GetLength()) + return false; + + if (strUseDir.GetLength() == strAllRecordingsPathExtension.GetLength()) + return strUseDir.Equals(strAllRecordingsPathExtension); + + return strUseDir.Right(strAllRecordingsPathExtension.GetLength() + 1).Equals("/" + strAllRecordingsPathExtension); +} + +CStdString CPVRRecordings::AddAllRecordingsPathExtension(const CStdString &strDirectory) +{ + if (HasAllRecordingsPathExtension(strDirectory)) + return strDirectory; + + CStdString strResult = strDirectory; + if (!strDirectory.Right(1).Equals("/")) + strResult = strResult + "/"; + + return strResult + PVR_ALL_RECORDINGS_PATH_EXTENSION + "/"; +} + +CStdString CPVRRecordings::RemoveAllRecordingsPathExtension(const CStdString &strDirectory) +{ + if (!HasAllRecordingsPathExtension(strDirectory)) + return strDirectory; + + return strDirectory.Left(strDirectory.GetLength() - strlen(PVR_ALL_RECORDINGS_PATH_EXTENSION) - 1); +} + +int CPVRRecordings::Load(void) +{ + Update(); + + return m_recordings.size(); +} + +void CPVRRecordings::Unload() +{ + Clear(); +} + +void CPVRRecordings::Update(void) +{ + CSingleLock lock(m_critSection); + if (m_bIsUpdating) + return; + m_bIsUpdating = true; + lock.Leave(); + + CLog::Log(LOGDEBUG, "CPVRRecordings - %s - updating recordings", __FUNCTION__); + UpdateFromClients(); + + lock.Enter(); + m_bIsUpdating = false; + SetChanged(); + lock.Leave(); + + NotifyObservers(ObservableMessageRecordings); +} + +int CPVRRecordings::GetNumRecordings() +{ + CSingleLock lock(m_critSection); + return m_recordings.size(); +} + +int CPVRRecordings::GetRecordings(CFileItemList* results) +{ + CSingleLock lock(m_critSection); + + for (unsigned int iRecordingPtr = 0; iRecordingPtr < m_recordings.size(); iRecordingPtr++) + { + CFileItemPtr pFileItem(new CFileItem(*m_recordings.at(iRecordingPtr))); + results->Add(pFileItem); + } + + return m_recordings.size(); +} + +bool CPVRRecordings::DeleteRecording(const CFileItem &item) +{ + if (!item.IsPVRRecording()) + { + CLog::Log(LOGERROR, "CPVRRecordings - %s - cannot delete file: no valid recording tag", __FUNCTION__); + return false; + } + + CPVRRecording *tag = (CPVRRecording *)item.GetPVRRecordingInfoTag(); + return tag->Delete(); +} + +bool CPVRRecordings::RenameRecording(CFileItem &item, CStdString &strNewName) +{ + bool bReturn = false; + + if (!item.IsPVRRecording()) + { + CLog::Log(LOGERROR, "CPVRRecordings - %s - cannot rename file: no valid recording tag", __FUNCTION__); + return bReturn; + } + + CPVRRecording* tag = item.GetPVRRecordingInfoTag(); + return tag->Rename(strNewName); +} + +bool CPVRRecordings::SetRecordingsPlayCount(const CFileItemPtr &item, int count) +{ + bool bResult = false; + + CVideoDatabase database; + if (database.Open()) + { + bResult = true; + + CLog::Log(LOGDEBUG, "CPVRRecordings - %s - item path %s", __FUNCTION__, item->GetPath().c_str()); + CFileItemList items; + if (item->m_bIsFolder) + { + CStdString strPath = item->GetPath(); + CDirectory::GetDirectory(strPath, items); + } + else + items.Add(item); + + CLog::Log(LOGDEBUG, "CPVRRecordings - %s - will set watched for %d items", __FUNCTION__, items.Size()); + for (int i=0;i<items.Size();++i) + { + CLog::Log(LOGDEBUG, "CPVRRecordings - %s - setting watched for item %d", __FUNCTION__, i); + + CFileItemPtr pItem=items[i]; + if (pItem->m_bIsFolder) + { + CLog::Log(LOGDEBUG, "CPVRRecordings - %s - path %s is a folder, will call recursively", __FUNCTION__, pItem->GetPath().c_str()); + if (pItem->GetLabel() != "..") + { + SetRecordingsPlayCount(pItem, count); + } + continue; + } + + pItem->GetPVRRecordingInfoTag()->SetPlayCount(count); + + // Clear resume bookmark + if (count > 0) + database.ClearBookMarksOfFile(pItem->GetPath(), CBookmark::RESUME); + + database.SetPlayCount(*pItem, count); + } + + database.Close(); + } + + return bResult; +} + +bool CPVRRecordings::GetDirectory(const CStdString& strPath, CFileItemList &items) +{ + bool bSuccess(false); + CFileItemList files; + + { + CSingleLock lock(m_critSection); + + CURL url(strPath); + CStdString strFileName = url.GetFileName(); + URIUtils::RemoveSlashAtEnd(strFileName); + + if (strFileName.Left(10) == "recordings") + { + strFileName.erase(0, 10); + GetSubDirectories(strFileName, &items, true); + GetContents(strFileName, &files); + bSuccess = true; + } + } + + if(bSuccess) + { + for (int i = 0; i < files.Size(); i++) + { + CFileItemPtr pFileItem = files.Get(i); + CFileItemPtr pThumbItem = items.Get(pFileItem->GetPath()); + if (!pThumbItem->HasThumbnail()) + m_thumbLoader.LoadItem(pThumbItem.get()); + } + } + + return bSuccess; +} + +void CPVRRecordings::SetPlayCount(const CFileItem &item, int iPlayCount) +{ + if (!item.HasPVRRecordingInfoTag()) + return; + + const CPVRRecording *recording = item.GetPVRRecordingInfoTag(); + CSingleLock lock(m_critSection); + for (unsigned int iRecordingPtr = 0; iRecordingPtr < m_recordings.size(); iRecordingPtr++) + { + CPVRRecording *current = m_recordings.at(iRecordingPtr); + if (*current == *recording) + { + current->SetPlayCount(iPlayCount); + break; + } + } +} + +void CPVRRecordings::GetAll(CFileItemList &items) +{ + CSingleLock lock(m_critSection); + for (unsigned int iRecordingPtr = 0; iRecordingPtr < m_recordings.size(); iRecordingPtr++) + { + CPVRRecording *current = m_recordings.at(iRecordingPtr); + + CFileItemPtr pFileItem(new CFileItem(*current)); + pFileItem->SetLabel2(current->RecordingTimeAsLocalTime().GetAsLocalizedDateTime(true, false)); + pFileItem->m_dateTime = current->RecordingTimeAsLocalTime(); + pFileItem->SetPath(current->m_strFileNameAndPath); + + // Set the play count either directly from client (if supported) or from video db + if (g_PVRClients->SupportsRecordingPlayCount(pFileItem->GetPVRRecordingInfoTag()->m_iClientId)) + { + pFileItem->GetPVRRecordingInfoTag()->m_playCount=pFileItem->GetPVRRecordingInfoTag()->m_iRecPlayCount; + } + else + { + CVideoDatabase db; + if (db.Open()) + pFileItem->GetPVRRecordingInfoTag()->m_playCount=db.GetPlayCount(*pFileItem); + } + pFileItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, pFileItem->GetPVRRecordingInfoTag()->m_playCount > 0); + + items.Add(pFileItem); + } +} + +CFileItemPtr CPVRRecordings::GetByPath(const CStdString &path) +{ + CURL url(path); + CStdString fileName = url.GetFileName(); + URIUtils::RemoveSlashAtEnd(fileName); + + CSingleLock lock(m_critSection); + + if (fileName.Left(11) == "recordings/") + { + for (unsigned int iRecordingPtr = 0; iRecordingPtr < m_recordings.size(); iRecordingPtr++) + { + if(path.Equals(m_recordings.at(iRecordingPtr)->m_strFileNameAndPath)) + { + CFileItemPtr fileItem(new CFileItem(*m_recordings.at(iRecordingPtr))); + return fileItem; + } + } + } + + CFileItemPtr fileItem(new CFileItem); + return fileItem; +} + +void CPVRRecordings::Clear() +{ + CSingleLock lock(m_critSection); + + for (unsigned int iRecordingPtr = 0; iRecordingPtr < m_recordings.size(); iRecordingPtr++) + delete m_recordings.at(iRecordingPtr); + m_recordings.erase(m_recordings.begin(), m_recordings.end()); +} + +void CPVRRecordings::UpdateEntry(const CPVRRecording &tag) +{ + bool bFound = false; + CSingleLock lock(m_critSection); + + for (unsigned int iRecordingPtr = 0; iRecordingPtr < m_recordings.size(); iRecordingPtr++) + { + CPVRRecording *currentTag = m_recordings.at(iRecordingPtr); + if (currentTag->m_iClientId == tag.m_iClientId && + currentTag->m_strRecordingId.Equals(tag.m_strRecordingId)) + { + currentTag->Update(tag); + bFound = true; + break; + } + } + + if (!bFound) + { + CPVRRecording *newTag = new CPVRRecording(); + newTag->Update(tag); + m_recordings.push_back(newTag); + } +} diff --git a/xbmc/pvr/recordings/PVRRecordings.h b/xbmc/pvr/recordings/PVRRecordings.h new file mode 100644 index 0000000000..f9ddbbb7cb --- /dev/null +++ b/xbmc/pvr/recordings/PVRRecordings.h @@ -0,0 +1,79 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "PVRRecording.h" +#include "XBDateTime.h" +#include "threads/Thread.h" +#include "utils/Observer.h" +#include "ThumbLoader.h" + +#define PVR_ALL_RECORDINGS_PATH_EXTENSION "-1" + +namespace PVR +{ + class CPVRRecordings : public Observable + { + private: + CCriticalSection m_critSection; + bool m_bIsUpdating; + CStdString m_strDirectoryHistory; + CVideoThumbLoader m_thumbLoader; + std::vector<CPVRRecording *> m_recordings; + + virtual void UpdateFromClients(void); + virtual CStdString TrimSlashes(const CStdString &strOrig) const; + virtual const CStdString GetDirectoryFromPath(const CStdString &strPath, const CStdString &strBase) const; + virtual bool IsDirectoryMember(const CStdString &strDirectory, const CStdString &strEntryDirectory, bool bDirectMember = true) const; + virtual void GetContents(const CStdString &strDirectory, CFileItemList *results); + virtual void GetSubDirectories(const CStdString &strBase, CFileItemList *results, bool bAutoSkip = true); + + bool HasAllRecordingsPathExtension(const CStdString &strDirectory); + CStdString AddAllRecordingsPathExtension(const CStdString &strDirectory); + CStdString RemoveAllRecordingsPathExtension(const CStdString &strDirectory); + + public: + CPVRRecordings(void); + virtual ~CPVRRecordings(void) { Clear(); }; + + int Load(); + void Unload(); + void Clear(); + void UpdateEntry(const CPVRRecording &tag); + void UpdateFromClient(const CPVRRecording &tag) { UpdateEntry(tag); } + + /** + * @brief refresh the recordings list from the clients. + */ + void Update(void); + + int GetNumRecordings(); + int GetRecordings(CFileItemList* results); + bool DeleteRecording(const CFileItem &item); + bool RenameRecording(CFileItem &item, CStdString &strNewName); + bool SetRecordingsPlayCount(const CFileItemPtr &item, int count); + + bool GetDirectory(const CStdString& strPath, CFileItemList &items); + CFileItemPtr GetByPath(const CStdString &path); + void SetPlayCount(const CFileItem &item, int iPlayCount); + void GetAll(CFileItemList &items); + }; +} diff --git a/xbmc/pvr/timers/Makefile b/xbmc/pvr/timers/Makefile new file mode 100644 index 0000000000..0af3a3bcd3 --- /dev/null +++ b/xbmc/pvr/timers/Makefile @@ -0,0 +1,7 @@ +SRCS=PVRTimerInfoTag.cpp \ + PVRTimers.cpp + +LIB=pvrtimers.a + +include ../../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/xbmc/pvr/timers/PVRTimerInfoTag.cpp b/xbmc/pvr/timers/PVRTimerInfoTag.cpp new file mode 100644 index 0000000000..258cf9f393 --- /dev/null +++ b/xbmc/pvr/timers/PVRTimerInfoTag.cpp @@ -0,0 +1,598 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "Application.h" +#include "settings/GUISettings.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogYesNo.h" +#include "settings/AdvancedSettings.h" +#include "utils/log.h" +#include "utils/StringUtils.h" + +#include "PVRTimers.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/channels/PVRChannelGroupInternal.h" +#include "epg/EpgContainer.h" +#include "pvr/addons/PVRClients.h" + +#include "epg/Epg.h" + +using namespace std; +using namespace PVR; +using namespace EPG; + +CPVRTimerInfoTag::CPVRTimerInfoTag(void) +{ + m_strTitle = StringUtils::EmptyString; + m_strDirectory = "/"; + m_strSummary = StringUtils::EmptyString; + m_iClientId = g_PVRClients->GetFirstConnectedClientID(); + m_iClientIndex = -1; + m_iClientChannelUid = -1; + m_iPriority = g_guiSettings.GetInt("pvrrecord.defaultpriority"); + m_iLifetime = g_guiSettings.GetInt("pvrrecord.defaultlifetime"); + m_bIsRepeating = false; + m_iWeekdays = 0; + m_strFileNameAndPath = StringUtils::EmptyString; + m_iChannelNumber = 0; + m_bIsRadio = false; + CEpgInfoTagPtr emptyTag; + m_epgTag = emptyTag; + m_iMarginStart = g_guiSettings.GetInt("pvrrecord.marginstart"); + m_iMarginEnd = g_guiSettings.GetInt("pvrrecord.marginend"); + m_iGenreType = 0; + m_iGenreSubType = 0; + m_StartTime = CDateTime::GetUTCDateTime(); + m_StopTime = m_StartTime; + m_state = PVR_TIMER_STATE_SCHEDULED; + m_FirstDay.SetValid(false); +} + +CPVRTimerInfoTag::CPVRTimerInfoTag(const PVR_TIMER &timer, CPVRChannelPtr channel, unsigned int iClientId) +{ + m_strTitle = timer.strTitle; + m_strDirectory = timer.strDirectory; + m_strSummary = StringUtils::EmptyString; + m_iClientId = iClientId; + m_iClientIndex = timer.iClientIndex; + m_iClientChannelUid = channel ? channel->UniqueID() : timer.iClientChannelUid; + m_iChannelNumber = channel ? g_PVRChannelGroups->GetGroupAll(channel->IsRadio())->GetChannelNumber(*channel) : 0; + m_StartTime = timer.startTime + g_advancedSettings.m_iPVRTimeCorrection; + m_StopTime = timer.endTime + g_advancedSettings.m_iPVRTimeCorrection; + m_bIsRepeating = timer.bIsRepeating; + m_FirstDay = timer.firstDay + g_advancedSettings.m_iPVRTimeCorrection; + m_iWeekdays = timer.iWeekdays; + m_iPriority = timer.iPriority; + m_iLifetime = timer.iLifetime; + m_iMarginStart = timer.iMarginStart; + m_iMarginEnd = timer.iMarginEnd; + m_genre = StringUtils::Split(CEpg::ConvertGenreIdToString(timer.iGenreType, timer.iGenreSubType), g_advancedSettings.m_videoItemSeparator); + m_iGenreType = timer.iGenreType; + m_iGenreSubType = timer.iGenreSubType; + CEpgInfoTagPtr emptyTag; + m_epgTag = emptyTag; + m_channel = channel; + m_bIsRadio = channel && channel->IsRadio(); + m_state = timer.state; + m_strFileNameAndPath.Format("pvr://client%i/timers/%i", m_iClientId, m_iClientIndex); + + UpdateSummary(); +} + +bool CPVRTimerInfoTag::operator ==(const CPVRTimerInfoTag& right) const +{ + bool bChannelsMatch = true; + if (m_channel && right.m_channel) + bChannelsMatch = *m_channel == *right.m_channel; + else if (m_channel != right.m_channel) + bChannelsMatch = false; + + return (bChannelsMatch && + m_iClientIndex == right.m_iClientIndex && + m_strSummary == right.m_strSummary && + m_iClientChannelUid == right.m_iClientChannelUid && + m_bIsRepeating == right.m_bIsRepeating && + m_StartTime == right.m_StartTime && + m_StopTime == right.m_StopTime && + m_FirstDay == right.m_FirstDay && + m_iWeekdays == right.m_iWeekdays && + m_iPriority == right.m_iPriority && + m_iLifetime == right.m_iLifetime && + m_strFileNameAndPath == right.m_strFileNameAndPath && + m_strTitle == right.m_strTitle && + m_strDirectory == right.m_strDirectory && + m_iClientId == right.m_iClientId && + m_iMarginStart == right.m_iMarginStart && + m_iMarginEnd == right.m_iMarginEnd && + m_state == right.m_state); +} + +CPVRTimerInfoTag &CPVRTimerInfoTag::operator=(const CPVRTimerInfoTag &orig) +{ + m_channel = orig.m_channel; + m_iClientIndex = orig.m_iClientIndex; + m_strSummary = orig.m_strSummary; + m_iClientChannelUid = orig.m_iClientChannelUid; + m_bIsRepeating = orig.m_bIsRepeating; + m_StartTime = orig.m_StartTime; + m_StopTime = orig.m_StopTime; + m_FirstDay = orig.m_FirstDay; + m_iWeekdays = orig.m_iWeekdays; + m_iPriority = orig.m_iPriority; + m_iLifetime = orig.m_iLifetime; + m_strFileNameAndPath = orig.m_strFileNameAndPath; + m_strTitle = orig.m_strTitle; + m_strDirectory = orig.m_strDirectory; + m_iClientId = orig.m_iClientId; + m_iMarginStart = orig.m_iMarginStart; + m_iMarginEnd = orig.m_iMarginEnd; + m_state = orig.m_state; + m_iChannelNumber = orig.m_iChannelNumber; + + return *this; +} + +CPVRTimerInfoTag::~CPVRTimerInfoTag(void) +{ + ClearEpgTag(); +} + +/** + * Compare not equal for two CPVRTimerInfoTag + */ +bool CPVRTimerInfoTag::operator !=(const CPVRTimerInfoTag& right) const +{ + return !(*this == right); +} + +int CPVRTimerInfoTag::Compare(const CPVRTimerInfoTag &timer) const +{ + CSingleLock lock(m_critSection); + int iTimerDelta = 0; + if (StartAsUTC() != timer.StartAsUTC()) + { + CDateTimeSpan timerDelta = StartAsUTC() - timer.StartAsUTC(); + iTimerDelta = (timerDelta.GetSeconds() + timerDelta.GetMinutes() * 60 + timerDelta.GetHours() * 3600 + timerDelta.GetDays() * 86400); + } + + /* if the start times are equal, compare the priority of the timers */ + return iTimerDelta == 0 ? + timer.m_iPriority - m_iPriority : + iTimerDelta; +} + +void CPVRTimerInfoTag::UpdateSummary(void) +{ + CSingleLock lock(m_critSection); + m_strSummary.clear(); + + if (!m_bIsRepeating || !m_iWeekdays) + { + m_strSummary.Format("%s %s %s %s %s", + StartAsLocalTime().GetAsLocalizedDate(), + g_localizeStrings.Get(19159), + StartAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false), + g_localizeStrings.Get(19160), + EndAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false)); + } + else if (m_FirstDay.IsValid()) + { + m_strSummary.Format("%s-%s-%s-%s-%s-%s-%s %s %s %s %s %s %s", + m_iWeekdays & 0x01 ? g_localizeStrings.Get(19149) : "__", + m_iWeekdays & 0x02 ? g_localizeStrings.Get(19150) : "__", + m_iWeekdays & 0x04 ? g_localizeStrings.Get(19151) : "__", + m_iWeekdays & 0x08 ? g_localizeStrings.Get(19152) : "__", + m_iWeekdays & 0x10 ? g_localizeStrings.Get(19153) : "__", + m_iWeekdays & 0x20 ? g_localizeStrings.Get(19154) : "__", + m_iWeekdays & 0x40 ? g_localizeStrings.Get(19155) : "__", + g_localizeStrings.Get(19156), + FirstDayAsLocalTime().GetAsLocalizedDate(false), + g_localizeStrings.Get(19159), + StartAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false), + g_localizeStrings.Get(19160), + EndAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false)); + } + else + { + m_strSummary.Format("%s-%s-%s-%s-%s-%s-%s %s %s %s %s", + m_iWeekdays & 0x01 ? g_localizeStrings.Get(19149) : "__", + m_iWeekdays & 0x02 ? g_localizeStrings.Get(19150) : "__", + m_iWeekdays & 0x04 ? g_localizeStrings.Get(19151) : "__", + m_iWeekdays & 0x08 ? g_localizeStrings.Get(19152) : "__", + m_iWeekdays & 0x10 ? g_localizeStrings.Get(19153) : "__", + m_iWeekdays & 0x20 ? g_localizeStrings.Get(19154) : "__", + m_iWeekdays & 0x40 ? g_localizeStrings.Get(19155) : "__", + g_localizeStrings.Get(19159), + StartAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false), + g_localizeStrings.Get(19160), + EndAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false)); + } +} + +/** + * Get the status string of this Timer, is used by the GUIInfoManager + */ +CStdString CPVRTimerInfoTag::GetStatus() const +{ + CStdString strReturn = g_localizeStrings.Get(305); + CSingleLock lock(m_critSection); + if (m_strFileNameAndPath == "pvr://timers/add.timer") + strReturn = g_localizeStrings.Get(19026); + else if (m_state == PVR_TIMER_STATE_CANCELLED || m_state == PVR_TIMER_STATE_ABORTED) + strReturn = g_localizeStrings.Get(13106); + else if (m_state == PVR_TIMER_STATE_RECORDING) + strReturn = g_localizeStrings.Get(19162); + + return strReturn; +} + +bool CPVRTimerInfoTag::AddToClient(void) const +{ + PVR_ERROR error = g_PVRClients->AddTimer(*this); + if (error != PVR_ERROR_NO_ERROR) + { + DisplayError(error); + return false; + } + + return true; +} + +bool CPVRTimerInfoTag::DeleteFromClient(bool bForce /* = false */) const +{ + PVR_ERROR error = g_PVRClients->DeleteTimer(*this, bForce); + if (error == PVR_ERROR_RECORDING_RUNNING) + { + // recording running. ask the user if it should be deleted anyway + if (!CGUIDialogYesNo::ShowAndGetInput(122,0,19122,0)) + return false; + + error = g_PVRClients->DeleteTimer(*this, true); + } + + if (error != PVR_ERROR_NO_ERROR) + { + DisplayError(error); + return false; + } + + + return true; +} + +bool CPVRTimerInfoTag::RenameOnClient(const CStdString &strNewName) +{ + { + CSingleLock lock(m_critSection); + m_strTitle = strNewName; + } + + PVR_ERROR error = g_PVRClients->RenameTimer(*this, strNewName); + if (error != PVR_ERROR_NO_ERROR) + { + if (error == PVR_ERROR_NOT_IMPLEMENTED) + return UpdateOnClient(); + + DisplayError(error); + return false; + } + + return true; +} + +bool CPVRTimerInfoTag::UpdateEntry(const CPVRTimerInfoTag &tag) +{ + CSingleLock lock(m_critSection); + + m_iClientId = tag.m_iClientId; + m_iClientIndex = tag.m_iClientIndex; + m_strTitle = tag.m_strTitle; + m_strDirectory = tag.m_strDirectory; + m_iClientChannelUid = tag.m_iClientChannelUid; + m_StartTime = tag.m_StartTime; + m_StopTime = tag.m_StopTime; + m_FirstDay = tag.m_FirstDay; + m_iPriority = tag.m_iPriority; + m_iLifetime = tag.m_iLifetime; + m_state = tag.m_state; + m_bIsRepeating = tag.m_bIsRepeating; + m_iWeekdays = tag.m_iWeekdays; + m_iChannelNumber = tag.m_iChannelNumber; + m_bIsRadio = tag.m_bIsRadio; + m_iMarginStart = tag.m_iMarginStart; + m_iMarginEnd = tag.m_iMarginEnd; + m_epgTag = tag.m_epgTag; + m_genre = tag.m_genre; + m_iGenreType = tag.m_iGenreType; + m_iGenreSubType = tag.m_iGenreSubType; + m_strSummary = tag.m_strSummary; + + if (m_strSummary.IsEmpty()) + UpdateSummary(); + + return true; +} + +bool CPVRTimerInfoTag::UpdateOnClient() +{ + PVR_ERROR error = g_PVRClients->UpdateTimer(*this); + if (error != PVR_ERROR_NO_ERROR) + { + DisplayError(error); + return false; + } + + return true; +} + +void CPVRTimerInfoTag::DisplayError(PVR_ERROR err) const +{ + if (err == PVR_ERROR_SERVER_ERROR) + CGUIDialogOK::ShowAndGetInput(19033,19111,19110,0); /* print info dialog "Server error!" */ + else if (err == PVR_ERROR_REJECTED) + CGUIDialogOK::ShowAndGetInput(19033,19109,19110,0); /* print info dialog "Couldn't delete timer!" */ + else if (err == PVR_ERROR_ALREADY_PRESENT) + CGUIDialogOK::ShowAndGetInput(19033,19109,0,19067); /* print info dialog */ + else + CGUIDialogOK::ShowAndGetInput(19033,19147,19110,0); /* print info dialog "Unknown error!" */ +} + +void CPVRTimerInfoTag::SetEpgInfoTag(CEpgInfoTagPtr tag) +{ + CSingleLock lock(m_critSection); + if (tag && m_epgTag != tag) + CLog::Log(LOGINFO, "cPVRTimerInfoTag: timer %s set to epg event %s", m_strTitle.c_str(), tag->Title().c_str()); + else if (!tag && m_epgTag) + CLog::Log(LOGINFO, "cPVRTimerInfoTag: timer %s set to no epg event", m_strTitle.c_str()); + m_epgTag = tag; +} + +int CPVRTimerInfoTag::ChannelNumber() const +{ + CPVRChannelPtr channeltag = ChannelTag(); + return channeltag ? channeltag->ChannelNumber() : 0; +} + +CStdString CPVRTimerInfoTag::ChannelName() const +{ + CStdString strReturn; + CPVRChannelPtr channeltag = ChannelTag(); + if (channeltag) + strReturn = channeltag->ChannelName(); + return strReturn; +} + +CStdString CPVRTimerInfoTag::ChannelIcon() const +{ + CStdString strReturn; + CPVRChannelPtr channeltag = ChannelTag(); + if (channeltag) + strReturn = channeltag->IconPath(); + return strReturn; +} + +bool CPVRTimerInfoTag::SetDuration(int iDuration) +{ + CSingleLock lock(m_critSection); + if (m_StartTime.IsValid()) + { + m_StopTime = m_StartTime + CDateTimeSpan(0, iDuration / 60, iDuration % 60, 0); + return true; + } + + return false; +} + +CPVRTimerInfoTag *CPVRTimerInfoTag::CreateFromEpg(const CEpgInfoTag &tag) +{ + /* create a new timer */ + CPVRTimerInfoTag *newTag = new CPVRTimerInfoTag(); + if (!newTag) + { + CLog::Log(LOGERROR, "%s - couldn't create new timer", __FUNCTION__); + return NULL; + } + + /* check if a valid channel is set */ + CPVRChannelPtr channel = tag.ChannelTag(); + if (!channel) + { + CLog::Log(LOGERROR, "%s - no channel set", __FUNCTION__); + return NULL; + } + + /* check if the epg end date is in the future */ + if (tag.EndAsLocalTime() < CDateTime::GetCurrentDateTime()) + { + CLog::Log(LOGERROR, "%s - end time is in the past", __FUNCTION__); + return NULL; + } + + /* set the timer data */ + CDateTime newStart = tag.StartAsUTC(); + CDateTime newEnd = tag.EndAsUTC(); + newTag->m_iClientIndex = -1; + newTag->m_strTitle = tag.Title().empty() ? channel->ChannelName() : tag.Title(); + newTag->m_iChannelNumber = channel->ChannelNumber(); + newTag->m_iClientChannelUid = channel->UniqueID(); + newTag->m_iClientId = channel->ClientID(); + newTag->m_bIsRadio = channel->IsRadio(); + newTag->m_iGenreType = tag.GenreType(); + newTag->m_iGenreSubType = tag.GenreSubType(); + newTag->m_channel = channel; + newTag->SetStartFromUTC(newStart); + newTag->SetEndFromUTC(newEnd); + + if (tag.Plot().empty()) + { + newTag->m_strSummary.Format("%s %s %s %s %s", + newTag->StartAsLocalTime().GetAsLocalizedDate(), + g_localizeStrings.Get(19159), + newTag->StartAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false), + g_localizeStrings.Get(19160), + newTag->EndAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false)); + } + else + { + newTag->m_strSummary = tag.Plot(); + } + + newTag->m_epgTag = g_EpgContainer.GetById(tag.EpgID())->GetTag(tag.StartAsUTC()); + + /* unused only for reference */ + newTag->m_strFileNameAndPath = "pvr://timers/new"; + + return newTag; +} + +CDateTime CPVRTimerInfoTag::StartAsUTC(void) const +{ + CDateTime retVal = m_StartTime; + return retVal; +} + +CDateTime CPVRTimerInfoTag::StartAsLocalTime(void) const +{ + CDateTime retVal; + retVal.SetFromUTCDateTime(m_StartTime); + return retVal; +} + +CDateTime CPVRTimerInfoTag::EndAsUTC(void) const +{ + CDateTime retVal = m_StopTime; + return retVal; +} + +CDateTime CPVRTimerInfoTag::EndAsLocalTime(void) const +{ + CDateTime retVal; + retVal.SetFromUTCDateTime(m_StopTime); + return retVal; +} + +CDateTime CPVRTimerInfoTag::FirstDayAsUTC(void) const +{ + CDateTime retVal = m_FirstDay; + return retVal; +} + +CDateTime CPVRTimerInfoTag::FirstDayAsLocalTime(void) const +{ + CDateTime retVal; + retVal.SetFromUTCDateTime(m_FirstDay); + return retVal; +} + +void CPVRTimerInfoTag::GetNotificationText(CStdString &strText) const +{ + CSingleLock lock(m_critSection); + switch (m_state) + { + case PVR_TIMER_STATE_ABORTED: + case PVR_TIMER_STATE_CANCELLED: + strText.Format("%s: '%s'", g_localizeStrings.Get(19224), m_strTitle.c_str()); + break; + case PVR_TIMER_STATE_SCHEDULED: + strText.Format("%s: '%s'", g_localizeStrings.Get(19225), m_strTitle.c_str()); + break; + case PVR_TIMER_STATE_RECORDING: + strText.Format("%s: '%s'", g_localizeStrings.Get(19226), m_strTitle.c_str()); + break; + case PVR_TIMER_STATE_COMPLETED: + strText.Format("%s: '%s'", g_localizeStrings.Get(19227), m_strTitle.c_str()); + break; + default: + break; + } +} + +void CPVRTimerInfoTag::QueueNotification(void) const +{ + if (g_guiSettings.GetBool("pvrrecord.timernotifications")) + { + CStdString strMessage; + GetNotificationText(strMessage); + + if (!strMessage.IsEmpty()) + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(19166), strMessage); + } +} + +CEpgInfoTagPtr CPVRTimerInfoTag::GetEpgInfoTag(void) const +{ + return m_epgTag; +} + +bool CPVRTimerInfoTag::SupportsFolders() const +{ + return g_PVRClients->SupportsRecordingFolders(m_iClientId); +} + +void CPVRTimerInfoTag::ClearEpgTag(void) +{ + CEpgInfoTagPtr deletedTag; + + { + CSingleLock lock(m_critSection); + deletedTag = m_epgTag; + + CEpgInfoTagPtr emptyTag; + m_epgTag = emptyTag; + } + + if (deletedTag) + deletedTag->ClearTimer(); +} + +CPVRChannelPtr CPVRTimerInfoTag::ChannelTag(void) const +{ + return m_channel; +} + +void CPVRTimerInfoTag::UpdateChannel(void) +{ + CSingleLock lock(m_critSection); + m_channel = g_PVRChannelGroups->Get(m_bIsRadio)->GetGroupAll()->GetByClient(m_iClientChannelUid, m_iClientId); +} + +CStdString CPVRTimerInfoTag::Title(void) const +{ + CStdString strReturn; + strReturn = m_strTitle; + return strReturn; +} + +CStdString CPVRTimerInfoTag::Summary(void) const +{ + CStdString strReturn; + strReturn = m_strSummary; + return strReturn; +} + +CStdString CPVRTimerInfoTag::Path(void) const +{ + CStdString strReturn; + strReturn = m_strFileNameAndPath; + return strReturn; +} diff --git a/xbmc/pvr/timers/PVRTimerInfoTag.h b/xbmc/pvr/timers/PVRTimerInfoTag.h new file mode 100644 index 0000000000..f3234b6107 --- /dev/null +++ b/xbmc/pvr/timers/PVRTimerInfoTag.h @@ -0,0 +1,183 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +/* + * DESCRIPTION: + * + * CPVRTimerInfoTag is part of the PVRManager to support sheduled recordings. + * + * The timer information tag holds data about current programmed timers for + * the PVRManager. It is possible to create timers directly based upon + * a EPG entry by giving the EPG information tag or as instant timer + * on currently tuned channel, or give a blank tag to modify later. + * + * With exception of the blank one, the tag can easily and unmodified added + * by the PVRManager function "bool AddTimer(const CFileItem &item)" to + * the backend server. + * + * The filename inside the tag is for reference only and gives the index + * number of the tag reported by the PVR backend and can not be played! + */ + +#include "XBDateTime.h" +#include "../addons/include/xbmc_pvr_types.h" + +#include <boost/shared_ptr.hpp> + +class CFileItem; + +namespace EPG +{ + class CEpgInfoTag; + typedef boost::shared_ptr<EPG::CEpgInfoTag> CEpgInfoTagPtr; +} + +namespace PVR +{ + class CGUIDialogPVRTimerSettings; + class CPVRTimers; + class CPVRChannelGroupInternal; + + class CPVRChannel; + typedef boost::shared_ptr<PVR::CPVRChannel> CPVRChannelPtr; + + class CPVRTimerInfoTag; + typedef boost::shared_ptr<PVR::CPVRTimerInfoTag> CPVRTimerInfoTagPtr; + + class CPVRTimerInfoTag + { + friend class CPVRTimers; + friend class CGUIDialogPVRTimerSettings; + + public: + CPVRTimerInfoTag(void); + CPVRTimerInfoTag(const PVR_TIMER &timer, CPVRChannelPtr channel, unsigned int iClientId); + virtual ~CPVRTimerInfoTag(void); + + bool operator ==(const CPVRTimerInfoTag& right) const; + bool operator !=(const CPVRTimerInfoTag& right) const; + CPVRTimerInfoTag &operator=(const CPVRTimerInfoTag &orig); + + int Compare(const CPVRTimerInfoTag &timer) const; + + void UpdateSummary(void); + + void DisplayError(PVR_ERROR err) const; + + CStdString GetStatus() const; + + bool SetDuration(int iDuration); + + static CPVRTimerInfoTag *CreateFromEpg(const EPG::CEpgInfoTag &tag); + EPG::CEpgInfoTagPtr GetEpgInfoTag(void) const; + + int ChannelNumber(void) const; + CStdString ChannelName(void) const; + CStdString ChannelIcon(void) const; + CPVRChannelPtr ChannelTag(void) const; + + bool UpdateEntry(const CPVRTimerInfoTag &tag); + + void UpdateEpgEvent(bool bClear = false); + + bool IsActive(void) const { return m_state == PVR_TIMER_STATE_SCHEDULED || m_state == PVR_TIMER_STATE_RECORDING; } + bool IsRecording(void) const { return m_state == PVR_TIMER_STATE_RECORDING; } + + CDateTime StartAsUTC(void) const; + CDateTime StartAsLocalTime(void) const; + void SetStartFromUTC(CDateTime &start) { m_StartTime = start; } + void SetStartFromLocalTime(CDateTime &start) { m_StartTime = start.GetAsUTCDateTime(); } + + CDateTime EndAsUTC(void) const; + CDateTime EndAsLocalTime(void) const; + void SetEndFromUTC(CDateTime &end) { m_StopTime = end; } + void SetEndFromLocalTime(CDateTime &end) { m_StopTime = end.GetAsUTCDateTime(); } + + CDateTime FirstDayAsUTC(void) const; + CDateTime FirstDayAsLocalTime(void) const; + void SetFirstDayFromUTC(CDateTime &firstDay) { m_FirstDay = firstDay; } + void SetFirstDayFromLocalTime(CDateTime &firstDay) { m_FirstDay = firstDay.GetAsUTCDateTime(); } + + unsigned int MarginStart(void) const { return m_iMarginStart; } + void SetMarginStart(unsigned int iMinutes) { m_iMarginStart = iMinutes; } + + unsigned int MarginEnd(void) const { return m_iMarginEnd; } + void SetMarginEnd(unsigned int iMinutes) { m_iMarginEnd = iMinutes; } + + bool SupportsFolders() const; + + /*! + * @brief Show a notification for this timer in the UI + */ + void QueueNotification(void) const; + + /*! + * @brief Get the text for the notification. + * @param strText The notification. + */ + void GetNotificationText(CStdString &strText) const; + + CStdString Title(void) const; + CStdString Summary(void) const; + CStdString Path(void) const; + + /* Client control functions */ + bool AddToClient() const; + bool DeleteFromClient(bool bForce = false) const; + bool RenameOnClient(const CStdString &strNewName); + bool UpdateOnClient(); + + void SetEpgInfoTag(EPG::CEpgInfoTagPtr tag); + void ClearEpgTag(void); + + void UpdateChannel(void); + + CStdString m_strTitle; /*!< @brief name of this timer */ + CStdString m_strDirectory; /*!< @brief directory where the recording must be stored */ + CStdString m_strSummary; /*!< @brief summary string with the time to show inside a GUI list */ + PVR_TIMER_STATE m_state; /*!< @brief the state of this timer */ + int m_iClientId; /*!< @brief ID of the backend */ + int m_iClientIndex; /*!< @brief index number of the tag, given by the backend, -1 for new */ + int m_iClientChannelUid; /*!< @brief channel uid */ + int m_iPriority; /*!< @brief priority of the timer */ + int m_iLifetime; /*!< @brief lifetime of the timer in days */ + bool m_bIsRepeating; /*!< @brief repeating timer if true, use the m_FirstDay and repeat flags */ + int m_iWeekdays; /*!< @brief bit based store of weekdays to repeat */ + CStdString m_strFileNameAndPath; /*!< @brief filename is only for reference */ + int m_iChannelNumber; /*!< @brief integer value of the channel number */ + bool m_bIsRadio; /*!< @brief is radio channel if set */ + + CPVRChannelPtr m_channel; + unsigned int m_iMarginStart; /*!< @brief (optional) if set, the backend starts the recording iMarginStart minutes before startTime. */ + unsigned int m_iMarginEnd; /*!< @brief (optional) if set, the backend ends the recording iMarginEnd minutes after endTime. */ + std::vector<std::string> m_genre; /*!< @brief genre of the timer */ + int m_iGenreType; /*!< @brief genre type of the timer */ + int m_iGenreSubType; /*!< @brief genre subtype of the timer */ + + private: + CCriticalSection m_critSection; + EPG::CEpgInfoTagPtr m_epgTag; + CDateTime m_StartTime; /*!< start time */ + CDateTime m_StopTime; /*!< stop time */ + CDateTime m_FirstDay; /*!< if it is a repeating timer the first date it starts */ + }; +} diff --git a/xbmc/pvr/timers/PVRTimers.cpp b/xbmc/pvr/timers/PVRTimers.cpp new file mode 100644 index 0000000000..b976f8edfc --- /dev/null +++ b/xbmc/pvr/timers/PVRTimers.cpp @@ -0,0 +1,736 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "Application.h" +#include "FileItem.h" +#include "settings/GUISettings.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "dialogs/GUIDialogOK.h" +#include "threads/SingleLock.h" +#include "utils/log.h" +#include "utils/URIUtils.h" +#include "utils/StringUtils.h" +#include "URL.h" + +#include "PVRTimers.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "epg/EpgContainer.h" +#include "pvr/addons/PVRClients.h" + +using namespace std; +using namespace PVR; +using namespace EPG; + +CPVRTimers::CPVRTimers(void) +{ + m_bIsUpdating = false; +} + +CPVRTimers::~CPVRTimers(void) +{ + Unload(); +} + +bool CPVRTimers::Load(void) +{ + // unload previous timers + Unload(); + + // (re)register observer + g_EpgContainer.RegisterObserver(this); + + // update from clients + return Update(); +} + +void CPVRTimers::Unload() +{ + // unregister observer + g_EpgContainer.UnregisterObserver(this); + + // remove all tags + CSingleLock lock(m_critSection); + m_tags.clear(); +} + +bool CPVRTimers::Update(void) +{ + { + CSingleLock lock(m_critSection); + if (m_bIsUpdating) + return false; + m_bIsUpdating = true; + } + + CLog::Log(LOGDEBUG, "CPVRTimers - %s - updating timers", __FUNCTION__); + CPVRTimers newTimerList; + g_PVRClients->GetTimers(&newTimerList); + return UpdateEntries(newTimerList); +} + +bool CPVRTimers::IsRecording(void) const +{ + CSingleLock lock(m_critSection); + + for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++) + if ((*timerIt)->IsRecording()) + return true; + + return false; +} + +bool CPVRTimers::UpdateEntries(const CPVRTimers &timers) +{ + bool bChanged(false); + bool bAddedOrDeleted(false); + vector<CStdString> timerNotifications; + + CSingleLock lock(m_critSection); + + /* go through the timer list and check for updated or new timers */ + for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = timers.m_tags.begin(); it != timers.m_tags.end(); it++) + { + for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++) + { + /* check if this timer is present in this container */ + CPVRTimerInfoTagPtr existingTimer = GetByClient((*timerIt)->m_iClientId, (*timerIt)->m_iClientIndex); + if (existingTimer) + { + /* if it's present, update the current tag */ + bool bStateChanged(existingTimer->m_state != (*timerIt)->m_state); + if (existingTimer->UpdateEntry(*(*timerIt))) + { + bChanged = true; + UpdateEpgEvent(existingTimer); + + if (bStateChanged && g_PVRManager.IsStarted()) + { + CStdString strMessage; + existingTimer->GetNotificationText(strMessage); + timerNotifications.push_back(strMessage); + } + + CLog::Log(LOGDEBUG,"PVRTimers - %s - updated timer %d on client %d", + __FUNCTION__, (*timerIt)->m_iClientIndex, (*timerIt)->m_iClientId); + } + } + else + { + /* new timer */ + CPVRTimerInfoTagPtr newTimer = CPVRTimerInfoTagPtr(new CPVRTimerInfoTag); + newTimer->UpdateEntry(*(*timerIt)); + UpdateEpgEvent(newTimer); + + vector<CPVRTimerInfoTagPtr>* addEntry = NULL; + map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::iterator itr = m_tags.find(newTimer->StartAsUTC()); + if (itr == m_tags.end()) + { + addEntry = new vector<CPVRTimerInfoTagPtr>; + m_tags.insert(make_pair(newTimer->StartAsUTC(), addEntry)); + } + else + { + addEntry = itr->second; + } + + addEntry->push_back(newTimer); + UpdateEpgEvent(newTimer); + bChanged = true; + bAddedOrDeleted = true; + + if (g_PVRManager.IsStarted()) + { + CStdString strMessage; + newTimer->GetNotificationText(strMessage); + timerNotifications.push_back(strMessage); + } + + CLog::Log(LOGDEBUG,"PVRTimers - %s - added timer %d on client %d", + __FUNCTION__, (*timerIt)->m_iClientIndex, (*timerIt)->m_iClientId); + } + } + } + + /* to collect timer with changed starting time */ + vector<CPVRTimerInfoTagPtr> timersToMove; + + /* check for deleted timers */ + for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::iterator it = m_tags.begin(); it != m_tags.end();) + { + for (int iTimerPtr = it->second->size() - 1; iTimerPtr >= 0; iTimerPtr--) + { + CPVRTimerInfoTagPtr timer = it->second->at(iTimerPtr); + if (!timers.GetByClient(timer->m_iClientId, timer->m_iClientIndex)) + { + /* timer was not found */ + CLog::Log(LOGDEBUG,"PVRTimers - %s - deleted timer %d on client %d", + __FUNCTION__, timer->m_iClientIndex, timer->m_iClientId); + + if (g_PVRManager.IsStarted()) + { + CStdString strMessage; + strMessage.Format("%s: '%s'", + (timer->EndAsUTC() <= CDateTime::GetCurrentDateTime().GetAsUTCDateTime()) ? + g_localizeStrings.Get(19227) : + g_localizeStrings.Get(19228), + timer->m_strTitle.c_str()); + timerNotifications.push_back(strMessage); + } + + it->second->erase(it->second->begin() + iTimerPtr); + + bChanged = true; + bAddedOrDeleted = true; + } + else if (timer->StartAsUTC() != it->first) + { + /* timer start has changed */ + CLog::Log(LOGDEBUG,"PVRTimers - %s - changed start time timer %d on client %d", + __FUNCTION__, timer->m_iClientIndex, timer->m_iClientId); + + timer->ClearEpgTag(); + + /* remember timer */ + timersToMove.push_back(timer); + + /* remove timer for now, reinsert later */ + it->second->erase(it->second->begin() + iTimerPtr); + + bChanged = true; + bAddedOrDeleted = true; + } + } + if (it->second->size() == 0) + m_tags.erase(it++); + else + ++it; + } + + /* reinsert timers with changed timer start */ + for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = timersToMove.begin(); timerIt != timersToMove.end(); timerIt++) + { + vector<CPVRTimerInfoTagPtr>* addEntry = NULL; + map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator itr = m_tags.find((*timerIt)->StartAsUTC()); + if (itr == m_tags.end()) + { + addEntry = new vector<CPVRTimerInfoTagPtr>; + m_tags.insert(make_pair((*timerIt)->StartAsUTC(), addEntry)); + } + else + { + addEntry = itr->second; + } + + addEntry->push_back(*timerIt); + UpdateEpgEvent(*timerIt); + } + + m_bIsUpdating = false; + if (bChanged) + { + UpdateChannels(); + SetChanged(); + lock.Leave(); + + NotifyObservers(bAddedOrDeleted ? ObservableMessageTimersReset : ObservableMessageTimers, false); + + if (g_guiSettings.GetBool("pvrrecord.timernotifications")) + { + /* queue notifications */ + for (unsigned int iNotificationPtr = 0; iNotificationPtr < timerNotifications.size(); iNotificationPtr++) + { + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, + g_localizeStrings.Get(19166), + timerNotifications.at(iNotificationPtr)); + } + } + } + + return bChanged; +} + +bool CPVRTimers::UpdateFromClient(const CPVRTimerInfoTag &timer) +{ + CSingleLock lock(m_critSection); + CPVRTimerInfoTagPtr tag = GetByClient(timer.m_iClientId, timer.m_iClientIndex); + if (!tag) + { + tag = CPVRTimerInfoTagPtr(new CPVRTimerInfoTag()); + vector<CPVRTimerInfoTagPtr>* addEntry = NULL; + map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::iterator itr = m_tags.find(timer.StartAsUTC()); + if (itr == m_tags.end()) + { + addEntry = new vector<CPVRTimerInfoTagPtr>; + m_tags.insert(make_pair(timer.StartAsUTC(), addEntry)); + } + else + { + addEntry = itr->second; + } + addEntry->push_back(tag); + } + + UpdateEpgEvent(tag); + + return tag->UpdateEntry(timer); +} + +/********** getters **********/ + +CFileItemPtr CPVRTimers::GetNextActiveTimer(void) const +{ + CSingleLock lock(m_critSection); + for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + { + for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++) + { + CPVRTimerInfoTagPtr current = *timerIt; + if (current->IsActive() && !current->IsRecording()) + { + CFileItemPtr fileItem(new CFileItem(*current)); + return fileItem; + } + } + } + + CFileItemPtr fileItem; + return fileItem; +} + +vector<CFileItemPtr> CPVRTimers::GetActiveTimers(void) const +{ + vector<CFileItemPtr> tags; + CSingleLock lock(m_critSection); + + for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + { + for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++) + { + CPVRTimerInfoTagPtr current = *timerIt; + if (current->IsActive()) + { + CFileItemPtr fileItem(new CFileItem(*current)); + tags.push_back(fileItem); + } + } + } + + return tags; +} + +int CPVRTimers::AmountActiveTimers(void) const +{ + int iReturn(0); + CSingleLock lock(m_critSection); + + for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++) + if ((*timerIt)->IsActive()) + ++iReturn; + + return iReturn; +} + +std::vector<CFileItemPtr> CPVRTimers::GetActiveRecordings(void) const +{ + std::vector<CFileItemPtr> tags; + CSingleLock lock(m_critSection); + + for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + { + for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++) + { + CPVRTimerInfoTagPtr current = *timerIt; + if (current->IsRecording()) + { + CFileItemPtr fileItem(new CFileItem(*current)); + tags.push_back(fileItem); + } + } + } + + return tags; +} + +int CPVRTimers::AmountActiveRecordings(void) const +{ + int iReturn(0); + CSingleLock lock(m_critSection); + + for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++) + if ((*timerIt)->IsRecording()) + ++iReturn; + + return iReturn; +} + +bool CPVRTimers::HasActiveTimers(void) const +{ + CSingleLock lock(m_critSection); + for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++) + if ((*timerIt)->IsActive()) + return true; + + return false; +} + +bool CPVRTimers::GetDirectory(const CStdString& strPath, CFileItemList &items) const +{ + CStdString base(strPath); + URIUtils::RemoveSlashAtEnd(base); + + CURL url(strPath); + CStdString fileName = url.GetFileName(); + URIUtils::RemoveSlashAtEnd(fileName); + + if (fileName == "timers") + { + CFileItemPtr item; + + item.reset(new CFileItem(base + "/add.timer", false)); + item->SetLabel(g_localizeStrings.Get(19026)); + item->SetLabelPreformated(true); + items.Add(item); + + CSingleLock lock(m_critSection); + for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + { + for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++) + { + CPVRTimerInfoTagPtr current = *timerIt; + item.reset(new CFileItem(*current)); + items.Add(item); + } + } + + return true; + } + return false; +} + +/********** channel methods **********/ + +bool CPVRTimers::DeleteTimersOnChannel(const CPVRChannel &channel, bool bDeleteRepeating /* = true */, bool bCurrentlyActiveOnly /* = false */) +{ + bool bReturn = false; + CSingleLock lock(m_critSection); + + for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::reverse_iterator it = m_tags.rbegin(); it != m_tags.rend(); it++) + { + for (vector<CPVRTimerInfoTagPtr>::iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++) + { + CPVRTimerInfoTagPtr timer = (*timerIt); + + if (bCurrentlyActiveOnly && + (CDateTime::GetCurrentDateTime() < timer->StartAsLocalTime() || + CDateTime::GetCurrentDateTime() > timer->EndAsLocalTime())) + continue; + + if (!bDeleteRepeating && timer->m_bIsRepeating) + continue; + + if (timer->ChannelNumber() == channel.ChannelNumber() && timer->m_bIsRadio == channel.IsRadio()) + { + bReturn = timer->DeleteFromClient(true) || bReturn; + it->second->erase(timerIt); + } + } + } + + return bReturn; +} + +bool CPVRTimers::InstantTimer(const CPVRChannel &channel) +{ + if (!g_PVRManager.CheckParentalLock(channel)) + return false; + + CEpgInfoTag epgTag; + bool bHasEpgNow = channel.GetEPGNow(epgTag); + CPVRTimerInfoTag *newTimer = bHasEpgNow ? CPVRTimerInfoTag::CreateFromEpg(epgTag) : NULL; + if (!newTimer) + { + newTimer = new CPVRTimerInfoTag; + /* set the timer data */ + newTimer->m_iClientIndex = -1; + newTimer->m_strTitle = channel.ChannelName(); + newTimer->m_strSummary = g_localizeStrings.Get(19056); + newTimer->m_iChannelNumber = channel.ChannelNumber(); + newTimer->m_iClientChannelUid = channel.UniqueID(); + newTimer->m_iClientId = channel.ClientID(); + newTimer->m_bIsRadio = channel.IsRadio(); + + /* generate summary string */ + newTimer->m_strSummary.Format("%s %s %s %s %s", + newTimer->StartAsLocalTime().GetAsLocalizedDate(), + g_localizeStrings.Get(19159), + newTimer->StartAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false), + g_localizeStrings.Get(19160), + newTimer->EndAsLocalTime().GetAsLocalizedTime(StringUtils::EmptyString, false)); + } + + CDateTime startTime(0); + newTimer->SetStartFromUTC(startTime); + newTimer->m_iMarginStart = 0; /* set the start margin to 0 for instant timers */ + + int iDuration = g_guiSettings.GetInt("pvrrecord.instantrecordtime"); + CDateTime endTime = CDateTime::GetUTCDateTime() + CDateTimeSpan(0, 0, iDuration ? iDuration : 120, 0); + newTimer->SetEndFromUTC(endTime); + + /* unused only for reference */ + newTimer->m_strFileNameAndPath = "pvr://timers/new"; + + bool bReturn = newTimer->AddToClient(); + if (!bReturn) + CLog::Log(LOGERROR, "PVRTimers - %s - unable to add an instant timer on the client", __FUNCTION__); + + delete newTimer; + + return bReturn; +} + +/********** static methods **********/ + +bool CPVRTimers::AddTimer(const CPVRTimerInfoTag &item) +{ + if (!item.m_channel) + return false; + + if (!g_PVRClients->SupportsTimers(item.m_iClientId)) + { + CGUIDialogOK::ShowAndGetInput(19033,0,19215,0); + return false; + } + + if (!g_PVRManager.CheckParentalLock(*item.m_channel)) + return false; + + return item.AddToClient(); +} + +bool CPVRTimers::DeleteTimer(const CFileItem &item, bool bForce /* = false */) +{ + /* Check if a CPVRTimerInfoTag is inside file item */ + if (!item.IsPVRTimer()) + { + CLog::Log(LOGERROR, "PVRTimers - %s - no TimerInfoTag given", __FUNCTION__); + return false; + } + + const CPVRTimerInfoTag *tag = item.GetPVRTimerInfoTag(); + if (!tag) + return false; + + return tag->DeleteFromClient(bForce); +} + +bool CPVRTimers::RenameTimer(CFileItem &item, const CStdString &strNewName) +{ + /* Check if a CPVRTimerInfoTag is inside file item */ + if (!item.IsPVRTimer()) + { + CLog::Log(LOGERROR, "PVRTimers - %s - no TimerInfoTag given", __FUNCTION__); + return false; + } + + CPVRTimerInfoTag *tag = item.GetPVRTimerInfoTag(); + if (!tag) + return false; + + return tag->RenameOnClient(strNewName); +} + +bool CPVRTimers::UpdateTimer(CFileItem &item) +{ + /* Check if a CPVRTimerInfoTag is inside file item */ + if (!item.IsPVRTimer()) + { + CLog::Log(LOGERROR, "PVRTimers - %s - no TimerInfoTag given", __FUNCTION__); + return false; + } + + CPVRTimerInfoTag *tag = item.GetPVRTimerInfoTag(); + if (!tag) + return false; + + return tag->UpdateOnClient(); +} + +CPVRTimerInfoTagPtr CPVRTimers::GetByClient(int iClientId, int iClientTimerId) const +{ + CSingleLock lock(m_critSection); + + for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + { + for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++) + { + if ((*timerIt)->m_iClientId == iClientId && + (*timerIt)->m_iClientIndex == iClientTimerId) + return *timerIt; + } + } + + CPVRTimerInfoTagPtr empty; + return empty; +} + +bool CPVRTimers::IsRecordingOnChannel(const CPVRChannel &channel) const +{ + CSingleLock lock(m_critSection); + + for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + { + for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++) + { + if ((*timerIt)->IsRecording() && + (*timerIt)->m_iClientChannelUid == channel.UniqueID() && + (*timerIt)->m_iClientId == channel.ClientID()) + return true; + } + } + + return false; +} + +CFileItemPtr CPVRTimers::GetTimerForEpgTag(const CFileItem *item) const +{ + if (item && item->HasEPGInfoTag() && item->GetEPGInfoTag()->ChannelTag()) + { + const CEpgInfoTag *epgTag = item->GetEPGInfoTag(); + const CPVRChannelPtr channel = epgTag->ChannelTag(); + CSingleLock lock(m_critSection); + + for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + { + for (vector<CPVRTimerInfoTagPtr>::const_iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++) + { + CPVRTimerInfoTagPtr timer = *timerIt; + if (timer->m_iClientChannelUid == channel->UniqueID() && + timer->m_bIsRadio == channel->IsRadio() && + timer->StartAsUTC() <= epgTag->StartAsUTC() && + timer->EndAsUTC() >= epgTag->EndAsUTC()) + { + CFileItemPtr fileItem(new CFileItem(*timer)); + return fileItem; + } + } + } + } + + CFileItemPtr fileItem; + return fileItem; +} + +void CPVRTimers::Notify(const Observable &obs, const ObservableMessage msg) +{ + g_PVRManager.TriggerTimersUpdate(); +} + +CDateTime CPVRTimers::GetNextEventTime(void) const +{ + const bool dailywakup = g_guiSettings.GetBool("pvrpowermanagement.dailywakeup"); + const CDateTime now = CDateTime::GetUTCDateTime(); + const CDateTimeSpan prewakeup(0, 0, g_guiSettings.GetInt("pvrpowermanagement.prewakeup"), 0); + const CDateTimeSpan idle(0, 0, g_guiSettings.GetInt("pvrpowermanagement.backendidletime"), 0); + + CDateTime wakeuptime; + + /* Check next active time */ + CFileItemPtr item = GetNextActiveTimer(); + if (item && item->HasPVRTimerInfoTag()) + { + const CDateTime start = item->GetPVRTimerInfoTag()->StartAsUTC(); + wakeuptime = ((start - idle) > now) ? + start - prewakeup: + now + idle; + } + + /* check daily wake up */ + if (dailywakup) + { + CDateTime dailywakeuptime; + dailywakeuptime.SetFromDBTime(g_guiSettings.GetString("pvrpowermanagement.dailywakeuptime", false)); + dailywakeuptime = dailywakeuptime.GetAsUTCDateTime(); + + dailywakeuptime.SetDateTime( + now.GetYear(), now.GetMonth(), now.GetDay(), + dailywakeuptime.GetHour(), dailywakeuptime.GetMinute(), dailywakeuptime.GetSecond() + ); + + if ((dailywakeuptime - idle) < now) + { + const CDateTimeSpan oneDay(1,0,0,0); + dailywakeuptime += oneDay; + } + if (dailywakeuptime < wakeuptime) + wakeuptime = dailywakeuptime; + } + + const CDateTime retVal(wakeuptime); + return retVal; +} + +void CPVRTimers::UpdateEpgEvent(CPVRTimerInfoTagPtr timer) +{ + CSingleLock lock(timer->m_critSection); + + /* already got an epg event set */ + if (timer->m_epgTag) + return; + + /* try to get the channel */ + CPVRChannelPtr channel = g_PVRChannelGroups->GetByUniqueID(timer->m_iClientChannelUid, timer->m_iClientId); + if (!channel) + return; + + /* try to get the EPG table */ + CEpg *epg = channel->GetEPG(); + if (!epg) + return; + + /* try to set the timer on the epg tag that matches with a 2 minute margin */ + CEpgInfoTagPtr epgTag = epg->GetTagBetween(timer->StartAsUTC() - CDateTimeSpan(0, 0, 2, 0), timer->EndAsUTC() + CDateTimeSpan(0, 0, 2, 0)); + if (!epgTag) + epgTag = epg->GetTagAround(timer->StartAsUTC()); + + if (epgTag) + { + timer->m_epgTag = epgTag; + timer->m_genre = epgTag->Genre(); + timer->m_iGenreType = epgTag->GenreType(); + timer->m_iGenreSubType = epgTag->GenreSubType(); + epgTag->SetTimer(timer); + } +} + +void CPVRTimers::UpdateChannels(void) +{ + CSingleLock lock(m_critSection); + for (map<CDateTime, vector<CPVRTimerInfoTagPtr>* >::iterator it = m_tags.begin(); it != m_tags.end(); it++) + { + for (vector<CPVRTimerInfoTagPtr>::iterator timerIt = it->second->begin(); timerIt != it->second->end(); timerIt++) + (*timerIt)->UpdateChannel(); + } +} diff --git a/xbmc/pvr/timers/PVRTimers.h b/xbmc/pvr/timers/PVRTimers.h new file mode 100644 index 0000000000..7d12b64e53 --- /dev/null +++ b/xbmc/pvr/timers/PVRTimers.h @@ -0,0 +1,180 @@ +#pragma once +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "PVRTimerInfoTag.h" +#include "XBDateTime.h" +#include "addons/include/xbmc_pvr_types.h" +#include "utils/Observer.h" + +class CFileItem; +namespace EPG +{ + class CEpgInfoTag; +} + +namespace PVR +{ + class CGUIDialogPVRTimerSettings; + + class CPVRTimers : public Observer, + public Observable + { + public: + CPVRTimers(void); + virtual ~CPVRTimers(void); + + /** + * (re)load the timers from the clients. + * True when loaded, false otherwise. + */ + bool Load(void); + + /** + * @brief refresh the channel list from the clients. + */ + bool Update(void); + + /** + * Add a timer entry to this container, called by the client callback. + */ + bool UpdateFromClient(const CPVRTimerInfoTag &timer); + + /*! + * @return The timer that will be active next (state scheduled), or an empty fileitemptr if none. + */ + CFileItemPtr GetNextActiveTimer(void) const; + + /*! + * @return All timers that are active (states scheduled or recording) + */ + std::vector<CFileItemPtr> GetActiveTimers(void) const; + + /*! + * @return True when there is at least one timer that is active (states scheduled or recording), false otherwise. + */ + bool HasActiveTimers(void) const; + + /*! + * @return The amount of timers that are active (states scheduled or recording) + */ + int AmountActiveTimers(void) const; + + /*! + * @return All timers that are recording + */ + std::vector<CFileItemPtr> GetActiveRecordings(void) const; + + /*! + * @return True when recording, false otherwise. + */ + bool IsRecording(void) const; + + /*! + * @brief Check if a recording is running on the given channel. + * @param channel The channel to check. + * @return True when recording, false otherwise. + */ + bool IsRecordingOnChannel(const CPVRChannel &channel) const; + + /*! + * @return The amount of timers that are currently recording + */ + int AmountActiveRecordings(void) const; + + /*! + * @brief Get all timers for the given path. + * @param strPath The vfs path to get the timers for. + * @param items The results. + * @return True when the path was valid, false otherwise. + */ + bool GetDirectory(const CStdString& strPath, CFileItemList &items) const; + + /*! + * @brief Delete all timers on a channel. + * @param channel The channel to delete the timers for. + * @param bDeleteRepeating True to delete repeating events too, false otherwise. + * @param bCurrentlyActiveOnly True to delete timers that are currently running only. + * @return True if timers any were deleted, false otherwise. + */ + bool DeleteTimersOnChannel(const CPVRChannel &channel, bool bDeleteRepeating = true, bool bCurrentlyActiveOnly = false); + + /*! + * @brief Create a new instant timer on a channel. + * @param channel The channel to create the timer on. + * @return True if the timer was created, false otherwise. + */ + bool InstantTimer(const CPVRChannel &channel); + + /*! + * @return Next event time (timer or daily wake up) + */ + CDateTime GetNextEventTime(void) const; + + /*! + * @brief Add a timer to the client. Doesn't add the timer to the container. The backend will do this. + * @return True if it was sent correctly, false if not. + */ + static bool AddTimer(const CPVRTimerInfoTag &item); + + /*! + * @brief Delete a timer on the client. Doesn't delete the timer from the container. The backend will do this. + * @return True if it was sent correctly, false if not. + */ + static bool DeleteTimer(const CFileItem &item, bool bForce = false); + + /*! + * @brief Rename a timer on the client. Doesn't update the timer in the container. The backend will do this. + * @return True if it was sent correctly, false if not. + */ + static bool RenameTimer(CFileItem &item, const CStdString &strNewName); + + /** + * @brief Update the timer on the client. Doesn't update the timer in the container. The backend will do this. + * @return True if it was sent correctly, false if not. + */ + static bool UpdateTimer(CFileItem &item); + + /*! + * @brief Get the timer tag that matches the given epg tag. + * @param item The epg tag. + * @return The requested timer tag, or an empty fileitemptr if none was found. + */ + CFileItemPtr GetTimerForEpgTag(const CFileItem *item) const; + + /*! + * @brief Update the channel pointers. + */ + void UpdateChannels(void); + + void Notify(const Observable &obs, const ObservableMessage msg); + + private: + void Unload(void); + void UpdateEpgEvent(CPVRTimerInfoTagPtr timer); + bool UpdateEntries(const CPVRTimers &timers); + CPVRTimerInfoTagPtr GetByClient(int iClientId, int iClientTimerId) const; + + CCriticalSection m_critSection; + bool m_bIsUpdating; + std::map<CDateTime, std::vector<CPVRTimerInfoTagPtr>* > m_tags; + }; +} diff --git a/xbmc/pvr/windows/GUIViewStatePVR.cpp b/xbmc/pvr/windows/GUIViewStatePVR.cpp new file mode 100644 index 0000000000..886de45917 --- /dev/null +++ b/xbmc/pvr/windows/GUIViewStatePVR.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIViewStatePVR.h" +#include "GUIWindowPVR.h" +#include "GUIWindowPVRCommon.h" +#include "guilib/GUIWindowManager.h" +#include "settings/GUISettings.h" +#include "settings/Settings.h" + +using namespace PVR; + +CGUIViewStatePVR::CGUIViewStatePVR(const CFileItemList& items) : + CGUIViewState(items) +{ + PVRWindow ActiveView = GetActiveView(); + if (ActiveView == PVR_WINDOW_RECORDINGS) + { + if (g_guiSettings.GetBool("filelists.ignorethewhensorting")) + AddSortMethod(SORT_METHOD_LABEL_IGNORE_THE, 551, LABEL_MASKS("%L", "%I", "%L", "")); // FileName, Size | Foldername, e + else + AddSortMethod(SORT_METHOD_LABEL, 551, LABEL_MASKS("%L", "%I", "%L", "")); // FileName, Size | Foldername, empty + AddSortMethod(SORT_METHOD_SIZE, 553, LABEL_MASKS("%L", "%I", "%L", "%I")); // FileName, Size | Foldername, Size + AddSortMethod(SORT_METHOD_DATE, 552, LABEL_MASKS("%L", "%J", "%L", "%J")); // FileName, Date | Foldername, Date + AddSortMethod(SORT_METHOD_FILE, 561, LABEL_MASKS("%L", "%I", "%L", "")); // Filename, Size | FolderName, empty + } + + LoadViewState(items.GetPath(), ActiveView == PVR_WINDOW_UNKNOWN ? WINDOW_PVR : WINDOW_PVR + 100 - ActiveView ); +} + +PVRWindow CGUIViewStatePVR::GetActiveView() +{ + PVRWindow returnWindow = PVR_WINDOW_UNKNOWN; + + int iActiveWindow = g_windowManager.GetActiveWindow(); + if (iActiveWindow == WINDOW_PVR) + { + CGUIWindowPVR *pWindow = (CGUIWindowPVR *) g_windowManager.GetWindow(WINDOW_PVR); + CGUIWindowPVRCommon *pActiveView = NULL; + if (pWindow && (pActiveView = pWindow->GetActiveView()) != NULL) + returnWindow = pActiveView->GetWindowId(); + } + + return returnWindow; +} + +void CGUIViewStatePVR::SaveViewState(void) +{ + PVRWindow ActiveView = GetActiveView(); + SaveViewToDb(m_items.GetPath(), ActiveView == PVR_WINDOW_UNKNOWN ? WINDOW_PVR : WINDOW_PVR + 100 - ActiveView, NULL); +} diff --git a/xbmc/pvr/windows/GUIViewStatePVR.h b/xbmc/pvr/windows/GUIViewStatePVR.h new file mode 100644 index 0000000000..a846186f08 --- /dev/null +++ b/xbmc/pvr/windows/GUIViewStatePVR.h @@ -0,0 +1,40 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIViewState.h" +#include "GUIWindowPVRCommon.h" + +namespace PVR +{ + class CGUIViewStatePVR : public CGUIViewState + { + public: + CGUIViewStatePVR(const CFileItemList& items); + virtual ~CGUIViewStatePVR(void) {} + PVRWindow GetActiveView(void); + protected: + bool AutoPlayNextItem(void) { return false; }; + bool HideParentDirItems(void) { return true; } + void SaveViewState(void); + }; +} diff --git a/xbmc/pvr/windows/GUIWindowPVR.cpp b/xbmc/pvr/windows/GUIWindowPVR.cpp new file mode 100644 index 0000000000..c97061ca60 --- /dev/null +++ b/xbmc/pvr/windows/GUIWindowPVR.cpp @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWindowPVR.h" + +#include "GUIWindowPVRChannels.h" +#include "GUIWindowPVRGuide.h" +#include "GUIWindowPVRRecordings.h" +#include "GUIWindowPVRSearch.h" +#include "GUIWindowPVRTimers.h" + +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClients.h" +#include "guilib/GUIMessage.h" +#include "guilib/GUIWindowManager.h" +#include "dialogs/GUIDialogBusy.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "threads/SingleLock.h" + +using namespace PVR; + +CGUIWindowPVR::CGUIWindowPVR(void) : + CGUIMediaWindow(WINDOW_PVR, "MyPVR.xml"), + m_guideGrid(NULL), + m_currentSubwindow(NULL), + m_savedSubwindow(NULL), + m_windowChannelsTV(NULL), + m_windowChannelsRadio(NULL), + m_windowGuide(NULL), + m_windowRecordings(NULL), + m_windowSearch(NULL), + m_windowTimers(NULL) +{ +} + +CGUIWindowPVR::~CGUIWindowPVR(void) +{ + Cleanup(); +} + +CGUIWindowPVRCommon *CGUIWindowPVR::GetActiveView(void) const +{ + CSingleLock lock(m_critSection); + return m_currentSubwindow; +} + +void CGUIWindowPVR::SetActiveView(CGUIWindowPVRCommon *window) +{ + CSingleLock lock(m_critSection); + m_currentSubwindow = window; +} + +void CGUIWindowPVR::GetContextButtons(int itemNumber, CContextButtons &buttons) +{ + CGUIWindowPVRCommon *view = GetActiveView(); + if (view) + view->GetContextButtons(itemNumber, buttons); + + CGUIMediaWindow::GetContextButtons(itemNumber, buttons); +} + +CGUIWindowPVRCommon *CGUIWindowPVR::GetSavedView(void) const +{ + CSingleLock lock(m_critSection); + return m_savedSubwindow; +} + +bool CGUIWindowPVR::OnAction(const CAction &action) +{ + CGUIWindowPVRCommon *view = GetActiveView(); + return (view && view->OnAction(action)) || + CGUIMediaWindow::OnAction(action); +} + +bool CGUIWindowPVR::OnContextButton(int itemNumber, CONTEXT_BUTTON button) +{ + CGUIWindowPVRCommon *view = GetActiveView(); + return (view && view->OnContextButton(itemNumber, button)) || + CGUIMediaWindow::OnContextButton(itemNumber, button); +} + +void CGUIWindowPVR::OnInitWindow(void) +{ + if (!g_PVRManager.IsStarted() || !g_PVRClients->HasConnectedClients()) + { + g_windowManager.PreviousWindow(); + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, + g_localizeStrings.Get(19045), + g_localizeStrings.Get(19044)); + return; + } + + CreateViews(); + + CSingleLock graphicsLock(g_graphicsContext); + SET_CONTROL_VISIBLE(CONTROL_LIST_TIMELINE); + + CSingleLock lock(m_critSection); + if (m_savedSubwindow) + m_savedSubwindow->OnInitWindow(); + lock.Leave(); + graphicsLock.Leave(); + + CGUIMediaWindow::OnInitWindow(); +} + +bool CGUIWindowPVR::OnMessage(CGUIMessage& message) +{ + return (OnMessageFocus(message) ||OnMessageClick(message) || + CGUIMediaWindow::OnMessage(message)); +} + +void CGUIWindowPVR::OnWindowLoaded(void) +{ + CreateViews(); + + CGUIMediaWindow::OnWindowLoaded(); + m_viewControl.Reset(); + m_viewControl.SetParentWindow(GetID()); + + m_viewControl.AddView(GetControl(CONTROL_LIST_CHANNELS_TV)); + m_viewControl.AddView(GetControl(CONTROL_LIST_CHANNELS_RADIO)); + m_viewControl.AddView(GetControl(CONTROL_LIST_RECORDINGS)); + m_viewControl.AddView(GetControl(CONTROL_LIST_TIMERS)); + m_viewControl.AddView(GetControl(CONTROL_LIST_GUIDE_CHANNEL)); + m_viewControl.AddView(GetControl(CONTROL_LIST_GUIDE_NOW_NEXT)); + m_viewControl.AddView(GetControl(CONTROL_LIST_TIMELINE)); + m_viewControl.AddView(GetControl(CONTROL_LIST_SEARCH)); +} + +void CGUIWindowPVR::OnWindowUnload(void) +{ + CGUIWindowPVRCommon *view = GetActiveView(); + if (view) + { + view->OnWindowUnload(); + m_savedSubwindow = view; + } + else + { + m_savedSubwindow = NULL; + } + + m_currentSubwindow = NULL; + + m_viewControl.Reset(); + CGUIMediaWindow::OnWindowUnload(); +} + +void CGUIWindowPVR::SetLabel(int iControl, const CStdString &strLabel) +{ + SET_CONTROL_LABEL(iControl, strLabel); +} + +void CGUIWindowPVR::SetLabel(int iControl, int iLabel) +{ + SET_CONTROL_LABEL(iControl, iLabel); +} + +void CGUIWindowPVR::UpdateButtons(void) +{ + m_windowGuide->UpdateButtons(); +} + +bool CGUIWindowPVR::OnMessageFocus(CGUIMessage &message) +{ + bool bReturn = false; + + if (message.GetMessage() == GUI_MSG_FOCUSED) + { + m_windowChannelsRadio->OnMessageFocus(message) || + m_windowChannelsTV->OnMessageFocus(message) || + m_windowGuide->OnMessageFocus(message) || + m_windowRecordings->OnMessageFocus(message) || + m_windowSearch->OnMessageFocus(message) || + m_windowTimers->OnMessageFocus(message); + + m_savedSubwindow = NULL; + } + + return bReturn; +} + +bool CGUIWindowPVR::OnMessageClick(CGUIMessage &message) +{ + bool bReturn = false; + + if (message.GetMessage() == GUI_MSG_CLICKED) + { + bReturn = m_windowChannelsRadio->OnClickButton(message) || + m_windowChannelsTV->OnClickButton(message) || + m_windowGuide->OnClickButton(message) || + m_windowRecordings->OnClickButton(message) || + m_windowSearch->OnClickButton(message) || + m_windowTimers->OnClickButton(message) || + + m_windowChannelsRadio->OnClickList(message) || + m_windowChannelsTV->OnClickList(message) || + m_windowGuide->OnClickList(message) || + m_windowRecordings->OnClickList(message) || + m_windowSearch->OnClickList(message) || + m_windowTimers->OnClickList(message); + } + + return bReturn; +} + +void CGUIWindowPVR::CreateViews(void) +{ + CSingleLock lock(m_critSection); + if (!m_windowChannelsRadio) + m_windowChannelsRadio = new CGUIWindowPVRChannels(this, true); + + if (!m_windowChannelsTV) + m_windowChannelsTV = new CGUIWindowPVRChannels(this, false); + + if (!m_windowGuide) + m_windowGuide = new CGUIWindowPVRGuide(this); + + if (!m_windowRecordings) + m_windowRecordings = new CGUIWindowPVRRecordings(this); + + if (!m_windowSearch) + m_windowSearch = new CGUIWindowPVRSearch(this); + + if (!m_windowTimers) + m_windowTimers = new CGUIWindowPVRTimers(this); +} + +void CGUIWindowPVR::Reset(void) +{ + CSingleLock graphicsLock(g_graphicsContext); + CSingleLock lock(m_critSection); + + Cleanup(); + CreateViews(); + + m_windowChannelsRadio->ResetObservers(); + m_windowChannelsTV->ResetObservers(); + m_windowGuide->ResetObservers(); + m_windowRecordings->ResetObservers(); + m_windowTimers->ResetObservers(); +} + +void CGUIWindowPVR::Cleanup(void) +{ + if (m_windowChannelsRadio) + m_windowChannelsRadio->UnregisterObservers(); + SAFE_DELETE(m_windowChannelsRadio); + + if (m_windowChannelsTV) + m_windowChannelsTV->UnregisterObservers(); + SAFE_DELETE(m_windowChannelsTV); + + if (m_windowGuide) + m_windowGuide->UnregisterObservers(); + SAFE_DELETE(m_windowGuide); + + if (m_windowRecordings) + m_windowRecordings->UnregisterObservers(); + SAFE_DELETE(m_windowRecordings); + + SAFE_DELETE(m_windowSearch); + + if (m_windowTimers) + m_windowTimers->UnregisterObservers(); + SAFE_DELETE(m_windowTimers); + + m_currentSubwindow = NULL; + m_savedSubwindow = NULL; + + ClearFileItems(); + FreeResources(); +} diff --git a/xbmc/pvr/windows/GUIWindowPVR.h b/xbmc/pvr/windows/GUIWindowPVR.h new file mode 100644 index 0000000000..ff36d1cfa1 --- /dev/null +++ b/xbmc/pvr/windows/GUIWindowPVR.h @@ -0,0 +1,88 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWindowPVRCommon.h" +#include "epg/GUIEPGGridContainer.h" +#include "threads/CriticalSection.h" + +namespace PVR +{ + class CGUIWindowPVRCommon; + class CGUIWindowPVRChannels; + class CGUIWindowPVRGuide; + class CGUIWindowPVRRecordings; + class CGUIWindowPVRSearch; + class CGUIWindowPVRTimers; + + class CGUIWindowPVR : public CGUIMediaWindow + { + friend class CGUIWindowPVRCommon; + friend class CGUIWindowPVRChannels; + friend class CGUIWindowPVRGuide; + friend class CGUIWindowPVRRecordings; + friend class CGUIWindowPVRSearch; + friend class CGUIWindowPVRTimers; + + public: + CGUIWindowPVR(void); + virtual ~CGUIWindowPVR(void); + + virtual CGUIWindowPVRCommon *GetActiveView(void) const; + virtual void SetActiveView(CGUIWindowPVRCommon *window); + virtual void GetContextButtons(int itemNumber, CContextButtons &buttons); + virtual CGUIWindowPVRCommon *GetSavedView(void) const; + virtual bool OnAction(const CAction &action); + virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button); + virtual void OnInitWindow(void); + virtual bool OnMessage(CGUIMessage& message); + virtual void OnWindowLoaded(void); + virtual void OnWindowUnload(void); + virtual void Reset(void); + virtual void Cleanup(void); + + EPG::CGUIEPGGridContainer *m_guideGrid; + + protected: + virtual void SetLabel(int iControl, const CStdString &strLabel); + virtual void SetLabel(int iControl, int iLabel); + virtual void UpdateButtons(void); + + private: + virtual bool OnMessageFocus(CGUIMessage &message); + virtual bool OnMessageClick(CGUIMessage &message); + + virtual void CreateViews(void); + + CGUIWindowPVRCommon * m_currentSubwindow; + CGUIWindowPVRCommon * m_savedSubwindow; + + CGUIWindowPVRChannels * m_windowChannelsTV; + CGUIWindowPVRChannels * m_windowChannelsRadio; + CGUIWindowPVRGuide * m_windowGuide; + CGUIWindowPVRRecordings *m_windowRecordings; + CGUIWindowPVRSearch * m_windowSearch; + CGUIWindowPVRTimers * m_windowTimers; + + CCriticalSection m_critSection; + }; +} diff --git a/xbmc/pvr/windows/GUIWindowPVRChannels.cpp b/xbmc/pvr/windows/GUIWindowPVRChannels.cpp new file mode 100644 index 0000000000..10e3955e1f --- /dev/null +++ b/xbmc/pvr/windows/GUIWindowPVRChannels.cpp @@ -0,0 +1,613 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWindowPVRChannels.h" + +#include "dialogs/GUIDialogFileBrowser.h" +#include "dialogs/GUIDialogNumeric.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogYesNo.h" +#include "guilib/GUIKeyboardFactory.h" +#include "guilib/GUIWindowManager.h" +#include "GUIInfoManager.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/dialogs/GUIDialogPVRGroupManager.h" +#include "pvr/windows/GUIWindowPVR.h" +#include "pvr/addons/PVRClients.h" +#include "pvr/timers/PVRTimers.h" +#include "epg/EpgContainer.h" +#include "settings/GUISettings.h" +#include "settings/Settings.h" +#include "storage/MediaManager.h" +#include "utils/log.h" +#include "threads/SingleLock.h" + +using namespace PVR; +using namespace EPG; + +CGUIWindowPVRChannels::CGUIWindowPVRChannels(CGUIWindowPVR *parent, bool bRadio) : + CGUIWindowPVRCommon(parent, + bRadio ? PVR_WINDOW_CHANNELS_RADIO : PVR_WINDOW_CHANNELS_TV, + bRadio ? CONTROL_BTNCHANNELS_RADIO : CONTROL_BTNCHANNELS_TV, + bRadio ? CONTROL_LIST_CHANNELS_RADIO: CONTROL_LIST_CHANNELS_TV), + CThread("PVR Channel Window") +{ + m_bRadio = bRadio; + m_bShowHiddenChannels = false; + m_bThreadCreated = false; +} + +CGUIWindowPVRChannels::~CGUIWindowPVRChannels(void) +{ + if (m_bThreadCreated) + StopThread(true); +} + +void CGUIWindowPVRChannels::ResetObservers(void) +{ + CSingleLock lock(m_critSection); + g_EpgContainer.RegisterObserver(this); + g_PVRTimers->RegisterObserver(this); + g_infoManager.RegisterObserver(this); +} + +void CGUIWindowPVRChannels::UnregisterObservers(void) +{ + CSingleLock lock(m_critSection); + g_EpgContainer.UnregisterObserver(this); + if (g_PVRTimers) + g_PVRTimers->UnregisterObserver(this); + g_infoManager.UnregisterObserver(this); +} + +void CGUIWindowPVRChannels::GetContextButtons(int itemNumber, CContextButtons &buttons) const +{ + if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size()) + return; + CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber); + CPVRChannel *channel = pItem->GetPVRChannelInfoTag(); + + if (pItem->GetPath() == "pvr://channels/.add.channel") + { + /* If yes show only "New Channel" on context menu */ + buttons.Add(CONTEXT_BUTTON_ADD, 19046); /* add new channel */ + } + else + { + buttons.Add(CONTEXT_BUTTON_INFO, 19047); /* channel info */ + buttons.Add(CONTEXT_BUTTON_FIND, 19003); /* find similar program */ + buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 19000); /* switch to channel */ + buttons.Add(CONTEXT_BUTTON_RECORD_ITEM, channel->IsRecording() ? 19256 : 19255); /* start/stop recording on channel */ + buttons.Add(CONTEXT_BUTTON_SET_THUMB, 20019); /* change icon */ + buttons.Add(CONTEXT_BUTTON_GROUP_MANAGER, 19048); /* group manager */ + buttons.Add(CONTEXT_BUTTON_HIDE, m_bShowHiddenChannels ? 19049 : 19054); /* show/hide channel */ + + if (m_parent->m_vecItems->Size() > 1 && !m_bShowHiddenChannels) + buttons.Add(CONTEXT_BUTTON_MOVE, 116); /* move channel up or down */ + + if (m_bShowHiddenChannels || g_PVRChannelGroups->GetGroupAllTV()->GetNumHiddenChannels() > 0) + buttons.Add(CONTEXT_BUTTON_SHOW_HIDDEN, m_bShowHiddenChannels ? 19050 : 19051); /* show hidden/visible channels */ + + if (g_PVRClients->HasMenuHooks(pItem->GetPVRChannelInfoTag()->ClientID())) + buttons.Add(CONTEXT_BUTTON_MENU_HOOKS, 19195); /* PVR client specific action */ + + CPVRChannel *channel = pItem->GetPVRChannelInfoTag(); + buttons.Add(CONTEXT_BUTTON_ADD_LOCK, channel->IsLocked() ? 19258 : 19257); /* show lock/unlock channel */ + + buttons.Add(CONTEXT_BUTTON_FILTER, 19249); /* filter channels */ + buttons.Add(CONTEXT_BUTTON_UPDATE_EPG, 19251); /* update EPG information */ + } +} + +bool CGUIWindowPVRChannels::OnContextButton(int itemNumber, CONTEXT_BUTTON button) +{ + if (itemNumber < 0 || itemNumber >= (int) m_parent->m_vecItems->Size()) + return false; + CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber); + + return OnContextButtonPlay(pItem.get(), button) || + OnContextButtonMove(pItem.get(), button) || + OnContextButtonHide(pItem.get(), button) || + OnContextButtonShowHidden(pItem.get(), button) || + OnContextButtonSetThumb(pItem.get(), button) || + OnContextButtonAdd(pItem.get(), button) || + OnContextButtonInfo(pItem.get(), button) || + OnContextButtonGroupManager(pItem.get(), button) || + OnContextButtonFilter(pItem.get(), button) || + OnContextButtonUpdateEpg(pItem.get(), button) || + OnContextButtonRecord(pItem.get(), button) || + OnContextButtonLock(pItem.get(), button) || + CGUIWindowPVRCommon::OnContextButton(itemNumber, button); +} + +CPVRChannelGroupPtr CGUIWindowPVRChannels::SelectedGroup(void) +{ + if (!m_selectedGroup) + SetSelectedGroup(g_PVRManager.GetPlayingGroup(m_bRadio)); + + return m_selectedGroup; +} + +void CGUIWindowPVRChannels::SetSelectedGroup(CPVRChannelGroupPtr group) +{ + if (!group) + return; + + if (m_selectedGroup) + m_selectedGroup->UnregisterObserver(this); + m_selectedGroup = group; + m_selectedGroup->RegisterObserver(this); + g_PVRManager.SetPlayingGroup(m_selectedGroup); +} + +void CGUIWindowPVRChannels::Notify(const Observable &obs, const ObservableMessage msg) +{ + if (msg == ObservableMessageChannelGroup || msg == ObservableMessageTimers || msg == ObservableMessageEpgActiveItem || msg == ObservableMessageCurrentItem) + { + if (IsVisible()) + SetInvalid(); + else + m_bUpdateRequired = true; + } + else if (msg == ObservableMessageChannelGroupReset) + { + if (IsVisible()) + UpdateData(false); + else + m_bUpdateRequired = true; + } +} + +CPVRChannelGroupPtr CGUIWindowPVRChannels::SelectNextGroup(void) +{ + CPVRChannelGroupPtr currentGroup = SelectedGroup(); + CPVRChannelGroupPtr nextGroup = currentGroup->GetNextGroup(); + while (nextGroup && *nextGroup != *currentGroup && nextGroup->Size() == 0) + nextGroup = nextGroup->GetNextGroup(); + + /* always update so users can reset the list */ + if (nextGroup) + { + SetSelectedGroup(nextGroup); + UpdateData(); + } + + return m_selectedGroup; +} + +void CGUIWindowPVRChannels::UpdateData(bool bUpdateSelectedFile /* = true */) +{ + CSingleLock lock(m_critSection); + CLog::Log(LOGDEBUG, "CGUIWindowPVRChannels - %s - update window '%s'. set view to %d", + __FUNCTION__, GetName(), m_iControlList); + m_bUpdateRequired = false; + + /* lock the graphics context while updating */ + CSingleLock graphicsLock(g_graphicsContext); + + m_iSelected = m_parent->m_viewControl.GetSelectedItem(); + m_parent->m_viewControl.Clear(); + m_parent->m_vecItems->Clear(); + m_parent->m_viewControl.SetCurrentView(m_iControlList); + + CPVRChannelGroupPtr currentGroup = g_PVRManager.GetPlayingGroup(m_bRadio); + if (!currentGroup) + return; + + CStdString strPath; + strPath.Format("pvr://channels/%s/%s/", + m_bRadio ? "radio" : "tv", + m_bShowHiddenChannels ? ".hidden" : currentGroup->GroupName()); + + m_parent->m_vecItems->SetPath(strPath); + m_parent->Update(m_parent->m_vecItems->GetPath()); + m_parent->m_viewControl.SetItems(*m_parent->m_vecItems); + + if (bUpdateSelectedFile) + { + if (!SelectPlayingFile()) + m_parent->m_viewControl.SetSelectedItem(m_iSelected); + } + + /* empty list */ + if (m_parent->m_vecItems->Size() == 0) + { + if (m_bShowHiddenChannels) + { + /* show the visible channels instead */ + m_bShowHiddenChannels = false; + graphicsLock.Leave(); + lock.Leave(); + + UpdateData(bUpdateSelectedFile); + return; + } + else if (currentGroup->GroupID() > 0) + { + if (*currentGroup != *SelectNextGroup()) + return; + } + } + + m_parent->SetLabel(CONTROL_LABELHEADER, g_localizeStrings.Get(m_bRadio ? 19024 : 19023)); + if (m_bShowHiddenChannels) + m_parent->SetLabel(CONTROL_LABELGROUP, g_localizeStrings.Get(19022)); + else + m_parent->SetLabel(CONTROL_LABELGROUP, currentGroup->GroupName()); + + if (!m_bThreadCreated) + { + m_bThreadCreated = true; + Create(); + SetPriority(-1); + } +} + +bool CGUIWindowPVRChannels::OnClickButton(CGUIMessage &message) +{ + bool bReturn = false; + + if (IsSelectedButton(message)) + { + bReturn = true; + SelectNextGroup(); + } + + return bReturn; +} + +bool CGUIWindowPVRChannels::OnClickList(CGUIMessage &message) +{ + bool bReturn = false; + + if (IsSelectedList(message)) + { + bReturn = true; + int iAction = message.GetParam1(); + int iItem = m_parent->m_viewControl.GetSelectedItem(); + + if (iItem < 0 || iItem >= (int) m_parent->m_vecItems->Size()) + return bReturn; + CFileItemPtr pItem = m_parent->m_vecItems->Get(iItem); + + /* process actions */ + if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK || iAction == ACTION_PLAY) + ActionPlayChannel(pItem.get()); + else if (iAction == ACTION_SHOW_INFO) + ShowEPGInfo(pItem.get()); + else if (iAction == ACTION_DELETE_ITEM) + ActionDeleteChannel(pItem.get()); + else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK) + m_parent->OnPopupMenu(iItem); + else + bReturn = false; + } + + return bReturn; +} + +bool CGUIWindowPVRChannels::OnContextButtonAdd(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_ADD) + { + CGUIDialogOK::ShowAndGetInput(19033,0,19038,0); + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRChannels::OnContextButtonGroupManager(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_GROUP_MANAGER) + { + ShowGroupManager(); + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRChannels::OnContextButtonHide(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_HIDE) + { + CPVRChannel *channel = item->GetPVRChannelInfoTag(); + if (!channel || channel->IsRadio() != m_bRadio) + return bReturn; + + CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + if (!pDialog) + return bReturn; + + pDialog->SetHeading(19039); + pDialog->SetLine(0, ""); + pDialog->SetLine(1, channel->ChannelName()); + pDialog->SetLine(2, ""); + pDialog->DoModal(); + + if (!pDialog->IsConfirmed()) + return bReturn; + + g_PVRManager.GetPlayingGroup(m_bRadio)->RemoveFromGroup(*channel); + UpdateData(); + + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRChannels::OnContextButtonLock(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_ADD_LOCK) + { + // ask for PIN first + if (!g_PVRManager.CheckParentalPIN(g_localizeStrings.Get(19262).c_str())) + return bReturn; + + CPVRChannelGroupPtr group = g_PVRChannelGroups->GetGroupAll(m_bRadio); + if (!group) + return bReturn; + + group->ToggleChannelLocked(*item); + UpdateData(); + + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRChannels::OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_INFO) + { + ShowEPGInfo(item); + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRChannels::OnContextButtonMove(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_MOVE) + { + CPVRChannel *channel = item->GetPVRChannelInfoTag(); + if (!channel || channel->IsRadio() != m_bRadio) + return bReturn; + + CStdString strIndex; + strIndex.Format("%i", channel->ChannelNumber()); + CGUIDialogNumeric::ShowAndGetNumber(strIndex, g_localizeStrings.Get(19052)); + int newIndex = atoi(strIndex.c_str()); + + if (newIndex != channel->ChannelNumber()) + { + g_PVRManager.GetPlayingGroup()->MoveChannel(channel->ChannelNumber(), newIndex); + UpdateData(); + } + + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRChannels::OnContextButtonPlay(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_PLAY_ITEM) + { + /* play channel */ + bReturn = PlayFile(item, g_guiSettings.GetBool("pvrplayback.playminimized")); + } + + return bReturn; +} + +bool CGUIWindowPVRChannels::OnContextButtonSetThumb(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_SET_THUMB) + { + if (g_settings.GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked()) + return bReturn; + else if (!g_passwordManager.IsMasterLockUnlocked(true)) + return bReturn; + + /* setup our thumb list */ + CFileItemList items; + CPVRChannel *channel = item->GetPVRChannelInfoTag(); + + if (!channel->IconPath().IsEmpty()) + { + /* add the current thumb, if available */ + CFileItemPtr current(new CFileItem("thumb://Current", false)); + current->SetThumbnailImage(channel->IconPath()); + current->SetLabel(g_localizeStrings.Get(20016)); + items.Add(current); + } + else if (item->HasThumbnail()) + { + /* already have a thumb that the share doesn't know about - must be a local one, so we may as well reuse it */ + CFileItemPtr current(new CFileItem("thumb://Current", false)); + current->SetThumbnailImage(item->GetThumbnailImage()); + current->SetLabel(g_localizeStrings.Get(20016)); + items.Add(current); + } + + /* and add a "no thumb" entry as well */ + CFileItemPtr nothumb(new CFileItem("thumb://None", false)); + nothumb->SetIconImage(item->GetIconImage()); + nothumb->SetLabel(g_localizeStrings.Get(20018)); + items.Add(nothumb); + + CStdString strThumb; + VECSOURCES shares; + if (g_guiSettings.GetString("pvrmenu.iconpath") != "") + { + CMediaSource share1; + share1.strPath = g_guiSettings.GetString("pvrmenu.iconpath"); + share1.strName = g_localizeStrings.Get(19018); + shares.push_back(share1); + } + g_mediaManager.GetLocalDrives(shares); + if (!CGUIDialogFileBrowser::ShowAndGetImage(items, shares, g_localizeStrings.Get(1030), strThumb)) + return bReturn; + + if (strThumb != "thumb://Current") + { + if (strThumb == "thumb://None") + strThumb = ""; + + channel->SetIconPath(strThumb, true); + channel->Persist(); + UpdateData(); + } + + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRChannels::OnContextButtonShowHidden(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_SHOW_HIDDEN) + { + m_bShowHiddenChannels = !m_bShowHiddenChannels; + UpdateData(); + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRChannels::OnContextButtonFilter(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_FILTER) + { + CStdString filter = m_parent->GetProperty("filter").asString(); + CGUIKeyboardFactory::ShowAndGetFilter(filter, false); + m_parent->OnFilterItems(filter); + + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRChannels::OnContextButtonRecord(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn(false); + + if (button == CONTEXT_BUTTON_RECORD_ITEM) + { + CPVRChannel *channel = item->GetPVRChannelInfoTag(); + + if (channel) + return g_PVRManager.ToggleRecordingOnChannel(channel->ChannelID()); + } + + return bReturn; +} + +bool CGUIWindowPVRChannels::OnContextButtonUpdateEpg(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_UPDATE_EPG) + { + CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + if (!pDialog) + return bReturn; + + CPVRChannel *channel = item->GetPVRChannelInfoTag(); + pDialog->SetHeading(19251); + pDialog->SetLine(0, g_localizeStrings.Get(19252)); + pDialog->SetLine(1, channel->ChannelName()); + pDialog->SetLine(2, ""); + pDialog->DoModal(); + + if (!pDialog->IsConfirmed()) + return bReturn; + + bReturn = UpdateEpgForChannel(item); + + CStdString strMessage; + strMessage.Format("%s: '%s'", g_localizeStrings.Get(bReturn ? 19253 : 19254), channel->ChannelName()); + CGUIDialogKaiToast::QueueNotification(bReturn ? CGUIDialogKaiToast::Info : CGUIDialogKaiToast::Error, + g_localizeStrings.Get(19166), + strMessage); + } + + return bReturn; +} + +void CGUIWindowPVRChannels::ShowGroupManager(void) +{ + /* Load group manager dialog */ + CGUIDialogPVRGroupManager* pDlgInfo = (CGUIDialogPVRGroupManager*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GROUP_MANAGER); + if (!pDlgInfo) + return; + + pDlgInfo->SetRadio(m_bRadio); + pDlgInfo->DoModal(); + + return; +} + +void CGUIWindowPVRChannels::Process(void) +{ + // ugly hack to refresh the progress bars and item contents every 5 seconds + int iCount(0); + while (!m_bStop) + { + if (++iCount == 100) + { + iCount = 0; + SetInvalid(); + } + Sleep(50); + } +} diff --git a/xbmc/pvr/windows/GUIWindowPVRChannels.h b/xbmc/pvr/windows/GUIWindowPVRChannels.h new file mode 100644 index 0000000000..7c1854c9d2 --- /dev/null +++ b/xbmc/pvr/windows/GUIWindowPVRChannels.h @@ -0,0 +1,76 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWindowPVRCommon.h" +#include "utils/Observer.h" +#include "threads/Thread.h" +#include "../channels/PVRChannelGroup.h" + +namespace PVR +{ + class CGUIWindowPVR; + + class CGUIWindowPVRChannels : public CGUIWindowPVRCommon, private Observer, private CThread + { + friend class CGUIWindowPVR; + + public: + CGUIWindowPVRChannels(CGUIWindowPVR *parent, bool bRadio); + virtual ~CGUIWindowPVRChannels(void); + + void GetContextButtons(int itemNumber, CContextButtons &buttons) const; + bool OnContextButton(int itemNumber, CONTEXT_BUTTON button); + CPVRChannelGroupPtr SelectedGroup(void); + void SetSelectedGroup(CPVRChannelGroupPtr group); + CPVRChannelGroupPtr SelectNextGroup(void); + void UpdateData(bool bUpdateSelectedFile = true); + void Notify(const Observable &obs, const ObservableMessage msg); + void ResetObservers(void); + void UnregisterObservers(void); + + private: + void Process(void); + bool OnClickButton(CGUIMessage &message); + bool OnClickList(CGUIMessage &message); + + bool OnContextButtonAdd(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonGroupManager(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonHide(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonMove(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonPlay(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonSetThumb(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonShowHidden(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonFilter(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonUpdateEpg(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonRecord(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonLock(CFileItem *item, CONTEXT_BUTTON button); + + void ShowGroupManager(void); + + CPVRChannelGroupPtr m_selectedGroup; + bool m_bShowHiddenChannels; + bool m_bRadio; + bool m_bThreadCreated; + }; +} diff --git a/xbmc/pvr/windows/GUIWindowPVRCommon.cpp b/xbmc/pvr/windows/GUIWindowPVRCommon.cpp new file mode 100644 index 0000000000..026f66a78a --- /dev/null +++ b/xbmc/pvr/windows/GUIWindowPVRCommon.cpp @@ -0,0 +1,862 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWindowPVRCommon.h" + +#include "Application.h" +#include "ApplicationMessenger.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogYesNo.h" +#include "filesystem/StackDirectory.h" +#include "guilib/GUIMessage.h" +#include "guilib/GUIWindowManager.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/dialogs/GUIDialogPVRGuideInfo.h" +#include "pvr/dialogs/GUIDialogPVRRecordingInfo.h" +#include "pvr/dialogs/GUIDialogPVRTimerSettings.h" +#include "epg/Epg.h" +#include "pvr/timers/PVRTimers.h" +#include "pvr/addons/PVRClients.h" +#include "pvr/windows/GUIWindowPVR.h" +#include "pvr/windows/GUIWindowPVRSearch.h" +#include "pvr/recordings/PVRRecordings.h" +#include "settings/GUISettings.h" +#include "settings/Settings.h" +#include "utils/log.h" +#include "utils/URIUtils.h" +#include "GUIUserMessages.h" + +using namespace std; +using namespace PVR; +using namespace EPG; + +CGUIWindowPVRCommon::CGUIWindowPVRCommon(CGUIWindowPVR *parent, PVRWindow window, + unsigned int iControlButton, unsigned int iControlList) +{ + m_parent = parent; + m_window = window; + m_iControlButton = iControlButton; + m_iControlList = iControlList; + m_bUpdateRequired = false; + m_iSelected = 0; + m_iSortOrder = SortOrderAscending; + m_iSortMethod = SORT_METHOD_DATE; + if( m_parent->GetViewState() ) + { + m_iSortOrder = m_parent->GetViewState()->GetSortOrder(); + m_iSortMethod = m_parent->GetViewState()->GetSortMethod(); + } +} + +bool CGUIWindowPVRCommon::operator ==(const CGUIWindowPVRCommon &right) const +{ + return (this == &right || m_window == right.m_window); +} + +bool CGUIWindowPVRCommon::operator !=(const CGUIWindowPVRCommon &right) const +{ + return !(*this == right); +} + +const char *CGUIWindowPVRCommon::GetName(void) const +{ + switch(m_window) + { + case PVR_WINDOW_EPG: + return "epg"; + case PVR_WINDOW_CHANNELS_RADIO: + return "radio"; + case PVR_WINDOW_CHANNELS_TV: + return "tv"; + case PVR_WINDOW_RECORDINGS: + return "recordings"; + case PVR_WINDOW_SEARCH: + return "search"; + case PVR_WINDOW_TIMERS: + return "timers"; + default: + return "unknown"; + } +} + +bool CGUIWindowPVRCommon::IsFocused(void) const +{ + return !g_application.IsPlayingFullScreenVideo() && + g_windowManager.GetFocusedWindow() == WINDOW_PVR && + IsActive(); +} + +bool CGUIWindowPVRCommon::IsVisible(void) const +{ + return !g_application.IsPlayingFullScreenVideo() && + g_windowManager.GetActiveWindow() == WINDOW_PVR && + IsActive(); +} + +bool CGUIWindowPVRCommon::IsActive(void) const +{ + CGUIWindowPVRCommon *window = m_parent->GetActiveView(); + return (window && *window == *this); +} + +bool CGUIWindowPVRCommon::IsSavedView(void) const +{ + CGUIWindowPVRCommon *window = m_parent->GetSavedView(); + return (window && *window == *this); +} + +bool CGUIWindowPVRCommon::IsSelectedButton(CGUIMessage &message) const +{ + return (message.GetSenderId() == (int) m_iControlButton); +} + +bool CGUIWindowPVRCommon::IsSelectedControl(CGUIMessage &message) const +{ + return (message.GetControlId() == (int) m_iControlButton); +} + +bool CGUIWindowPVRCommon::IsSelectedList(CGUIMessage &message) const +{ + return (message.GetSenderId() == (int) m_iControlList); +} + +void CGUIWindowPVRCommon::SetInvalid() +{ + for (int iItemPtr = 0; iItemPtr < m_parent->m_vecItems->Size(); iItemPtr++) + m_parent->m_vecItems->Get(iItemPtr)->SetInvalid(); + m_parent->SetInvalid(); +} + +void CGUIWindowPVRCommon::OnInitWindow() +{ + m_parent->m_viewControl.SetCurrentView(m_iControlList); +} + +bool CGUIWindowPVRCommon::SelectPlayingFile(void) +{ + bool bReturn(false); + + if (g_PVRManager.IsPlaying()) + { + m_parent->m_viewControl.SetSelectedItem(g_application.CurrentFile()); + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRCommon::OnMessageFocus(CGUIMessage &message) +{ + bool bReturn = false; + + if (message.GetMessage() == GUI_MSG_FOCUSED && + (IsSelectedControl(message) || IsSavedView())) + { + CLog::Log(LOGDEBUG, "CGUIWindowPVRCommon - %s - focus set to window '%s'", __FUNCTION__, GetName()); + bool bIsActive = IsActive(); + m_parent->SetActiveView(this); + + if (!bIsActive || m_bUpdateRequired) + UpdateData(); + else + m_iSelected = m_parent->m_viewControl.GetSelectedItem(); + + bReturn = true; + } + + return bReturn; +} + +void CGUIWindowPVRCommon::OnWindowUnload(void) +{ + m_iSelected = m_parent->m_viewControl.GetSelectedItem(); +} + +bool CGUIWindowPVRCommon::OnAction(const CAction &action) +{ + bool bReturn = false; + + if (action.GetID() == ACTION_NAV_BACK || + action.GetID() == ACTION_PARENT_DIR) + { + g_windowManager.PreviousWindow(); + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRCommon::OnContextButton(int itemNumber, CONTEXT_BUTTON button) +{ + if (itemNumber < 0 || itemNumber >= (int) m_parent->m_vecItems->Size()) + return false; + CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber); + + return (OnContextButtonSortAsc(pItem.get(), button) || + OnContextButtonSortBy(pItem.get(), button) || + OnContextButtonSortByChannel(pItem.get(), button) || + OnContextButtonSortByName(pItem.get(), button) || + OnContextButtonSortByDate(pItem.get(), button) || + OnContextButtonFind(pItem.get(), button) || + OnContextButtonMenuHooks(pItem.get(), button)); +} + +bool CGUIWindowPVRCommon::OnContextButtonSortByDate(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_SORTBY_DATE) + { + bReturn = true; + + if (m_iSortMethod != SORT_METHOD_DATE) + { + m_iSortMethod = SORT_METHOD_DATE; + m_iSortOrder = SortOrderAscending; + CGUIMessage message(GUI_MSG_CHANGE_SORT_METHOD, m_parent->GetID(), 0, m_iSortMethod, 0); + m_parent->OnMessage(message); + } + else + { + m_iSortOrder = m_iSortOrder == SortOrderAscending ? SortOrderDescending : SortOrderAscending; + } + CGUIMessage message(GUI_MSG_CHANGE_SORT_DIRECTION, m_parent->GetID(), 0, m_iSortOrder, 0); + m_parent->OnMessage(message); + UpdateData(); + } + + return bReturn; +} + +bool CGUIWindowPVRCommon::OnContextButtonSortByName(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_SORTBY_NAME) + { + bReturn = true; + + if (m_iSortMethod != SORT_METHOD_LABEL) + { + m_iSortMethod = SORT_METHOD_LABEL; + m_iSortOrder = SortOrderAscending; + CGUIMessage message(GUI_MSG_CHANGE_SORT_METHOD, m_parent->GetID(), 0, m_iSortMethod, 0); + m_parent->OnMessage(message); + } + else + { + m_iSortOrder = m_iSortOrder == SortOrderAscending ? SortOrderDescending : SortOrderAscending; + } + CGUIMessage message(GUI_MSG_CHANGE_SORT_DIRECTION, m_parent->GetID(), 0, m_iSortOrder, 0); + m_parent->OnMessage(message); + UpdateData(); + } + + return bReturn; +} + +bool CGUIWindowPVRCommon::OnContextButtonSortByChannel(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_SORTBY_CHANNEL) + { + bReturn = true; + + if (m_iSortMethod != SORT_METHOD_CHANNEL) + { + m_iSortMethod = SORT_METHOD_CHANNEL; + m_iSortOrder = SortOrderAscending; + } + else + { + m_iSortOrder = m_iSortOrder == SortOrderAscending ? SortOrderDescending : SortOrderAscending; + } + + UpdateData(); + } + + return bReturn; +} + +bool CGUIWindowPVRCommon::OnContextButtonSortAsc(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_SORTASC) + { + bReturn = true; + + if (m_parent->m_guiState.get()) + m_parent->m_guiState->SetNextSortOrder(); + m_parent->UpdateFileList(); + } + + return bReturn; +} + +bool CGUIWindowPVRCommon::OnContextButtonSortBy(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_SORTBY) + { + bReturn = true; + + if (m_parent->m_guiState.get()) + m_parent->m_guiState->SetNextSortMethod(); + + m_parent->UpdateFileList(); + } + + return bReturn; +} + +bool CGUIWindowPVRCommon::OnContextButtonMenuHooks(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_MENU_HOOKS) + { + bReturn = true; + + if (item->IsEPG() && item->GetEPGInfoTag()->HasPVRChannel()) + g_PVRClients->ProcessMenuHooks(item->GetEPGInfoTag()->ChannelTag()->ClientID()); + else if (item->IsPVRChannel()) + g_PVRClients->ProcessMenuHooks(item->GetPVRChannelInfoTag()->ClientID()); + else if (item->IsPVRRecording()) + g_PVRClients->ProcessMenuHooks(item->GetPVRRecordingInfoTag()->m_iClientId); + else if (item->IsPVRTimer()) + g_PVRClients->ProcessMenuHooks(item->GetPVRTimerInfoTag()->m_iClientId); + } + + return bReturn; +} + +bool CGUIWindowPVRCommon::ActionDeleteTimer(CFileItem *item) +{ + /* check if the timer tag is valid */ + CPVRTimerInfoTag *timerTag = item->GetPVRTimerInfoTag(); + if (!timerTag || timerTag->m_iClientIndex < 0) + return false; + + /* show a confirmation dialog */ + CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + if (!pDialog) + return false; + pDialog->SetHeading(122); + pDialog->SetLine(0, 19040); + pDialog->SetLine(1, ""); + pDialog->SetLine(2, timerTag->m_strTitle); + pDialog->DoModal(); + + /* prompt for the user's confirmation */ + if (!pDialog->IsConfirmed()) + return false; + + /* delete the timer */ + return g_PVRTimers->DeleteTimer(*item); +} + +bool CGUIWindowPVRCommon::ShowNewTimerDialog(void) +{ + bool bReturn(false); + + CPVRTimerInfoTag *newTimer = new CPVRTimerInfoTag; + CFileItem *newItem = new CFileItem(*newTimer); + if (ShowTimerSettings(newItem)) + { + /* Add timer to backend */ + bReturn = g_PVRTimers->AddTimer(*newItem->GetPVRTimerInfoTag()); + } + + delete newItem; + delete newTimer; + + return bReturn; +} + +bool CGUIWindowPVRCommon::ActionShowTimer(CFileItem *item) +{ + bool bReturn = false; + + /* Check if "Add timer..." entry is pressed by OK, if yes + create a new timer and open settings dialog, otherwise + open settings for selected timer entry */ + if (item->GetPath() == "pvr://timers/add.timer") + { + bReturn = ShowNewTimerDialog(); + } + else + { + if (ShowTimerSettings(item)) + { + /* Update timer on pvr backend */ + bReturn = g_PVRTimers->UpdateTimer(*item); + } + } + + return bReturn; +} + +bool CGUIWindowPVRCommon::ActionRecord(CFileItem *item) +{ + bool bReturn = false; + + CEpgInfoTag *epgTag = item->GetEPGInfoTag(); + if (!epgTag) + return bReturn; + + CPVRChannelPtr channel = epgTag->ChannelTag(); + if (!channel || !g_PVRManager.CheckParentalLock(*channel)) + return bReturn; + + if (epgTag->Timer() == NULL) + { + /* create a confirmation dialog */ + CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*) g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + if (!pDialog) + return bReturn; + + pDialog->SetHeading(264); + pDialog->SetLine(0, ""); + pDialog->SetLine(1, epgTag->Title()); + pDialog->SetLine(2, ""); + pDialog->DoModal(); + + /* prompt for the user's confirmation */ + if (!pDialog->IsConfirmed()) + return bReturn; + + CPVRTimerInfoTag *newTimer = CPVRTimerInfoTag::CreateFromEpg(*epgTag); + if (newTimer) + { + bReturn = g_PVRTimers->AddTimer(*newTimer); + delete newTimer; + } + else + { + bReturn = false; + } + } + else + { + CGUIDialogOK::ShowAndGetInput(19033,19034,0,0); + bReturn = true; + } + + return bReturn; +} + + +bool CGUIWindowPVRCommon::ActionDeleteRecording(CFileItem *item) +{ + bool bReturn = false; + + /* check if the recording tag is valid */ + CPVRRecording *recTag = (CPVRRecording *) item->GetPVRRecordingInfoTag(); + if (!recTag || recTag->m_strRecordingId.IsEmpty()) + return bReturn; + + /* show a confirmation dialog */ + CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + if (!pDialog) + return bReturn; + pDialog->SetHeading(122); + pDialog->SetLine(0, 19043); + pDialog->SetLine(1, ""); + pDialog->SetLine(2, recTag->m_strTitle); + pDialog->DoModal(); + + /* prompt for the user's confirmation */ + if (!pDialog->IsConfirmed()) + return bReturn; + + /* delete the recording */ + if (g_PVRRecordings->DeleteRecording(*item)) + { + g_PVRManager.TriggerRecordingsUpdate(); + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRCommon::ActionPlayChannel(CFileItem *item) +{ + bool bReturn = false; + + if (item->GetPath() == "pvr://channels/.add.channel") + { + /* show "add channel" dialog */ + CGUIDialogOK::ShowAndGetInput(19033,0,19038,0); + bReturn = true; + } + else + { + /* open channel */ + bReturn = PlayFile(item, g_guiSettings.GetBool("pvrplayback.playminimized")); + } + + return bReturn; +} + +bool CGUIWindowPVRCommon::ActionPlayEpg(CFileItem *item) +{ + bool bReturn = false; + + CEpgInfoTag *epgTag = item->GetEPGInfoTag(); + if (!epgTag) + return bReturn; + + CPVRChannelPtr channel = epgTag->ChannelTag(); + if (!channel || channel->ChannelNumber() > 0 || + !g_PVRManager.CheckParentalLock(*channel)) + return bReturn; + + bReturn = g_application.PlayFile(CFileItem(*channel)); + + if (!bReturn) + { + /* cannot play file */ + CGUIDialogOK::ShowAndGetInput(19033,0,19035,0); + } + + return bReturn; +} + +bool CGUIWindowPVRCommon::ActionDeleteChannel(CFileItem *item) +{ + CPVRChannel *channel = item->GetPVRChannelInfoTag(); + + /* check if the channel tag is valid */ + if (!channel || channel->ChannelNumber() <= 0) + return false; + + /* show a confirmation dialog */ + CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*) g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + if (pDialog) + return false; + pDialog->SetHeading(19039); + pDialog->SetLine(0, ""); + pDialog->SetLine(1, channel->ChannelName()); + pDialog->SetLine(2, ""); + pDialog->DoModal(); + + /* prompt for the user's confirmation */ + if (!pDialog->IsConfirmed()) + return false; + + g_PVRChannelGroups->GetGroupAll(channel->IsRadio())->RemoveFromGroup(*channel); + UpdateData(); + + return true; +} + +bool CGUIWindowPVRCommon::UpdateEpgForChannel(CFileItem *item) +{ + CPVRChannel *channel = item->GetPVRChannelInfoTag(); + CEpg *epg = channel->GetEPG(); + if (!epg) + return false; + + epg->ForceUpdate(); + return true; +} + +bool CGUIWindowPVRCommon::ShowTimerSettings(CFileItem *item) +{ + /* Check item is TV timer information tag */ + if (!item->IsPVRTimer()) + { + CLog::Log(LOGERROR, "CGUIWindowPVRTimers: Can't open timer settings dialog, no timer info tag!"); + return false; + } + + /* Load timer settings dialog */ + CGUIDialogPVRTimerSettings* pDlgInfo = (CGUIDialogPVRTimerSettings*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_TIMER_SETTING); + + if (!pDlgInfo) + return false; + + /* inform dialog about the file item */ + pDlgInfo->SetTimer(item); + + /* Open dialog window */ + pDlgInfo->DoModal(); + + /* Get modify flag from window and return it to caller */ + return pDlgInfo->GetOK(); +} + + +bool CGUIWindowPVRCommon::PlayRecording(CFileItem *item, bool bPlayMinimized /* = false */) +{ + if (item->GetPath().Left(17) != "pvr://recordings/") + return false; + + CStdString stream = item->GetPVRRecordingInfoTag()->m_strStreamURL; + if (stream == "") + return false; + + /* Isolate the folder from the filename */ + size_t found = stream.find_last_of("/"); + if (found == CStdString::npos) + found = stream.find_last_of("\\"); + + if (found != CStdString::npos) + { + /* Check here for asterisk at the begin of the filename */ + if (stream[found+1] == '*') + { + /* Create a "stack://" url with all files matching the extension */ + CStdString ext = URIUtils::GetExtension(stream); + CStdString dir = stream.substr(0, found).c_str(); + + CFileItemList items; + CDirectory::GetDirectory(dir, items); + items.Sort(SORT_METHOD_FILE, SortOrderAscending); + + vector<int> stack; + for (int i = 0; i < items.Size(); ++i) + { + if (URIUtils::GetExtension(items[i]->GetPath()) == ext) + stack.push_back(i); + } + + if (stack.size() > 0) + { + /* If we have a stack change the path of the item to it */ + CStackDirectory dir; + CStdString stackPath = dir.ConstructStackPath(items, stack); + item->SetPath(stackPath); + } + } + else + { + /* If no asterisk is present play only the given stream URL */ + item->SetPath(stream); + } + } + else + { + CLog::Log(LOGERROR, "PVRManager - %s - can't open recording: no valid filename", __FUNCTION__); + CGUIDialogOK::ShowAndGetInput(19033,0,19036,0); + return false; + } + + CApplicationMessenger::Get().PlayFile(*item, false); + + return true; +} + +bool CGUIWindowPVRCommon::PlayFile(CFileItem *item, bool bPlayMinimized /* = false */) +{ + if (item->GetPath() == g_application.CurrentFile()) + { + CGUIMessage msg(GUI_MSG_FULLSCREEN, 0, m_parent->GetID()); + g_windowManager.SendMessage(msg); + return true; + } + + g_settings.m_bStartVideoWindowed = bPlayMinimized; + + if (item->GetPath().Left(17) == "pvr://recordings/") + { + return PlayRecording(item, bPlayMinimized); + } + else + { + bool bSwitchSuccessful(false); + + CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL; + + if (g_PVRManager.CheckParentalLock(*channel)) + { + /* try a fast switch */ + if (channel && (g_PVRManager.IsPlayingTV() || g_PVRManager.IsPlayingRadio()) && + (channel->IsRadio() == g_PVRManager.IsPlayingRadio()) && g_application.m_pPlayer) + { + if (channel->StreamURL().IsEmpty()) + bSwitchSuccessful = g_application.m_pPlayer->SwitchChannel(*channel); + } + + if (!bSwitchSuccessful) + { + CApplicationMessenger::Get().PlayFile(*item, false); + return true; + } + } + + if (!bSwitchSuccessful) + { + CGUIDialogOK::ShowAndGetInput(19033,0,19035,0); + return false; + } + } + + return true; +} + +bool CGUIWindowPVRCommon::StartRecordFile(CFileItem *item) +{ + if (!item->HasEPGInfoTag()) + return false; + + CEpgInfoTag *tag = item->GetEPGInfoTag(); + CPVRChannelPtr channel; + if (tag) + channel = tag->ChannelTag(); + + if (!channel || !g_PVRManager.CheckParentalLock(*channel)) + return false; + + CFileItemPtr timer = g_PVRTimers->GetTimerForEpgTag(item); + if (timer && timer->HasPVRTimerInfoTag()) + { + CGUIDialogOK::ShowAndGetInput(19033,19034,0,0); + return false; + } + + CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + if (!pDialog) + return false; + pDialog->SetHeading(264); + pDialog->SetLine(0, tag->PVRChannelName()); + pDialog->SetLine(1, ""); + pDialog->SetLine(2, tag->Title()); + pDialog->DoModal(); + + if (!pDialog->IsConfirmed()) + return false; + + CPVRTimerInfoTag *newTimer = CPVRTimerInfoTag::CreateFromEpg(*tag); + bool bReturn(false); + if (newTimer) + { + bReturn = g_PVRTimers->AddTimer(*newTimer); + delete newTimer; + } + return bReturn; +} + +bool CGUIWindowPVRCommon::StopRecordFile(CFileItem *item) +{ + if (!item->HasEPGInfoTag()) + return false; + + CEpgInfoTag *tag = item->GetEPGInfoTag(); + if (!tag || !tag->HasPVRChannel()) + return false; + + CFileItemPtr timer = g_PVRTimers->GetTimerForEpgTag(item); + if (!timer || !timer->HasPVRTimerInfoTag() || timer->GetPVRTimerInfoTag()->m_bIsRepeating) + return false; + + return g_PVRTimers->DeleteTimer(*timer); +} + +void CGUIWindowPVRCommon::ShowEPGInfo(CFileItem *item) +{ + CFileItem *tag = NULL; + bool bHasChannel(false); + CPVRChannel channel; + if (item->IsEPG()) + { + tag = new CFileItem(*item); + if (item->GetEPGInfoTag()->HasPVRChannel()) + { + channel = *item->GetEPGInfoTag()->ChannelTag(); + bHasChannel = true; + } + } + else if (item->IsPVRChannel()) + { + CEpgInfoTag epgnow; + channel = *item->GetPVRChannelInfoTag(); + bHasChannel = true; + if (!item->GetPVRChannelInfoTag()->GetEPGNow(epgnow)) + { + CGUIDialogOK::ShowAndGetInput(19033,0,19055,0); + return; + } + tag = new CFileItem(epgnow); + } + + if (tag) + { + if (!bHasChannel || g_PVRManager.CheckParentalLock(channel)) + { + CGUIDialogPVRGuideInfo* pDlgInfo = (CGUIDialogPVRGuideInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GUIDE_INFO); + if (pDlgInfo) + { + pDlgInfo->SetProgInfo(tag); + pDlgInfo->DoModal(); + + UpdateData(); + } + } + delete tag; + } +} + +void CGUIWindowPVRCommon::ShowRecordingInfo(CFileItem *item) +{ + if (!item->IsPVRRecording()) + return; + + CGUIDialogPVRRecordingInfo* pDlgInfo = (CGUIDialogPVRRecordingInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_RECORDING_INFO); + if (!pDlgInfo) + return; + + pDlgInfo->SetRecording(item); + pDlgInfo->DoModal(); +} + +bool CGUIWindowPVRCommon::OnContextButtonFind(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_FIND) + { + bReturn = true; + if (m_parent->m_windowSearch) + { + CEpgInfoTag tag; + m_parent->m_windowSearch->m_searchfilter.Reset(); + if (item->IsEPG()) + m_parent->m_windowSearch->m_searchfilter.m_strSearchTerm = "\"" + item->GetEPGInfoTag()->Title() + "\""; + else if (item->IsPVRChannel() && item->GetPVRChannelInfoTag()->GetEPGNow(tag)) + m_parent->m_windowSearch->m_searchfilter.m_strSearchTerm = "\"" + tag.Title() + "\""; + else if (item->IsPVRRecording()) + m_parent->m_windowSearch->m_searchfilter.m_strSearchTerm = "\"" + item->GetPVRRecordingInfoTag()->m_strTitle + "\""; + + m_parent->m_windowSearch->m_bSearchConfirmed = true; + m_parent->SetLabel(m_iControlButton, 0); + m_parent->SetActiveView(m_parent->m_windowSearch); + m_parent->m_windowSearch->UpdateData(); + m_parent->SetLabel(m_iControlList, 0); + } + } + + return bReturn; +} diff --git a/xbmc/pvr/windows/GUIWindowPVRCommon.h b/xbmc/pvr/windows/GUIWindowPVRCommon.h new file mode 100644 index 0000000000..287dd5378c --- /dev/null +++ b/xbmc/pvr/windows/GUIWindowPVRCommon.h @@ -0,0 +1,142 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "FileItem.h" +#include "windows/GUIMediaWindow.h" +#include "GUIWindowPVRCommon.h" +#include "threads/CriticalSection.h" + +namespace PVR +{ + enum PVRWindow + { + PVR_WINDOW_UNKNOWN = 0, + PVR_WINDOW_EPG = 1, + PVR_WINDOW_CHANNELS_TV = 2, + PVR_WINDOW_CHANNELS_RADIO = 3, + PVR_WINDOW_RECORDINGS = 4, + PVR_WINDOW_TIMERS = 5, + PVR_WINDOW_SEARCH = 6 + }; + + #define CONTROL_LIST_TIMELINE 10 + #define CONTROL_LIST_CHANNELS_TV 11 + #define CONTROL_LIST_CHANNELS_RADIO 12 + #define CONTROL_LIST_RECORDINGS 13 + #define CONTROL_LIST_TIMERS 14 + #define CONTROL_LIST_GUIDE_CHANNEL 15 + #define CONTROL_LIST_GUIDE_NOW_NEXT 16 + #define CONTROL_LIST_SEARCH 17 + + #define CONTROL_LABELHEADER 29 + #define CONTROL_LABELGROUP 30 + + #define CONTROL_BTNGUIDE 31 + #define CONTROL_BTNCHANNELS_TV 32 + #define CONTROL_BTNCHANNELS_RADIO 33 + #define CONTROL_BTNRECORDINGS 34 + #define CONTROL_BTNTIMERS 35 + #define CONTROL_BTNSEARCH 36 + #define CONTROL_BTNGUIDE_CHANNEL 37 + #define CONTROL_BTNGUIDE_NOW 38 + #define CONTROL_BTNGUIDE_NEXT 39 + #define CONTROL_BTNGUIDE_TIMELINE 40 + + class CGUIWindowPVR; + + class CGUIWindowPVRCommon + { + friend class CGUIWindowPVR; + + public: + CGUIWindowPVRCommon(CGUIWindowPVR *parent, PVRWindow window, + unsigned int iControlButton, unsigned int iControlList); + virtual ~CGUIWindowPVRCommon(void) {}; + + bool operator ==(const CGUIWindowPVRCommon &right) const; + bool operator !=(const CGUIWindowPVRCommon &right) const; + + virtual const char *GetName(void) const; + virtual PVRWindow GetWindowId(void) const { return m_window; } + virtual bool IsFocused(void) const; + virtual bool IsVisible(void) const; + virtual bool IsActive(void) const; + virtual bool IsSavedView(void) const; + virtual bool IsSelectedButton(CGUIMessage &message) const; + virtual bool IsSelectedControl(CGUIMessage &message) const; + virtual bool IsSelectedList(CGUIMessage &message) const; + + virtual bool OnAction(const CAction &action); + virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button); + + virtual void GetContextButtons(int itemNumber, CContextButtons &buttons) const = 0; + virtual void UpdateData(bool bUpdateSelectedFile = true) = 0; + virtual void SetInvalid(void); + + virtual void OnInitWindow(void); + virtual void OnWindowUnload(void); + + protected: + virtual bool SelectPlayingFile(void); + virtual bool OnMessageFocus(CGUIMessage &message); + + virtual bool OnClickButton(CGUIMessage &message) = 0; + virtual bool OnClickList(CGUIMessage &message) = 0; + + virtual bool ActionDeleteTimer(CFileItem *item); + virtual bool ActionShowTimer(CFileItem *item); + virtual bool ActionRecord(CFileItem *item); + virtual bool ActionDeleteRecording(CFileItem *item); + virtual bool ActionPlayChannel(CFileItem *item); + virtual bool ActionPlayEpg(CFileItem *item); + virtual bool ActionDeleteChannel(CFileItem *item); + + virtual bool PlayRecording(CFileItem *item, bool bPlayMinimized = false); + virtual bool PlayFile(CFileItem *item, bool bPlayMinimized = false); + virtual bool StartRecordFile(CFileItem *item); + virtual bool StopRecordFile(CFileItem *item); + virtual void ShowEPGInfo(CFileItem *item); + virtual void ShowRecordingInfo(CFileItem *item); + virtual bool UpdateEpgForChannel(CFileItem *item); + virtual bool ShowTimerSettings(CFileItem *item); + virtual bool ShowNewTimerDialog(void); + + virtual bool OnContextButtonMenuHooks(CFileItem *item, CONTEXT_BUTTON button); + virtual bool OnContextButtonSortAsc(CFileItem *item, CONTEXT_BUTTON button); + virtual bool OnContextButtonSortBy(CFileItem *item, CONTEXT_BUTTON button); + virtual bool OnContextButtonSortByDate(CFileItem *item, CONTEXT_BUTTON button); + virtual bool OnContextButtonSortByName(CFileItem *item, CONTEXT_BUTTON button); + virtual bool OnContextButtonSortByChannel(CFileItem *item, CONTEXT_BUTTON button); + virtual bool OnContextButtonFind(CFileItem *item, CONTEXT_BUTTON button); + + CGUIWindowPVR * m_parent; + PVRWindow m_window; + unsigned int m_iControlButton; + unsigned int m_iControlList; + bool m_bUpdateRequired; + int m_iSelected; + SortOrder m_iSortOrder; + SORT_METHOD m_iSortMethod; + CCriticalSection m_critSection; + }; +} diff --git a/xbmc/pvr/windows/GUIWindowPVRGuide.cpp b/xbmc/pvr/windows/GUIWindowPVRGuide.cpp new file mode 100644 index 0000000000..5a58526cd6 --- /dev/null +++ b/xbmc/pvr/windows/GUIWindowPVRGuide.cpp @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWindowPVRGuide.h" + +#include "Application.h" +#include "dialogs/GUIDialogOK.h" +#include "guilib/GUIWindowManager.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "epg/EpgContainer.h" +#include "pvr/windows/GUIWindowPVR.h" +#include "settings/AdvancedSettings.h" +#include "settings/GUISettings.h" +#include "settings/Settings.h" +#include "threads/SingleLock.h" +#include "utils/log.h" +#include "pvr/addons/PVRClients.h" +#include "pvr/timers/PVRTimers.h" + +using namespace PVR; +using namespace EPG; + +CGUIWindowPVRGuide::CGUIWindowPVRGuide(CGUIWindowPVR *parent) : + CGUIWindowPVRCommon(parent, PVR_WINDOW_EPG, CONTROL_BTNGUIDE, CONTROL_LIST_GUIDE_NOW_NEXT), + Observer(), + m_iGuideView(g_guiSettings.GetInt("epg.defaultguideview")) +{ + m_cachedTimeline = new CFileItemList; + m_cachedChannelGroup = CPVRChannelGroupPtr(new CPVRChannelGroup); +} + +CGUIWindowPVRGuide::~CGUIWindowPVRGuide(void) +{ + delete m_cachedTimeline; +} + +void CGUIWindowPVRGuide::UnregisterObservers(void) +{ + g_EpgContainer.UnregisterObserver(this); +} + +void CGUIWindowPVRGuide::ResetObservers(void) +{ + g_EpgContainer.RegisterObserver(this); +} + +void CGUIWindowPVRGuide::Notify(const Observable &obs, const ObservableMessage msg) +{ + if (msg == ObservableMessageEpg) + { + m_bUpdateRequired = true; + + /* update the current window if the EPG timeline view is visible */ + if (IsFocused() && m_iGuideView == GUIDE_VIEW_TIMELINE) + UpdateData(false); + } + else if (msg == ObservableMessageEpgActiveItem) + { + if (IsVisible() && m_iGuideView != GUIDE_VIEW_TIMELINE) + SetInvalid(); + else + m_bUpdateRequired = true; + } +} + +void CGUIWindowPVRGuide::GetContextButtons(int itemNumber, CContextButtons &buttons) const +{ + if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size()) + return; + CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber); + + CFileItemPtr timer = g_PVRTimers->GetTimerForEpgTag(pItem.get()); + if (timer && timer->HasPVRTimerInfoTag()) + { + if (timer->GetPVRTimerInfoTag()->IsRecording()) + buttons.Add(CONTEXT_BUTTON_STOP_RECORD, 19059); /* stop recording */ + else + buttons.Add(CONTEXT_BUTTON_STOP_RECORD, 19060); /* delete timer */ + } + else if (pItem->GetEPGInfoTag()->EndAsLocalTime() > CDateTime::GetCurrentDateTime()) + { + if (pItem->GetEPGInfoTag()->StartAsLocalTime() < CDateTime::GetCurrentDateTime()) + buttons.Add(CONTEXT_BUTTON_START_RECORD, 264); /* record */ + else + buttons.Add(CONTEXT_BUTTON_START_RECORD, 19061); /* add timer */ + } + + buttons.Add(CONTEXT_BUTTON_INFO, 19047); /* epg info */ + buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 19000); /* switch channel */ + buttons.Add(CONTEXT_BUTTON_FIND, 19003); /* find similar program */ + if (m_iGuideView == GUIDE_VIEW_TIMELINE) + { + buttons.Add(CONTEXT_BUTTON_BEGIN, 19063); /* go to begin */ + buttons.Add(CONTEXT_BUTTON_END, 19064); /* go to end */ + } + if (pItem->GetEPGInfoTag()->HasPVRChannel() && + g_PVRClients->HasMenuHooks(pItem->GetEPGInfoTag()->ChannelTag()->ClientID())) + buttons.Add(CONTEXT_BUTTON_MENU_HOOKS, 19195); /* PVR client specific action */ +} + + +bool CGUIWindowPVRGuide::OnContextButton(int itemNumber, CONTEXT_BUTTON button) +{ + if (itemNumber < 0 || itemNumber >= (int) m_parent->m_vecItems->Size()) + return false; + CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber); + + return OnContextButtonPlay(pItem.get(), button) || + OnContextButtonInfo(pItem.get(), button) || + OnContextButtonStartRecord(pItem.get(), button) || + OnContextButtonStopRecord(pItem.get(), button) || + OnContextButtonBegin(pItem.get(), button) || + OnContextButtonEnd(pItem.get(), button) || + CGUIWindowPVRCommon::OnContextButton(itemNumber, button); +} + +void CGUIWindowPVRGuide::UpdateViewChannel(bool bUpdateSelectedFile) +{ + CPVRChannelPtr CurrentChannel; + bool bGotCurrentChannel = g_PVRManager.GetCurrentChannel(CurrentChannel); + + m_parent->m_guideGrid = NULL; + m_parent->m_viewControl.SetCurrentView(CONTROL_LIST_GUIDE_CHANNEL); + + m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19029)); + if (bGotCurrentChannel) + m_parent->SetLabel(CONTROL_LABELGROUP, CurrentChannel->ChannelName().c_str()); + + if (!bGotCurrentChannel || g_PVRManager.GetCurrentEpg(*m_parent->m_vecItems) == 0) + { + CFileItemPtr item; + item.reset(new CFileItem("pvr://guide/" + CurrentChannel->ChannelName() + "/empty.epg", false)); + item->SetLabel(g_localizeStrings.Get(19028)); + item->SetLabelPreformated(true); + m_parent->m_vecItems->Add(item); + } + m_parent->m_viewControl.SetItems(*m_parent->m_vecItems); +} + +void CGUIWindowPVRGuide::UpdateViewNow(bool bUpdateSelectedFile) +{ + CPVRChannelPtr CurrentChannel; + bool bGotCurrentChannel = g_PVRManager.GetCurrentChannel(CurrentChannel); + bool bRadio = bGotCurrentChannel ? CurrentChannel->IsRadio() : false; + + m_parent->m_guideGrid = NULL; + m_parent->m_viewControl.SetCurrentView(CONTROL_LIST_GUIDE_NOW_NEXT); + + m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19030)); + m_parent->SetLabel(CONTROL_LABELGROUP, g_localizeStrings.Get(19030)); + + int iEpgItems = g_PVRManager.GetPlayingGroup(bRadio)->GetEPGNow(*m_parent->m_vecItems); + if (iEpgItems == 0 && bRadio) + // if we didn't get any events for radio, get tv instead + iEpgItems = g_PVRManager.GetPlayingGroup(false)->GetEPGNow(*m_parent->m_vecItems); + + if (iEpgItems == 0) + { + CFileItemPtr item; + item.reset(new CFileItem("pvr://guide/now/empty.epg", false)); + item->SetLabel(g_localizeStrings.Get(19028)); + item->SetLabelPreformated(true); + m_parent->m_vecItems->Add(item); + } + m_parent->m_viewControl.SetItems(*m_parent->m_vecItems); +} + +void CGUIWindowPVRGuide::UpdateViewNext(bool bUpdateSelectedFile) +{ + CPVRChannelPtr CurrentChannel; + bool bGotCurrentChannel = g_PVRManager.GetCurrentChannel(CurrentChannel); + bool bRadio = bGotCurrentChannel ? CurrentChannel->IsRadio() : false; + + m_parent->m_guideGrid = NULL; + m_parent->m_viewControl.SetCurrentView(CONTROL_LIST_GUIDE_NOW_NEXT); + + m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19031)); + m_parent->SetLabel(CONTROL_LABELGROUP, g_localizeStrings.Get(19031)); + + int iEpgItems = g_PVRManager.GetPlayingGroup(bRadio)->GetEPGNext(*m_parent->m_vecItems); + if (iEpgItems == 0 && bRadio) + // if we didn't get any events for radio, get tv instead + iEpgItems = g_PVRManager.GetPlayingGroup(false)->GetEPGNext(*m_parent->m_vecItems); + + if (iEpgItems) + { + CFileItemPtr item; + item.reset(new CFileItem("pvr://guide/next/empty.epg", false)); + item->SetLabel(g_localizeStrings.Get(19028)); + item->SetLabelPreformated(true); + m_parent->m_vecItems->Add(item); + } + m_parent->m_viewControl.SetItems(*m_parent->m_vecItems); +} + +void CGUIWindowPVRGuide::UpdateViewTimeline(bool bUpdateSelectedFile) +{ + m_parent->m_guideGrid = (CGUIEPGGridContainer*) m_parent->GetControl(CONTROL_LIST_TIMELINE); + if (!m_parent->m_guideGrid) + return; + + CPVRChannelPtr CurrentChannel; + bool bGotCurrentChannel = g_PVRManager.GetCurrentChannel(CurrentChannel); + bool bRadio = bGotCurrentChannel ? CurrentChannel->IsRadio() : false; + + if (m_bUpdateRequired || m_cachedTimeline->IsEmpty() || + *m_cachedChannelGroup != *g_PVRManager.GetPlayingGroup(bRadio)) + { + m_bUpdateRequired = false; + + m_cachedTimeline->Clear(); + m_cachedChannelGroup = g_PVRManager.GetPlayingGroup(bRadio); + if (m_cachedChannelGroup->GetEPGAll(*m_cachedTimeline) == 0 && bRadio) + { + // if we didn't get any events for radio, get tv instead + m_cachedChannelGroup = g_PVRManager.GetPlayingGroup(false); + m_cachedChannelGroup->GetEPGAll(*m_cachedTimeline); + } + } + + m_parent->m_vecItems->RemoveDiscCache(m_parent->GetID()); + m_parent->m_vecItems->Assign(*m_cachedTimeline, false); + + CDateTime gridStart = CDateTime::GetCurrentDateTime().GetAsUTCDateTime(); + CDateTime firstDate(g_EpgContainer.GetFirstEPGDate()); + CDateTime lastDate(g_EpgContainer.GetLastEPGDate()); + m_parent->m_guideGrid->SetStartEnd(firstDate > gridStart ? firstDate : gridStart, lastDate); + + m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19032)); + m_parent->SetLabel(CONTROL_LABELGROUP, g_localizeStrings.Get(19032)); + m_parent->m_viewControl.SetCurrentView(CONTROL_LIST_TIMELINE, true); + + if (bUpdateSelectedFile) + SelectPlayingFile(); +} + +bool CGUIWindowPVRGuide::SelectPlayingFile(void) +{ + if (m_iGuideView == GUIDE_VIEW_TIMELINE) + { + if (m_parent->m_guideGrid && g_PVRManager.IsPlaying()) + m_parent->m_guideGrid->SetChannel(g_application.CurrentFile()); + return true; + } + return false; +} + +void CGUIWindowPVRGuide::UpdateData(bool bUpdateSelectedFile /* = true */) +{ + CSingleLock lock(m_critSection); + CLog::Log(LOGDEBUG, "CGUIWindowPVRGuide - %s - update window '%s'. set view to %d", __FUNCTION__, GetName(), m_iControlList); + + /* lock the graphics context while updating */ + CSingleLock graphicsLock(g_graphicsContext); + m_parent->m_viewControl.Clear(); + m_parent->m_vecItems->Clear(); + + if (m_iGuideView == GUIDE_VIEW_CHANNEL) + UpdateViewChannel(bUpdateSelectedFile); + else if (m_iGuideView == GUIDE_VIEW_NOW) + UpdateViewNow(bUpdateSelectedFile); + else if (m_iGuideView == GUIDE_VIEW_NEXT) + UpdateViewNext(bUpdateSelectedFile); + else if (m_iGuideView == GUIDE_VIEW_TIMELINE) + UpdateViewTimeline(bUpdateSelectedFile); + + m_bUpdateRequired = false; + m_parent->SetLabel(CONTROL_LABELHEADER, g_localizeStrings.Get(19222)); + UpdateButtons(); +} + +bool CGUIWindowPVRGuide::IsSelectedButton(CGUIMessage &message) const +{ + unsigned int iControl = message.GetSenderId(); + return (iControl == CONTROL_BTNGUIDE || + iControl == CONTROL_BTNGUIDE_CHANNEL || + iControl == CONTROL_BTNGUIDE_NOW || + iControl == CONTROL_BTNGUIDE_NEXT || + iControl == CONTROL_BTNGUIDE_TIMELINE); +} + +bool CGUIWindowPVRGuide::IsSelectedList(CGUIMessage &message) const +{ + return ((message.GetSenderId() == CONTROL_LIST_TIMELINE && m_iGuideView == GUIDE_VIEW_TIMELINE) || + (message.GetSenderId() == CONTROL_LIST_GUIDE_CHANNEL && m_iGuideView == GUIDE_VIEW_CHANNEL) || + (message.GetSenderId() == CONTROL_LIST_GUIDE_NOW_NEXT && (m_iGuideView == GUIDE_VIEW_NOW || m_iGuideView == GUIDE_VIEW_NEXT))); +} + +bool CGUIWindowPVRGuide::OnClickButton(CGUIMessage &message) +{ + bool bReturn = false; + + if (IsSelectedButton(message)) + { + unsigned int iControl = message.GetSenderId(); + bReturn = true; + + if (iControl == CONTROL_BTNGUIDE) + { + if (++m_iGuideView > GUIDE_VIEW_TIMELINE) + m_iGuideView = GUIDE_VIEW_CHANNEL; + } + else if (iControl == CONTROL_BTNGUIDE_CHANNEL) + m_iGuideView = GUIDE_VIEW_CHANNEL; + else if (iControl == CONTROL_BTNGUIDE_NOW) + m_iGuideView = GUIDE_VIEW_NOW; + else if (iControl == CONTROL_BTNGUIDE_NEXT) + m_iGuideView = GUIDE_VIEW_NEXT; + else if (iControl == CONTROL_BTNGUIDE_TIMELINE) + m_iGuideView = GUIDE_VIEW_TIMELINE; + else + bReturn = false; + + if (bReturn) + UpdateData(); + } + + return bReturn; +} + +bool CGUIWindowPVRGuide::OnClickList(CGUIMessage &message) +{ + bool bReturn = false; + + if (IsSelectedList(message)) + { + int iAction = message.GetParam1(); + int iItem = m_parent->m_viewControl.GetSelectedItem(); + + /* get the fileitem pointer */ + if (iItem < 0 || iItem >= (int) m_parent->m_vecItems->Size()) + return bReturn; + CFileItemPtr pItem = m_parent->m_vecItems->Get(iItem); + + /* process actions */ + bReturn = true; + if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK) + { + if (g_advancedSettings.m_bPVRShowEpgInfoOnEpgItemSelect) + ShowEPGInfo(pItem.get()); + else + PlayEpgItem(pItem.get()); + } + else if (iAction == ACTION_SHOW_INFO) + ShowEPGInfo(pItem.get()); + else if (iAction == ACTION_RECORD) + ActionRecord(pItem.get()); + else if (iAction == ACTION_PLAY) + ActionPlayEpg(pItem.get()); + else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK) + m_parent->OnPopupMenu(iItem); + else + bReturn = false; + } + + return bReturn; +} + +bool CGUIWindowPVRGuide::OnContextButtonBegin(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_BEGIN) + { + CGUIWindowPVR *pWindow = (CGUIWindowPVR *) g_windowManager.GetWindow(WINDOW_PVR); + if (pWindow) + pWindow->m_guideGrid->GoToBegin(); + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRGuide::OnContextButtonEnd(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_END) + { + CGUIWindowPVR *pWindow = (CGUIWindowPVR *) g_windowManager.GetWindow(WINDOW_PVR); + if (pWindow) + pWindow->m_guideGrid->GoToEnd(); + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRGuide::OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_INFO) + { + ShowEPGInfo(item); + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRGuide::PlayEpgItem(CFileItem *item) +{ + CPVRChannelPtr channel; + if (item && item->HasEPGInfoTag() && item->GetEPGInfoTag()->HasPVRChannel()) + channel = item->GetEPGInfoTag()->ChannelTag(); + + if (!channel || !g_PVRManager.CheckParentalLock(*channel)) + return false; + + CLog::Log(LOGDEBUG, "play channel '%s'", channel->ChannelName().c_str()); + bool bReturn = g_application.PlayFile(CFileItem(*channel)); + if (!bReturn) + CGUIDialogOK::ShowAndGetInput(19033,0,19035,0); + + return bReturn; +} + +bool CGUIWindowPVRGuide::OnContextButtonPlay(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_PLAY_ITEM) + { + bReturn = PlayEpgItem(item); + } + + return bReturn; +} + +bool CGUIWindowPVRGuide::OnContextButtonStartRecord(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_START_RECORD) + { + StartRecordFile(item); + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRGuide::OnContextButtonStopRecord(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_STOP_RECORD) + { + StopRecordFile(item); + bReturn = true; + } + + return bReturn; +} + +void CGUIWindowPVRGuide::UpdateButtons(void) +{ + if (m_iGuideView == GUIDE_VIEW_CHANNEL) + m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19029)); + else if (m_iGuideView == GUIDE_VIEW_NOW) + m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19030)); + else if (m_iGuideView == GUIDE_VIEW_NEXT) + m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19031)); + else if (m_iGuideView == GUIDE_VIEW_TIMELINE) + m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19032)); +} diff --git a/xbmc/pvr/windows/GUIWindowPVRGuide.h b/xbmc/pvr/windows/GUIWindowPVRGuide.h new file mode 100644 index 0000000000..6f0a1494b8 --- /dev/null +++ b/xbmc/pvr/windows/GUIWindowPVRGuide.h @@ -0,0 +1,75 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWindowPVRCommon.h" +#include "epg/GUIEPGGridContainer.h" +#include "threads/CriticalSection.h" +#include "utils/Observer.h" +#include "../channels/PVRChannelGroup.h" + +namespace PVR +{ + class CGUIWindowPVR; + + class CGUIWindowPVRGuide : public CGUIWindowPVRCommon, public Observer + { + friend class CGUIWindowPVR; + + public: + CGUIWindowPVRGuide(CGUIWindowPVR *parent); + virtual ~CGUIWindowPVRGuide(void); + + void GetContextButtons(int itemNumber, CContextButtons &buttons) const; + bool OnContextButton(int itemNumber, CONTEXT_BUTTON button); + void UpdateData(bool bUpdateSelectedFile = true); + void Notify(const Observable &obs, const ObservableMessage msg); + void SetInvalid(void) { UpdateData(); } + void UnregisterObservers(void); + void ResetObservers(void); + + private: + bool SelectPlayingFile(void); + bool IsSelectedButton(CGUIMessage &message) const; + bool IsSelectedList(CGUIMessage &message) const; + bool OnClickButton(CGUIMessage &message); + bool OnClickList(CGUIMessage &message); + bool PlayEpgItem(CFileItem *item); + + bool OnContextButtonBegin(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonEnd(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonPlay(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonStartRecord(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonStopRecord(CFileItem *item, CONTEXT_BUTTON button); + + void UpdateButtons(void); + void UpdateViewChannel(bool bUpdateSelectedFile); + void UpdateViewNow(bool bUpdateSelectedFile); + void UpdateViewNext(bool bUpdateSelectedFile); + void UpdateViewTimeline(bool bUpdateSelectedFile); + + int m_iGuideView; + CFileItemList *m_cachedTimeline; + CPVRChannelGroupPtr m_cachedChannelGroup; + }; +} diff --git a/xbmc/pvr/windows/GUIWindowPVRRecordings.cpp b/xbmc/pvr/windows/GUIWindowPVRRecordings.cpp new file mode 100644 index 0000000000..88da4944dc --- /dev/null +++ b/xbmc/pvr/windows/GUIWindowPVRRecordings.cpp @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWindowPVRRecordings.h" + +#include "guilib/GUIKeyboardFactory.h" +#include "dialogs/GUIDialogYesNo.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "GUIInfoManager.h" +#include "pvr/PVRManager.h" +#include "pvr/recordings/PVRRecordings.h" +#include "pvr/timers/PVRTimers.h" +#include "pvr/windows/GUIWindowPVR.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "threads/SingleLock.h" +#include "video/VideoDatabase.h" + +using namespace PVR; + +CGUIWindowPVRRecordings::CGUIWindowPVRRecordings(CGUIWindowPVR *parent) : + CGUIWindowPVRCommon(parent, PVR_WINDOW_RECORDINGS, CONTROL_BTNRECORDINGS, CONTROL_LIST_RECORDINGS) +{ + m_strSelectedPath = "pvr://recordings/"; +} + +void CGUIWindowPVRRecordings::UnregisterObservers(void) +{ + CSingleLock lock(m_critSection); + if(g_PVRRecordings) + g_PVRRecordings->UnregisterObserver(this); + if(g_PVRTimers) + g_PVRTimers->UnregisterObserver(this); + g_infoManager.UnregisterObserver(this); +} + +void CGUIWindowPVRRecordings::ResetObservers(void) +{ + CSingleLock lock(m_critSection); + g_PVRRecordings->RegisterObserver(this); + g_PVRTimers->RegisterObserver(this); + g_infoManager.RegisterObserver(this); +} + +CStdString CGUIWindowPVRRecordings::GetResumeString(CFileItem item) +{ + CStdString resumeString; + if (item.IsPVRRecording()) + { + + // First try to find the resume position on the back-end, if that fails use video database + CPVRRecording recording = *item.GetPVRRecordingInfoTag(); + int positionInSeconds = g_PVRManager.GetRecordingLastPlayedPosition(recording); + // If the back-end does report a saved position then make sure there is a corresponding resume bookmark + if (positionInSeconds > 0) + { + CBookmark bookmark; + bookmark.timeInSeconds = positionInSeconds; + CVideoDatabase db; + if (db.Open()) + { + CStdString itemPath(item.GetPVRRecordingInfoTag()->m_strFileNameAndPath); + db.AddBookMarkToFile(itemPath, bookmark, CBookmark::RESUME); + db.Close(); + } + } + else if (positionInSeconds < 0) + { + CVideoDatabase db; + if (db.Open()) + { + CBookmark bookmark; + CStdString itemPath(item.GetPVRRecordingInfoTag()->m_strFileNameAndPath); + if (db.GetResumeBookMark(itemPath, bookmark) ) + positionInSeconds = lrint(bookmark.timeInSeconds); + db.Close(); + } + } + + // Suppress resume from 0 + if (positionInSeconds > 0) + resumeString.Format(g_localizeStrings.Get(12022).c_str(), StringUtils::SecondsToTimeString(positionInSeconds).c_str()); + } + return resumeString; +} + +void CGUIWindowPVRRecordings::GetContextButtons(int itemNumber, CContextButtons &buttons) const +{ + if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size()) + return; + CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber); + + if (pItem->HasPVRRecordingInfoTag()) + { + buttons.Add(CONTEXT_BUTTON_INFO, 19053); /* Get Information of this recording */ + buttons.Add(CONTEXT_BUTTON_FIND, 19003); /* Find similar program */ + buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 12021); /* Play this recording */ + CStdString resumeString = GetResumeString(*pItem); + if (!resumeString.IsEmpty()) + { + buttons.Add(CONTEXT_BUTTON_RESUME_ITEM, resumeString); + } + } + if (pItem->m_bIsFolder) + { + // Have both options for folders since we don't know whether all childs are watched/unwatched + buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); /* Mark as UnWatched */ + buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103); /* Mark as Watched */ + } + if (pItem->HasPVRRecordingInfoTag()) + { + if (pItem->GetPVRRecordingInfoTag()->m_playCount > 0) + buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); /* Mark as UnWatched */ + else + buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103); /* Mark as Watched */ + + buttons.Add(CONTEXT_BUTTON_RENAME, 118); /* Rename this recording */ + buttons.Add(CONTEXT_BUTTON_DELETE, 117); /* Delete this recording */ + } + buttons.Add(CONTEXT_BUTTON_SORTBY_NAME, 103); /* sort by name */ + buttons.Add(CONTEXT_BUTTON_SORTBY_DATE, 104); /* sort by date */ + // Update sort by button +//if (m_guiState->GetSortMethod()!=SORT_METHOD_NONE) +//{ +// CStdString sortLabel; +// sortLabel.Format(g_localizeStrings.Get(550).c_str(), g_localizeStrings.Get(m_guiState->GetSortMethodLabel()).c_str()); +// buttons.Add(CONTEXT_BUTTON_SORTBY, sortLabel); /* Sort method */ +// +// if (m_guiState->GetDisplaySortOrder()==SORT_ORDER_ASC) +// buttons.Add(CONTEXT_BUTTON_SORTASC, 584); /* Sort up or down */ +// else +// buttons.Add(CONTEXT_BUTTON_SORTASC, 585); /* Sort up or down */ +//} +} + +bool CGUIWindowPVRRecordings::OnAction(const CAction &action) +{ + if (action.GetID() == ACTION_PARENT_DIR || + action.GetID() == ACTION_NAV_BACK) + { + if (m_parent->m_vecItems->GetPath() != "pvr://recordings/") + m_parent->GoParentFolder(); + else + g_windowManager.PreviousWindow(); + + return true; + } + + return CGUIWindowPVRCommon::OnAction(action); +} + +bool CGUIWindowPVRRecordings::OnContextButton(int itemNumber, CONTEXT_BUTTON button) +{ + if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size()) + return false; + CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber); + + return OnContextButtonPlay(pItem.get(), button) || + OnContextButtonRename(pItem.get(), button) || + OnContextButtonDelete(pItem.get(), button) || + OnContextButtonInfo(pItem.get(), button) || + OnContextButtonMarkWatched(pItem, button) || + CGUIWindowPVRCommon::OnContextButton(itemNumber, button); +} + +void CGUIWindowPVRRecordings::OnWindowUnload(void) +{ + m_strSelectedPath = m_parent->m_vecItems->GetPath(); + CGUIWindowPVRCommon::OnWindowUnload(); +} + +void CGUIWindowPVRRecordings::UpdateData(bool bUpdateSelectedFile /* = true */) +{ + CSingleLock lock(m_critSection); + CLog::Log(LOGDEBUG, "CGUIWindowPVRRecordings - %s - update window '%s'. set view to %d", __FUNCTION__, GetName(), m_iControlList); + m_bUpdateRequired = false; + + /* lock the graphics context while updating */ + CSingleLock graphicsLock(g_graphicsContext); + + m_iSelected = m_parent->m_viewControl.GetSelectedItem(); + if (m_parent->m_vecItems->GetPath().Left(17) != "pvr://recordings/") + m_strSelectedPath = "pvr://recordings/"; + else + m_strSelectedPath = m_parent->m_vecItems->GetPath(); + + m_parent->m_viewControl.Clear(); + m_parent->m_vecItems->Clear(); + m_parent->m_viewControl.SetCurrentView(m_iControlList); + m_parent->m_vecItems->SetPath(m_strSelectedPath); + m_parent->Update(m_strSelectedPath); + m_parent->m_viewControl.SetItems(*m_parent->m_vecItems); + + if (bUpdateSelectedFile) + { + if (!SelectPlayingFile()) + m_parent->m_viewControl.SetSelectedItem(m_iSelected); + } + + m_parent->SetLabel(CONTROL_LABELHEADER, g_localizeStrings.Get(19017)); + m_parent->SetLabel(CONTROL_LABELGROUP, ""); +} + +void CGUIWindowPVRRecordings::Notify(const Observable &obs, const ObservableMessage msg) +{ + if (msg == ObservableMessageRecordings || msg == ObservableMessageTimers || msg == ObservableMessageCurrentItem) + { + if (IsVisible()) + SetInvalid(); + else + m_bUpdateRequired = true; + } + else if (msg == ObservableMessageRecordings || msg == ObservableMessageTimersReset) + { + if (IsVisible()) + UpdateData(false); + else + m_bUpdateRequired = true; + } +} + +bool CGUIWindowPVRRecordings::OnClickButton(CGUIMessage &message) +{ + bool bReturn = false; + + if (IsSelectedButton(message)) + { + bReturn = true; + g_PVRManager.TriggerRecordingsUpdate(); + } + + return bReturn; +} + +bool CGUIWindowPVRRecordings::OnClickList(CGUIMessage &message) +{ + bool bReturn = false; + + if (IsSelectedList(message)) + { + bReturn = true; + int iAction = message.GetParam1(); + int iItem = m_parent->m_viewControl.GetSelectedItem(); + + /* get the fileitem pointer */ + if (iItem < 0 || iItem >= (int) m_parent->m_vecItems->Size()) + return bReturn; + CFileItemPtr pItem = m_parent->m_vecItems->Get(iItem); + + /* process actions */ + if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK || iAction == ACTION_PLAY) + { + int choice = CONTEXT_BUTTON_PLAY_ITEM; + CStdString resumeString = GetResumeString(*pItem); + if (!resumeString.IsEmpty()) + { + CContextButtons choices; + choices.Add(CONTEXT_BUTTON_RESUME_ITEM, resumeString); + choices.Add(CONTEXT_BUTTON_PLAY_ITEM, 12021); + choice = CGUIDialogContextMenu::ShowAndGetChoice(choices); + } + if (choice < 0) + bReturn = true; + else + bReturn = OnContextButtonPlay(pItem.get(), (CONTEXT_BUTTON)choice); + } + else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK) + m_parent->OnPopupMenu(iItem); + else if (iAction == ACTION_SHOW_INFO) + ShowRecordingInfo(pItem.get()); + else if (iAction == ACTION_DELETE_ITEM) + bReturn = ActionDeleteRecording(pItem.get()); + else + bReturn = false; + } + + return bReturn; +} + +bool CGUIWindowPVRRecordings::OnContextButtonDelete(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_DELETE) + { + bReturn = false; + + CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + if (!pDialog) + return bReturn; + pDialog->SetHeading(122); + pDialog->SetLine(0, 19043); + pDialog->SetLine(1, ""); + pDialog->SetLine(2, item->GetPVRRecordingInfoTag()->m_strTitle); + pDialog->DoModal(); + + if (!pDialog->IsConfirmed()) + return bReturn; + + bReturn = g_PVRRecordings->DeleteRecording(*item); + } + + return bReturn; +} + +bool CGUIWindowPVRRecordings::OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_INFO) + { + bReturn = true; + ShowRecordingInfo(item); + } + + return bReturn; +} + +bool CGUIWindowPVRRecordings::OnContextButtonPlay(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if ((button == CONTEXT_BUTTON_PLAY_ITEM) || + (button == CONTEXT_BUTTON_RESUME_ITEM)) + { + item->m_lStartOffset = button == CONTEXT_BUTTON_RESUME_ITEM ? STARTOFFSET_RESUME : 0; + bReturn = PlayFile(item, false); /* play recording */ + } + + return bReturn; +} + +bool CGUIWindowPVRRecordings::OnContextButtonRename(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_RENAME) + { + bReturn = true; + + CPVRRecording *recording = item->GetPVRRecordingInfoTag(); + CStdString strNewName = recording->m_strTitle; + if (CGUIKeyboardFactory::ShowAndGetInput(strNewName, g_localizeStrings.Get(19041), false)) + { + if (g_PVRRecordings->RenameRecording(*item, strNewName)) + UpdateData(); + } + } + + return bReturn; +} + +bool CGUIWindowPVRRecordings::OnContextButtonMarkWatched(const CFileItemPtr &item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_MARK_WATCHED) + { + bReturn = true; + + int newSelection = m_parent->m_viewControl.GetSelectedItem(); + g_PVRRecordings->SetRecordingsPlayCount(item, 1); + m_parent->m_viewControl.SetSelectedItem(newSelection); + + UpdateData(); + } + + if (button == CONTEXT_BUTTON_MARK_UNWATCHED) + { + bReturn = true; + + g_PVRRecordings->SetRecordingsPlayCount(item, 0); + + UpdateData(); + } + + return bReturn; +} diff --git a/xbmc/pvr/windows/GUIWindowPVRRecordings.h b/xbmc/pvr/windows/GUIWindowPVRRecordings.h new file mode 100644 index 0000000000..36a004e986 --- /dev/null +++ b/xbmc/pvr/windows/GUIWindowPVRRecordings.h @@ -0,0 +1,62 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWindowPVRCommon.h" +#include "utils/Observer.h" + +namespace PVR +{ + class CGUIWindowPVR; + + class CGUIWindowPVRRecordings : public CGUIWindowPVRCommon, private Observer + { + friend class CGUIWindowPVR; + + public: + CGUIWindowPVRRecordings(CGUIWindowPVR *parent); + virtual ~CGUIWindowPVRRecordings(void) {}; + + static CStdString GetResumeString(CFileItem item); + + void GetContextButtons(int itemNumber, CContextButtons &buttons) const; + bool OnAction(const CAction &action); + bool OnContextButton(int itemNumber, CONTEXT_BUTTON button); + void OnWindowUnload(void); + void UpdateData(bool bUpdateSelectedFile = true); + void Notify(const Observable &obs, const ObservableMessage msg); + void UnregisterObservers(void); + void ResetObservers(void); + + private: + bool OnClickButton(CGUIMessage &message); + bool OnClickList(CGUIMessage &message); + + bool OnContextButtonDelete(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonPlay(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonRename(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonMarkWatched(const CFileItemPtr &item, CONTEXT_BUTTON button); + + CStdString m_strSelectedPath; + }; +} diff --git a/xbmc/pvr/windows/GUIWindowPVRSearch.cpp b/xbmc/pvr/windows/GUIWindowPVRSearch.cpp new file mode 100644 index 0000000000..7a53bd31c9 --- /dev/null +++ b/xbmc/pvr/windows/GUIWindowPVRSearch.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWindowPVRSearch.h" + +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogProgress.h" +#include "guilib/GUIWindowManager.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/dialogs/GUIDialogPVRGuideSearch.h" +#include "epg/EpgContainer.h" +#include "pvr/recordings/PVRRecordings.h" +#include "GUIWindowPVR.h" +#include "utils/log.h" +#include "pvr/addons/PVRClients.h" + +using namespace PVR; +using namespace EPG; + +CGUIWindowPVRSearch::CGUIWindowPVRSearch(CGUIWindowPVR *parent) : + CGUIWindowPVRCommon(parent, PVR_WINDOW_SEARCH, CONTROL_BTNSEARCH, CONTROL_LIST_SEARCH), + m_bSearchStarted(false), + m_bSearchConfirmed(false) +{ +} + +void CGUIWindowPVRSearch::GetContextButtons(int itemNumber, CContextButtons &buttons) const +{ + if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size()) + return; + CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber); + + if (pItem->GetLabel() != g_localizeStrings.Get(19027)) + { + if (pItem->GetEPGInfoTag()->EndAsLocalTime() > CDateTime::GetCurrentDateTime()) + { + if (!pItem->GetEPGInfoTag()->HasTimer()) + { + if (pItem->GetEPGInfoTag()->StartAsLocalTime() < CDateTime::GetCurrentDateTime()) + buttons.Add(CONTEXT_BUTTON_START_RECORD, 264); /* RECORD programme */ + else + buttons.Add(CONTEXT_BUTTON_START_RECORD, 19061); /* Create a Timer */ + } + else + { + if (pItem->GetEPGInfoTag()->StartAsLocalTime() < CDateTime::GetCurrentDateTime()) + buttons.Add(CONTEXT_BUTTON_STOP_RECORD, 19059); /* Stop recording */ + else + buttons.Add(CONTEXT_BUTTON_STOP_RECORD, 19060); /* Delete Timer */ + } + } + + buttons.Add(CONTEXT_BUTTON_INFO, 19047); /* Epg info button */ + buttons.Add(CONTEXT_BUTTON_SORTBY_CHANNEL, 19062); /* Sort by channel */ + buttons.Add(CONTEXT_BUTTON_SORTBY_NAME, 103); /* Sort by Name */ + buttons.Add(CONTEXT_BUTTON_SORTBY_DATE, 104); /* Sort by Date */ + buttons.Add(CONTEXT_BUTTON_CLEAR, 19232); /* Clear search results */ + if (pItem->GetEPGInfoTag()->HasPVRChannel() && + g_PVRClients->HasMenuHooks(pItem->GetEPGInfoTag()->ChannelTag()->ClientID())) + buttons.Add(CONTEXT_BUTTON_MENU_HOOKS, 19195); /* PVR client specific action */ + } +} + +bool CGUIWindowPVRSearch::OnContextButton(int itemNumber, CONTEXT_BUTTON button) +{ + if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size()) + return false; + CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber); + + return OnContextButtonClear(pItem.get(), button) || + OnContextButtonInfo(pItem.get(), button) || + OnContextButtonStopRecord(pItem.get(), button) || + OnContextButtonStartRecord(pItem.get(), button) || + CGUIWindowPVRCommon::OnContextButton(itemNumber, button); +} + +void CGUIWindowPVRSearch::UpdateData(bool bUpdateSelectedFile /* = true */) +{ + CLog::Log(LOGDEBUG, "CGUIWindowPVRSearch - %s - update window '%s'. set view to %d", __FUNCTION__, GetName(), m_iControlList); + m_bUpdateRequired = false; + + /* lock the graphics context while updating */ + CSingleLock graphicsLock(g_graphicsContext); + + m_iSelected = m_parent->m_viewControl.GetSelectedItem(); + m_parent->m_viewControl.Clear(); + m_parent->m_vecItems->Clear(); + m_parent->m_viewControl.SetCurrentView(m_iControlList); + + if (m_bSearchConfirmed) + { + CGUIDialogProgress* dlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS); + if (dlgProgress) + { + dlgProgress->SetHeading(194); + dlgProgress->SetLine(0, m_searchfilter.m_strSearchTerm); + dlgProgress->SetLine(1, ""); + dlgProgress->SetLine(2, ""); + dlgProgress->StartModal(); + dlgProgress->Progress(); + } + + // TODO get this from the selected channel group + g_EpgContainer.GetEPGSearch(*m_parent->m_vecItems, m_searchfilter); + if (dlgProgress) + dlgProgress->Close(); + + if (m_parent->m_vecItems->Size() == 0) + { + CGUIDialogOK::ShowAndGetInput(194, 284, 0, 0); + m_bSearchConfirmed = false; + } + } + + if (m_parent->m_vecItems->Size() == 0) + { + CFileItemPtr item; + item.reset(new CFileItem("pvr://guide/searchresults/empty.epg", false)); + item->SetLabel(g_localizeStrings.Get(19027)); + item->SetLabelPreformated(true); + m_parent->m_vecItems->Add(item); + } + else + { + m_parent->m_vecItems->Sort(m_iSortMethod, m_iSortOrder); + } + + m_parent->m_viewControl.SetItems(*m_parent->m_vecItems); + + if (bUpdateSelectedFile) + m_parent->m_viewControl.SetSelectedItem(m_iSelected); + + m_parent->SetLabel(CONTROL_LABELHEADER, g_localizeStrings.Get(283)); + m_parent->SetLabel(CONTROL_LABELGROUP, ""); +} + +bool CGUIWindowPVRSearch::OnClickButton(CGUIMessage &message) +{ + bool bReturn = false; + + if (IsSelectedButton(message)) + { + bReturn = true; + ShowSearchResults(); + } + + return bReturn; +} + +bool CGUIWindowPVRSearch::OnClickList(CGUIMessage &message) +{ + bool bReturn = false; + + if (IsSelectedList(message)) + { + bReturn = true; + int iAction = message.GetParam1(); + int iItem = m_parent->m_viewControl.GetSelectedItem(); + + /* get the fileitem pointer */ + if (iItem < 0 || iItem >= m_parent->m_vecItems->Size()) + return bReturn; + CFileItemPtr pItem = m_parent->m_vecItems->Get(iItem); + + /* process actions */ + if (iAction == ACTION_SHOW_INFO || iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK) + ActionShowSearch(pItem.get()); + else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK) + m_parent->OnPopupMenu(iItem); + else if (iAction == ACTION_RECORD) + ActionRecord(pItem.get()); + } + + return bReturn; +} + +bool CGUIWindowPVRSearch::OnContextButtonClear(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_CLEAR) + { + bReturn = true; + + m_bSearchStarted = false; + m_bSearchConfirmed = false; + m_searchfilter.Reset(); + + UpdateData(); + } + + return bReturn; +} + +bool CGUIWindowPVRSearch::OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_INFO) + { + bReturn = true; + + ShowEPGInfo(item); + } + + return bReturn; +} + +bool CGUIWindowPVRSearch::OnContextButtonStartRecord(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_START_RECORD) + { + bReturn = true; + + StartRecordFile(item); + } + + return bReturn; +} + +bool CGUIWindowPVRSearch::OnContextButtonStopRecord(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_STOP_RECORD) + { + bReturn = true; + + StopRecordFile(item); + } + + return bReturn; +} + +bool CGUIWindowPVRSearch::ActionShowSearch(CFileItem *item) +{ + if (item->GetPath() == "pvr://guide/searchresults/empty.epg") + ShowSearchResults(); + else + ShowEPGInfo(item); + + return true; +} + +void CGUIWindowPVRSearch::ShowSearchResults() +{ + /* Load timer settings dialog */ + CGUIDialogPVRGuideSearch* pDlgInfo = (CGUIDialogPVRGuideSearch*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GUIDE_SEARCH); + + if (!pDlgInfo) + return; + + if (!m_bSearchStarted) + { + m_bSearchStarted = true; + m_searchfilter.Reset(); + } + + pDlgInfo->SetFilterData(&m_searchfilter); + + /* Open dialog window */ + pDlgInfo->DoModal(); + + if (pDlgInfo->IsConfirmed()) + { + m_bSearchConfirmed = true; + UpdateData(); + } +} diff --git a/xbmc/pvr/windows/GUIWindowPVRSearch.h b/xbmc/pvr/windows/GUIWindowPVRSearch.h new file mode 100644 index 0000000000..095b1a7251 --- /dev/null +++ b/xbmc/pvr/windows/GUIWindowPVRSearch.h @@ -0,0 +1,61 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWindowPVRCommon.h" +#include "epg/EpgSearchFilter.h" + +namespace PVR +{ + class CGUIWindowPVR; + + class CGUIWindowPVRSearch : public CGUIWindowPVRCommon + { + friend class CGUIWindowPVR; + friend class CGUIWindowPVRCommon; + + public: + CGUIWindowPVRSearch(CGUIWindowPVR *parent); + virtual ~CGUIWindowPVRSearch(void) {}; + + void GetContextButtons(int itemNumber, CContextButtons &buttons) const; + bool OnContextButton(int itemNumber, CONTEXT_BUTTON button); + void UpdateData(bool bUpdateSelectedFile = true); + + private: + + bool OnClickButton(CGUIMessage &message); + bool OnClickList(CGUIMessage &message); + + bool OnContextButtonClear(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonStartRecord(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonStopRecord(CFileItem *item, CONTEXT_BUTTON button); + + bool ActionShowSearch(CFileItem *item); + void ShowSearchResults(); + + bool m_bSearchStarted; + bool m_bSearchConfirmed; + EPG::EpgSearchFilter m_searchfilter; + }; +} diff --git a/xbmc/pvr/windows/GUIWindowPVRTimers.cpp b/xbmc/pvr/windows/GUIWindowPVRTimers.cpp new file mode 100644 index 0000000000..e2586d2a1a --- /dev/null +++ b/xbmc/pvr/windows/GUIWindowPVRTimers.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWindowPVRTimers.h" + +#include "guilib/GUIKeyboardFactory.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogYesNo.h" +#include "guilib/GUIWindowManager.h" +#include "pvr/PVRManager.h" +#include "pvr/timers/PVRTimers.h" +#include "pvr/addons/PVRClients.h" +#include "GUIWindowPVR.h" +#include "threads/SingleLock.h" + +using namespace PVR; + +CGUIWindowPVRTimers::CGUIWindowPVRTimers(CGUIWindowPVR *parent) : + CGUIWindowPVRCommon(parent, PVR_WINDOW_TIMERS, CONTROL_BTNTIMERS, CONTROL_LIST_TIMERS) +{ +} + +void CGUIWindowPVRTimers::UnregisterObservers(void) +{ + CSingleLock lock(m_critSection); + if (g_PVRTimers) + g_PVRTimers->UnregisterObserver(this); +} + +void CGUIWindowPVRTimers::ResetObservers(void) +{ + CSingleLock lock(m_critSection); + g_PVRTimers->RegisterObserver(this); +} + +void CGUIWindowPVRTimers::GetContextButtons(int itemNumber, CContextButtons &buttons) const +{ + if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size()) + return; + CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber); + + /* Check for a empty file item list, means only a + file item with the name "Add timer..." is present */ + if (pItem->GetPath() == "pvr://timers/add.timer") + { + buttons.Add(CONTEXT_BUTTON_ADD, 19056); /* new timer */ + if (m_parent->m_vecItems->Size() > 1) + { + buttons.Add(CONTEXT_BUTTON_SORTBY_NAME, 103); /* sort by name */ + buttons.Add(CONTEXT_BUTTON_SORTBY_DATE, 104); /* sort by date */ + } + } + else + { + buttons.Add(CONTEXT_BUTTON_EDIT, 19057); /* edit timer */ + buttons.Add(CONTEXT_BUTTON_ADD, 19056); /* new timer */ + buttons.Add(CONTEXT_BUTTON_ACTIVATE, 19058); /* activate/deactivate */ + buttons.Add(CONTEXT_BUTTON_RENAME, 118); /* rename timer */ + buttons.Add(CONTEXT_BUTTON_DELETE, 117); /* delete timer */ + buttons.Add(CONTEXT_BUTTON_SORTBY_NAME, 103); /* sort by name */ + buttons.Add(CONTEXT_BUTTON_SORTBY_DATE, 104); /* sort by date */ + if (g_PVRClients->HasMenuHooks(pItem->GetPVRTimerInfoTag()->m_iClientId)) + buttons.Add(CONTEXT_BUTTON_MENU_HOOKS, 19195); /* PVR client specific action */ + } +} + +bool CGUIWindowPVRTimers::OnContextButton(int itemNumber, CONTEXT_BUTTON button) +{ + if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size()) + return false; + CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber); + + return OnContextButtonActivate(pItem.get(), button) || + OnContextButtonAdd(pItem.get(), button) || + OnContextButtonDelete(pItem.get(), button) || + OnContextButtonEdit(pItem.get(), button) || + OnContextButtonRename(pItem.get(), button) || + CGUIWindowPVRCommon::OnContextButton(itemNumber, button); +} + +void CGUIWindowPVRTimers::UpdateData(bool bUpdateSelectedFile /* = true */) +{ + CSingleLock lock(m_critSection); + CLog::Log(LOGDEBUG, "CGUIWindowPVRTimers - %s - update window '%s'. set view to %d", __FUNCTION__, GetName(), m_iControlList); + m_bUpdateRequired = false; + + /* lock the graphics context while updating */ + CSingleLock graphicsLock(g_graphicsContext); + + m_iSelected = m_parent->m_viewControl.GetSelectedItem(); + m_parent->m_viewControl.Clear(); + m_parent->m_vecItems->Clear(); + m_parent->m_viewControl.SetCurrentView(m_iControlList); + m_parent->m_vecItems->SetPath("pvr://timers/"); + m_parent->Update(m_parent->m_vecItems->GetPath()); + m_parent->m_vecItems->Sort(m_iSortMethod, m_iSortOrder); + m_parent->m_viewControl.SetItems(*m_parent->m_vecItems); + + if (bUpdateSelectedFile) + m_parent->m_viewControl.SetSelectedItem(m_iSelected); + + m_parent->SetLabel(CONTROL_LABELHEADER, g_localizeStrings.Get(19025)); + m_parent->SetLabel(CONTROL_LABELGROUP, ""); +} + +bool CGUIWindowPVRTimers::OnClickButton(CGUIMessage &message) +{ + bool bReturn = false; + + if (IsSelectedButton(message)) + { + bReturn = true; + g_PVRManager.TriggerTimersUpdate(); + } + + return bReturn; +} + +bool CGUIWindowPVRTimers::OnClickList(CGUIMessage &message) +{ + bool bReturn = false; + + if (IsSelectedList(message)) + { + bReturn = true; + int iAction = message.GetParam1(); + int iItem = m_parent->m_viewControl.GetSelectedItem(); + + /* get the fileitem pointer */ + if (iItem < 0 || iItem >= m_parent->m_vecItems->Size()) + return bReturn; + CFileItemPtr pItem = m_parent->m_vecItems->Get(iItem); + + /* process actions */ + if (iAction == ACTION_SHOW_INFO || iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK) + ActionShowTimer(pItem.get()); + else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK) + m_parent->OnPopupMenu(iItem); + else if (iAction == ACTION_DELETE_ITEM) + ActionDeleteTimer(pItem.get()); + } + + return bReturn; +} + +bool CGUIWindowPVRTimers::OnContextButtonActivate(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_ACTIVATE) + { + bReturn = true; + if (!item->HasPVRTimerInfoTag()) + return bReturn; + + CPVRTimerInfoTag *timer = item->GetPVRTimerInfoTag(); + int iLabelId; + if (timer->IsActive()) + { + timer->m_state = PVR_TIMER_STATE_CANCELLED; + iLabelId = 13106; + } + else + { + timer->m_state = PVR_TIMER_STATE_SCHEDULED; + iLabelId = 305; + } + + CGUIDialogOK::ShowAndGetInput(19033, 19040, 0, iLabelId); + g_PVRTimers->UpdateTimer(*item); + } + + return bReturn; +} + +bool CGUIWindowPVRTimers::OnContextButtonAdd(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_ADD) + bReturn = ShowNewTimerDialog(); + + return bReturn; +} + +bool CGUIWindowPVRTimers::OnContextButtonDelete(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_DELETE) + { + bReturn = true; + if (!item->HasPVRTimerInfoTag()) + return bReturn; + + CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + if (!pDialog) + return bReturn; + pDialog->SetHeading(122); + pDialog->SetLine(0, 19040); + pDialog->SetLine(1, ""); + pDialog->SetLine(2, item->GetPVRTimerInfoTag()->m_strTitle); + pDialog->DoModal(); + + if (!pDialog->IsConfirmed()) + return bReturn; + + g_PVRTimers->DeleteTimer(*item); + } + + return bReturn; +} + +bool CGUIWindowPVRTimers::OnContextButtonEdit(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_EDIT) + { + bReturn = true; + if (!item->HasPVRTimerInfoTag()) + return bReturn; + + if (ShowTimerSettings(item)) + g_PVRTimers->UpdateTimer(*item); + } + + return bReturn; +} + +bool CGUIWindowPVRTimers::OnContextButtonRename(CFileItem *item, CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_RENAME) + { + bReturn = true; + if (!item->HasPVRTimerInfoTag()) + return bReturn; + CPVRTimerInfoTag *timer = item->GetPVRTimerInfoTag(); + + CStdString strNewName(timer->m_strTitle); + if (CGUIKeyboardFactory::ShowAndGetInput(strNewName, g_localizeStrings.Get(19042), false)) + g_PVRTimers->RenameTimer(*item, strNewName); + } + + return bReturn; +} + +void CGUIWindowPVRTimers::Notify(const Observable &obs, const ObservableMessage msg) +{ + if (msg == ObservableMessageTimers) + { + if (IsVisible()) + SetInvalid(); + else + m_bUpdateRequired = true; + } + else if (msg == ObservableMessageTimersReset) + { + if (IsVisible()) + UpdateData(false); + else + m_bUpdateRequired = true; + } +} diff --git a/xbmc/pvr/windows/GUIWindowPVRTimers.h b/xbmc/pvr/windows/GUIWindowPVRTimers.h new file mode 100644 index 0000000000..0838382dc1 --- /dev/null +++ b/xbmc/pvr/windows/GUIWindowPVRTimers.h @@ -0,0 +1,56 @@ +#pragma once + +/* + * Copyright (C) 2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "GUIWindowPVRCommon.h" +#include "utils/Observer.h" + +namespace PVR +{ + class CGUIWindowPVR; + + class CGUIWindowPVRTimers : public CGUIWindowPVRCommon, private Observer + { + friend class CGUIWindowPVR; + + public: + CGUIWindowPVRTimers(CGUIWindowPVR *parent); + virtual ~CGUIWindowPVRTimers(void) {}; + + void GetContextButtons(int itemNumber, CContextButtons &buttons) const; + bool OnContextButton(int itemNumber, CONTEXT_BUTTON button); + void UpdateData(bool bUpdateSelectedFile = true); + void Notify(const Observable &obs, const ObservableMessage msg); + void UnregisterObservers(void); + void ResetObservers(void); + + private: + bool OnClickButton(CGUIMessage &message); + bool OnClickList(CGUIMessage &message); + + bool OnContextButtonActivate(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonAdd(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonDelete(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonEdit(CFileItem *item, CONTEXT_BUTTON button); + bool OnContextButtonRename(CFileItem *item, CONTEXT_BUTTON button); + }; +} diff --git a/xbmc/pvr/windows/Makefile b/xbmc/pvr/windows/Makefile new file mode 100644 index 0000000000..128eb24196 --- /dev/null +++ b/xbmc/pvr/windows/Makefile @@ -0,0 +1,13 @@ +SRCS=GUIViewStatePVR.cpp \ + GUIWindowPVR.cpp \ + GUIWindowPVRChannels.cpp \ + GUIWindowPVRCommon.cpp \ + GUIWindowPVRGuide.cpp \ + GUIWindowPVRRecordings.cpp \ + GUIWindowPVRSearch.cpp \ + GUIWindowPVRTimers.cpp + +LIB=pvrwindows.a + +include ../../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp index 1108b418e6..9726cf3a3f 100644 --- a/xbmc/settings/AdvancedSettings.cpp +++ b/xbmc/settings/AdvancedSettings.cpp @@ -232,6 +232,14 @@ void CAdvancedSettings::Initialize() m_iMythMovieLength = 0; // 0 == Off + m_iEpgLingerTime = 60; /* keep 1 hour by default */ + m_iEpgUpdateCheckInterval = 300; /* check if tables need to be updated every 5 minutes */ + m_iEpgCleanupInterval = 900; /* remove old entries from the EPG every 15 minutes */ + m_iEpgActiveTagCheckInterval = 60; /* check for updated active tags every minute */ + m_iEpgRetryInterruptedUpdateInterval = 30; /* retry an interrupted epg update after 30 seconds */ + m_bEpgDisplayUpdatePopup = true; /* display a progress popup while updating EPG data from clients */ + m_bEpgDisplayIncrementalUpdatePopup = false; /* also display a progress popup while doing incremental EPG updates */ + m_bEdlMergeShortCommBreaks = false; // Off by default m_iEdlMaxCommBreakLength = 8 * 30 + 10; // Just over 8 * 30 second commercial break. m_iEdlMinCommBreakLength = 3 * 30; // 3 * 30 second commercial breaks. @@ -280,6 +288,13 @@ void CAdvancedSettings::Initialize() m_bgInfoLoaderMaxThreads = 5; + m_iPVRTimeCorrection = 0; + m_iPVRInfoToggleInterval = 3000; + m_bPVRShowEpgInfoOnEpgItemSelect = true; + m_iPVRMinVideoCacheLevel = 5; + m_iPVRMinAudioCacheLevel = 5; + m_bPVRCacheInDvdPlayer = true; + m_measureRefreshrate = false; m_cacheMemBufferSize = 1024 * 1024 * 20; @@ -762,6 +777,19 @@ void CAdvancedSettings::ParseSettingsFile(const CStdString &file) XMLUtils::GetInt(pElement, "movielength", m_iMythMovieLength); } + // EPG + pElement = pRootElement->FirstChildElement("epg"); + if (pElement) + { + XMLUtils::GetInt(pElement, "lingertime", m_iEpgLingerTime); + XMLUtils::GetInt(pElement, "updatecheckinterval", m_iEpgUpdateCheckInterval); + XMLUtils::GetInt(pElement, "cleanupinterval", m_iEpgCleanupInterval); + XMLUtils::GetInt(pElement, "activetagcheckinterval", m_iEpgActiveTagCheckInterval); + XMLUtils::GetInt(pElement, "retryinterruptedupdateinterval", m_iEpgRetryInterruptedUpdateInterval); + XMLUtils::GetBoolean(pElement, "displayupdatepopup", m_bEpgDisplayUpdatePopup); + XMLUtils::GetBoolean(pElement, "displayincrementalupdatepopup", m_bEpgDisplayIncrementalUpdatePopup); + } + // EDL commercial break handling pElement = pRootElement->FirstChildElement("edl"); if (pElement) @@ -938,6 +966,17 @@ void CAdvancedSettings::ParseSettingsFile(const CStdString &file) XMLUtils::GetInt(pRootElement, "bginfoloadermaxthreads", m_bgInfoLoaderMaxThreads); m_bgInfoLoaderMaxThreads = std::max(1, m_bgInfoLoaderMaxThreads); + TiXmlElement *pPVR = pRootElement->FirstChildElement("pvr"); + if (pPVR) + { + XMLUtils::GetInt(pPVR, "timecorrection", m_iPVRTimeCorrection, 0, 1440); + XMLUtils::GetInt(pPVR, "infotoggleinterval", m_iPVRInfoToggleInterval, 0, 30000); + XMLUtils::GetBoolean(pPVR, "showepginfoonselect", m_bPVRShowEpgInfoOnEpgItemSelect); + XMLUtils::GetInt(pPVR, "minvideocachelevel", m_iPVRMinVideoCacheLevel, 0, 100); + XMLUtils::GetInt(pPVR, "minaudiocachelevel", m_iPVRMinAudioCacheLevel, 0, 100); + XMLUtils::GetBoolean(pPVR, "cacheindvdplayer", m_bPVRCacheInDvdPlayer); + } + XMLUtils::GetBoolean(pRootElement, "measurerefreshrate", m_measureRefreshrate); TiXmlElement* pDatabase = pRootElement->FirstChildElement("videodatabase"); @@ -963,6 +1002,28 @@ void CAdvancedSettings::ParseSettingsFile(const CStdString &file) XMLUtils::GetString(pDatabase, "name", m_databaseMusic.name); } + pDatabase = pRootElement->FirstChildElement("tvdatabase"); + if (pDatabase) + { + XMLUtils::GetString(pDatabase, "type", m_databaseTV.type); + XMLUtils::GetString(pDatabase, "host", m_databaseTV.host); + XMLUtils::GetString(pDatabase, "port", m_databaseTV.port); + XMLUtils::GetString(pDatabase, "user", m_databaseTV.user); + XMLUtils::GetString(pDatabase, "pass", m_databaseTV.pass); + XMLUtils::GetString(pDatabase, "name", m_databaseTV.name); + } + + pDatabase = pRootElement->FirstChildElement("epgdatabase"); + if (pDatabase) + { + XMLUtils::GetString(pDatabase, "type", m_databaseEpg.type); + XMLUtils::GetString(pDatabase, "host", m_databaseEpg.host); + XMLUtils::GetString(pDatabase, "port", m_databaseEpg.port); + XMLUtils::GetString(pDatabase, "user", m_databaseEpg.user); + XMLUtils::GetString(pDatabase, "pass", m_databaseEpg.pass); + XMLUtils::GetString(pDatabase, "name", m_databaseEpg.name); + } + pElement = pRootElement->FirstChildElement("enablemultimediakeys"); if (pElement) { diff --git a/xbmc/settings/AdvancedSettings.h b/xbmc/settings/AdvancedSettings.h index b51d3827d2..e777764c53 100644 --- a/xbmc/settings/AdvancedSettings.h +++ b/xbmc/settings/AdvancedSettings.h @@ -271,6 +271,14 @@ class CAdvancedSettings int m_iMythMovieLength; // minutes + int m_iEpgLingerTime; // minutes + int m_iEpgUpdateCheckInterval; // seconds + int m_iEpgCleanupInterval; // seconds + int m_iEpgActiveTagCheckInterval; // seconds + int m_iEpgRetryInterruptedUpdateInterval; // seconds + bool m_bEpgDisplayUpdatePopup; + bool m_bEpgDisplayIncrementalUpdatePopup; + // EDL Commercial Break bool m_bEdlMergeShortCommBreaks; int m_iEdlMaxCommBreakLength; // seconds @@ -319,11 +327,21 @@ class CAdvancedSettings CStdString m_gpuTempCmd; int m_bgInfoLoaderMaxThreads; + /* PVR/TV related advanced settings */ + int m_iPVRTimeCorrection; /*!< @brief correct all times (epg tags, timer tags, recording tags) by this amount of minutes. defaults to 0. */ + int m_iPVRInfoToggleInterval; /*!< @brief if there are more than 1 pvr gui info item available (e.g. multiple recordings active at the same time), use this toggle delay in milliseconds. defaults to 3000. */ + bool m_bPVRShowEpgInfoOnEpgItemSelect; /*!< @brief when selecting an EPG fileitem, show the EPG info dialog if this setting is true. start playback on the selected channel if false */ + int m_iPVRMinVideoCacheLevel; /*!< @brief cache up to this level in the video buffer buffer before resuming playback if the buffers run dry */ + int m_iPVRMinAudioCacheLevel; /*!< @brief cache up to this level in the audio buffer before resuming playback if the buffers run dry */ + bool m_bPVRCacheInDvdPlayer; /*!< @brief true to use "CACHESTATE_PVR" in CDVDPlayer (default) */ + bool m_measureRefreshrate; //when true the videoreferenceclock will measure the refreshrate when direct3d is used //otherwise it will use the windows refreshrate DatabaseSettings m_databaseMusic; // advanced music database setup DatabaseSettings m_databaseVideo; // advanced video database setup + DatabaseSettings m_databaseTV; // advanced tv database setup + DatabaseSettings m_databaseEpg; /*!< advanced EPG database setup */ bool m_guiVisualizeDirtyRegions; int m_guiAlgorithmDirtyRegions; diff --git a/xbmc/settings/GUIDialogSettings.cpp b/xbmc/settings/GUIDialogSettings.cpp index 4c1f56321a..3c998af941 100644 --- a/xbmc/settings/GUIDialogSettings.cpp +++ b/xbmc/settings/GUIDialogSettings.cpp @@ -20,6 +20,7 @@ */ #include "GUIDialogSettings.h" +#include "guilib/GUIEditControl.h" #include "guilib/GUISpinControlEx.h" #include "guilib/GUIRadioButtonControl.h" #include "guilib/GUISettingsSliderControl.h" @@ -38,6 +39,8 @@ #define CONTROL_DEFAULT_SPIN 9 #define CONTROL_DEFAULT_SLIDER 10 #define CONTROL_DEFAULT_SEPARATOR 11 +#define CONTROL_DEFAULT_EDIT 12 +#define CONTROL_DEFAULT_EDIT_NUM 13 #define CONTROL_OKAY_BUTTON 28 #define CONTROL_CANCEL_BUTTON 29 #define CONTROL_START 30 @@ -48,6 +51,8 @@ using namespace std; CGUIDialogSettings::CGUIDialogSettings(int id, const char *xmlFile) : CGUIDialog(id, xmlFile) { + m_pOriginalEdit = NULL; + m_pOriginalEditNum = NULL; m_pOriginalSpin = NULL; m_pOriginalRadioButton = NULL; m_pOriginalSettingsButton = NULL; @@ -88,11 +93,15 @@ void CGUIDialogSettings::SetupPage() { // cleanup first, if necessary FreeControls(); + m_pOriginalEdit = (CGUIEditControl*)GetControl(CONTROL_DEFAULT_EDIT); + m_pOriginalEditNum = (CGUIEditControl*)GetControl(CONTROL_DEFAULT_EDIT_NUM); m_pOriginalSpin = (CGUISpinControlEx*)GetControl(CONTROL_DEFAULT_SPIN); m_pOriginalRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_DEFAULT_RADIOBUTTON); m_pOriginalSettingsButton = (CGUIButtonControl *)GetControl(CONTROL_DEFAULT_BUTTON); m_pOriginalSlider = (CGUISettingsSliderControl *)GetControl(CONTROL_DEFAULT_SLIDER); m_pOriginalSeparator = (CGUIImage *)GetControl(CONTROL_DEFAULT_SEPARATOR); + if (m_pOriginalEdit) m_pOriginalEdit->SetVisible(false); + if (m_pOriginalEditNum) m_pOriginalEditNum->SetVisible(false); if (m_pOriginalSpin) m_pOriginalSpin->SetVisible(false); if (m_pOriginalRadioButton) m_pOriginalRadioButton->SetVisible(false); if (m_pOriginalSettingsButton) m_pOriginalSettingsButton->SetVisible(false); @@ -100,7 +109,14 @@ void CGUIDialogSettings::SetupPage() if (m_pOriginalSeparator) m_pOriginalSeparator->SetVisible(false); // update our settings label + if (GetID() == WINDOW_DIALOG_PVR_TIMER_SETTING) + { + SET_CONTROL_LABEL(CONTROL_SETTINGS_LABEL, g_localizeStrings.Get(19057)); + } + else + { SET_CONTROL_LABEL(CONTROL_SETTINGS_LABEL, g_localizeStrings.Get(13395 + GetID() - WINDOW_DIALOG_VIDEO_OSD_SETTINGS)); + } CGUIControlGroupList *group = (CGUIControlGroupList *)GetControl(CONTROL_GROUP_LIST); if (!group) @@ -185,11 +201,25 @@ void CGUIDialogSettings::UpdateSetting(unsigned int id) if (setting.formatFunction) pControl->SetTextValue(setting.formatFunction(value, setting.interval)); } } - else if (setting.type == SettingInfo::BUTTON) + else if (setting.type == SettingInfo::BUTTON_DIALOG) { SET_CONTROL_LABEL(controlID,setting.name); - if (m_usePopupSliders && setting.data && setting.formatFunction) - SET_CONTROL_LABEL2(controlID,setting.formatFunction(*(float *)setting.data, setting.interval)); + CGUIButtonControl *pControl = (CGUIButtonControl *)GetControl(controlID); + if (pControl && setting.data) pControl->SetLabel2(*(CStdString *)setting.data); + } + else if (setting.type == SettingInfo::EDIT) + { + CGUIEditControl *pControl = (CGUIEditControl *)GetControl(controlID); + if (pControl && setting.data) pControl->SetLabel2(*(CStdString *)setting.data); + } + else if (setting.type == SettingInfo::EDIT_NUM) + { + CGUIEditControl *pControl = (CGUIEditControl *)GetControl(controlID); + if (pControl && setting.data) { + CStdString strIndex; + strIndex.Format("%i", *(int *)setting.data); + pControl->SetLabel2(strIndex); + } } else if (setting.type == SettingInfo::STRING) { @@ -238,6 +268,24 @@ void CGUIDialogSettings::OnClick(int iID) CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(iID); if (setting.data) *(int *)setting.data = pControl->GetValue(); } + else if (setting.type == SettingInfo::BUTTON_DIALOG) + { + CGUIButtonControl *pControl = (CGUIButtonControl *)GetControl(iID); + if (setting.data) *(CStdString *)setting.data = pControl->GetLabel2(); + } + else if (setting.type == SettingInfo::EDIT) + { + CGUIEditControl *pControl = (CGUIEditControl *)GetControl(iID); + if (setting.data) *(CStdString *)setting.data = pControl->GetLabel2(); + } + else if (setting.type == SettingInfo::EDIT_NUM) + { + CGUIEditControl *pControl = (CGUIEditControl *)GetControl(iID); + if (setting.data) { + CStdString strIndex = pControl->GetLabel2(); + *(int *)setting.data = atol(strIndex.c_str()); + } + } else if (setting.type == SettingInfo::CHECK) { CGUIRadioButtonControl *pControl = (CGUIRadioButtonControl *)GetControl(iID); @@ -285,7 +333,15 @@ void CGUIDialogSettings::FreeControls() void CGUIDialogSettings::AddSetting(SettingInfo &setting, float width, int iControlID) { CGUIControl *pControl = NULL; - if (setting.type == SettingInfo::BUTTON && m_pOriginalSettingsButton) + if (setting.type == SettingInfo::BUTTON_DIALOG && m_pOriginalSettingsButton) + { + pControl = new CGUIButtonControl(*m_pOriginalSettingsButton); + if (!pControl) return ; + ((CGUIButtonControl *)pControl)->SetLabel(setting.name); + pControl->SetWidth(width); + if (setting.data) ((CGUIButtonControl *)pControl)->SetLabel2(*(CStdString *)setting.data); + } + else if (setting.type == SettingInfo::BUTTON && m_pOriginalSettingsButton) { pControl = new CGUIButtonControl(*m_pOriginalSettingsButton); if (!pControl) return ; @@ -294,6 +350,27 @@ void CGUIDialogSettings::AddSetting(SettingInfo &setting, float width, int iCont ((CGUIButtonControl *)pControl)->SetLabel2(setting.formatFunction(*(float *)setting.data, setting.interval)); pControl->SetWidth(width); } + else if (setting.type == SettingInfo::EDIT && m_pOriginalEdit) + { + pControl = new CGUIEditControl(*m_pOriginalEdit); + if (!pControl) return ; + ((CGUIEditControl *)pControl)->SetLabel(setting.name); + pControl->SetWidth(width); + if (setting.data) ((CGUIEditControl *)pControl)->SetLabel2(*(CStdString *)setting.data); + } + else if (setting.type == SettingInfo::EDIT_NUM && m_pOriginalEditNum) + { + pControl = new CGUIEditControl(*m_pOriginalEditNum); + if (!pControl) return ; + ((CGUIEditControl *)pControl)->SetLabel(setting.name); + pControl->SetWidth(width); + ((CGUIEditControl *)pControl)->SetInputType(CGUIEditControl::INPUT_TYPE_NUMBER, 0); + if (setting.data) { + CStdString strIndex; + strIndex.Format("%i", *(int *)setting.data); + ((CGUIEditControl *)pControl)->SetLabel2(strIndex); + } + } else if (setting.type == SettingInfo::SEPARATOR && m_pOriginalSeparator) { pControl = new CGUIImage(*m_pOriginalSeparator); @@ -360,6 +437,28 @@ void CGUIDialogSettings::AddSetting(SettingInfo &setting, float width, int iCont delete pControl; } +void CGUIDialogSettings::AddEdit(unsigned int id, int label, CStdString *str, bool enabled) +{ + SettingInfo setting; + setting.id = id; + setting.name = g_localizeStrings.Get(label); + setting.type = SettingInfo::EDIT; + setting.enabled = enabled; + setting.data = str; + m_settings.push_back(setting); +} + +void CGUIDialogSettings::AddNumEdit(unsigned int id, int label, int *current, bool enabled) +{ + SettingInfo setting; + setting.id = id; + setting.name = g_localizeStrings.Get(label); + setting.type = SettingInfo::EDIT_NUM; + setting.enabled = enabled; + setting.data = current; + m_settings.push_back(setting); +} + void CGUIDialogSettings::AddButton(unsigned int id, int label, float *current, float min, float interval, float max, FORMATFUNCTION function) { SettingInfo setting; @@ -374,6 +473,17 @@ void CGUIDialogSettings::AddButton(unsigned int id, int label, float *current, f m_settings.push_back(setting); } +void CGUIDialogSettings::AddButton(unsigned int id, int label, CStdString *str, bool bOn) +{ + SettingInfo setting; + setting.id = id; + setting.name = g_localizeStrings.Get(label); + setting.type = SettingInfo::BUTTON_DIALOG; + setting.enabled = bOn; + setting.data = str; + m_settings.push_back(setting); +} + void CGUIDialogSettings::AddString(unsigned int id, int label, CStdString *current) { SettingInfo setting; @@ -396,6 +506,18 @@ void CGUIDialogSettings::AddBool(unsigned int id, int label, bool *on, bool enab m_settings.push_back(setting); } +void CGUIDialogSettings::AddSpin(unsigned int id, int label, int *current, unsigned int max, const SETTINGSTRINGS &entries) +{ + SettingInfo setting; + setting.id = id; + setting.name = g_localizeStrings.Get(label); + setting.type = SettingInfo::SPIN; + setting.data = current; + for (unsigned int i = 0; i < max; i++) + setting.entry.push_back(make_pair(i, entries[i])); + m_settings.push_back(setting); +} + void CGUIDialogSettings::AddSpin(unsigned int id, int label, int *current, unsigned int max, const int *entries) { SettingInfo setting; diff --git a/xbmc/settings/GUIDialogSettings.h b/xbmc/settings/GUIDialogSettings.h index 9768bf3a5b..e1866383d7 100644 --- a/xbmc/settings/GUIDialogSettings.h +++ b/xbmc/settings/GUIDialogSettings.h @@ -28,14 +28,16 @@ class CGUISpinControlEx; class CGUIButtonControl; class CGUIRadioButtonControl; class CGUISettingsSliderControl; +class CGUIEditControl; class CGUIImage; +typedef std::vector<CStdString> SETTINGSTRINGS; typedef CStdString (*FORMATFUNCTION) (float value, float min); class SettingInfo { public: - enum SETTING_TYPE { NONE=0, BUTTON, CHECK, CHECK_UCHAR, SPIN, SLIDER, SEPARATOR, STRING }; + enum SETTING_TYPE { NONE=0, EDIT, EDIT_NUM, BUTTON, BUTTON_DIALOG, CHECK, CHECK_UCHAR, SPIN, SLIDER, SEPARATOR, STRING }; SettingInfo() { id = 0; @@ -83,8 +85,12 @@ protected: void AddSetting(SettingInfo &setting, float width, int iControlID); + void AddEdit(unsigned int id, int label, CStdString *str, bool enabled = true); + void AddNumEdit(unsigned int id, int label, int *current, bool enabled = true); void AddButton(unsigned int id, int label, float *current = NULL, float min = 0, float interval = 0, float max = 0, FORMATFUNCTION function = NULL); + void AddButton(unsigned int it, int label, CStdString *str, bool bOn=true); void AddBool(unsigned int id, int label, bool *on, bool enabled = true); + void AddSpin(unsigned int id, int label, int *current, unsigned int max, const SETTINGSTRINGS &entries); void AddString(unsigned int id, int label, CStdString *current); void AddSpin(unsigned int id, int label, int *current, unsigned int max, const int *entries); void AddSpin(unsigned int id, int label, int *current, unsigned int min, unsigned int max, const char* minLabel = NULL); @@ -93,6 +99,8 @@ protected: void AddSlider(unsigned int id, int label, float *current, float min, float interval, float max, FORMATFUNCTION formatFunction, bool allowPopup = true); void AddSeparator(unsigned int id); + CGUIEditControl *m_pOriginalEdit; + CGUIEditControl *m_pOriginalEditNum; CGUISpinControlEx *m_pOriginalSpin; CGUIRadioButtonControl *m_pOriginalRadioButton; CGUIButtonControl *m_pOriginalSettingsButton; diff --git a/xbmc/settings/GUISettings.cpp b/xbmc/settings/GUISettings.cpp index 41cce36283..8edd76c9bf 100644 --- a/xbmc/settings/GUISettings.cpp +++ b/xbmc/settings/GUISettings.cpp @@ -47,6 +47,7 @@ #include "guilib/GUIFontManager.h" #include "utils/Weather.h" #include "LangInfo.h" +#include "pvr/PVRManager.h" #include "utils/XMLUtils.h" #if defined(TARGET_DARWIN) #include "osx/DarwinUtils.h" @@ -55,13 +56,17 @@ using namespace std; using namespace ADDON; +using namespace PVR; // String id's of the masks +#define MASK_DAYS 17999 +#define MASK_HOURS 17998 #define MASK_MINS 14044 #define MASK_SECS 14045 #define MASK_MS 14046 #define MASK_PERCENT 14047 #define MASK_KBPS 14048 +#define MASK_MB 17997 #define MASK_KB 14049 #define MASK_DB 14050 @@ -233,6 +238,16 @@ void CSettingsGroup::GetCategories(vecSettingsCategory &vecCategories) } } +#define SETTINGS_PICTURES WINDOW_SETTINGS_MYPICTURES - WINDOW_SETTINGS_START +#define SETTINGS_PROGRAMS WINDOW_SETTINGS_MYPROGRAMS - WINDOW_SETTINGS_START +#define SETTINGS_WEATHER WINDOW_SETTINGS_MYWEATHER - WINDOW_SETTINGS_START +#define SETTINGS_MUSIC WINDOW_SETTINGS_MYMUSIC - WINDOW_SETTINGS_START +#define SETTINGS_SYSTEM WINDOW_SETTINGS_SYSTEM - WINDOW_SETTINGS_START +#define SETTINGS_VIDEOS WINDOW_SETTINGS_MYVIDEOS - WINDOW_SETTINGS_START +#define SETTINGS_SERVICE WINDOW_SETTINGS_SERVICE - WINDOW_SETTINGS_START +#define SETTINGS_APPEARANCE WINDOW_SETTINGS_APPEARANCE - WINDOW_SETTINGS_START +#define SETTINGS_PVR WINDOW_SETTINGS_MYPVR - WINDOW_SETTINGS_START + // Settings are case sensitive CGUISettings::CGUISettings(void) { @@ -243,8 +258,8 @@ void CGUISettings::Initialize() ZeroMemory(&m_replayGain, sizeof(ReplayGainSettings)); // Pictures settings - AddGroup(0, 1); - CSettingsCategory* pic = AddCategory(0, "pictures", 14081); + AddGroup(SETTINGS_PICTURES, 1); + CSettingsCategory* pic = AddCategory(SETTINGS_PICTURES, "pictures", 14081); AddBool(pic, "pictures.usetags", 14082, true); AddBool(pic,"pictures.generatethumbs",13360,true); AddBool(pic, "pictures.useexifrotation", 20184, true); @@ -252,7 +267,7 @@ void CGUISettings::Initialize() // FIXME: hide this setting until it is properly respected. In the meanwhile, default to AUTO. AddInt(NULL, "pictures.displayresolution", 169, (int)RES_AUTORES, (int)RES_AUTORES, 1, (int)RES_AUTORES, SPIN_CONTROL_TEXT); - CSettingsCategory* cat = AddCategory(0, "slideshow", 108); + CSettingsCategory* cat = AddCategory(SETTINGS_PICTURES, "slideshow", 108); AddInt(cat, "slideshow.staytime", 12378, 5, 1, 1, 100, SPIN_CONTROL_INT_PLUS, MASK_SECS); AddBool(cat, "slideshow.displayeffects", 12379, true); AddBool(NULL, "slideshow.shuffle", 13319, false); @@ -261,15 +276,15 @@ void CGUISettings::Initialize() // AddGroup(1, 0); // My Weather settings - AddGroup(2, 8); - CSettingsCategory* wea = AddCategory(2, "weather", 16000); + AddGroup(SETTINGS_WEATHER, 8); + CSettingsCategory* wea = AddCategory(SETTINGS_WEATHER, "weather", 16000); AddInt(NULL, "weather.currentlocation", 0, 1, 1, 1, 3, SPIN_CONTROL_INT_PLUS); AddDefaultAddon(wea, "weather.addon", 24027, "weather.wunderground", ADDON_SCRIPT_WEATHER); AddString(wea, "weather.addonsettings", 21417, "", BUTTON_CONTROL_STANDARD, true); // My Music Settings - AddGroup(3, 2); - CSettingsCategory* ml = AddCategory(3,"musiclibrary",14022); + AddGroup(SETTINGS_MUSIC, 2); + CSettingsCategory* ml = AddCategory(SETTINGS_MUSIC,"musiclibrary",14022); AddBool(NULL, "musiclibrary.enabled", 418, true); AddBool(ml, "musiclibrary.showcompilationartists", 13414, true); AddSeparator(ml,"musiclibrary.sep1"); @@ -283,7 +298,7 @@ void CGUISettings::Initialize() AddString(ml, "musiclibrary.export", 20196, "", BUTTON_CONTROL_STANDARD); AddString(ml, "musiclibrary.import", 20197, "", BUTTON_CONTROL_STANDARD); - CSettingsCategory* mp = AddCategory(3, "musicplayer", 14086); + CSettingsCategory* mp = AddCategory(SETTINGS_MUSIC, "musicplayer", 14086); AddBool(mp, "musicplayer.autoplaynextitem", 489, true); AddBool(mp, "musicplayer.queuebydefault", 14084, false); AddSeparator(mp, "musicplayer.sep1"); @@ -302,7 +317,7 @@ void CGUISettings::Initialize() AddSeparator(mp, "musicplayer.sep3"); AddDefaultAddon(mp, "musicplayer.visualisation", 250, DEFAULT_VISUALISATION, ADDON_VIZ); - CSettingsCategory* mf = AddCategory(3, "musicfiles", 14081); + CSettingsCategory* mf = AddCategory(SETTINGS_MUSIC, "musicfiles", 14081); AddBool(mf, "musicfiles.usetags", 258, true); AddString(mf, "musicfiles.trackformat", 13307, "[%N. ]%A - %T", EDIT_CONTROL_INPUT, false, 16016); AddString(mf, "musicfiles.trackformatright", 13387, "%D", EDIT_CONTROL_INPUT, false, 16016); @@ -313,7 +328,7 @@ void CGUISettings::Initialize() AddString(NULL, "musicfiles.librarytrackformatright", 13387, "", EDIT_CONTROL_INPUT, false, 16016); AddBool(mf, "musicfiles.findremotethumbs", 14059, true); - CSettingsCategory* scr = AddCategory(3, "scrobbler", 15221); + CSettingsCategory* scr = AddCategory(SETTINGS_MUSIC, "scrobbler", 15221); AddBool(scr, "scrobbler.lastfmsubmit", 15201, false); AddBool(scr, "scrobbler.lastfmsubmitradio", 15250, false); AddString(scr,"scrobbler.lastfmusername", 15202, "", EDIT_CONTROL_INPUT, false, 15202); @@ -323,7 +338,7 @@ void CGUISettings::Initialize() AddString(scr, "scrobbler.librefmusername", 15218, "", EDIT_CONTROL_INPUT, false, 15218); AddString(scr, "scrobbler.librefmpass", 15219, "", EDIT_CONTROL_MD5_INPUT, false, 15219); - CSettingsCategory* acd = AddCategory(3, "audiocds", 620); + CSettingsCategory* acd = AddCategory(SETTINGS_MUSIC, "audiocds", 620); map<int,int> autocd; autocd.insert(make_pair(16018, AUTOCD_NONE)); autocd.insert(make_pair(14098, AUTOCD_PLAY)); @@ -357,7 +372,7 @@ void CGUISettings::Initialize() AddBool(acd, "audiocds.ejectonrip", 14099, true); #ifdef HAS_KARAOKE - CSettingsCategory* kar = AddCategory(3, "karaoke", 13327); + CSettingsCategory* kar = AddCategory(SETTINGS_MUSIC, "karaoke", 13327); AddBool(kar, "karaoke.enabled", 13323, false); // auto-popup the song selector dialog when the karaoke song was just finished and playlist is empty. AddBool(kar, "karaoke.autopopupselector", 22037, false); @@ -375,8 +390,8 @@ void CGUISettings::Initialize() #endif // System settings - AddGroup(4, 13000); - CSettingsCategory* vs = AddCategory(4, "videoscreen", 21373); + AddGroup(SETTINGS_SYSTEM, 13000); + CSettingsCategory* vs = AddCategory(SETTINGS_SYSTEM, "videoscreen", 21373); // this setting would ideally not be saved, as its value is systematically derived from videoscreen.screenmode. // contains a DISPLAYMODE @@ -439,7 +454,7 @@ void CGUISettings::Initialize() AddBool(vs, "videoscreen.haslcd", 4501, false); #endif - CSettingsCategory* ao = AddCategory(4, "audiooutput", 772); + CSettingsCategory* ao = AddCategory(SETTINGS_SYSTEM, "audiooutput", 772); map<int,int> audiomode; audiomode.insert(make_pair(338,AUDIO_ANALOG)); @@ -497,7 +512,7 @@ void CGUISettings::Initialize() guimode.insert(make_pair(34123, AE_SOUND_OFF )); AddInt(ao, "audiooutput.guisoundmode", 34120, AE_SOUND_IDLE, guimode, SPIN_CONTROL_TEXT); - CSettingsCategory* in = AddCategory(4, "input", 14094); + CSettingsCategory* in = AddCategory(SETTINGS_SYSTEM, "input", 14094); AddString(in, "input.peripherals", 35000, "", BUTTON_CONTROL_STANDARD); #if defined(TARGET_DARWIN) map<int,int> remotemode; @@ -524,7 +539,7 @@ void CGUISettings::Initialize() AddBool(in, "input.enablejoystick", 35100, true); #endif - CSettingsCategory* net = AddCategory(4, "network", 798); + CSettingsCategory* net = AddCategory(SETTINGS_SYSTEM, "network", 798); if (g_application.IsStandAlone()) { #if !defined(TARGET_DARWIN) @@ -562,7 +577,7 @@ void CGUISettings::Initialize() AddString(net, "network.httpproxypassword", 733, "", EDIT_CONTROL_HIDDEN_INPUT,true,733); AddInt(net, "network.bandwidth", 14041, 0, 0, 512, 100*1024, SPIN_CONTROL_INT_PLUS, MASK_KBPS, TEXT_OFF); - CSettingsCategory* pwm = AddCategory(4, "powermanagement", 14095); + CSettingsCategory* pwm = AddCategory(SETTINGS_SYSTEM, "powermanagement", 14095); // Note: Application.cpp might hide powersaving settings if not supported. AddInt(pwm, "powermanagement.displaysoff", 1450, 0, 0, 5, 120, SPIN_CONTROL_INT_PLUS, MASK_MINS, TEXT_OFF); AddInt(pwm, "powermanagement.shutdowntime", 357, 0, 0, 5, 120, SPIN_CONTROL_INT_PLUS, MASK_MINS, TEXT_OFF); @@ -587,17 +602,17 @@ void CGUISettings::Initialize() AddInt(pwm, "powermanagement.shutdownstate", 13008, POWERSTATE_QUIT, shutdown, SPIN_CONTROL_TEXT); } - CSettingsCategory* dbg = AddCategory(4, "debug", 14092); + CSettingsCategory* dbg = AddCategory(SETTINGS_SYSTEM, "debug", 14092); AddBool(dbg, "debug.showloginfo", 20191, false); AddPath(dbg, "debug.screenshotpath",20004,"select writable folder",BUTTON_CONTROL_PATH_INPUT,false,657); - CSettingsCategory* mst = AddCategory(4, "masterlock", 12360); + CSettingsCategory* mst = AddCategory(SETTINGS_SYSTEM, "masterlock", 12360); AddString(mst, "masterlock.lockcode" , 20100, "-", BUTTON_CONTROL_STANDARD); AddBool(mst, "masterlock.startuplock" , 20076,false); // hidden masterlock settings AddInt(NULL,"masterlock.maxretries", 12364, 3, 3, 1, 100, SPIN_CONTROL_TEXT); - AddCategory(4, "cache", 439); + AddCategory(SETTINGS_SYSTEM, "cache", 439); AddInt(NULL, "cache.harddisk", 14025, 256, 0, 256, 4096, SPIN_CONTROL_INT_PLUS, MASK_KB, TEXT_OFF); AddSeparator(NULL, "cache.sep1"); AddInt(NULL, "cachevideo.dvdrom", 14026, 2048, 0, 256, 16384, SPIN_CONTROL_INT_PLUS, MASK_KB, TEXT_OFF); @@ -614,8 +629,8 @@ void CGUISettings::Initialize() AddInt(NULL, "cacheunknown.internet", 14060, 4096, 0, 256, 16384, SPIN_CONTROL_INT_PLUS, MASK_KB, TEXT_OFF); // video settings - AddGroup(5, 3); - CSettingsCategory* vdl = AddCategory(5, "videolibrary", 14022); + AddGroup(SETTINGS_VIDEOS, 3); + CSettingsCategory* vdl = AddCategory(SETTINGS_VIDEOS, "videolibrary", 14022); AddBool(NULL, "videolibrary.enabled", 418, true); AddBool(vdl, "videolibrary.showunwatchedplots", 20369, true); AddBool(NULL, "videolibrary.seasonthumbs", 20382, true); @@ -635,7 +650,7 @@ void CGUISettings::Initialize() AddString(vdl, "videolibrary.export", 647, "", BUTTON_CONTROL_STANDARD); AddString(vdl, "videolibrary.import", 648, "", BUTTON_CONTROL_STANDARD); - CSettingsCategory* vp = AddCategory(5, "videoplayer", 14086); + CSettingsCategory* vp = AddCategory(SETTINGS_VIDEOS, "videoplayer", 14086); map<int, int> renderers; renderers.insert(make_pair(13416, RENDER_METHOD_AUTO)); @@ -740,7 +755,7 @@ void CGUISettings::Initialize() AddSeparator(vp, "videoplayer.sep5"); AddBool(vp, "videoplayer.teletextenabled", 23050, true); - CSettingsCategory* vid = AddCategory(5, "myvideos", 14081); + CSettingsCategory* vid = AddCategory(SETTINGS_VIDEOS, "myvideos", 14081); map<int, int> myVideosSelectActions; myVideosSelectActions.insert(make_pair(22080, SELECT_ACTION_CHOOSE)); @@ -753,7 +768,7 @@ void CGUISettings::Initialize() AddBool(vid, "myvideos.replacelabels", 20419, true); AddBool(NULL, "myvideos.extractthumb",20433, true); - CSettingsCategory* sub = AddCategory(5, "subtitles", 287); + CSettingsCategory* sub = AddCategory(SETTINGS_VIDEOS, "subtitles", 287); AddString(sub, "subtitles.font", 14089, "arial.ttf", SPIN_CONTROL_TEXT); AddInt(sub, "subtitles.height", 289, 28, 16, 2, 74, SPIN_CONTROL_TEXT); // use text as there is a disk based lookup needed @@ -778,7 +793,7 @@ void CGUISettings::Initialize() subtitleAlignments.insert(make_pair(21465, SUBTITLE_ALIGN_TOP_OUTSIDE)); AddInt(sub, "subtitles.align", 21460, SUBTITLE_ALIGN_MANUAL, subtitleAlignments, SPIN_CONTROL_TEXT); - CSettingsCategory* dvd = AddCategory(5, "dvds", 14087); + CSettingsCategory* dvd = AddCategory(SETTINGS_VIDEOS, "dvds", 14087); AddBool(dvd, "dvds.autorun", 14088, false); AddInt(dvd, "dvds.playerregion", 21372, 0, 0, 1, 8, SPIN_CONTROL_INT_PLUS, -1, TEXT_OFF); AddBool(dvd, "dvds.automenu", 21882, false); @@ -788,17 +803,17 @@ void CGUISettings::Initialize() AddDefaultAddon(NULL, "scrapers.musicvideosdefault", 21415, "metadata.musicvideos.last.fm", ADDON_SCRAPER_MUSICVIDEOS); // service settings - AddGroup(6, 14036); + AddGroup(SETTINGS_SERVICE, 14036); - CSettingsCategory* srvGeneral = AddCategory(6, "general", 16000); + CSettingsCategory* srvGeneral = AddCategory(SETTINGS_SERVICE, "general", 16000); AddString(srvGeneral,"services.devicename", 1271, "XBMC", EDIT_CONTROL_INPUT); - CSettingsCategory* srvUpnp = AddCategory(6, "upnp", 20187); + CSettingsCategory* srvUpnp = AddCategory(SETTINGS_SERVICE, "upnp", 20187); AddBool(srvUpnp, "services.upnpserver", 21360, false); AddBool(srvUpnp, "services.upnprenderer", 21881, false); #ifdef HAS_WEB_SERVER - CSettingsCategory* srvWeb = AddCategory(6, "webserver", 33101); + CSettingsCategory* srvWeb = AddCategory(SETTINGS_SERVICE, "webserver", 33101); AddBool(srvWeb, "services.webserver", 263, false); AddString(srvWeb,"services.webserverport", 730, CUtil::CanBindPrivileged()?"80":"8080", EDIT_CONTROL_NUMBER_INPUT, false, 730); AddString(srvWeb,"services.webserverusername",1048, "xbmc", EDIT_CONTROL_INPUT); @@ -806,7 +821,7 @@ void CGUISettings::Initialize() AddDefaultAddon(srvWeb, "services.webskin",199, DEFAULT_WEB_INTERFACE, ADDON_WEB_INTERFACE); #endif #ifdef HAS_EVENT_SERVER - CSettingsCategory* srvEvent = AddCategory(6, "remotecontrol", 790); + CSettingsCategory* srvEvent = AddCategory(SETTINGS_SERVICE, "remotecontrol", 790); AddBool(srvEvent, "services.esenabled", 791, true); AddString(NULL,"services.esport", 792, "9777", EDIT_CONTROL_NUMBER_INPUT, false, 792); AddInt(NULL, "services.esportrange", 793, 10, 1, 1, 100, SPIN_CONTROL_INT); @@ -816,7 +831,7 @@ void CGUISettings::Initialize() AddInt(NULL, "services.escontinuousdelay", 796, 25, 5, 5, 10000, SPIN_CONTROL_INT); #endif #ifdef HAS_ZEROCONF - CSettingsCategory* srvZeroconf = AddCategory(6, "zeroconf", 1259); + CSettingsCategory* srvZeroconf = AddCategory(SETTINGS_SERVICE, "zeroconf", 1259); #ifdef TARGET_WINDOWS AddBool(srvZeroconf, "services.zeroconf", 1260, false); #else @@ -825,21 +840,21 @@ void CGUISettings::Initialize() #endif #ifdef HAS_AIRPLAY - CSettingsCategory* srvAirplay = AddCategory(6, "airplay", 1273); + CSettingsCategory* srvAirplay = AddCategory(SETTINGS_SERVICE, "airplay", 1273); AddBool(srvAirplay, "services.airplay", 1270, false); AddBool(srvAirplay, "services.useairplaypassword", 1272, false); AddString(srvAirplay, "services.airplaypassword", 733, "", EDIT_CONTROL_HIDDEN_INPUT, false, 733); #endif #ifndef _WIN32 - CSettingsCategory* srvSmb = AddCategory(6, "smb", 1200); + CSettingsCategory* srvSmb = AddCategory(SETTINGS_SERVICE, "smb", 1200); AddString(srvSmb, "smb.winsserver", 1207, "", EDIT_CONTROL_IP_INPUT); AddString(srvSmb, "smb.workgroup", 1202, "WORKGROUP", EDIT_CONTROL_INPUT, false, 1202); #endif // appearance settings - AddGroup(7, 480); - CSettingsCategory* laf = AddCategory(7,"lookandfeel", 166); + AddGroup(SETTINGS_APPEARANCE, 480); + CSettingsCategory* laf = AddCategory(SETTINGS_APPEARANCE,"lookandfeel", 166); AddDefaultAddon(laf, "lookandfeel.skin",166,DEFAULT_SKIN, ADDON_SKIN); AddString(laf, "lookandfeel.skintheme",15111,"SKINDEFAULT", SPIN_CONTROL_TEXT); AddString(laf, "lookandfeel.skincolors",14078, "SKINDEFAULT", SPIN_CONTROL_TEXT); @@ -851,7 +866,7 @@ void CGUISettings::Initialize() AddBool(laf, "lookandfeel.enablerssfeeds",13305, true); AddString(laf, "lookandfeel.rssedit", 21450, "", BUTTON_CONTROL_STANDARD); - CSettingsCategory* loc = AddCategory(7, "locale", 14090); + CSettingsCategory* loc = AddCategory(SETTINGS_APPEARANCE, "locale", 14090); AddString(loc, "locale.language",248,"english", SPIN_CONTROL_TEXT); AddString(loc, "locale.country", 20026, "USA", SPIN_CONTROL_TEXT); AddString(loc, "locale.charset", 14091, "DEFAULT", SPIN_CONTROL_TEXT); // charset is set by the language file @@ -882,7 +897,7 @@ void CGUISettings::Initialize() AddString(loc, "locale.audiolanguage", 285, "original", SPIN_CONTROL_TEXT); AddString(loc, "locale.subtitlelanguage", 286, "original", SPIN_CONTROL_TEXT); - CSettingsCategory* fl = AddCategory(7, "filelists", 14081); + CSettingsCategory* fl = AddCategory(SETTINGS_APPEARANCE, "filelists", 14081); AddBool(fl, "filelists.showparentdiritems", 13306, true); AddBool(fl, "filelists.showextensions", 497, true); AddBool(fl, "filelists.ignorethewhensorting", 13399, true); @@ -890,7 +905,7 @@ void CGUISettings::Initialize() AddBool(fl, "filelists.showaddsourcebuttons", 21382, true); AddBool(fl, "filelists.showhidden", 21330, false); - CSettingsCategory* ss = AddCategory(7, "screensaver", 360); + CSettingsCategory* ss = AddCategory(SETTINGS_APPEARANCE, "screensaver", 360); AddInt(ss, "screensaver.time", 355, 3, 1, 1, 60, SPIN_CONTROL_INT_PLUS, MASK_MINS); AddDefaultAddon(ss, "screensaver.mode", 356, "screensaver.xbmc.builtin.dim", ADDON_SCREENSAVER); AddString(ss, "screensaver.settings", 21417, "", BUTTON_CONTROL_STANDARD); @@ -899,14 +914,80 @@ void CGUISettings::Initialize() AddBool(ss, "screensaver.usemusicvisinstead", 13392, true); AddBool(ss, "screensaver.usedimonpause", 22014, true); - AddCategory(7, "window", 0); + AddCategory(SETTINGS_APPEARANCE, "window", 0); AddInt(NULL, "window.width", 0, 720, 10, 1, INT_MAX, SPIN_CONTROL_INT); AddInt(NULL, "window.height", 0, 480, 10, 1, INT_MAX, SPIN_CONTROL_INT); AddPath(NULL,"system.playlistspath",20006,"set default",BUTTON_CONTROL_PATH_INPUT,false); - // PVR-related setting typically used by skins that are aimed at PVR and non-PVR builds - AddBool(NULL, "pvrmanager.enabled", 449, false); + // tv settings (access over TV menu from home window) + AddGroup(SETTINGS_PVR, 19180); + CSettingsCategory* pvr = AddCategory(SETTINGS_PVR, "pvrmanager", 128); + AddBool(pvr, "pvrmanager.enabled", 449, false); + AddSeparator(pvr, "pvrmanager.sep1"); + AddBool(pvr, "pvrmanager.syncchannelgroups", 19221, true); + AddBool(pvr, "pvrmanager.backendchannelorder", 19231, false); + AddBool(pvr, "pvrmanager.usebackendchannelnumbers", 19234, false); + AddSeparator(pvr, "pvrmanager.sep2"); + AddString(pvr, "pvrmanager.channelmanager", 19199, "", BUTTON_CONTROL_STANDARD); + AddString(pvr, "pvrmanager.channelscan", 19117, "", BUTTON_CONTROL_STANDARD); + AddString(pvr, "pvrmanager.resetdb", 19185, "", BUTTON_CONTROL_STANDARD); + AddSeparator(pvr, "pvrmanager.sep3"); + AddBool(pvr, "pvrmanager.hideconnectionlostwarning", 19269, false); + + CSettingsCategory* pvrm = AddCategory(SETTINGS_PVR, "pvrmenu", 19181); + AddBool(pvrm, "pvrmenu.infoswitch", 19178, true); + AddBool(pvrm, "pvrmenu.infotimeout", 19179, true); + AddBool(pvrm, "pvrmenu.closechannelosdonswitch", 19229, false); + AddInt(pvrm, "pvrmenu.infotime", 19184, 5, 1, 1, 10, SPIN_CONTROL_INT_PLUS, MASK_SECS); + AddBool(pvrm, "pvrmenu.hidevideolength", 19169, true); + AddSeparator(pvrm, "pvrmenu.sep1"); + AddString(pvrm, "pvrmenu.iconpath", 19018, "", BUTTON_CONTROL_PATH_INPUT, false, 657); + AddString(pvrm, "pvrmenu.searchicons", 19167, "", BUTTON_CONTROL_STANDARD); + + CSettingsCategory* pvre = AddCategory(SETTINGS_PVR, "epg", 19069); + AddInt(pvre, "epg.defaultguideview", 19065, GUIDE_VIEW_NOW, GUIDE_VIEW_CHANNEL, 1, GUIDE_VIEW_TIMELINE, SPIN_CONTROL_TEXT); + AddInt(pvre, "epg.daystodisplay", 19182, 2, 1, 1, 14, SPIN_CONTROL_INT_PLUS, MASK_DAYS); + AddSeparator(pvre, "epg.sep1"); + AddInt(pvre, "epg.epgupdate", 19071, 120, 15, 15, 480, SPIN_CONTROL_INT_PLUS, MASK_MINS); + AddBool(pvre, "epg.preventupdateswhileplayingtv", 19230, false); + AddBool(pvre, "epg.ignoredbforclient", 19072, false); + AddBool(pvre, "epg.hidenoinfoavailable", 19268, true); + AddString(pvre, "epg.resetepg", 19187, "", BUTTON_CONTROL_STANDARD); + + CSettingsCategory* pvrp = AddCategory(SETTINGS_PVR, "pvrplayback", 19177); + AddBool(pvrp, "pvrplayback.playminimized", 19171, true); + AddInt(pvrp, "pvrplayback.startlast", 19189, START_LAST_CHANNEL_OFF, START_LAST_CHANNEL_OFF, 1, START_LAST_CHANNEL_ON, SPIN_CONTROL_TEXT); + AddBool(pvrp, "pvrplayback.switchautoclose", 19168, true); + AddBool(pvrp, "pvrplayback.signalquality", 19037, true); + AddSeparator(pvrp, "pvrplayback.sep1"); + AddInt(pvrp, "pvrplayback.scantime", 19170, 15, 1, 1, 60, SPIN_CONTROL_INT_PLUS, MASK_SECS); + AddInt(pvrp, "pvrplayback.channelentrytimeout", 19073, 0, 0, 250, 2000, SPIN_CONTROL_INT_PLUS, MASK_MS); + + CSettingsCategory* pvrr = AddCategory(SETTINGS_PVR, "pvrrecord", 19043); + AddInt(pvrr, "pvrrecord.instantrecordtime", 19172, 180, 1, 1, 720, SPIN_CONTROL_INT_PLUS, MASK_MINS); + AddInt(pvrr, "pvrrecord.defaultpriority", 19173, 50, 1, 1, 100, SPIN_CONTROL_INT_PLUS); + AddInt(pvrr, "pvrrecord.defaultlifetime", 19174, 99, 1, 1, 365, SPIN_CONTROL_INT_PLUS, MASK_DAYS); + AddInt(pvrr, "pvrrecord.marginstart", 19175, 2, 0, 1, 60, SPIN_CONTROL_INT_PLUS, MASK_MINS); + AddInt(pvrr, "pvrrecord.marginend", 19176, 10, 0, 1, 60, SPIN_CONTROL_INT_PLUS, MASK_MINS); + AddSeparator(pvrr, "pvrrecord.sep1"); + AddBool(pvrr, "pvrrecord.timernotifications", 19233, true); + + CSettingsCategory* pvrpwr = AddCategory(SETTINGS_PVR, "pvrpowermanagement", 14095); + AddBool(pvrpwr, "pvrpowermanagement.enabled", 305, false); + AddSeparator(pvrpwr, "pvrpowermanagement.sep1"); + AddInt(pvrpwr, "pvrpowermanagement.backendidletime", 19244, 15, 0, 5, 360, SPIN_CONTROL_INT_PLUS, MASK_MINS, TEXT_OFF); + AddString(pvrpwr, "pvrpowermanagement.setwakeupcmd", 19245, "/usr/bin/setwakeup.sh", EDIT_CONTROL_INPUT, true); + AddInt(pvrpwr, "pvrpowermanagement.prewakeup", 19246, 15, 0, 1, 60, SPIN_CONTROL_INT_PLUS, MASK_MINS, TEXT_OFF); + AddSeparator(pvrpwr, "pvrpowermanagement.sep2"); + AddBool(pvrpwr, "pvrpowermanagement.dailywakeup", 19247, false); + AddString(pvrpwr, "pvrpowermanagement.dailywakeuptime", 19248, "00:00:00", EDIT_CONTROL_INPUT); + + CSettingsCategory* pvrpa = AddCategory(SETTINGS_PVR, "pvrparental", 19259); + AddBool(pvrpa, "pvrparental.enabled", 449 , false); + AddSeparator(pvrpa, "pvrparental.sep1"); + AddString(pvrpa, "pvrparental.pin", 19261, "", EDIT_CONTROL_HIDDEN_NUMBER_VERIFY_NEW, true); + AddInt(pvrpa, "pvrparental.duration", 19260, 300, 5, 5, 1200, SPIN_CONTROL_INT_PLUS, MASK_SECS); } CGUISettings::~CGUISettings(void) @@ -995,6 +1076,9 @@ void CGUISettings::SetBool(const char *strSetting, bool bSetting) if (it != settingsMap.end()) { // old category ((CSettingBool*)(*it).second)->SetData(bSetting); + + SetChanged(); + return ; } // Assert here and write debug output @@ -1008,6 +1092,9 @@ void CGUISettings::ToggleBool(const char *strSetting) if (it != settingsMap.end()) { // old category ((CSettingBool*)(*it).second)->SetData(!((CSettingBool *)(*it).second)->GetData()); + + SetChanged(); + return ; } // Assert here and write debug output @@ -1043,6 +1130,9 @@ void CGUISettings::SetFloat(const char *strSetting, float fSetting) if (it != settingsMap.end()) { ((CSettingFloat *)(*it).second)->SetData(fSetting); + + SetChanged(); + return ; } // Assert here and write debug output @@ -1114,6 +1204,9 @@ void CGUISettings::SetInt(const char *strSetting, int iSetting) if (it != settingsMap.end()) { ((CSettingInt *)(*it).second)->SetData(iSetting); + + SetChanged(); + return ; } // Assert here and write debug output @@ -1185,6 +1278,9 @@ void CGUISettings::SetString(const char *strSetting, const char *strData) if (it != settingsMap.end()) { ((CSettingString *)(*it).second)->SetData(strData); + + SetChanged(); + return ; } // Assert here and write debug output @@ -1327,6 +1423,8 @@ void CGUISettings::LoadFromXML(TiXmlElement *pRootElement, mapIter &it, bool adv } } } + + SetChanged(); } void CGUISettings::SaveXML(TiXmlNode *pRootNode) @@ -1363,6 +1461,8 @@ void CGUISettings::SaveXML(TiXmlNode *pRootNode) } } } + + SetChanged(); } void CGUISettings::Clear() @@ -1373,6 +1473,8 @@ void CGUISettings::Clear() for (unsigned int i = 0; i < settingsGroups.size(); i++) delete settingsGroups[i]; settingsGroups.clear(); + + SetChanged(); } float square_error(float x, float y) @@ -1439,6 +1541,8 @@ void CGUISettings::SetResolution(RESOLUTION res) } SetString("videoscreen.screenmode", mode); m_LookAndFeelResolution = res; + + SetChanged(); } bool CGUISettings::SetLanguage(const CStdString &strLanguage) @@ -1470,6 +1574,7 @@ bool CGUISettings::SetLanguage(const CStdString &strLanguage) // also tell our weather and skin to reload as these are localized g_weatherManager.Refresh(); + g_PVRManager.LocalizationChanged(); g_application.ReloadSkin(); } diff --git a/xbmc/settings/GUISettings.h b/xbmc/settings/GUISettings.h index 350d7fcba2..ef42e3b59c 100644 --- a/xbmc/settings/GUISettings.h +++ b/xbmc/settings/GUISettings.h @@ -25,6 +25,7 @@ #include <map> #include "guilib/Resolution.h" #include "addons/IAddon.h" +#include "utils/Observer.h" class TiXmlNode; class TiXmlElement; @@ -130,6 +131,15 @@ class TiXmlElement; #define APM_HIPOWER_STANDBY 2 #define APM_LOPOWER_STANDBY 3 +#define GUIDE_VIEW_CHANNEL 0 +#define GUIDE_VIEW_NOW 1 +#define GUIDE_VIEW_NEXT 2 +#define GUIDE_VIEW_TIMELINE 3 + +#define START_LAST_CHANNEL_OFF 0 +#define START_LAST_CHANNEL_MIN 1 +#define START_LAST_CHANNEL_ON 2 + #define SETTINGS_TYPE_BOOL 1 #define SETTINGS_TYPE_FLOAT 2 #define SETTINGS_TYPE_INT 3 @@ -139,20 +149,21 @@ class TiXmlElement; #define SETTINGS_TYPE_PATH 7 #define SETTINGS_TYPE_ADDON 8 -#define CHECKMARK_CONTROL 1 -#define SPIN_CONTROL_FLOAT 2 -#define SPIN_CONTROL_INT 3 -#define SPIN_CONTROL_INT_PLUS 4 -#define SPIN_CONTROL_TEXT 5 -#define EDIT_CONTROL_INPUT 6 -#define EDIT_CONTROL_HIDDEN_INPUT 7 -#define EDIT_CONTROL_NUMBER_INPUT 8 -#define EDIT_CONTROL_IP_INPUT 9 -#define EDIT_CONTROL_MD5_INPUT 10 -#define BUTTON_CONTROL_STANDARD 11 -#define BUTTON_CONTROL_MISC_INPUT 12 -#define BUTTON_CONTROL_PATH_INPUT 13 -#define SEPARATOR_CONTROL 14 +#define CHECKMARK_CONTROL 1 +#define SPIN_CONTROL_FLOAT 2 +#define SPIN_CONTROL_INT 3 +#define SPIN_CONTROL_INT_PLUS 4 +#define SPIN_CONTROL_TEXT 5 +#define EDIT_CONTROL_INPUT 6 +#define EDIT_CONTROL_HIDDEN_INPUT 7 +#define EDIT_CONTROL_NUMBER_INPUT 8 +#define EDIT_CONTROL_IP_INPUT 9 +#define EDIT_CONTROL_MD5_INPUT 10 +#define BUTTON_CONTROL_STANDARD 11 +#define BUTTON_CONTROL_MISC_INPUT 12 +#define BUTTON_CONTROL_PATH_INPUT 13 +#define SEPARATOR_CONTROL 14 +#define EDIT_CONTROL_HIDDEN_NUMBER_VERIFY_NEW 15 #define REPLAY_GAIN_NONE 0 #define REPLAY_GAIN_ALBUM 1 @@ -449,7 +460,7 @@ private: typedef std::vector<CSetting *> vecSettings; -class CGUISettings +class CGUISettings : public Observable { public: CGUISettings(); diff --git a/xbmc/settings/GUIWindowSettingsCategory.cpp b/xbmc/settings/GUIWindowSettingsCategory.cpp index 8e84b1c19c..179791ed13 100644 --- a/xbmc/settings/GUIWindowSettingsCategory.cpp +++ b/xbmc/settings/GUIWindowSettingsCategory.cpp @@ -45,6 +45,9 @@ #include "network/libscrobbler/lastfmscrobbler.h" #include "network/libscrobbler/librefmscrobbler.h" #include "GUIPassword.h" +#include "GUIInfoManager.h" +#include "dialogs/GUIDialogGamepad.h" +#include "dialogs/GUIDialogNumeric.h" #include "dialogs/GUIDialogFileBrowser.h" #include "addons/GUIDialogAddonSettings.h" #include "addons/GUIWindowAddonBrowser.h" @@ -101,6 +104,9 @@ #include "utils/URIUtils.h" #include "utils/SystemInfo.h" #include "windowing/WindowingFactory.h" +#include "pvr/dialogs/GUIDialogPVRChannelManager.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClients.h" #if defined(HAVE_LIBCRYSTALHD) #include "cores/dvdplayer/DVDCodecs/Video/CrystalHD.h" @@ -117,6 +123,7 @@ using namespace std; using namespace XFILE; using namespace ADDON; +using namespace PVR; using namespace PERIPHERALS; #define CONTROL_GROUP_BUTTONS 0 @@ -143,7 +150,17 @@ CGUIWindowSettingsCategory::CGUIWindowSettingsCategory(void) m_pOriginalImage = NULL; m_pOriginalEdit = NULL; // set the correct ID range... - m_idRange = 8; + m_idRange.clear(); + m_idRange.push_back(WINDOW_SETTINGS_MYPICTURES); + m_idRange.push_back(WINDOW_SETTINGS_MYPROGRAMS); + m_idRange.push_back(WINDOW_SETTINGS_MYWEATHER); + m_idRange.push_back(WINDOW_SETTINGS_MYMUSIC); + m_idRange.push_back(WINDOW_SETTINGS_SYSTEM); + m_idRange.push_back(WINDOW_SETTINGS_MYVIDEOS); + m_idRange.push_back(WINDOW_SETTINGS_SERVICE); + m_idRange.push_back(WINDOW_SETTINGS_APPEARANCE); + m_idRange.push_back(WINDOW_SETTINGS_MYPVR); + m_iScreen = 0; m_strOldTrackFormat = ""; m_strOldTrackFormatRight = ""; @@ -197,6 +214,15 @@ bool CGUIWindowSettingsCategory::OnMessage(CGUIMessage &message) return false; } } + if (m_vecSections[focusedControl-CONTROL_START_BUTTONS]->m_strCategory == "pvrparental") + { + if (!g_PVRManager.CheckParentalPIN(g_localizeStrings.Get(19262).c_str())) + { // unable to go to this category - focus the previous one + SET_CONTROL_FOCUS(CONTROL_START_BUTTONS + m_iSection, 0); + return false; + } + } + m_iSection = focusedControl - CONTROL_START_BUTTONS; CreateSettings(); @@ -379,6 +405,10 @@ void CGUIWindowSettingsCategory::CreateSettings() FillInScreens(strSetting, g_guiSettings.GetResolution()); else if (strSetting.Equals("videoscreen.resolution")) FillInResolutions(strSetting, g_guiSettings.GetInt("videoscreen.screen"), g_guiSettings.GetResolution(), false); + else if (strSetting.Equals("epg.defaultguideview")) + FillInEpgGuideView(pSetting); + else if (strSetting.Equals("pvrplayback.startlast")) + FillInPvrStartLastChannel(pSetting); continue; } #ifdef HAS_WEB_SERVER @@ -678,6 +708,22 @@ void CGUIWindowSettingsCategory::UpdateSettings() CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID()); if (pControl) pControl->SetEnabled(g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE); } + else if (strSetting.Equals("pvrmanager.channelscan")) + { + CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID()); + if (pControl) pControl->SetEnabled(g_guiSettings.GetBool("pvrmanager.enabled") && g_PVRClients->GetClientsSupportingChannelScan().size() > 0); + } + else if (strSetting.Equals("pvrmanager.channelmanager") || strSetting.Equals("pvrmenu.searchicons")) + { + CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID()); + if (pControl) pControl->SetEnabled(g_guiSettings.GetBool("pvrmanager.enabled")); + } + else if (!strSetting.Equals("pvrparental.enabled") && + (strSetting.Equals("pvrparental.pin") || strSetting.Equals("pvrparental.duration"))) + { + CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID()); + if (pControl) pControl->SetEnabled(g_guiSettings.GetBool("pvrparental.enabled")); + } else if (!strSetting.Equals("services.esenabled") && strSetting.Left(11).Equals("services.es")) { @@ -963,6 +1009,9 @@ void CGUIWindowSettingsCategory::UpdateSettings() pControl->SetEnabled(g_peripherals.GetNumberOfPeripherals() > 0); } } + + g_guiSettings.SetChanged(); + g_guiSettings.NotifyObservers(ObservableMessageGuiSettings, true); } void CGUIWindowSettingsCategory::OnClick(CBaseSettingControl *pSettingControl) @@ -1538,7 +1587,7 @@ void CGUIWindowSettingsCategory::OnSettingChanged(CBaseSettingControl *pSettingC if (CAddonMgr::Get().GetAddon(g_guiSettings.GetString("screensaver.mode"), addon, ADDON_SCREENSAVER)) CGUIDialogAddonSettings::ShowAndGetInput(addon); } - else if (strSetting.Equals("debug.screenshotpath") || strSetting.Equals("audiocds.recordingpath") || strSetting.Equals("subtitles.custompath")) + else if (strSetting.Equals("debug.screenshotpath") || strSetting.Equals("audiocds.recordingpath") || strSetting.Equals("subtitles.custompath") || strSetting.Equals("pvrmenu.iconpath")) { CSettingString *pSettingString = (CSettingString *)pSettingControl->GetSetting(); CStdString path = g_guiSettings.GetString(strSetting,false); @@ -1546,7 +1595,11 @@ void CGUIWindowSettingsCategory::OnSettingChanged(CBaseSettingControl *pSettingC bool bWriteOnly = true; - if (strSetting.Equals("subtitles.custompath")) + if (strSetting.Equals("pvrmenu.iconpath")) + { + bWriteOnly = false; + } + else if (strSetting.Equals("subtitles.custompath")) { bWriteOnly = false; shares = g_settings.m_videoSources; @@ -1705,6 +1758,13 @@ void CGUIWindowSettingsCategory::OnSettingChanged(CBaseSettingControl *pSettingC } #endif } + else if (strSetting.Equals("pvrmanager.enabled")) + { + if (g_guiSettings.GetBool("pvrmanager.enabled")) + g_application.StartPVRManager(); + else + g_application.StopPVRManager(); + } else if (strSetting.Equals("masterlock.lockcode")) { // Now Prompt User to enter the old and then the new MasterCode! @@ -1832,6 +1892,34 @@ void CGUIWindowSettingsCategory::OnSettingChanged(CBaseSettingControl *pSettingC { CUtil::DeleteVideoDatabaseDirectoryCache(); } + else if (strSetting.Equals("pvrmenu.searchicons") && g_PVRManager.IsStarted()) + { + g_PVRManager.SearchMissingChannelIcons(); + } + else if (strSetting.Equals("pvrmanager.resetdb")) + { + if (g_PVRManager.CheckParentalPIN(g_localizeStrings.Get(19262).c_str()) && + CGUIDialogYesNo::ShowAndGetInput(19098, 19186, 750, 0)) + g_PVRManager.ResetDatabase(); + } + else if (strSetting.Equals("epg.resetepg")) + { + if (CGUIDialogYesNo::ShowAndGetInput(19098, 19188, 750, 0)) + g_PVRManager.ResetEPG(); + } + else if (strSetting.Equals("pvrmanager.channelscan") && g_PVRManager.IsStarted()) + { + if (CGUIDialogYesNo::ShowAndGetInput(19098, 19118, 19194, 0)) + g_PVRManager.StartChannelScan(); + } + else if (strSetting.Equals("pvrmanager.channelmanager") && g_PVRManager.IsStarted()) + { + CGUIDialogPVRChannelManager *dialog = (CGUIDialogPVRChannelManager *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_CHANNEL_MANAGER); + if (dialog) + { + dialog->DoModal(); + } + } else if (strSetting.compare(0, 12, "audiooutput.") == 0) { if (strSetting.Equals("audiooutput.audiodevice")) @@ -1853,6 +1941,24 @@ void CGUIWindowSettingsCategory::OnSettingChanged(CBaseSettingControl *pSettingC CAEFactory::OnSettingsChange(strSetting); } + else if (strSetting.Equals("pvrparental.enabled")) + { + if (g_guiSettings.GetBool("pvrparental.enabled") && g_guiSettings.GetString("pvrparental.pin").GetLength() == 0) + { + CStdString newPassword = ""; + bool bNewPassword = CGUIDialogNumeric::ShowAndVerifyNewPassword(newPassword); + if (bNewPassword) + { + // password set... save it + g_guiSettings.SetString("pvrparental.pin", newPassword); + } + else + { + // password not set... disable parental + g_guiSettings.SetBool("pvrparental.enabled", false); + } + } + } UpdateSettings(); } @@ -1919,7 +2025,8 @@ CGUIControl* CGUIWindowSettingsCategory::AddSetting(CSetting *pSetting, float wi pSetting->GetControlType() == EDIT_CONTROL_HIDDEN_INPUT || pSetting->GetControlType() == EDIT_CONTROL_MD5_INPUT || pSetting->GetControlType() == EDIT_CONTROL_NUMBER_INPUT || - pSetting->GetControlType() == EDIT_CONTROL_IP_INPUT) + pSetting->GetControlType() == EDIT_CONTROL_IP_INPUT || + pSetting->GetControlType() == EDIT_CONTROL_HIDDEN_NUMBER_VERIFY_NEW ) { pControl = new CGUIEditControl(*m_pOriginalEdit); if (!pControl) return NULL; @@ -2681,6 +2788,33 @@ void CGUIWindowSettingsCategory::FillInNetworkInterfaces(CSetting *pSetting, flo pControl->AddLabel(vecInterfaces[i], iInterface++); } +void CGUIWindowSettingsCategory::FillInEpgGuideView(CSetting *pSetting) +{ + CSettingInt *pSettingInt = (CSettingInt*)pSetting; + CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(GetSetting(pSetting->GetSetting())->GetID()); + pControl->Clear(); + + pControl->AddLabel(g_localizeStrings.Get(19029), GUIDE_VIEW_CHANNEL); + pControl->AddLabel(g_localizeStrings.Get(19030), GUIDE_VIEW_NOW); + pControl->AddLabel(g_localizeStrings.Get(19031), GUIDE_VIEW_NEXT); + pControl->AddLabel(g_localizeStrings.Get(19032), GUIDE_VIEW_TIMELINE); + + pControl->SetValue(pSettingInt->GetData()); +} + +void CGUIWindowSettingsCategory::FillInPvrStartLastChannel(CSetting *pSetting) +{ + CSettingInt *pSettingInt = (CSettingInt*)pSetting; + CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(GetSetting(pSetting->GetSetting())->GetID()); + pControl->Clear(); + + pControl->AddLabel(g_localizeStrings.Get(106), START_LAST_CHANNEL_OFF); + pControl->AddLabel(g_localizeStrings.Get(19190), START_LAST_CHANNEL_MIN); + pControl->AddLabel(g_localizeStrings.Get(107), START_LAST_CHANNEL_ON); + + pControl->SetValue(pSettingInt->GetData()); +} + void CGUIWindowSettingsCategory::FillInAudioDevices(CSetting* pSetting, bool Passthrough) { CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(GetSetting(pSetting->GetSetting())->GetID()); diff --git a/xbmc/settings/GUIWindowSettingsCategory.h b/xbmc/settings/GUIWindowSettingsCategory.h index ccf3468fe0..51b963ceb7 100644 --- a/xbmc/settings/GUIWindowSettingsCategory.h +++ b/xbmc/settings/GUIWindowSettingsCategory.h @@ -57,6 +57,8 @@ protected: void FillInStartupWindow(CSetting *pSetting); void FillInViewModes(CSetting *pSetting, int windowID); void FillInSortMethods(CSetting *pSetting, int windowID); + void FillInEpgGuideView(CSetting *pSetting); + void FillInPvrStartLastChannel(CSetting *pSetting); void FillInSkinThemes(CSetting *pSetting); void FillInSkinColors(CSetting *pSetting); diff --git a/xbmc/settings/Settings.cpp b/xbmc/settings/Settings.cpp index 3d6b19db37..a8837c5c2e 100644 --- a/xbmc/settings/Settings.cpp +++ b/xbmc/settings/Settings.cpp @@ -105,6 +105,8 @@ void CSettings::Initialize() m_discStubExtensions = ".disc"; // internal music extensions m_musicExtensions += "|.sidstream|.oggstream|.nsfstream|.asapstream|.cdda"; + // internal video extensions + m_videoExtensions += "|.pvr"; #if defined(TARGET_DARWIN) CStdString logDir = getenv("HOME"); diff --git a/xbmc/settings/SettingsControls.cpp b/xbmc/settings/SettingsControls.cpp index 760d8a78d5..fb44ca1f31 100644 --- a/xbmc/settings/SettingsControls.cpp +++ b/xbmc/settings/SettingsControls.cpp @@ -194,6 +194,8 @@ CEditSettingControl::CEditSettingControl(CGUIEditControl *pEdit, int id, CSettin m_pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_IPADDRESS, heading); else if (pSetting->GetControlType() == EDIT_CONTROL_NUMBER_INPUT) m_pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_NUMBER, heading); + else if (pSetting->GetControlType() == EDIT_CONTROL_HIDDEN_NUMBER_VERIFY_NEW) + m_pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW, heading); else m_pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_TEXT, heading); Update(); diff --git a/xbmc/settings/VideoSettings.h b/xbmc/settings/VideoSettings.h index 6641145953..4b462bf837 100644 --- a/xbmc/settings/VideoSettings.h +++ b/xbmc/settings/VideoSettings.h @@ -58,14 +58,13 @@ enum EINTERLACEMETHOD VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF=13, VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL=14, VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF=15, - VS_INTERLACEMETHOD_DEINTERLACE_HALF=16, - VS_INTERLACEMETHOD_DXVA_BOB = 17, VS_INTERLACEMETHOD_DXVA_BEST = 18, // VS_INTERLACEMETHOD_DXVA_ANY = 19, Legacy VS_INTERLACEMETHOD_SW_BLEND = 20, + VS_INTERLACEMETHOD_AUTO_ION = 21, VS_INTERLACEMETHOD_MAX // do not use and keep as last enum value. }; diff --git a/xbmc/system.h b/xbmc/system.h index 7104fc7072..584a2b3aeb 100644 --- a/xbmc/system.h +++ b/xbmc/system.h @@ -39,6 +39,7 @@ #define HAS_UPNP #define HAS_VIDEO_PLAYBACK #define HAS_VISUALISATION +#define HAS_PVRCLIENTS #ifdef HAVE_LIBMICROHTTPD #define HAS_WEB_SERVER diff --git a/xbmc/utils/DatabaseUtils.h b/xbmc/utils/DatabaseUtils.h index 6b384ad176..4f4027710e 100644 --- a/xbmc/utils/DatabaseUtils.h +++ b/xbmc/utils/DatabaseUtils.h @@ -111,6 +111,7 @@ typedef enum { FieldSubtitleLanguage, FieldProductionCode, FieldTag, + FieldChannelName, FieldInstruments, FieldBiography, FieldBorn, diff --git a/xbmc/utils/Makefile b/xbmc/utils/Makefile index af362eb857..7085eea429 100644 --- a/xbmc/utils/Makefile +++ b/xbmc/utils/Makefile @@ -36,6 +36,7 @@ SRCS=AlarmClock.cpp \ LCDFactory.cpp \ log.cpp \ md5.cpp \ + Observer.cpp \ Mime.cpp \ PerformanceSample.cpp \ PerformanceStats.cpp \ @@ -54,6 +55,7 @@ SRCS=AlarmClock.cpp \ StreamUtils.cpp \ StringUtils.cpp \ SystemInfo.cpp \ + TextSearch.cpp \ TimeSmoother.cpp \ TimeUtils.cpp \ TuxBoxUtil.cpp \ diff --git a/xbmc/utils/Observer.cpp b/xbmc/utils/Observer.cpp new file mode 100644 index 0000000000..4903e0e778 --- /dev/null +++ b/xbmc/utils/Observer.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2005-2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "Application.h" +#include "Observer.h" +#include "threads/SingleLock.h" +#include "utils/JobManager.h" + +using namespace std; +using namespace ANNOUNCEMENT; + +class ObservableMessageJob : public CJob +{ +private: + Observable m_observable; + ObservableMessage m_message; +public: + ObservableMessageJob(const Observable &obs, const ObservableMessage message); + virtual ~ObservableMessageJob() {} + virtual const char *GetType() const { return "observable-message-job"; } + + virtual bool DoWork(); +}; + +Observer::~Observer(void) +{ + StopObserving(); +} + +void Observer::StopObserving(void) +{ + CSingleLock lock(m_obsCritSection); + for (unsigned int iObsPtr = 0; iObsPtr < m_observables.size(); iObsPtr++) + m_observables.at(iObsPtr)->UnregisterObserver(this); + m_observables.clear(); +} + +bool Observer::IsObserving(const Observable &obs) const +{ + CSingleLock lock(m_obsCritSection); + return find(m_observables.begin(), m_observables.end(), &obs) != m_observables.end(); +} + +void Observer::RegisterObservable(Observable *obs) +{ + CSingleLock lock(m_obsCritSection); + if (!IsObserving(*obs)) + m_observables.push_back(obs); +} + +void Observer::UnregisterObservable(Observable *obs) +{ + CSingleLock lock(m_obsCritSection); + vector<Observable *>::iterator it = find(m_observables.begin(), m_observables.end(), obs); + if (it != m_observables.end()) + m_observables.erase(it); +} + +Observable::Observable() : + m_bObservableChanged(false), + m_bAsyncAllowed(true) +{ + CAnnouncementManager::AddAnnouncer(this); +} + +Observable::~Observable() +{ + CAnnouncementManager::RemoveAnnouncer(this); + StopObserver(); +} + +Observable &Observable::operator=(const Observable &observable) +{ + CSingleLock lock(m_obsCritSection); + + m_bObservableChanged = observable.m_bObservableChanged; + m_observers.clear(); + for (unsigned int iObsPtr = 0; iObsPtr < observable.m_observers.size(); iObsPtr++) + m_observers.push_back(observable.m_observers.at(iObsPtr)); + + return *this; +} + +void Observable::StopObserver(void) +{ + CSingleLock lock(m_obsCritSection); + for (unsigned int iObsPtr = 0; iObsPtr < m_observers.size(); iObsPtr++) + m_observers.at(iObsPtr)->UnregisterObservable(this); + m_observers.clear(); +} + +bool Observable::IsObserving(const Observer &obs) const +{ + CSingleLock lock(m_obsCritSection); + return find(m_observers.begin(), m_observers.end(), &obs) != m_observers.end(); +} + +void Observable::RegisterObserver(Observer *obs) +{ + CSingleLock lock(m_obsCritSection); + if (!IsObserving(*obs)) + { + m_observers.push_back(obs); + obs->RegisterObservable(this); + } +} + +void Observable::UnregisterObserver(Observer *obs) +{ + CSingleLock lock(m_obsCritSection); + vector<Observer *>::iterator it = find(m_observers.begin(), m_observers.end(), obs); + if (it != m_observers.end()) + { + obs->UnregisterObservable(this); + m_observers.erase(it); + } +} + +void Observable::NotifyObservers(const ObservableMessage message /* = ObservableMessageNone */, bool bAsync /* = false */) +{ + bool bNotify(false); + { + CSingleLock lock(m_obsCritSection); + if (m_bObservableChanged && !g_application.m_bStop) + bNotify = true; + m_bObservableChanged = false; + } + + if (bNotify) + { + if (bAsync && m_bAsyncAllowed) + CJobManager::GetInstance().AddJob(new ObservableMessageJob(*this, message), NULL); + else + SendMessage(*this, message); + } +} + +void Observable::SetChanged(bool SetTo) +{ + CSingleLock lock(m_obsCritSection); + m_bObservableChanged = SetTo; +} + +void Observable::Announce(AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data) +{ + if (flag == System && !strcmp(sender, "xbmc") && !strcmp(message, "ApplicationStop")) + { + CSingleLock lock(m_obsCritSection); + m_bAsyncAllowed = false; + } +} + +void Observable::SendMessage(const Observable& obs, const ObservableMessage message) +{ + CSingleLock lock(obs.m_obsCritSection); + for(int ptr = obs.m_observers.size() - 1; ptr >= 0; ptr--) + { + if (ptr < obs.m_observers.size()) + { + Observer *observer = obs.m_observers.at(ptr); + if (observer) + { + lock.Leave(); + observer->Notify(obs, message); + lock.Enter(); + } + } + } +} + +ObservableMessageJob::ObservableMessageJob(const Observable &obs, const ObservableMessage message) +{ + m_message = message; + m_observable = obs; +} + +bool ObservableMessageJob::DoWork() +{ + Observable::SendMessage(m_observable, m_message); + + return true; +} diff --git a/xbmc/utils/Observer.h b/xbmc/utils/Observer.h new file mode 100644 index 0000000000..02ae6add3c --- /dev/null +++ b/xbmc/utils/Observer.h @@ -0,0 +1,150 @@ +#pragma once + +/* + * Copyright (C) 2005-2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "threads/CriticalSection.h" +#include "interfaces/AnnouncementManager.h" + +class Observable; +class ObservableMessageJob; + +typedef enum +{ + ObservableMessageNone, + ObservableMessageCurrentItem, + ObservableMessageGuiSettings, + ObservableMessageAddons, + ObservableMessageEpg, + ObservableMessageEpgContainer, + ObservableMessageEpgActiveItem, + ObservableMessageChannelGroup, + ObservableMessageChannelGroupReset, + ObservableMessageTimers, + ObservableMessageTimersReset, + ObservableMessageRecordings, +} ObservableMessage; + +class Observer +{ + friend class Observable; + +public: + Observer(void) {}; + virtual ~Observer(void); + + /*! + * @brief Remove this observer from all observables. + */ + virtual void StopObserving(void); + + /*! + * @brief Check whether this observer is observing an observable. + * @param obs The observable to check. + * @return True if this observer is observing the given observable, false otherwise. + */ + virtual bool IsObserving(const Observable &obs) const; + + /*! + * @brief Process a message from an observable. + * @param obs The observable that sends the message. + * @param msg The message. + */ + virtual void Notify(const Observable &obs, const ObservableMessage msg) = 0; + +protected: + /*! + * @brief Callback to register an observable. + * @param obs The observable to register. + */ + virtual void RegisterObservable(Observable *obs); + + /*! + * @brief Callback to unregister an observable. + * @param obs The observable to unregister. + */ + virtual void UnregisterObservable(Observable *obs); + + std::vector<Observable *> m_observables; /*!< all observables that are watched */ + CCriticalSection m_obsCritSection; /*!< mutex */ +}; + +class Observable : public ANNOUNCEMENT::IAnnouncer +{ + friend class ObservableMessageJob; + +public: + Observable(); + virtual ~Observable(); + virtual Observable &operator=(const Observable &observable); + + /*! + * @brief Remove this observable from all observers. + */ + virtual void StopObserver(void); + + /*! + * @brief Register an observer. + * @param obs The observer to register. + */ + virtual void RegisterObserver(Observer *obs); + + /*! + * @brief Unregister an observer. + * @param obs The observer to unregister. + */ + virtual void UnregisterObserver(Observer *obs); + + /*! + * @brief Send a message to all observers when m_bObservableChanged is true. + * @param message The message to send. + * @param bAsync True to send the message async, using the jobmanager. + */ + virtual void NotifyObservers(const ObservableMessage message = ObservableMessageNone, bool bAsync = false); + + /*! + * @brief Mark an observable changed. + * @param bSetTo True to mark the observable changed, false to mark it as unchanged. + */ + virtual void SetChanged(bool bSetTo = true); + + /*! + * @brief Check whether this observable is being observed by an observer. + * @param obs The observer to check. + * @return True if this observable is being observed by the given observer, false otherwise. + */ + virtual bool IsObserving(const Observer &obs) const; + + virtual void Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data); + +protected: + /*! + * @brief Send a message to all observer when m_bObservableChanged is true. + * @param obs The observer that sends the message. + * @param message The message to send. + */ + static void SendMessage(const Observable& obs, const ObservableMessage message); + + bool m_bObservableChanged; /*!< true when the observable is marked as changed, false otherwise */ + std::vector<Observer *> m_observers; /*!< all observers */ + CCriticalSection m_obsCritSection; /*!< mutex */ + bool m_bAsyncAllowed; /*!< true when async messages are allowed, false otherwise */ +}; diff --git a/xbmc/utils/SortUtils.cpp b/xbmc/utils/SortUtils.cpp index ec5e152cba..59db0b3247 100644 --- a/xbmc/utils/SortUtils.cpp +++ b/xbmc/utils/SortUtils.cpp @@ -399,6 +399,11 @@ string ByRandom(SortAttribute attributes, const SortItem &values) return label; } +string ByChannel(SortAttribute attributes, const SortItem &values) +{ + return values.at(FieldChannelName).asString(); +} + bool preliminarySort(const SortItem &left, const SortItem &right, bool handleFolder, bool &result, std::wstring &labelLeft, std::wstring &labelRight) { // make sure both items have the necessary data to do the sorting @@ -553,6 +558,7 @@ map<SortBy, SortUtils::SortPreparator> fillPreparators() preparators[SortByListeners] = ByListeners; preparators[SortByBitrate] = ByBitrate; preparators[SortByRandom] = ByRandom; + preparators[SortByChannel] = ByChannel; return preparators; } @@ -619,6 +625,7 @@ map<SortBy, Fields> fillSortingFields() sortingFields[SortByPlaycount].insert(FieldPlaycount); sortingFields[SortByListeners].insert(FieldListeners); sortingFields[SortByBitrate].insert(FieldBitrate); + sortingFields[SortByChannel].insert(FieldChannelName); sortingFields.insert(pair<SortBy, Fields>(SortByRandom, Fields())); return sortingFields; diff --git a/xbmc/utils/SortUtils.h b/xbmc/utils/SortUtils.h index 78d03910d1..06d48c1ebc 100644 --- a/xbmc/utils/SortUtils.h +++ b/xbmc/utils/SortUtils.h @@ -86,7 +86,8 @@ typedef enum { SortByPlaycount, SortByListeners, SortByBitrate, - SortByRandom + SortByRandom, + SortByChannel } SortBy; typedef struct SortDescription { diff --git a/xbmc/utils/TextSearch.cpp b/xbmc/utils/TextSearch.cpp new file mode 100644 index 0000000000..6477ab51e5 --- /dev/null +++ b/xbmc/utils/TextSearch.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2005-2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "TextSearch.h" + +using namespace std; + +CTextSearch::CTextSearch(const CStdString &strSearchTerms, bool bCaseSensitive /* = false */, TextSearchDefault defaultSearchMode /* = SEARCH_DEFAULT_OR */) +{ + m_bCaseSensitive = bCaseSensitive; + ExtractSearchTerms(strSearchTerms, defaultSearchMode); +} + +CTextSearch::~CTextSearch(void) +{ + m_AND.clear(); + m_OR.clear(); + m_NOT.clear(); +} + +bool CTextSearch::IsValid(void) const +{ + return m_AND.size() > 0 || m_OR.size() > 0 || m_NOT.size() > 0; +} + +bool CTextSearch::Search(const CStdString &strHaystack) const +{ + if (strHaystack.IsEmpty() || !IsValid()) + return false; + + CStdString strSearch(strHaystack); + if (!m_bCaseSensitive) + strSearch = strSearch.ToLower(); + + /* check whether any of the NOT terms matches and return false if there's a match */ + for (unsigned int iNotPtr = 0; iNotPtr < m_NOT.size(); iNotPtr++) + { + if (strSearch.Find(m_NOT.at(iNotPtr)) != -1) + return false; + } + + /* check whether at least one of the OR terms matches and return false if there's no match found */ + bool bFound(m_OR.size() == 0); + for (unsigned int iOrPtr = 0; iOrPtr < m_OR.size(); iOrPtr++) + { + if (strSearch.Find(m_OR.at(iOrPtr)) != -1) + { + bFound = true; + break; + } + } + if (!bFound) + return false; + + /* check whether all of the AND terms match and return false if one of them wasn't found */ + for (unsigned int iAndPtr = 0; iAndPtr < m_AND.size(); iAndPtr++) + { + if (strSearch.Find(m_AND[iAndPtr]) == -1) + return false; + } + + /* all ok, return true */ + return true; +} + +void CTextSearch::GetAndCutNextTerm(CStdString &strSearchTerm, CStdString &strNextTerm) +{ + CStdString strFindNext(" "); + + if (strSearchTerm.Left(1).Equals("\"")) + { + strSearchTerm.erase(0, 1); + strFindNext = "\""; + } + + int iNextPos = strSearchTerm.Find(strFindNext); + if (iNextPos != -1) + { + strNextTerm = strSearchTerm.Left(iNextPos); + strSearchTerm.erase(0, iNextPos + 1); + } + else + { + strNextTerm = strSearchTerm; + strSearchTerm.clear(); + } +} + +void CTextSearch::ExtractSearchTerms(const CStdString &strSearchTerm, TextSearchDefault defaultSearchMode) +{ + CStdString strParsedSearchTerm(strSearchTerm); + strParsedSearchTerm = strParsedSearchTerm.Trim(); + + if (!m_bCaseSensitive) + strParsedSearchTerm = strParsedSearchTerm.ToLower(); + + bool bNextAND(defaultSearchMode == SEARCH_DEFAULT_AND); + bool bNextOR(defaultSearchMode == SEARCH_DEFAULT_OR); + bool bNextNOT(defaultSearchMode == SEARCH_DEFAULT_NOT); + + while (strParsedSearchTerm.length() > 0) + { + strParsedSearchTerm = strParsedSearchTerm.TrimLeft(); + + if (strParsedSearchTerm.Left(1).Equals("!") || strParsedSearchTerm.Left(3).Equals("NOT")) + { + CStdString strDummy; + GetAndCutNextTerm(strParsedSearchTerm, strDummy); + bNextNOT = true; + } + else if (strParsedSearchTerm.Left(1).Equals("+") || strParsedSearchTerm.Left(3).Equals("AND")) + { + CStdString strDummy; + GetAndCutNextTerm(strParsedSearchTerm, strDummy); + bNextAND = true; + } + else if (strParsedSearchTerm.Left(1).Equals("|") || strParsedSearchTerm.Left(2).Equals("OR")) + { + CStdString strDummy; + GetAndCutNextTerm(strParsedSearchTerm, strDummy); + bNextOR = true; + } + else + { + CStdString strTerm; + GetAndCutNextTerm(strParsedSearchTerm, strTerm); + if (strTerm.length() > 0) + { + if (bNextAND) + m_AND.push_back(strTerm); + else if (bNextOR) + m_OR.push_back(strTerm); + else + m_NOT.push_back(strTerm); + } + else + { + break; + } + + bNextAND = (defaultSearchMode == SEARCH_DEFAULT_AND); + bNextOR = (defaultSearchMode == SEARCH_DEFAULT_OR); + bNextNOT = (defaultSearchMode == SEARCH_DEFAULT_NOT); + } + + strParsedSearchTerm = strParsedSearchTerm.TrimLeft(); + } +} diff --git a/xbmc/utils/TextSearch.h b/xbmc/utils/TextSearch.h new file mode 100644 index 0000000000..52991780e4 --- /dev/null +++ b/xbmc/utils/TextSearch.h @@ -0,0 +1,50 @@ +#pragma once +/* + * Copyright (C) 2005-2012 Team XBMC + * http://www.xbmc.org + * + * 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, 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 XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include <vector> +#include "StringUtils.h" + +typedef enum TextSearchDefault +{ + SEARCH_DEFAULT_AND = 0, + SEARCH_DEFAULT_OR, + SEARCH_DEFAULT_NOT +} TextSearchDefault; + +class CTextSearch +{ +public: + CTextSearch(const CStdString &strSearchTerms, bool bCaseSensitive = false, TextSearchDefault defaultSearchMode = SEARCH_DEFAULT_OR); + virtual ~CTextSearch(void); + + bool Search(const CStdString &strHaystack) const; + bool IsValid(void) const; + +private: + void GetAndCutNextTerm(CStdString &strSearchTerm, CStdString &strNextTerm); + void ExtractSearchTerms(const CStdString &strSearchTerm, TextSearchDefault defaultSearchMode); + + bool m_bCaseSensitive; + std::vector<CStdString> m_AND; + std::vector<CStdString> m_OR; + std::vector<CStdString> m_NOT; +}; diff --git a/xbmc/utils/URIUtils.cpp b/xbmc/utils/URIUtils.cpp index 376d2d7b78..eeb2fc5a29 100644 --- a/xbmc/utils/URIUtils.cpp +++ b/xbmc/utils/URIUtils.cpp @@ -757,12 +757,16 @@ bool URIUtils::IsHTSP(const CStdString& strFile) bool URIUtils::IsLiveTV(const CStdString& strFile) { + CStdString strFileWithoutSlash(strFile); + RemoveSlashAtEnd(strFileWithoutSlash); + if(IsTuxBox(strFile) || IsVTP(strFile) || IsHDHomeRun(strFile) || IsSlingbox(strFile) || IsHTSP(strFile) - || strFile.Left(4).Equals("sap:")) + || strFile.Left(4).Equals("sap:") + ||(strFileWithoutSlash.Right(4).Equals(".pvr") && !strFileWithoutSlash.Left(16).Equals("pvr://recordings"))) return true; if (IsMythTV(strFile) && CMythDirectory::IsLiveTV(strFile)) @@ -771,6 +775,15 @@ bool URIUtils::IsLiveTV(const CStdString& strFile) return false; } +bool URIUtils::IsPVRRecording(const CStdString& strFile) +{ + CStdString strFileWithoutSlash(strFile); + RemoveSlashAtEnd(strFileWithoutSlash); + + return strFileWithoutSlash.Right(4).Equals(".pvr") && + strFile.Left(16).Equals("pvr://recordings"); +} + bool URIUtils::IsMusicDb(const CStdString& strFile) { return strFile.Left(8).Equals("musicdb:"); diff --git a/xbmc/utils/URIUtils.h b/xbmc/utils/URIUtils.h index 33c9b53732..e6c65c68ff 100644 --- a/xbmc/utils/URIUtils.h +++ b/xbmc/utils/URIUtils.h @@ -69,6 +69,7 @@ public: static bool IsISO9660(const CStdString& strFile); static bool IsLastFM(const CStdString& strFile); static bool IsLiveTV(const CStdString& strFile); + static bool IsPVRRecording(const CStdString& strFile); static bool IsMultiPath(const CStdString& strPath); static bool IsMusicDb(const CStdString& strFile); static bool IsMythTV(const CStdString& strFile); diff --git a/xbmc/utils/XMLUtils.cpp b/xbmc/utils/XMLUtils.cpp index 1b5d58533f..615cc0607a 100644 --- a/xbmc/utils/XMLUtils.cpp +++ b/xbmc/utils/XMLUtils.cpp @@ -131,6 +131,14 @@ bool XMLUtils::GetString(const TiXmlNode* pRootNode, const char* strTag, CStdStr return false; } +bool XMLUtils::HasChild(const TiXmlNode* pRootNode, const char* strTag) +{ + const TiXmlElement* pElement = pRootNode->FirstChildElement(strTag); + if (!pElement) return false; + const TiXmlNode* pNode = pElement->FirstChild(); + return (pNode != NULL); +} + bool XMLUtils::GetAdditiveString(const TiXmlNode* pRootNode, const char* strTag, const CStdString& strSeparator, CStdString& strStringValue, bool clear) diff --git a/xbmc/utils/XMLUtils.h b/xbmc/utils/XMLUtils.h index c84bf0618c..26abc14a5e 100644 --- a/xbmc/utils/XMLUtils.h +++ b/xbmc/utils/XMLUtils.h @@ -30,6 +30,7 @@ class XMLUtils { public: static bool HasUTF8Declaration(const CStdString &strXML); + static bool HasChild(const TiXmlNode* pRootNode, const char* strTag); static bool GetHex(const TiXmlNode* pRootNode, const char* strTag, uint32_t& dwHexValue); static bool GetUInt(const TiXmlNode* pRootNode, const char* strTag, uint32_t& dwUIntValue); diff --git a/xbmc/video/GUIViewStateVideo.cpp b/xbmc/video/GUIViewStateVideo.cpp index d3cd0aeb34..a0eee3ab67 100644 --- a/xbmc/video/GUIViewStateVideo.cpp +++ b/xbmc/video/GUIViewStateVideo.cpp @@ -21,6 +21,8 @@ #include "GUIViewStateVideo.h" #include "PlayListPlayer.h" +#include "filesystem/PluginDirectory.h" +#include "filesystem/PVRDirectory.h" #include "filesystem/VideoDatabaseDirectory.h" #include "filesystem/Directory.h" #include "VideoDatabase.h" diff --git a/xbmc/video/VideoDatabase.cpp b/xbmc/video/VideoDatabase.cpp index ed827a0c3c..7e2e0201ef 100644 --- a/xbmc/video/VideoDatabase.cpp +++ b/xbmc/video/VideoDatabase.cpp @@ -52,6 +52,8 @@ #include "dbwrappers/dataset.h" #include "utils/LabelFormatter.h" #include "XBDateTime.h" +#include "pvr/PVRManager.h" +#include "pvr/recordings/PVRRecordings.h" #include "URL.h" #include "video/VideoDbUrl.h" #include "playlists/SmartPlayList.h" @@ -61,6 +63,7 @@ using namespace dbiplus; using namespace XFILE; using namespace VIDEO; using namespace ADDON; +using namespace PVR; //******************************************************************************************************************************** CVideoDatabase::CVideoDatabase(void) @@ -4284,6 +4287,10 @@ void CVideoDatabase::SetPlayCount(const CFileItem &item, int count, const CDateT m_pDS->exec(strSQL.c_str()); + // PVR: Set recording's play count on the backend (if supported) + if (item.HasPVRRecordingInfoTag() && g_PVRManager.IsStarted()) + g_PVRRecordings->SetPlayCount(item, count); + // We only need to announce changes to video items in the library if (item.HasVideoInfoTag() && item.GetVideoInfoTag()->m_iDbId > 0) { diff --git a/xbmc/video/dialogs/GUIDialogAudioSubtitleSettings.cpp b/xbmc/video/dialogs/GUIDialogAudioSubtitleSettings.cpp index 031be11a39..26f89fc2da 100644 --- a/xbmc/video/dialogs/GUIDialogAudioSubtitleSettings.cpp +++ b/xbmc/video/dialogs/GUIDialogAudioSubtitleSettings.cpp @@ -36,10 +36,12 @@ #include "settings/Settings.h" #include "settings/GUISettings.h" #include "guilib/LocalizeStrings.h" +#include "pvr/PVRManager.h" #include "cores/AudioEngine/Utils/AEUtil.h" using namespace std; using namespace XFILE; +using namespace PVR; #ifdef HAS_VIDEO_PLAYBACK extern void xbox_audio_switch_channel(int iAudioStream, bool bAudioOnAllSpeakers); //lowlevel audio @@ -348,6 +350,9 @@ void CGUIDialogAudioSubtitleSettings::OnSettingChanged(SettingInfo &setting) g_settings.Save(); } } + + if (g_PVRManager.IsPlayingRadio() || g_PVRManager.IsPlayingTV()) + g_PVRManager.TriggerSaveChannelSettings(); } void CGUIDialogAudioSubtitleSettings::FrameMove() diff --git a/xbmc/video/dialogs/GUIDialogVideoOSD.cpp b/xbmc/video/dialogs/GUIDialogVideoOSD.cpp index 402fbb9477..6c65423c9a 100644 --- a/xbmc/video/dialogs/GUIDialogVideoOSD.cpp +++ b/xbmc/video/dialogs/GUIDialogVideoOSD.cpp @@ -21,10 +21,16 @@ #include "GUIDialogVideoOSD.h" #include "Application.h" +#include "FileItem.h" #include "GUIUserMessages.h" #include "guilib/GUIWindowManager.h" #include "input/MouseStat.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" + +using namespace PVR; + CGUIDialogVideoOSD::CGUIDialogVideoOSD(void) : CGUIDialog(WINDOW_DIALOG_VIDEO_OSD, "VideoOSD.xml") { @@ -42,6 +48,10 @@ void CGUIDialogVideoOSD::FrameMove() if (g_Mouse.IsActive() || g_windowManager.IsWindowActive(WINDOW_DIALOG_AUDIO_OSD_SETTINGS) || g_windowManager.IsWindowActive(WINDOW_DIALOG_VIDEO_OSD_SETTINGS) || g_windowManager.IsWindowActive(WINDOW_DIALOG_VIDEO_BOOKMARKS) + || g_windowManager.IsWindowActive(WINDOW_DIALOG_PVR_OSD_CHANNELS) + || g_windowManager.IsWindowActive(WINDOW_DIALOG_PVR_OSD_GUIDE) + || g_windowManager.IsWindowActive(WINDOW_DIALOG_PVR_OSD_DIRECTOR) + || g_windowManager.IsWindowActive(WINDOW_DIALOG_PVR_OSD_CUTTER) || g_windowManager.IsWindowActive(WINDOW_DIALOG_OSD_TELETEXT)) SetAutoClose(100); // enough for 10fps } @@ -99,6 +109,14 @@ bool CGUIDialogVideoOSD::OnMessage(CGUIMessage& message) if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true); pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_BOOKMARKS); if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true); + pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_CHANNELS); + if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true); + pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_GUIDE); + if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true); + pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_DIRECTOR); + if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true); + pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_CUTTER); + if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true); pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_OSD_TELETEXT); if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true); } diff --git a/xbmc/video/dialogs/GUIDialogVideoSettings.cpp b/xbmc/video/dialogs/GUIDialogVideoSettings.cpp index 025186c01f..ca13609a96 100644 --- a/xbmc/video/dialogs/GUIDialogVideoSettings.cpp +++ b/xbmc/video/dialogs/GUIDialogVideoSettings.cpp @@ -32,8 +32,10 @@ #include "dialogs/GUIDialogYesNo.h" #include "settings/Settings.h" #include "addons/Skin.h" +#include "pvr/PVRManager.h" using namespace std; +using namespace PVR; CGUIDialogVideoSettings::CGUIDialogVideoSettings(void) : CGUIDialogSettings(WINDOW_DIALOG_VIDEO_OSD_SETTINGS, "VideoOSDSettings.xml") @@ -110,6 +112,7 @@ void CGUIDialogVideoSettings::CreateSettings() entries.push_back(make_pair(VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE , 16314)); entries.push_back(make_pair(VS_INTERLACEMETHOD_DXVA_BOB , 16320)); entries.push_back(make_pair(VS_INTERLACEMETHOD_DXVA_BEST , 16321)); + entries.push_back(make_pair(VS_INTERLACEMETHOD_AUTO_ION , 16325)); /* remove unsupported methods */ for(vector<pair<int, int> >::iterator it = entries.begin(); it != entries.end();) @@ -240,6 +243,9 @@ void CGUIDialogVideoSettings::OnSettingChanged(SettingInfo &setting) { EnableSettings(VIDEO_SETTINGS_INTERLACEMETHOD, g_settings.m_currentVideoSettings.m_DeinterlaceMode != VS_DEINTERLACEMODE_OFF); } + + if (g_PVRManager.IsPlayingRadio() || g_PVRManager.IsPlayingTV()) + g_PVRManager.TriggerSaveChannelSettings(); } CStdString CGUIDialogVideoSettings::FormatInteger(float value, float minimum) diff --git a/xbmc/video/windows/GUIWindowFullScreen.cpp b/xbmc/video/windows/GUIWindowFullScreen.cpp index 1f0fc6c5c1..7a1972fb4f 100644 --- a/xbmc/video/windows/GUIWindowFullScreen.cpp +++ b/xbmc/video/windows/GUIWindowFullScreen.cpp @@ -23,6 +23,7 @@ #include "system.h" #include "GUIWindowFullScreen.h" #include "Application.h" +#include "ApplicationMessenger.h" #include "Util.h" #ifdef HAS_VIDEO_PLAYBACK #include "cores/VideoRenderers/RenderManager.h" @@ -41,6 +42,7 @@ #include "dialogs/GUIDialogKaiToast.h" #include "guilib/GUISliderControl.h" #include "settings/Settings.h" +#include "guilib/GUISelectButtonControl.h" #include "FileItem.h" #include "video/VideoReferenceClock.h" #include "settings/AdvancedSettings.h" @@ -52,6 +54,8 @@ #include "utils/TimeUtils.h" #include "XBDateTime.h" #include "input/ButtonTranslator.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" #include "windowing/WindowingFactory.h" #include <stdio.h> @@ -60,10 +64,13 @@ #include "linux/LinuxResourceCounter.h" #endif +using namespace PVR; + #define BLUE_BAR 0 #define LABEL_ROW1 10 #define LABEL_ROW2 11 #define LABEL_ROW3 12 +#define CONTROL_GROUP_CHOOSER 503 #define BTN_OSD_VIDEO 13 #define BTN_OSD_AUDIO 14 @@ -125,6 +132,7 @@ CGUIWindowFullScreen::CGUIWindowFullScreen(void) m_dwShowViewModeTimeout = 0; m_bShowCurrentTime = false; m_subsLayout = NULL; + m_bGroupSelectShow = false; m_sliderAction = 0; // audio // - language @@ -206,36 +214,52 @@ bool CGUIWindowFullScreen::OnAction(const CAction &action) break; case ACTION_STEP_BACK: - if (m_timeCodePosition > 0) - SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_BACKWARD); + if (!g_application.CurrentFileItem().HasPVRChannelInfoTag()) + { + if (m_timeCodePosition > 0) + SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_BACKWARD); + else + g_application.m_pPlayer->Seek(false, false); + } else - g_application.m_pPlayer->Seek(false, false); + SeekTV(false, false); return true; - break; case ACTION_STEP_FORWARD: - if (m_timeCodePosition > 0) - SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_FORWARD); + if (!g_application.CurrentFileItem().HasPVRChannelInfoTag()) + { + if (m_timeCodePosition > 0) + SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_FORWARD); + else + g_application.m_pPlayer->Seek(true, false); + } else - g_application.m_pPlayer->Seek(true, false); + SeekTV(true, false); return true; - break; case ACTION_BIG_STEP_BACK: - if (m_timeCodePosition > 0) - SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_BACKWARD); + if (!g_application.CurrentFileItem().HasPVRChannelInfoTag()) + { + if (m_timeCodePosition > 0) + SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_BACKWARD); + else + g_application.m_pPlayer->Seek(false, true); + } else - g_application.m_pPlayer->Seek(false, true); + SeekTV(false, true); return true; - break; case ACTION_BIG_STEP_FORWARD: - if (m_timeCodePosition > 0) - SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_FORWARD); + if (!g_application.CurrentFileItem().HasPVRChannelInfoTag()) + { + if (m_timeCodePosition > 0) + SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_FORWARD); + else + g_application.m_pPlayer->Seek(true, true); + } else - g_application.m_pPlayer->Seek(true, true); + SeekTV(true, true); return true; - break; case ACTION_NEXT_SCENE: if (g_application.m_pPlayer->SeekScene(true)) @@ -285,6 +309,7 @@ bool CGUIWindowFullScreen::OnAction(const CAction &action) CGUIDialogFullScreenInfo* pDialog = (CGUIDialogFullScreenInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_FULLSCREEN_INFO); if (pDialog) { + CFileItem item(g_application.CurrentFileItem()); pDialog->DoModal(); return true; } @@ -422,15 +447,36 @@ bool CGUIWindowFullScreen::OnAction(const CAction &action) { if (g_application.CurrentFileItem().IsLiveTV()) { - int channelNr = -1; + CPVRChannelPtr channel; + int iChannelNumber = -1; + g_PVRManager.GetCurrentChannel(channel); - CStdString strChannel; - strChannel.Format("%i", action.GetID() - REMOTE_0); - if (CGUIDialogNumeric::ShowAndGetNumber(strChannel, g_localizeStrings.Get(19000))) - channelNr = atoi(strChannel.c_str()); + if (action.GetID() == REMOTE_0) + { + iChannelNumber = g_PVRManager.GetPreviousChannel(); + if (iChannelNumber > 0) + CLog::Log(LOGDEBUG, "switch to channel number %d", iChannelNumber); + else + CLog::Log(LOGDEBUG, "no previous channel number found"); + } + else + { + int autoCloseTime = g_guiSettings.GetBool("pvrplayback.switchautoclose") ? 1500 : 0; + CStdString strChannel; + strChannel.Format("%i", action.GetID() - REMOTE_0); + if (CGUIDialogNumeric::ShowAndGetNumber(strChannel, g_localizeStrings.Get(19000), autoCloseTime) || autoCloseTime) + iChannelNumber = atoi(strChannel.c_str()); + } - if (channelNr > 0) - OnAction(CAction(ACTION_CHANNEL_SWITCH, (float)channelNr)); + if (iChannelNumber > 0 && iChannelNumber != channel->ChannelNumber()) + { + CPVRChannelGroupPtr selectedGroup = g_PVRManager.GetPlayingGroup(channel->IsRadio()); + CFileItemPtr channel = selectedGroup->GetByChannelNumber(iChannelNumber); + if (!channel || !channel->HasPVRChannelInfoTag()) + return false; + + OnAction(CAction(ACTION_CHANNEL_SWITCH, (float)iChannelNumber)); + } } else { @@ -465,6 +511,18 @@ bool CGUIWindowFullScreen::OnAction(const CAction &action) } return true; break; + case ACTION_SHOW_PLAYLIST: + { + CFileItem item(g_application.CurrentFileItem()); + if (item.HasPVRChannelInfoTag()) + g_windowManager.ActivateWindow(WINDOW_DIALOG_PVR_OSD_CHANNELS); + else if (item.HasVideoInfoTag()) + g_windowManager.ActivateWindow(WINDOW_VIDEO_PLAYLIST); + else if (item.HasMusicInfoTag()) + g_windowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST); + } + return true; + break; case ACTION_ZOOM_IN: { g_settings.m_currentVideoSettings.m_CustomZoomAmount += 0.01f; @@ -626,6 +684,7 @@ bool CGUIWindowFullScreen::OnAction(const CAction &action) default: break; } + return CGUIWindow::OnAction(action); } @@ -660,7 +719,10 @@ void CGUIWindowFullScreen::OnWindowLoaded() pLabel->SetVisible(true); pLabel->SetLabel("$INFO(VIDEOPLAYER.TIME) / $INFO(VIDEOPLAYER.DURATION)"); } + m_showCodec.Parse("player.showcodec", GetID()); + + FillInTVGroups(); } bool CGUIWindowFullScreen::OnMessage(CGUIMessage& message) @@ -679,6 +741,7 @@ bool CGUIWindowFullScreen::OnMessage(CGUIMessage& message) g_infoManager.SetShowInfo(false); g_infoManager.SetShowCodec(false); m_bShowCurrentTime = false; + m_bGroupSelectShow = false; g_infoManager.SetDisplayAfterSeek(0); // Make sure display after seek is off. // switch resolution @@ -726,6 +789,14 @@ bool CGUIWindowFullScreen::OnMessage(CGUIMessage& message) if (pDialog) pDialog->Close(true); pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_FULLSCREEN_INFO); if (pDialog) pDialog->Close(true); + pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_CHANNELS); + if (pDialog) pDialog->Close(true); + pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_GUIDE); + if (pDialog) pDialog->Close(true); + pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_DIRECTOR); + if (pDialog) pDialog->Close(true); + pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_CUTTER); + if (pDialog) pDialog->Close(true); FreeResources(true); @@ -749,6 +820,61 @@ bool CGUIWindowFullScreen::OnMessage(CGUIMessage& message) return true; } + case GUI_MSG_CLICKED: + { + unsigned int iControl = message.GetSenderId(); + if (iControl == CONTROL_GROUP_CHOOSER && g_PVRManager.IsStarted()) + { + // Get the currently selected label of the Select button + CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl); + OnMessage(msg); + CStdString strLabel = msg.GetLabel(); + + CPVRChannelPtr playingChannel; + if (g_PVRManager.GetCurrentChannel(playingChannel)) + { + CPVRChannelGroupPtr selectedGroup = g_PVRChannelGroups->Get(playingChannel->IsRadio())->GetByName(strLabel); + if (selectedGroup) + { + g_PVRManager.SetPlayingGroup(selectedGroup); + CLog::Log(LOGDEBUG, "%s - switched to group '%s'", __FUNCTION__, selectedGroup->GroupName().c_str()); + + if (!selectedGroup->IsGroupMember(*playingChannel)) + { + CLog::Log(LOGDEBUG, "%s - channel '%s' is not a member of '%s', switching to channel 1 of the new group", + __FUNCTION__, playingChannel->ChannelName().c_str(), selectedGroup->GroupName().c_str()); + CFileItemPtr switchChannel = selectedGroup->GetByChannelNumber(1); + + if (switchChannel && switchChannel->HasPVRChannelInfoTag()) + OnAction(CAction(ACTION_CHANNEL_SWITCH, (float) switchChannel->GetPVRChannelInfoTag()->ChannelNumber())); + else + { + CLog::Log(LOGERROR, "%s - cannot find channel '1' in group %s", __FUNCTION__, selectedGroup->GroupName().c_str()); + CApplicationMessenger::Get().MediaStop(false); + } + } + } + else + { + CLog::Log(LOGERROR, "%s - could not switch to group '%s'", __FUNCTION__, selectedGroup->GroupName().c_str()); + CApplicationMessenger::Get().MediaStop(false); + } + } + else + { + CLog::Log(LOGERROR, "%s - cannot find the current channel", __FUNCTION__); + CApplicationMessenger::Get().MediaStop(false); + } + + // hide the control and reset focus + m_bGroupSelectShow = false; + SET_CONTROL_HIDDEN(CONTROL_GROUP_CHOOSER); +// SET_CONTROL_FOCUS(0, 0); + + return true; + } + break; + } case GUI_MSG_SETFOCUS: case GUI_MSG_LOSTFOCUS: if (message.GetSenderId() != WINDOW_FULLSCREEN_VIDEO) return true; @@ -940,6 +1066,7 @@ void CGUIWindowFullScreen::FrameMove() SET_CONTROL_VISIBLE(LABEL_ROW2); SET_CONTROL_VISIBLE(LABEL_ROW3); SET_CONTROL_VISIBLE(BLUE_BAR); + SET_CONTROL_HIDDEN(CONTROL_GROUP_CHOOSER); } else if (m_timeCodeShow) { @@ -947,6 +1074,15 @@ void CGUIWindowFullScreen::FrameMove() SET_CONTROL_HIDDEN(LABEL_ROW2); SET_CONTROL_HIDDEN(LABEL_ROW3); SET_CONTROL_VISIBLE(BLUE_BAR); + SET_CONTROL_HIDDEN(CONTROL_GROUP_CHOOSER); + } + else if (m_bGroupSelectShow) + { + SET_CONTROL_HIDDEN(LABEL_ROW1); + SET_CONTROL_HIDDEN(LABEL_ROW2); + SET_CONTROL_HIDDEN(LABEL_ROW3); + SET_CONTROL_HIDDEN(BLUE_BAR); + SET_CONTROL_VISIBLE(CONTROL_GROUP_CHOOSER); } else { @@ -954,6 +1090,7 @@ void CGUIWindowFullScreen::FrameMove() SET_CONTROL_HIDDEN(LABEL_ROW2); SET_CONTROL_HIDDEN(LABEL_ROW3); SET_CONTROL_HIDDEN(BLUE_BAR); + SET_CONTROL_HIDDEN(CONTROL_GROUP_CHOOSER); } } @@ -1084,6 +1221,14 @@ void CGUIWindowFullScreen::SeekToTimeCodeStamp(SEEK_TYPE type, SEEK_DIRECTION di m_timeCodeShow = false; } +void CGUIWindowFullScreen::SeekTV(bool bPlus, bool bLargeStep) +{ + if (bLargeStep) + OnAction(CAction(bPlus ? ACTION_NEXT_ITEM : ACTION_PREV_ITEM)); + else if (!bLargeStep) + ChangetheTVGroup(bPlus); +} + double CGUIWindowFullScreen::GetTimeCodeStamp() { // Convert the timestamp into an integer @@ -1149,6 +1294,47 @@ void CGUIWindowFullScreen::OnSliderChange(void *data, CGUISliderControl *slider) } } +void CGUIWindowFullScreen::FillInTVGroups() +{ + if (!g_PVRManager.IsStarted()) + return; + + CGUIMessage msgReset(GUI_MSG_LABEL_RESET, GetID(), CONTROL_GROUP_CHOOSER); + g_windowManager.SendMessage(msgReset); + + const CPVRChannelGroups *groups = g_PVRChannelGroups->Get(g_PVRManager.IsPlayingRadio()); + if (groups) + groups->FillGroupsGUI(GetID(), CONTROL_GROUP_CHOOSER); +} + +void CGUIWindowFullScreen::ChangetheTVGroup(bool next) +{ + if (!g_PVRManager.IsStarted()) + return; + + CGUISelectButtonControl* pButton = (CGUISelectButtonControl*)GetControl(CONTROL_GROUP_CHOOSER); + if (!pButton) + return; + + if (!m_bGroupSelectShow) + { + SET_CONTROL_VISIBLE(CONTROL_GROUP_CHOOSER); + SET_CONTROL_FOCUS(CONTROL_GROUP_CHOOSER, 0); + + // fire off an event that we've pressed this button... + OnAction(CAction(ACTION_SELECT_ITEM)); + + m_bGroupSelectShow = true; + } + else + { + if (next) + pButton->OnRight(); + else + pButton->OnLeft(); + } +} + void CGUIWindowFullScreen::ToggleOSD() { CGUIDialogVideoOSD *pOSD = (CGUIDialogVideoOSD *)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_OSD); diff --git a/xbmc/video/windows/GUIWindowFullScreen.h b/xbmc/video/windows/GUIWindowFullScreen.h index 1f44df358e..222a7de40d 100644 --- a/xbmc/video/windows/GUIWindowFullScreen.h +++ b/xbmc/video/windows/GUIWindowFullScreen.h @@ -42,6 +42,7 @@ public: virtual void Render(); virtual void OnWindowLoaded(); void ChangetheTimeCode(int remote); + void ChangetheTVGroup(bool next); virtual void OnSliderChange(void *data, CGUISliderControl *slider); protected: @@ -49,7 +50,9 @@ protected: private: void RenderTTFSubtitles(); + void SeekTV(bool bPlus, bool bLargeStep); void SeekChapter(int iChapter); + void FillInTVGroups(); void ToggleOSD(); enum SEEK_TYPE { SEEK_ABSOLUTE, SEEK_RELATIVE }; @@ -82,6 +85,7 @@ private: bool m_bShowCurrentTime; + bool m_bGroupSelectShow; bool m_timeCodeShow; unsigned int m_timeCodeTimeout; int m_timeCodeStamp[6]; diff --git a/xbmc/video/windows/GUIWindowVideoBase.cpp b/xbmc/video/windows/GUIWindowVideoBase.cpp index 00c12890f2..fea20894d6 100644 --- a/xbmc/video/windows/GUIWindowVideoBase.cpp +++ b/xbmc/video/windows/GUIWindowVideoBase.cpp @@ -61,6 +61,9 @@ #include "utils/StringUtils.h" #include "utils/log.h" #include "utils/FileUtils.h" +#include "interfaces/AnnouncementManager.h" +#include "pvr/PVRManager.h" +#include "pvr/recordings/PVRRecordings.h" #include "utils/URIUtils.h" #include "GUIUserMessages.h" #include "addons/Skin.h" @@ -74,6 +77,7 @@ using namespace PLAYLIST; using namespace VIDEODATABASEDIRECTORY; using namespace VIDEO; using namespace ADDON; +using namespace PVR; #define CONTROL_BTNVIEWASICONS 2 #define CONTROL_BTNSORTBY 3 @@ -1462,6 +1466,65 @@ bool CGUIWindowVideoBase::OnPlayMedia(int iItem) } CLog::Log(LOGDEBUG, "%s %s", __FUNCTION__, item.GetPath().c_str()); + if (item.GetPath().Left(17) == "pvr://recordings/") + { + if (!g_PVRManager.IsStarted()) + return false; + + /* For recordings we check here for a available stream URL */ + CFileItemPtr tag = g_PVRRecordings->GetByPath(item.GetPath()); + if (tag && tag->HasPVRRecordingInfoTag() && !tag->GetPVRRecordingInfoTag()->m_strStreamURL.IsEmpty()) + { + CStdString stream = tag->GetPVRRecordingInfoTag()->m_strStreamURL; + + /* Isolate the folder from the filename */ + size_t found = stream.find_last_of("/"); + if (found == CStdString::npos) + found = stream.find_last_of("\\"); + + if (found != CStdString::npos) + { + /* Check here for asterix at the begin of the filename */ + if (stream[found+1] == '*') + { + /* Create a "stack://" url with all files matching the extension */ + CStdString ext = URIUtils::GetExtension(stream); + CStdString dir = stream.substr(0, found).c_str(); + + CFileItemList items; + CDirectory::GetDirectory(dir, items); + items.Sort(SORT_METHOD_FILE, SortOrderAscending); + + vector<int> stack; + for (int i = 0; i < items.Size(); ++i) + { + if (URIUtils::GetExtension(items[i]->GetPath()) == ext) + stack.push_back(i); + } + + if (stack.size() > 0) + { + /* If we have a stack change the path of the item to it */ + CStackDirectory dir; + CStdString stackPath = dir.ConstructStackPath(items, stack); + item.SetPath(stackPath); + } + } + else + { + /* If no asterix is present play only the given stream URL */ + item.SetPath(stream); + } + } + else + { + CLog::Log(LOGERROR, "CGUIWindowTV: Can't open recording, no valid filename!"); + CGUIDialogOK::ShowAndGetInput(19033,0,19036,0); + return false; + } + } + } + PlayMovie(&item); return true; diff --git a/xbmc/win32/stdbool.h b/xbmc/win32/stdbool.h new file mode 100644 index 0000000000..87a55750ae --- /dev/null +++ b/xbmc/win32/stdbool.h @@ -0,0 +1,53 @@ +/* Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC 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, or (at your option) +any later version. + +GCC 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 GCC; see the file COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +/* As a special exception, if you include this header file into source + files compiled by GCC, this header file does not by itself cause + the resulting executable to be covered by the GNU General Public + License. This exception does not however invalidate any other + reasons why the executable file might be covered by the GNU General + Public License. */ + +/* +* ISO C Standard: 7.16 Boolean type and values <stdbool.h> +*/ + +#ifndef _STDBOOL_H +#define _STDBOOL_H + +#ifndef __cplusplus + +#define bool _Bool +#define true 1 +#define false 0 + +#else /* __cplusplus */ + +/* Supporting <stdbool.h> in C++ is a GCC extension. */ +#define _Bool bool +#define bool bool +#define false false +#define true true + +#endif /* __cplusplus */ + +/* Signal that all the definitions are present. */ +#define __bool_true_false_are_defined 1 + +#endif /* stdbool.h */ diff --git a/xbmc/windows/GUIMediaWindow.cpp b/xbmc/windows/GUIMediaWindow.cpp index 59d592797b..0b1f05531f 100644 --- a/xbmc/windows/GUIMediaWindow.cpp +++ b/xbmc/windows/GUIMediaWindow.cpp @@ -835,7 +835,8 @@ bool CGUIMediaWindow::Update(const CStdString &strDirectory) if (!bSelectedFound) m_viewControl.SetSelectedItem(0); - m_history.AddPath(m_vecItems->GetPath()); + if (iWindow != WINDOW_PVR || (iWindow == WINDOW_PVR && m_vecItems->GetPath().Left(17) == "pvr://recordings/")) + m_history.AddPath(m_vecItems->GetPath()); //m_history.DumpPathHistory(); diff --git a/xbmc/windows/GUIWindowLoginScreen.cpp b/xbmc/windows/GUIWindowLoginScreen.cpp index 9752c88d5f..a2b0477981 100644 --- a/xbmc/windows/GUIWindowLoginScreen.cpp +++ b/xbmc/windows/GUIWindowLoginScreen.cpp @@ -269,6 +269,10 @@ void CGUIWindowLoginScreen::LoadProfile(unsigned int profile) // stop service addons and give it some time before we start it again ADDON::CAddonMgr::Get().StopServices(true); + // stop PVR related services + g_application.StopPVRManager(); + g_application.StopEPGManager(); + if (profile != 0 || !g_settings.IsMasterUser()) { g_application.getNetwork().NetworkMessage(CNetwork::SERVICES_DOWN,1); @@ -304,6 +308,10 @@ void CGUIWindowLoginScreen::LoadProfile(unsigned int profile) // start services which should run on login ADDON::CAddonMgr::Get().StartServices(false); + // start PVR related services + g_application.StartEPGManager(); + g_application.StartPVRManager(); + g_windowManager.ChangeActiveWindow(g_SkinInfo->GetFirstWindow()); g_application.UpdateLibraries(); diff --git a/xbmc/windows/GUIWindowSystemInfo.cpp b/xbmc/windows/GUIWindowSystemInfo.cpp index 0e6e6f4226..b248df08ab 100644 --- a/xbmc/windows/GUIWindowSystemInfo.cpp +++ b/xbmc/windows/GUIWindowSystemInfo.cpp @@ -35,9 +35,10 @@ #define CONTROL_BT_NETWORK 96 #define CONTROL_BT_VIDEO 97 #define CONTROL_BT_HARDWARE 98 +#define CONTROL_BT_PVR 99 #define CONTROL_START CONTROL_BT_STORAGE -#define CONTROL_END CONTROL_BT_HARDWARE +#define CONTROL_END CONTROL_BT_PVR CGUIWindowSystemInfo::CGUIWindowSystemInfo(void) :CGUIWindow(WINDOW_SYSTEM_INFORMATION, "SettingsSystemInfo.xml") @@ -151,6 +152,22 @@ void CGUIWindowSystemInfo::FrameMove() SetControlLabel(i++, "%s: %s", 22012, SYSTEM_TOTAL_MEMORY); SetControlLabel(i++, "%s: %s", 158, SYSTEM_FREE_MEMORY); } + else if(m_section == CONTROL_BT_PVR) + { + SET_CONTROL_LABEL(40,g_localizeStrings.Get(19166)); + int i = 2; + + SetControlLabel(i++, "%s: %s", 19120, PVR_BACKEND_NUMBER); + i++; // empty line + SetControlLabel(i++, "%s: %s", 19012, PVR_BACKEND_NAME); + SetControlLabel(i++, "%s: %s", 19114, PVR_BACKEND_VERSION); + SetControlLabel(i++, "%s: %s", 19115, PVR_BACKEND_HOST); + SetControlLabel(i++, "%s: %s", 19116, PVR_BACKEND_DISKSPACE); + SetControlLabel(i++, "%s: %s", 19019, PVR_BACKEND_CHANNELS); + SetControlLabel(i++, "%s: %s", 19163, PVR_BACKEND_RECORDINGS); + SetControlLabel(i++, "%s: %s", 19025, PVR_BACKEND_TIMERS); + } + CGUIWindow::FrameMove(); } |