aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Op den Kamp <lars@opdenkamp.eu>2012-09-01 17:12:07 +0200
committerLars Op den Kamp <lars@opdenkamp.eu>2012-09-05 00:17:22 +0200
commit4e9cb43928abbddd5d683cb5d9d66c72b3bbf88e (patch)
tree58c59c5d5621c553118edb569e1e815c45567f83
parentc79dd456775c37963a27d9f563c405d83ee19c5c (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
-rw-r--r--.gitignore37
-rw-r--r--Makefile.in33
-rw-r--r--XBMC-ATV2.xcodeproj/project.pbxproj358
-rw-r--r--XBMC-IOS.xcodeproj/project.pbxproj366
-rw-r--r--XBMC.xcodeproj/project.pbxproj358
-rw-r--r--addons/library.xbmc.addon/dlfcn-win32.cpp263
-rw-r--r--addons/library.xbmc.addon/dlfcn-win32.h46
-rw-r--r--addons/library.xbmc.addon/libXBMC_addon.h182
-rw-r--r--addons/library.xbmc.gui/libXBMC_gui.h310
-rw-r--r--addons/library.xbmc.pvr/libXBMC_pvr.h170
-rw-r--r--addons/skin.confluence/720p/DialogButtonMenu.xml40
-rw-r--r--addons/skin.confluence/720p/DialogExtendedProgressBar.xml48
-rw-r--r--addons/skin.confluence/720p/DialogPVRChannelManager.xml560
-rw-r--r--addons/skin.confluence/720p/DialogPVRChannelsOSD.xml359
-rw-r--r--addons/skin.confluence/720p/DialogPVRGroupManager.xml433
-rw-r--r--addons/skin.confluence/720p/DialogPVRGuideInfo.xml226
-rw-r--r--addons/skin.confluence/720p/DialogPVRGuideOSD.xml253
-rw-r--r--addons/skin.confluence/720p/DialogPVRGuideSearch.xml456
-rw-r--r--addons/skin.confluence/720p/DialogPVRRecordingInfo.xml267
-rw-r--r--addons/skin.confluence/720p/DialogPVRTimerSettings.xml155
-rw-r--r--addons/skin.confluence/720p/Home.xml454
-rw-r--r--addons/skin.confluence/720p/IncludesBackgroundBuilding.xml99
-rw-r--r--addons/skin.confluence/720p/IncludesHomeMenuItems.xml44
-rw-r--r--addons/skin.confluence/720p/MusicOSD.xml6
-rw-r--r--addons/skin.confluence/720p/MyPVR.xml258
-rw-r--r--addons/skin.confluence/720p/PlayerControls.xml149
-rw-r--r--addons/skin.confluence/720p/Settings.xml18
-rw-r--r--addons/skin.confluence/720p/SettingsSystemInfo.xml15
-rw-r--r--addons/skin.confluence/720p/VideoFullScreen.xml324
-rw-r--r--addons/skin.confluence/720p/VideoOSD.xml207
-rw-r--r--addons/skin.confluence/720p/ViewsPVR.xml2392
-rw-r--r--addons/skin.confluence/720p/defaults.xml1
-rw-r--r--addons/skin.confluence/720p/includes.xml82
-rw-r--r--addons/skin.confluence/backgrounds/tv.jpgbin0 -> 576461 bytes
-rw-r--r--addons/skin.confluence/language/Dutch/strings.po40
-rw-r--r--addons/skin.confluence/language/English/strings.po51
-rw-r--r--addons/skin.confluence/language/Finnish/strings.po44
-rw-r--r--addons/skin.confluence/media/OSDChannelDownFO.pngbin0 -> 4693 bytes
-rw-r--r--addons/skin.confluence/media/OSDChannelDownNF.pngbin0 -> 3686 bytes
-rw-r--r--addons/skin.confluence/media/OSDChannelListFO.pngbin0 -> 4766 bytes
-rw-r--r--addons/skin.confluence/media/OSDChannelListNF.pngbin0 -> 3654 bytes
-rw-r--r--addons/skin.confluence/media/OSDChannelUPFO.pngbin0 -> 4643 bytes
-rw-r--r--addons/skin.confluence/media/OSDChannelUPNF.pngbin0 -> 3698 bytes
-rw-r--r--addons/skin.confluence/media/OSDRecordOff.pngbin829 -> 0 bytes
-rw-r--r--addons/skin.confluence/media/OSDRecordOffFO.png (renamed from addons/skin.confluence/media/OSDRecordFO.png)bin5416 -> 5416 bytes
-rw-r--r--addons/skin.confluence/media/OSDRecordOffNF.png (renamed from addons/skin.confluence/media/OSDRecordNF.png)bin865 -> 865 bytes
-rw-r--r--addons/skin.confluence/media/OSDRecordOnFO.png (renamed from addons/skin.confluence/media/OSDRecord2.png)bin5389 -> 5389 bytes
-rw-r--r--addons/skin.confluence/media/OSDRecordOnNF.pngbin0 -> 1861 bytes
-rw-r--r--addons/skin.confluence/media/OSDTeleTextFO.pngbin0 -> 4838 bytes
-rw-r--r--addons/skin.confluence/media/OSDTeleTextNF.pngbin0 -> 3654 bytes
-rw-r--r--addons/skin.confluence/media/OSDepgFO.pngbin0 -> 4842 bytes
-rw-r--r--addons/skin.confluence/media/OSDepgNF.pngbin0 -> 3648 bytes
-rw-r--r--addons/skin.confluence/media/PVR-HasTimer.pngbin0 -> 3138 bytes
-rw-r--r--addons/skin.confluence/media/PVR-IsRecording.pngbin0 -> 3655 bytes
-rw-r--r--addons/skin.confluence/media/StackNF.pngbin2894 -> 2897 bytes
-rw-r--r--addons/skin.confluence/media/epg-genres/0.pngbin0 -> 2849 bytes
-rw-r--r--addons/skin.confluence/media/epg-genres/112.pngbin0 -> 2849 bytes
-rw-r--r--addons/skin.confluence/media/epg-genres/128.pngbin0 -> 2849 bytes
-rw-r--r--addons/skin.confluence/media/epg-genres/144.pngbin0 -> 2849 bytes
-rw-r--r--addons/skin.confluence/media/epg-genres/16.pngbin0 -> 2849 bytes
-rw-r--r--addons/skin.confluence/media/epg-genres/160.pngbin0 -> 2849 bytes
-rw-r--r--addons/skin.confluence/media/epg-genres/176.pngbin0 -> 2849 bytes
-rw-r--r--addons/skin.confluence/media/epg-genres/192.pngbin0 -> 2849 bytes
-rw-r--r--addons/skin.confluence/media/epg-genres/208.pngbin0 -> 2849 bytes
-rw-r--r--addons/skin.confluence/media/epg-genres/224.pngbin0 -> 2849 bytes
-rw-r--r--addons/skin.confluence/media/epg-genres/240.pngbin0 -> 2849 bytes
-rw-r--r--addons/skin.confluence/media/epg-genres/32.pngbin0 -> 2847 bytes
-rw-r--r--addons/skin.confluence/media/epg-genres/48.pngbin0 -> 2849 bytes
-rw-r--r--addons/skin.confluence/media/epg-genres/64.pngbin0 -> 2849 bytes
-rw-r--r--addons/skin.confluence/media/epg-genres/80.pngbin0 -> 2849 bytes
-rw-r--r--addons/skin.confluence/media/epg-genres/96.pngbin0 -> 2849 bytes
-rw-r--r--addons/skin.confluence/media/epg-genres/genre-numbers.txt18
-rw-r--r--addons/skin.confluence/media/gradient.pngbin0 -> 3585 bytes
-rw-r--r--addons/skin.confluence/media/home-power-inhibit-FO.pngbin0 -> 7098 bytes
-rw-r--r--addons/skin.confluence/media/home-power-inhibit.pngbin0 -> 6502 bytes
-rw-r--r--configure.in3
-rw-r--r--docs/README.pvr4
-rw-r--r--language/English/strings.po1482
-rw-r--r--lib/addons/library.xbmc.addon/Makefile.in27
-rw-r--r--lib/addons/library.xbmc.addon/libXBMC_addon.cpp128
-rw-r--r--lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj88
-rw-r--r--lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj.filters19
-rw-r--r--lib/addons/library.xbmc.gui/Makefile.in27
-rw-r--r--lib/addons/library.xbmc.gui/libXBMC_gui.cpp555
-rw-r--r--lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj85
-rw-r--r--lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj.filters18
-rw-r--r--lib/addons/library.xbmc.pvr/Makefile.in27
-rw-r--r--lib/addons/library.xbmc.pvr/libXBMC_pvr.cpp181
-rw-r--r--lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj85
-rw-r--r--lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj.filters18
-rw-r--r--project/VS2010Express/XBMC for Windows.sln73
-rw-r--r--project/VS2010Express/XBMC.vcxproj101
-rw-r--r--project/VS2010Express/XBMC.vcxproj.filters328
-rw-r--r--system/keymaps/keyboard.xml56
-rw-r--r--system/keymaps/remote.xml154
-rw-r--r--system/playercorefactory.xml3
-rw-r--r--xbmc/Application.cpp120
-rw-r--r--xbmc/Application.h8
-rw-r--r--xbmc/ApplicationMessenger.cpp34
-rw-r--r--xbmc/ApplicationMessenger.h7
-rw-r--r--xbmc/DatabaseManager.cpp6
-rw-r--r--xbmc/FileItem.cpp347
-rw-r--r--xbmc/FileItem.h72
-rw-r--r--xbmc/GUIInfoManager.cpp639
-rw-r--r--xbmc/GUIInfoManager.h114
-rw-r--r--xbmc/GUIViewControl.cpp9
-rw-r--r--xbmc/GUIViewControl.h2
-rw-r--r--xbmc/GUIViewState.cpp5
-rw-r--r--xbmc/SortFileItem.h1
-rw-r--r--xbmc/URL.cpp3
-rw-r--r--xbmc/Util.cpp36
-rw-r--r--xbmc/Util.h4
-rw-r--r--xbmc/XBDateTime.cpp39
-rw-r--r--xbmc/XBDateTime.h3
-rw-r--r--xbmc/addons/Addon.cpp6
-rw-r--r--xbmc/addons/Addon.h7
-rw-r--r--xbmc/addons/AddonCallbacks.cpp142
-rw-r--r--xbmc/addons/AddonCallbacks.h268
-rw-r--r--xbmc/addons/AddonCallbacksAddon.cpp250
-rw-r--r--xbmc/addons/AddonCallbacksAddon.h91
-rw-r--r--xbmc/addons/AddonCallbacksGUI.cpp1542
-rw-r--r--xbmc/addons/AddonCallbacksGUI.h182
-rw-r--r--xbmc/addons/AddonCallbacksPVR.cpp273
-rw-r--r--xbmc/addons/AddonCallbacksPVR.h160
-rw-r--r--xbmc/addons/AddonDatabase.cpp11
-rw-r--r--xbmc/addons/AddonDatabase.h4
-rw-r--r--xbmc/addons/AddonDll.h27
-rw-r--r--xbmc/addons/AddonManager.cpp70
-rw-r--r--xbmc/addons/AddonManager.h4
-rw-r--r--xbmc/addons/AddonStatusHandler.cpp31
-rw-r--r--xbmc/addons/DllPVRClient.h30
-rw-r--r--xbmc/addons/GUIDialogAddonInfo.cpp13
-rw-r--r--xbmc/addons/Makefile4
-rw-r--r--xbmc/addons/Skin.cpp1
-rw-r--r--xbmc/addons/include/NOTE3
-rw-r--r--xbmc/addons/include/xbmc_addon_types.h11
-rw-r--r--xbmc/addons/include/xbmc_epg_types.h96
-rw-r--r--xbmc/addons/include/xbmc_pvr_dll.h587
-rw-r--r--xbmc/addons/include/xbmc_pvr_types.h336
-rw-r--r--xbmc/cores/DllLoader/DllLoaderContainer.cpp2
-rw-r--r--xbmc/cores/DllLoader/Win32DllLoader.cpp3
-rw-r--r--xbmc/cores/IPlayer.h6
-rw-r--r--xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp26
-rw-r--r--xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h14
-rw-r--r--xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp1
-rw-r--r--xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp4
-rw-r--r--xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp321
-rw-r--r--xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h111
-rw-r--r--xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h37
-rw-r--r--xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h2
-rw-r--r--xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp39
-rw-r--r--xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in1
-rw-r--r--xbmc/cores/dvdplayer/DVDInputStreams/DVDFactoryInputStream.cpp3
-rw-r--r--xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStream.h19
-rw-r--r--xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.cpp12
-rw-r--r--xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.h12
-rw-r--r--xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.cpp381
-rw-r--r--xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.h115
-rw-r--r--xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.cpp8
-rw-r--r--xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.h7
-rw-r--r--xbmc/cores/dvdplayer/DVDInputStreams/Makefile1
-rw-r--r--xbmc/cores/dvdplayer/DVDMessage.h3
-rw-r--r--xbmc/cores/dvdplayer/DVDPlayer.cpp304
-rw-r--r--xbmc/cores/dvdplayer/DVDPlayer.h23
-rw-r--r--xbmc/cores/dvdplayer/Edl.cpp5
-rw-r--r--xbmc/cores/paplayer/CodecFactory.cpp3
-rw-r--r--xbmc/dialogs/GUIDialogContextMenu.h20
-rw-r--r--xbmc/dialogs/GUIDialogExtendedProgressBar.cpp172
-rw-r--r--xbmc/dialogs/GUIDialogExtendedProgressBar.h71
-rw-r--r--xbmc/dialogs/GUIDialogMediaSource.cpp10
-rw-r--r--xbmc/dialogs/GUIDialogNumeric.cpp9
-rw-r--r--xbmc/dialogs/GUIDialogNumeric.h2
-rw-r--r--xbmc/dialogs/GUIDialogSeekBar.cpp6
-rw-r--r--xbmc/dialogs/Makefile1
-rw-r--r--xbmc/epg/Epg.cpp960
-rw-r--r--xbmc/epg/Epg.h367
-rw-r--r--xbmc/epg/EpgContainer.cpp657
-rw-r--r--xbmc/epg/EpgContainer.h292
-rw-r--r--xbmc/epg/EpgDatabase.cpp437
-rw-r--r--xbmc/epg/EpgDatabase.h163
-rw-r--r--xbmc/epg/EpgInfoTag.cpp982
-rw-r--r--xbmc/epg/EpgInfoTag.h457
-rw-r--r--xbmc/epg/EpgSearchFilter.cpp249
-rw-r--r--xbmc/epg/EpgSearchFilter.h81
-rw-r--r--xbmc/epg/GUIEPGGridContainer.cpp1934
-rw-r--r--xbmc/epg/GUIEPGGridContainer.h230
-rw-r--r--xbmc/epg/Makefile13
-rw-r--r--xbmc/filesystem/AddonsDirectory.cpp5
-rw-r--r--xbmc/filesystem/DirectoryFactory.cpp6
-rw-r--r--xbmc/filesystem/DllLibCMyth.h17
-rw-r--r--xbmc/filesystem/FileFactory.cpp6
-rw-r--r--xbmc/filesystem/ILiveTV.h4
-rw-r--r--xbmc/filesystem/Makefile.in2
-rw-r--r--xbmc/filesystem/MythFile.cpp4
-rw-r--r--xbmc/filesystem/MythFile.h4
-rw-r--r--xbmc/filesystem/PVRDirectory.cpp126
-rw-r--r--xbmc/filesystem/PVRDirectory.h46
-rw-r--r--xbmc/filesystem/PVRFile.cpp318
-rw-r--r--xbmc/filesystem/PVRFile.h81
-rw-r--r--xbmc/filesystem/SlingboxFile.cpp4
-rw-r--r--xbmc/filesystem/SlingboxFile.h4
-rw-r--r--xbmc/filesystem/VTPFile.cpp4
-rw-r--r--xbmc/filesystem/VTPFile.h4
-rw-r--r--xbmc/guilib/GUIControl.h1
-rw-r--r--xbmc/guilib/GUIControlFactory.cpp14
-rw-r--r--xbmc/guilib/GUIDialog.cpp13
-rw-r--r--xbmc/guilib/GUIDialog.h3
-rw-r--r--xbmc/guilib/GUIEditControl.cpp28
-rw-r--r--xbmc/guilib/GUIEditControl.h4
-rw-r--r--xbmc/guilib/GUILabelControl.cpp7
-rw-r--r--xbmc/guilib/GUILabelControl.h1
-rw-r--r--xbmc/guilib/GUIListGroup.cpp44
-rw-r--r--xbmc/guilib/GUIListGroup.h2
-rw-r--r--xbmc/guilib/GUIListItemLayout.cpp14
-rw-r--r--xbmc/guilib/GUIListItemLayout.h2
-rw-r--r--xbmc/guilib/GUIListLabel.cpp12
-rw-r--r--xbmc/guilib/GUIListLabel.h1
-rw-r--r--xbmc/guilib/GUIProgressControl.cpp4
-rw-r--r--xbmc/guilib/GUIProgressControl.h1
-rw-r--r--xbmc/guilib/GUIWindow.cpp16
-rw-r--r--xbmc/guilib/GUIWindow.h9
-rw-r--r--xbmc/guilib/GUIWindowManager.cpp10
-rw-r--r--xbmc/guilib/Key.h26
-rw-r--r--xbmc/input/ButtonTranslator.cpp29
-rw-r--r--xbmc/input/XBIRRemote.h5
-rw-r--r--xbmc/interfaces/Builtins.cpp41
-rw-r--r--xbmc/interfaces/python/xbmcmodule/xbmcmodule.cpp3
-rw-r--r--xbmc/pvr/Makefile8
-rw-r--r--xbmc/pvr/PVRDatabase.cpp932
-rw-r--r--xbmc/pvr/PVRDatabase.h257
-rw-r--r--xbmc/pvr/PVRGUIInfo.cpp857
-rw-r--r--xbmc/pvr/PVRGUIInfo.h172
-rw-r--r--xbmc/pvr/PVRManager.cpp1223
-rw-r--r--xbmc/pvr/PVRManager.h629
-rw-r--r--xbmc/pvr/addons/Makefile7
-rw-r--r--xbmc/pvr/addons/PVRClient.cpp1334
-rw-r--r--xbmc/pvr/addons/PVRClient.h563
-rw-r--r--xbmc/pvr/addons/PVRClients.cpp1303
-rw-r--r--xbmc/pvr/addons/PVRClients.h614
-rw-r--r--xbmc/pvr/channels/Makefile10
-rw-r--r--xbmc/pvr/channels/PVRChannel.cpp866
-rw-r--r--xbmc/pvr/channels/PVRChannel.h494
-rw-r--r--xbmc/pvr/channels/PVRChannelGroup.cpp1153
-rw-r--r--xbmc/pvr/channels/PVRChannelGroup.h475
-rw-r--r--xbmc/pvr/channels/PVRChannelGroupInternal.cpp384
-rw-r--r--xbmc/pvr/channels/PVRChannelGroupInternal.h177
-rw-r--r--xbmc/pvr/channels/PVRChannelGroups.cpp462
-rw-r--r--xbmc/pvr/channels/PVRChannelGroups.h188
-rw-r--r--xbmc/pvr/channels/PVRChannelGroupsContainer.cpp285
-rw-r--r--xbmc/pvr/channels/PVRChannelGroupsContainer.h200
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRChannelManager.cpp854
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRChannelManager.h85
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp266
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.h53
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRCutterOSD.cpp62
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRCutterOSD.h37
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRDirectorOSD.cpp68
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRDirectorOSD.h37
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGroupManager.cpp385
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGroupManager.h72
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp241
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.h56
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGuideOSD.cpp165
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGuideOSD.h49
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp369
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.h58
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.cpp61
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.h41
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp370
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h61
-rw-r--r--xbmc/pvr/dialogs/Makefile15
-rw-r--r--xbmc/pvr/recordings/Makefile7
-rw-r--r--xbmc/pvr/recordings/PVRRecording.cpp219
-rw-r--r--xbmc/pvr/recordings/PVRRecording.h113
-rw-r--r--xbmc/pvr/recordings/PVRRecordings.cpp544
-rw-r--r--xbmc/pvr/recordings/PVRRecordings.h79
-rw-r--r--xbmc/pvr/timers/Makefile7
-rw-r--r--xbmc/pvr/timers/PVRTimerInfoTag.cpp598
-rw-r--r--xbmc/pvr/timers/PVRTimerInfoTag.h183
-rw-r--r--xbmc/pvr/timers/PVRTimers.cpp736
-rw-r--r--xbmc/pvr/timers/PVRTimers.h180
-rw-r--r--xbmc/pvr/windows/GUIViewStatePVR.cpp69
-rw-r--r--xbmc/pvr/windows/GUIViewStatePVR.h40
-rw-r--r--xbmc/pvr/windows/GUIWindowPVR.cpp292
-rw-r--r--xbmc/pvr/windows/GUIWindowPVR.h88
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRChannels.cpp613
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRChannels.h76
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRCommon.cpp862
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRCommon.h142
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRGuide.cpp486
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRGuide.h75
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRRecordings.cpp397
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRRecordings.h62
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRSearch.cpp290
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRSearch.h61
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRTimers.cpp284
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRTimers.h56
-rw-r--r--xbmc/pvr/windows/Makefile13
-rw-r--r--xbmc/settings/AdvancedSettings.cpp61
-rw-r--r--xbmc/settings/AdvancedSettings.h18
-rw-r--r--xbmc/settings/GUIDialogSettings.cpp130
-rw-r--r--xbmc/settings/GUIDialogSettings.h10
-rw-r--r--xbmc/settings/GUISettings.cpp191
-rw-r--r--xbmc/settings/GUISettings.h41
-rw-r--r--xbmc/settings/GUIWindowSettingsCategory.cpp142
-rw-r--r--xbmc/settings/GUIWindowSettingsCategory.h2
-rw-r--r--xbmc/settings/Settings.cpp2
-rw-r--r--xbmc/settings/SettingsControls.cpp2
-rw-r--r--xbmc/settings/VideoSettings.h3
-rw-r--r--xbmc/system.h1
-rw-r--r--xbmc/utils/DatabaseUtils.h1
-rw-r--r--xbmc/utils/Makefile2
-rw-r--r--xbmc/utils/Observer.cpp200
-rw-r--r--xbmc/utils/Observer.h150
-rw-r--r--xbmc/utils/SortUtils.cpp7
-rw-r--r--xbmc/utils/SortUtils.h3
-rw-r--r--xbmc/utils/TextSearch.cpp166
-rw-r--r--xbmc/utils/TextSearch.h50
-rw-r--r--xbmc/utils/URIUtils.cpp15
-rw-r--r--xbmc/utils/URIUtils.h1
-rw-r--r--xbmc/utils/XMLUtils.cpp8
-rw-r--r--xbmc/utils/XMLUtils.h1
-rw-r--r--xbmc/video/GUIViewStateVideo.cpp2
-rw-r--r--xbmc/video/VideoDatabase.cpp7
-rw-r--r--xbmc/video/dialogs/GUIDialogAudioSubtitleSettings.cpp5
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoOSD.cpp18
-rw-r--r--xbmc/video/dialogs/GUIDialogVideoSettings.cpp6
-rw-r--r--xbmc/video/windows/GUIWindowFullScreen.cpp232
-rw-r--r--xbmc/video/windows/GUIWindowFullScreen.h4
-rw-r--r--xbmc/video/windows/GUIWindowVideoBase.cpp63
-rw-r--r--xbmc/win32/stdbool.h53
-rw-r--r--xbmc/windows/GUIMediaWindow.cpp3
-rw-r--r--xbmc/windows/GUIWindowLoginScreen.cpp8
-rw-r--r--xbmc/windows/GUIWindowSystemInfo.cpp19
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
new file mode 100644
index 0000000000..9414690563
--- /dev/null
+++ b/addons/skin.confluence/backgrounds/tv.jpg
Binary files differ
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
new file mode 100644
index 0000000000..5116c327ba
--- /dev/null
+++ b/addons/skin.confluence/media/OSDChannelDownFO.png
Binary files differ
diff --git a/addons/skin.confluence/media/OSDChannelDownNF.png b/addons/skin.confluence/media/OSDChannelDownNF.png
new file mode 100644
index 0000000000..6795c43328
--- /dev/null
+++ b/addons/skin.confluence/media/OSDChannelDownNF.png
Binary files differ
diff --git a/addons/skin.confluence/media/OSDChannelListFO.png b/addons/skin.confluence/media/OSDChannelListFO.png
new file mode 100644
index 0000000000..a08bc13a7d
--- /dev/null
+++ b/addons/skin.confluence/media/OSDChannelListFO.png
Binary files differ
diff --git a/addons/skin.confluence/media/OSDChannelListNF.png b/addons/skin.confluence/media/OSDChannelListNF.png
new file mode 100644
index 0000000000..8339fdced4
--- /dev/null
+++ b/addons/skin.confluence/media/OSDChannelListNF.png
Binary files differ
diff --git a/addons/skin.confluence/media/OSDChannelUPFO.png b/addons/skin.confluence/media/OSDChannelUPFO.png
new file mode 100644
index 0000000000..a3e6dbad5f
--- /dev/null
+++ b/addons/skin.confluence/media/OSDChannelUPFO.png
Binary files differ
diff --git a/addons/skin.confluence/media/OSDChannelUPNF.png b/addons/skin.confluence/media/OSDChannelUPNF.png
new file mode 100644
index 0000000000..47e6e331bc
--- /dev/null
+++ b/addons/skin.confluence/media/OSDChannelUPNF.png
Binary files differ
diff --git a/addons/skin.confluence/media/OSDRecordOff.png b/addons/skin.confluence/media/OSDRecordOff.png
deleted file mode 100644
index cb73c77605..0000000000
--- a/addons/skin.confluence/media/OSDRecordOff.png
+++ /dev/null
Binary files differ
diff --git a/addons/skin.confluence/media/OSDRecordFO.png b/addons/skin.confluence/media/OSDRecordOffFO.png
index fd2bb18fa9..fd2bb18fa9 100644
--- a/addons/skin.confluence/media/OSDRecordFO.png
+++ b/addons/skin.confluence/media/OSDRecordOffFO.png
Binary files differ
diff --git a/addons/skin.confluence/media/OSDRecordNF.png b/addons/skin.confluence/media/OSDRecordOffNF.png
index db3d5753e1..db3d5753e1 100644
--- a/addons/skin.confluence/media/OSDRecordNF.png
+++ b/addons/skin.confluence/media/OSDRecordOffNF.png
Binary files differ
diff --git a/addons/skin.confluence/media/OSDRecord2.png b/addons/skin.confluence/media/OSDRecordOnFO.png
index cb4fcf9edf..cb4fcf9edf 100644
--- a/addons/skin.confluence/media/OSDRecord2.png
+++ b/addons/skin.confluence/media/OSDRecordOnFO.png
Binary files differ
diff --git a/addons/skin.confluence/media/OSDRecordOnNF.png b/addons/skin.confluence/media/OSDRecordOnNF.png
new file mode 100644
index 0000000000..b3358180db
--- /dev/null
+++ b/addons/skin.confluence/media/OSDRecordOnNF.png
Binary files differ
diff --git a/addons/skin.confluence/media/OSDTeleTextFO.png b/addons/skin.confluence/media/OSDTeleTextFO.png
new file mode 100644
index 0000000000..53eb5762fd
--- /dev/null
+++ b/addons/skin.confluence/media/OSDTeleTextFO.png
Binary files differ
diff --git a/addons/skin.confluence/media/OSDTeleTextNF.png b/addons/skin.confluence/media/OSDTeleTextNF.png
new file mode 100644
index 0000000000..111c0685b2
--- /dev/null
+++ b/addons/skin.confluence/media/OSDTeleTextNF.png
Binary files differ
diff --git a/addons/skin.confluence/media/OSDepgFO.png b/addons/skin.confluence/media/OSDepgFO.png
new file mode 100644
index 0000000000..141f7adfc7
--- /dev/null
+++ b/addons/skin.confluence/media/OSDepgFO.png
Binary files differ
diff --git a/addons/skin.confluence/media/OSDepgNF.png b/addons/skin.confluence/media/OSDepgNF.png
new file mode 100644
index 0000000000..cf9a86b656
--- /dev/null
+++ b/addons/skin.confluence/media/OSDepgNF.png
Binary files differ
diff --git a/addons/skin.confluence/media/PVR-HasTimer.png b/addons/skin.confluence/media/PVR-HasTimer.png
new file mode 100644
index 0000000000..99c2ee9b44
--- /dev/null
+++ b/addons/skin.confluence/media/PVR-HasTimer.png
Binary files differ
diff --git a/addons/skin.confluence/media/PVR-IsRecording.png b/addons/skin.confluence/media/PVR-IsRecording.png
new file mode 100644
index 0000000000..e64b346327
--- /dev/null
+++ b/addons/skin.confluence/media/PVR-IsRecording.png
Binary files differ
diff --git a/addons/skin.confluence/media/StackNF.png b/addons/skin.confluence/media/StackNF.png
index 17e5052671..0cbcd832fc 100644
--- a/addons/skin.confluence/media/StackNF.png
+++ b/addons/skin.confluence/media/StackNF.png
Binary files differ
diff --git a/addons/skin.confluence/media/epg-genres/0.png b/addons/skin.confluence/media/epg-genres/0.png
new file mode 100644
index 0000000000..80c6192880
--- /dev/null
+++ b/addons/skin.confluence/media/epg-genres/0.png
Binary files differ
diff --git a/addons/skin.confluence/media/epg-genres/112.png b/addons/skin.confluence/media/epg-genres/112.png
new file mode 100644
index 0000000000..7701fc6fb8
--- /dev/null
+++ b/addons/skin.confluence/media/epg-genres/112.png
Binary files differ
diff --git a/addons/skin.confluence/media/epg-genres/128.png b/addons/skin.confluence/media/epg-genres/128.png
new file mode 100644
index 0000000000..3071d4e6af
--- /dev/null
+++ b/addons/skin.confluence/media/epg-genres/128.png
Binary files differ
diff --git a/addons/skin.confluence/media/epg-genres/144.png b/addons/skin.confluence/media/epg-genres/144.png
new file mode 100644
index 0000000000..a9325a699e
--- /dev/null
+++ b/addons/skin.confluence/media/epg-genres/144.png
Binary files differ
diff --git a/addons/skin.confluence/media/epg-genres/16.png b/addons/skin.confluence/media/epg-genres/16.png
new file mode 100644
index 0000000000..ffaae9eff4
--- /dev/null
+++ b/addons/skin.confluence/media/epg-genres/16.png
Binary files differ
diff --git a/addons/skin.confluence/media/epg-genres/160.png b/addons/skin.confluence/media/epg-genres/160.png
new file mode 100644
index 0000000000..8d825ab34b
--- /dev/null
+++ b/addons/skin.confluence/media/epg-genres/160.png
Binary files differ
diff --git a/addons/skin.confluence/media/epg-genres/176.png b/addons/skin.confluence/media/epg-genres/176.png
new file mode 100644
index 0000000000..7ae6320ab2
--- /dev/null
+++ b/addons/skin.confluence/media/epg-genres/176.png
Binary files differ
diff --git a/addons/skin.confluence/media/epg-genres/192.png b/addons/skin.confluence/media/epg-genres/192.png
new file mode 100644
index 0000000000..80c6192880
--- /dev/null
+++ b/addons/skin.confluence/media/epg-genres/192.png
Binary files differ
diff --git a/addons/skin.confluence/media/epg-genres/208.png b/addons/skin.confluence/media/epg-genres/208.png
new file mode 100644
index 0000000000..80c6192880
--- /dev/null
+++ b/addons/skin.confluence/media/epg-genres/208.png
Binary files differ
diff --git a/addons/skin.confluence/media/epg-genres/224.png b/addons/skin.confluence/media/epg-genres/224.png
new file mode 100644
index 0000000000..80c6192880
--- /dev/null
+++ b/addons/skin.confluence/media/epg-genres/224.png
Binary files differ
diff --git a/addons/skin.confluence/media/epg-genres/240.png b/addons/skin.confluence/media/epg-genres/240.png
new file mode 100644
index 0000000000..80c6192880
--- /dev/null
+++ b/addons/skin.confluence/media/epg-genres/240.png
Binary files differ
diff --git a/addons/skin.confluence/media/epg-genres/32.png b/addons/skin.confluence/media/epg-genres/32.png
new file mode 100644
index 0000000000..be2bc8ee5f
--- /dev/null
+++ b/addons/skin.confluence/media/epg-genres/32.png
Binary files differ
diff --git a/addons/skin.confluence/media/epg-genres/48.png b/addons/skin.confluence/media/epg-genres/48.png
new file mode 100644
index 0000000000..57b4dee631
--- /dev/null
+++ b/addons/skin.confluence/media/epg-genres/48.png
Binary files differ
diff --git a/addons/skin.confluence/media/epg-genres/64.png b/addons/skin.confluence/media/epg-genres/64.png
new file mode 100644
index 0000000000..d85eb051b9
--- /dev/null
+++ b/addons/skin.confluence/media/epg-genres/64.png
Binary files differ
diff --git a/addons/skin.confluence/media/epg-genres/80.png b/addons/skin.confluence/media/epg-genres/80.png
new file mode 100644
index 0000000000..cdd6da3b1d
--- /dev/null
+++ b/addons/skin.confluence/media/epg-genres/80.png
Binary files differ
diff --git a/addons/skin.confluence/media/epg-genres/96.png b/addons/skin.confluence/media/epg-genres/96.png
new file mode 100644
index 0000000000..ba92ae36d7
--- /dev/null
+++ b/addons/skin.confluence/media/epg-genres/96.png
Binary files differ
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
new file mode 100644
index 0000000000..7db158f2ea
--- /dev/null
+++ b/addons/skin.confluence/media/gradient.png
Binary files differ
diff --git a/addons/skin.confluence/media/home-power-inhibit-FO.png b/addons/skin.confluence/media/home-power-inhibit-FO.png
new file mode 100644
index 0000000000..3656ffa5de
--- /dev/null
+++ b/addons/skin.confluence/media/home-power-inhibit-FO.png
Binary files differ
diff --git a/addons/skin.confluence/media/home-power-inhibit.png b/addons/skin.confluence/media/home-power-inhibit.png
new file mode 100644
index 0000000000..b90d844202
--- /dev/null
+++ b/addons/skin.confluence/media/home-power-inhibit.png
Binary files differ
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 &amp; 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 &amp; 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();
}