aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--XBMC-ATV2.xcodeproj/project.pbxproj6
-rw-r--r--XBMC-IOS.xcodeproj/project.pbxproj6
-rw-r--r--XBMC.xcodeproj/project.pbxproj6
-rw-r--r--addons/skin.confluence/720p/DialogMediaFilter.xml184
-rw-r--r--addons/skin.confluence/720p/MyMusicNav.xml9
-rw-r--r--addons/skin.confluence/720p/MyMusicSongs.xml9
-rw-r--r--addons/skin.confluence/720p/MyPics.xml8
-rw-r--r--addons/skin.confluence/720p/MyPrograms.xml8
-rw-r--r--addons/skin.confluence/720p/MyVideoNav.xml9
-rw-r--r--addons/skin.confluence/720p/includes.xml2
-rw-r--r--language/English/strings.po24
-rw-r--r--project/VS2010Express/XBMC.vcxproj2
-rw-r--r--project/VS2010Express/XBMC.vcxproj.filters6
-rw-r--r--xbmc/Application.cpp4
-rw-r--r--xbmc/DbUrl.h1
-rw-r--r--xbmc/GUIInfoManager.cpp23
-rw-r--r--xbmc/GUIInfoManager.h4
-rw-r--r--xbmc/dbwrappers/Database.cpp5
-rw-r--r--xbmc/dbwrappers/Database.h3
-rw-r--r--xbmc/dialogs/GUIDialogMediaFilter.cpp913
-rw-r--r--xbmc/dialogs/GUIDialogMediaFilter.h82
-rw-r--r--xbmc/dialogs/Makefile1
-rw-r--r--xbmc/filesystem/SmartPlaylistDirectory.cpp97
-rw-r--r--xbmc/filesystem/SmartPlaylistDirectory.h2
-rw-r--r--xbmc/guilib/GUISliderControl.cpp347
-rw-r--r--xbmc/guilib/GUISliderControl.h38
-rw-r--r--xbmc/guilib/Key.h3
-rw-r--r--xbmc/input/ButtonTranslator.cpp4
-rw-r--r--xbmc/interfaces/json-rpc/AudioLibrary.cpp4
-rw-r--r--xbmc/music/MusicDatabase.cpp223
-rw-r--r--xbmc/music/MusicDatabase.h15
-rw-r--r--xbmc/music/windows/GUIWindowMusicBase.cpp38
-rw-r--r--xbmc/music/windows/GUIWindowMusicBase.h2
-rw-r--r--xbmc/music/windows/GUIWindowMusicNav.cpp2
-rw-r--r--xbmc/playlists/SmartPlayList.cpp42
-rw-r--r--xbmc/playlists/SmartPlayList.h6
-rw-r--r--xbmc/settings/GUIDialogSettings.cpp107
-rw-r--r--xbmc/settings/GUIDialogSettings.h13
-rw-r--r--xbmc/utils/URIUtils.cpp16
-rw-r--r--xbmc/utils/UrlOptions.cpp39
-rw-r--r--xbmc/utils/UrlOptions.h6
-rw-r--r--xbmc/video/VideoDatabase.cpp194
-rw-r--r--xbmc/video/VideoDatabase.h22
-rw-r--r--xbmc/video/windows/GUIWindowVideoBase.cpp40
-rw-r--r--xbmc/video/windows/GUIWindowVideoBase.h2
-rw-r--r--xbmc/video/windows/GUIWindowVideoNav.cpp18
-rw-r--r--xbmc/windows/GUIMediaWindow.cpp237
-rw-r--r--xbmc/windows/GUIMediaWindow.h18
48 files changed, 2564 insertions, 286 deletions
diff --git a/XBMC-ATV2.xcodeproj/project.pbxproj b/XBMC-ATV2.xcodeproj/project.pbxproj
index e02d1ce8bc..9abd902f5f 100644
--- a/XBMC-ATV2.xcodeproj/project.pbxproj
+++ b/XBMC-ATV2.xcodeproj/project.pbxproj
@@ -26,6 +26,7 @@
36A9468815CF214300727135 /* VideoDbUrl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A9468615CF214300727135 /* VideoDbUrl.cpp */; };
36A9468B15CF215300727135 /* UrlOptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A9468915CF215300727135 /* UrlOptions.cpp */; };
36A9468E15CF217400727135 /* MusicDbUrl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A9468C15CF217400727135 /* MusicDbUrl.cpp */; };
+ 36A95DB41624898700727135 /* GUIDialogMediaFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A95DB21624898700727135 /* GUIDialogMediaFilter.cpp */; };
4D5D2E131301753F006ABC13 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D5D2E121301753F006ABC13 /* CFNetwork.framework */; };
7C0A7ECD13A5DBF900AFC2BD /* AppParamParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0A7ECB13A5DBF900AFC2BD /* AppParamParser.cpp */; };
7C0A7FC813A9E75400AFC2BD /* DirtyRegionSolvers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0A7FC413A9E75400AFC2BD /* DirtyRegionSolvers.cpp */; };
@@ -1031,6 +1032,8 @@
36A9468A15CF215300727135 /* UrlOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UrlOptions.h; sourceTree = "<group>"; };
36A9468C15CF217400727135 /* MusicDbUrl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MusicDbUrl.cpp; sourceTree = "<group>"; };
36A9468D15CF217400727135 /* MusicDbUrl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MusicDbUrl.h; sourceTree = "<group>"; };
+ 36A95DB21624898700727135 /* GUIDialogMediaFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogMediaFilter.cpp; sourceTree = "<group>"; };
+ 36A95DB31624898700727135 /* GUIDialogMediaFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogMediaFilter.h; sourceTree = "<group>"; };
4D5D2E121301753F006ABC13 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
7C0A7ECB13A5DBF900AFC2BD /* AppParamParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppParamParser.cpp; sourceTree = "<group>"; };
7C0A7ECC13A5DBF900AFC2BD /* AppParamParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppParamParser.h; sourceTree = "<group>"; };
@@ -4601,6 +4604,8 @@
F56C7374131EC151000AD0F6 /* GUIDialogKaiToast.h */,
DF830D4915BB2D2300602BE6 /* GUIDialogKeyboardGeneric.cpp */,
DF830D4A15BB2D2300602BE6 /* GUIDialogKeyboardGeneric.h */,
+ 36A95DB21624898700727135 /* GUIDialogMediaFilter.cpp */,
+ 36A95DB31624898700727135 /* GUIDialogMediaFilter.h */,
F56C7377131EC151000AD0F6 /* GUIDialogMediaSource.cpp */,
F56C7378131EC151000AD0F6 /* GUIDialogMediaSource.h */,
F56C7379131EC151000AD0F6 /* GUIDialogMuteBug.cpp */,
@@ -7568,6 +7573,7 @@
7C4458DB161E209100A905F6 /* Screenshot.cpp in Sources */,
1D638118161E20AC003603ED /* PeripheralImon.cpp in Sources */,
DF24EAC71621E58D00034265 /* DVDDemuxBXA.cpp in Sources */,
+ 36A95DB41624898700727135 /* GUIDialogMediaFilter.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/XBMC-IOS.xcodeproj/project.pbxproj b/XBMC-IOS.xcodeproj/project.pbxproj
index a12037c377..87e991f258 100644
--- a/XBMC-IOS.xcodeproj/project.pbxproj
+++ b/XBMC-IOS.xcodeproj/project.pbxproj
@@ -27,6 +27,7 @@
36A9467815CF20A500727135 /* UrlOptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A9467615CF20A500727135 /* UrlOptions.cpp */; };
36A9467B15CF20BD00727135 /* VideoDbUrl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A9467915CF20BD00727135 /* VideoDbUrl.cpp */; };
36A9467E15CF20E100727135 /* DbUrl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A9467C15CF20E100727135 /* DbUrl.cpp */; };
+ 36A95DAD1624896C00727135 /* GUIDialogMediaFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A95DAB1624896C00727135 /* GUIDialogMediaFilter.cpp */; };
4D5D2E1E1301758F006ABC13 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D5D2E1D1301758F006ABC13 /* CFNetwork.framework */; };
7C0A7EDE13A5DC2800AFC2BD /* AppParamParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0A7EDC13A5DC2800AFC2BD /* AppParamParser.cpp */; };
7C0A7F9D13A9E70800AFC2BD /* GUIWindowDebugInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C0A7F9B13A9E70800AFC2BD /* GUIWindowDebugInfo.cpp */; };
@@ -1035,6 +1036,8 @@
36A9467A15CF20BD00727135 /* VideoDbUrl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoDbUrl.h; sourceTree = "<group>"; };
36A9467C15CF20E100727135 /* DbUrl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DbUrl.cpp; sourceTree = "<group>"; };
36A9467D15CF20E100727135 /* DbUrl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DbUrl.h; sourceTree = "<group>"; };
+ 36A95DAB1624896C00727135 /* GUIDialogMediaFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogMediaFilter.cpp; sourceTree = "<group>"; };
+ 36A95DAC1624896C00727135 /* GUIDialogMediaFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogMediaFilter.h; sourceTree = "<group>"; };
4D5D2E1D1301758F006ABC13 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
7C0A7EDC13A5DC2800AFC2BD /* AppParamParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppParamParser.cpp; sourceTree = "<group>"; };
7C0A7EDD13A5DC2800AFC2BD /* AppParamParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppParamParser.h; sourceTree = "<group>"; };
@@ -4962,6 +4965,8 @@
F56C8357131F42E8000AD0F6 /* GUIDialogKaiToast.h */,
DF830C9315BB20FC00602BE6 /* GUIDialogKeyboardGeneric.cpp */,
DF830C9415BB20FC00602BE6 /* GUIDialogKeyboardGeneric.h */,
+ 36A95DAB1624896C00727135 /* GUIDialogMediaFilter.cpp */,
+ 36A95DAC1624896C00727135 /* GUIDialogMediaFilter.h */,
F56C835A131F42E8000AD0F6 /* GUIDialogMediaSource.cpp */,
F56C835B131F42E8000AD0F6 /* GUIDialogMediaSource.h */,
F56C835C131F42E8000AD0F6 /* GUIDialogMuteBug.cpp */,
@@ -7603,6 +7608,7 @@
7C4458C8161E206100A905F6 /* Screenshot.cpp in Sources */,
1D638120161E20F2003603ED /* PeripheralImon.cpp in Sources */,
DF24EADE1621E67200034265 /* DVDDemuxBXA.cpp in Sources */,
+ 36A95DAD1624896C00727135 /* GUIDialogMediaFilter.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/XBMC.xcodeproj/project.pbxproj b/XBMC.xcodeproj/project.pbxproj
index f20a32326a..c7d4ab4a7b 100644
--- a/XBMC.xcodeproj/project.pbxproj
+++ b/XBMC.xcodeproj/project.pbxproj
@@ -183,6 +183,7 @@
36A9466715CF1FD200727135 /* MusicDbUrl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A9466515CF1FD200727135 /* MusicDbUrl.cpp */; };
36A9466A15CF1FED00727135 /* UrlOptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A9466815CF1FED00727135 /* UrlOptions.cpp */; };
36A9466D15CF201F00727135 /* VideoDbUrl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A9466B15CF201F00727135 /* VideoDbUrl.cpp */; };
+ 36A95DA51624894400727135 /* GUIDialogMediaFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A95DA31624894400727135 /* GUIDialogMediaFilter.cpp */; };
3802709A13D5A653009493DD /* SystemClock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3802709813D5A653009493DD /* SystemClock.cpp */; };
384718D81325BA04000486D6 /* XBDateTime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 384718D61325BA04000486D6 /* XBDateTime.cpp */; };
38F4E57013CCCB3B00664821 /* Implementation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 38F4E56C13CCCB3B00664821 /* Implementation.cpp */; };
@@ -1361,6 +1362,8 @@
36A9466915CF1FED00727135 /* UrlOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UrlOptions.h; sourceTree = "<group>"; };
36A9466B15CF201F00727135 /* VideoDbUrl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VideoDbUrl.cpp; sourceTree = "<group>"; };
36A9466C15CF201F00727135 /* VideoDbUrl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoDbUrl.h; sourceTree = "<group>"; };
+ 36A95DA31624894400727135 /* GUIDialogMediaFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogMediaFilter.cpp; sourceTree = "<group>"; };
+ 36A95DA41624894400727135 /* GUIDialogMediaFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogMediaFilter.h; sourceTree = "<group>"; };
3802709713D5A62D009493DD /* ThreadLocal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadLocal.h; sourceTree = "<group>"; };
3802709813D5A653009493DD /* SystemClock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SystemClock.cpp; sourceTree = "<group>"; };
3802709913D5A653009493DD /* SystemClock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SystemClock.h; sourceTree = "<group>"; };
@@ -3865,6 +3868,8 @@
E38A06CD0D95AA5500FF8227 /* GUIDialogKaiToast.h */,
DF830D0A15BB260C00602BE6 /* GUIDialogKeyboardGeneric.cpp */,
DF830D0B15BB260C00602BE6 /* GUIDialogKeyboardGeneric.h */,
+ 36A95DA31624894400727135 /* GUIDialogMediaFilter.cpp */,
+ 36A95DA41624894400727135 /* GUIDialogMediaFilter.h */,
E38E17B80D25F9FA00618676 /* GUIDialogMediaSource.cpp */,
E38E17B90D25F9FA00618676 /* GUIDialogMediaSource.h */,
E38E17BE0D25F9FA00618676 /* GUIDialogMuteBug.cpp */,
@@ -7636,6 +7641,7 @@
7C4458BD161E203800A905F6 /* Screenshot.cpp in Sources */,
1D638128161E211E003603ED /* PeripheralImon.cpp in Sources */,
AE89ACA61621DAB800E17DBC /* DVDDemuxBXA.cpp in Sources */,
+ 36A95DA51624894400727135 /* GUIDialogMediaFilter.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/addons/skin.confluence/720p/DialogMediaFilter.xml b/addons/skin.confluence/720p/DialogMediaFilter.xml
new file mode 100644
index 0000000000..accb0c888c
--- /dev/null
+++ b/addons/skin.confluence/720p/DialogMediaFilter.xml
@@ -0,0 +1,184 @@
+<window id="151">
+ <defaultcontrol always="true">5</defaultcontrol>
+ <coordinates>
+ <system>1</system>
+ <posx>240</posx>
+ <posy>100</posy>
+ </coordinates>
+ <include>dialogeffect</include>
+ <controls>
+ <control type="image">
+ <description>background image</description>
+ <posx>0</posx>
+ <posy>0</posy>
+ <width>800</width>
+ <height>500</height>
+ <texture border="40">DialogBack.png</texture>
+ </control>
+ <control type="image">
+ <description>Dialog Header image</description>
+ <posx>40</posx>
+ <posy>16</posy>
+ <width>720</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>720</width>
+ <height>30</height>
+ <font>font13_title</font>
+ <label>587</label>
+ <align>center</align>
+ <aligny>center</aligny>
+ <textcolor>selected</textcolor>
+ <shadowcolor>black</shadowcolor>
+ </control>
+ <control type="button">
+ <description>Close Window button</description>
+ <posx>710</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>720</width>
+ <height>350</height>
+ <itemgap>4</itemgap>
+ <pagecontrol>6</pagecontrol>
+ <onup>9001</onup>
+ <ondown>9001</ondown>
+ <onleft>9001</onleft>
+ <onright>6</onright>
+ </control>
+ <control type="scrollbar" id="6">
+ <posx>755</posx>
+ <posy>70</posy>
+ <width>25</width>
+ <height>350</height>
+ <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
+ <texturesliderbar border="2,16,2,16">ScrollBarV_bar.png</texturesliderbar>
+ <texturesliderbarfocus border="2,16,2,16">ScrollBarV_bar_focus.png</texturesliderbarfocus>
+ <textureslidernib>ScrollBarNib.png</textureslidernib>
+ <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
+ <onleft>5</onleft>
+ <onright>9001</onright>
+ <showonepage>false</showonepage>
+ <orientation>vertical</orientation>
+ </control>
+
+ <control type="button" id="7">
+ <description>Default Button</description>
+ <posx>0</posx>
+ <posy>0</posy>
+ <height>40</height>
+ <font>font13</font>
+ <textcolor>grey2</textcolor>
+ <focusedcolor>white</focusedcolor>
+ <texturenofocus border="5">button-nofocus.png</texturenofocus>
+ <texturefocus border="5">button-focus2.png</texturefocus>
+ </control>
+ <control type="radiobutton" id="8">
+ <description>Default RadioButton</description>
+ <posx>0</posx>
+ <posy>0</posy>
+ <height>40</height>
+ <font>font13</font>
+ <textcolor>grey2</textcolor>
+ <focusedcolor>white</focusedcolor>
+ <texturenofocus border="5">button-nofocus.png</texturenofocus>
+ <texturefocus border="5">button-focus2.png</texturefocus>
+ </control>
+ <control type="spincontrolex" id="9">
+ <description>Default SpinControlex</description>
+ <posx>0</posx>
+ <posy>0</posy>
+ <height>40</height>
+ <font>font13</font>
+ <textcolor>grey2</textcolor>
+ <focusedcolor>white</focusedcolor>
+ <texturenofocus border="5">button-nofocus.png</texturenofocus>
+ <texturefocus border="5">button-focus2.png</texturefocus>
+ <aligny>center</aligny>
+ <reverse>yes</reverse>
+ </control>
+ <control type="sliderex" id="10">
+ <description>Default Slider</description>
+ <posx>0</posx>
+ <posy>0</posy>
+ <height>40</height>
+ <font>font13</font>
+ <textcolor>grey2</textcolor>
+ <focusedcolor>white</focusedcolor>
+ <texturenofocus border="5">button-nofocus.png</texturenofocus>
+ <texturefocus border="5">button-focus2.png</texturefocus>
+ <aligny>center</aligny>
+ </control>
+ <control type="edit" id="12">
+ <description>Default Edit</description>
+ <posx>0</posx>
+ <posy>0</posy>
+ <height>40</height>
+ <font>font13</font>
+ <textcolor>grey2</textcolor>
+ <focusedcolor>white</focusedcolor>
+ <texturenofocus border="5">button-nofocus.png</texturenofocus>
+ <texturefocus border="5">button-focus2.png</texturefocus>
+ </control>
+
+ <control type="group" id="9001">
+ <posx>190</posx>
+ <posy>435</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>
+ <texturenofocus border="5">button-nofocus.png</texturenofocus>
+ <texturefocus border="5">button-focus.png</texturefocus>
+ <label>186</label>
+ <font>font12_title</font>
+ <onup>5</onup>
+ <onleft>27</onleft>
+ <onright>27</onright>
+ <ondown>5</ondown>
+ </control>
+ <control type="button" id="27">
+ <description>Clear Button</description>
+ <posx>210</posx>
+ <posy>0</posy>
+ <width>200</width>
+ <height>40</height>
+ <align>center</align>
+ <aligny>center</aligny>
+ <texturenofocus border="5">button-nofocus.png</texturenofocus>
+ <texturefocus border="5">button-focus.png</texturefocus>
+ <label>192</label>
+ <font>font12_title</font>
+ <onup>5</onup>
+ <onleft>28</onleft>
+ <onright>28</onright>
+ <ondown>5</ondown>
+ </control>
+ </control>
+ </controls>
+</window>
diff --git a/addons/skin.confluence/720p/MyMusicNav.xml b/addons/skin.confluence/720p/MyMusicNav.xml
index b906bbe899..723c693982 100644
--- a/addons/skin.confluence/720p/MyMusicNav.xml
+++ b/addons/skin.confluence/720p/MyMusicNav.xml
@@ -101,11 +101,20 @@
<include>ButtonCommonValues</include>
</control>
<control type="edit" id="19">
+ <visible>Container.CanFilter + !Container.CanFilterAdvanced</visible>
<description>Filter</description>
<textwidth>230</textwidth>
<include>ButtonCommonValues</include>
<label>587</label>
</control>
+ <control type="radiobutton" id="20">
+ <visible>Container.CanFilterAdvanced</visible>
+ <description>Filter</description>
+ <include>ButtonCommonValues</include>
+ <label>587</label>
+ <selected>Container.Filtered</selected>
+ <onclick>Filter</onclick>
+ </control>
<control type="radiobutton" id="100">
<description>Show Info Toggle</description>
<textwidth>170</textwidth>
diff --git a/addons/skin.confluence/720p/MyMusicSongs.xml b/addons/skin.confluence/720p/MyMusicSongs.xml
index b5c449516b..193c0ae22d 100644
--- a/addons/skin.confluence/720p/MyMusicSongs.xml
+++ b/addons/skin.confluence/720p/MyMusicSongs.xml
@@ -98,11 +98,20 @@
<enable>Library.HasContent(Music)</enable>
</control>
<control type="edit" id="19">
+ <visible>Container.CanFilter + !Container.CanFilterAdvanced</visible>
<description>Filter</description>
<textwidth>230</textwidth>
<include>ButtonCommonValues</include>
<label>587</label>
</control>
+ <control type="radiobutton" id="20">
+ <visible>Container.CanFilterAdvanced</visible>
+ <description>Filter</description>
+ <include>ButtonCommonValues</include>
+ <label>587</label>
+ <selected>Container.Filtered</selected>
+ <onclick>Filter</onclick>
+ </control>
<include>CommonNowPlaying_Controls</include>
</control>
</control>
diff --git a/addons/skin.confluence/720p/MyPics.xml b/addons/skin.confluence/720p/MyPics.xml
index 7301d5e8c4..bf4ada48a6 100644
--- a/addons/skin.confluence/720p/MyPics.xml
+++ b/addons/skin.confluence/720p/MyPics.xml
@@ -103,11 +103,19 @@
<usealttexture>Container.SortDirection(Ascending)</usealttexture>
</control>
<control type="edit" id="19">
+ <visible>Container.CanFilter + !Container.CanFilterAdvanced</visible>
<description>Filter</description>
<textwidth>230</textwidth>
<include>ButtonCommonValues</include>
<label>587</label>
</control>
+ <control type="radiobutton" id="20">
+ <visible>Container.CanFilterAdvanced</visible>
+ <description>Filter</description>
+ <include>ButtonCommonValues</include>
+ <label>587</label>
+ <onclick>Filter</onclick>
+ </control>
<control type="label" id="201">
<width>250</width>
<height>35</height>
diff --git a/addons/skin.confluence/720p/MyPrograms.xml b/addons/skin.confluence/720p/MyPrograms.xml
index e1b8f699ea..44f907df39 100644
--- a/addons/skin.confluence/720p/MyPrograms.xml
+++ b/addons/skin.confluence/720p/MyPrograms.xml
@@ -89,11 +89,19 @@
<usealttexture>Container.SortDirection(Ascending)</usealttexture>
</control>
<control type="edit" id="19">
+ <visible>Container.CanFilter + !Container.CanFilterAdvanced</visible>
<description>Filter</description>
<textwidth>230</textwidth>
<include>ButtonCommonValues</include>
<label>587</label>
</control>
+ <control type="radiobutton" id="20">
+ <visible>Container.CanFilterAdvanced</visible>
+ <description>Filter</description>
+ <include>ButtonCommonValues</include>
+ <label>587</label>
+ <onclick>Filter</onclick>
+ </control>
<include>CommonNowPlaying_Controls</include>
</control>
</control>
diff --git a/addons/skin.confluence/720p/MyVideoNav.xml b/addons/skin.confluence/720p/MyVideoNav.xml
index 9c76c66e8b..fb4a645970 100644
--- a/addons/skin.confluence/720p/MyVideoNav.xml
+++ b/addons/skin.confluence/720p/MyVideoNav.xml
@@ -98,11 +98,20 @@
<usealttexture>Container.SortDirection(Ascending)</usealttexture>
</control>
<control type="edit" id="19">
+ <visible>Container.CanFilter + !Container.CanFilterAdvanced</visible>
<description>Filter</description>
<textwidth>230</textwidth>
<include>ButtonCommonValues</include>
<label>587</label>
</control>
+ <control type="radiobutton" id="20">
+ <visible>Container.CanFilterAdvanced</visible>
+ <description>Filter</description>
+ <include>ButtonCommonValues</include>
+ <label>587</label>
+ <selected>Container.Filtered</selected>
+ <onclick>Filter</onclick>
+ </control>
<control type="radiobutton" id="99">
<description>Show Info Toggle</description>
<textwidth>170</textwidth>
diff --git a/addons/skin.confluence/720p/includes.xml b/addons/skin.confluence/720p/includes.xml
index 6fa3b0cedd..1bf39a1df3 100644
--- a/addons/skin.confluence/720p/includes.xml
+++ b/addons/skin.confluence/720p/includes.xml
@@ -332,7 +332,7 @@
<scroll>false</scroll>
<align>right</align>
<aligny>center</aligny>
- <label>$INFO[Window.Property(filter),$LOCALIZE[587] ([COLOR=blue],[/COLOR]) - ]$INFO[Container.NumItems,([COLOR=blue],[/COLOR]) $LOCALIZE[31025]]$INFO[Container.CurrentPage, - $LOCALIZE[31024] ([COLOR=blue]]$INFO[Container.NumPages,/,[/COLOR])]</label>
+ <label>$INFO[Container.NumItems,([COLOR=blue],[/COLOR]) $LOCALIZE[31025]]$INFO[Container.CurrentPage, - $LOCALIZE[31024] ([COLOR=blue]]$INFO[Container.NumPages,/,[/COLOR])]</label>
<include>Window_OpenClose_Animation</include>
</control>
<control type="label">
diff --git a/language/English/strings.po b/language/English/strings.po
index fb5abf8327..e33ba304ed 100644
--- a/language/English/strings.po
+++ b/language/English/strings.po
@@ -3201,6 +3201,12 @@ msgctxt "#1273"
msgid "AirPlay"
msgstr ""
+#empty string id 1274
+
+msgctxt "#1275"
+msgid "Filter %s"
+msgstr ""
+
#empty strings from id 1274 to 1299
msgctxt "#1300"
@@ -9400,7 +9406,23 @@ msgctxt "#21465"
msgid "Above video"
msgstr ""
-#empty strings from id 21466 to 21799
+msgctxt "#21466"
+msgid "between"
+msgstr ""
+
+msgctxt "#21467"
+msgid "%.1f to %.1f"
+msgstr ""
+
+msgctxt "#21468"
+msgid "%d to %d"
+msgstr ""
+
+msgctxt "#21469"
+msgid "%s to %s"
+msgstr ""
+
+#empty strings from id 21470 to 21799
msgctxt "#21800"
msgid "File name"
diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj
index b3b359ef66..9f32e555be 100644
--- a/project/VS2010Express/XBMC.vcxproj
+++ b/project/VS2010Express/XBMC.vcxproj
@@ -419,6 +419,7 @@
<ClCompile Include="..\..\xbmc\dialogs\GUIDialogGamepad.cpp" />
<ClCompile Include="..\..\xbmc\dialogs\GUIDialogKaiToast.cpp" />
<ClCompile Include="..\..\xbmc\dialogs\GUIDialogKeyboardGeneric.cpp" />
+ <ClCompile Include="..\..\xbmc\dialogs\GUIDialogMediaFilter.cpp" />
<ClCompile Include="..\..\xbmc\dialogs\GUIDialogMediaSource.cpp" />
<ClCompile Include="..\..\xbmc\dialogs\GUIDialogMuteBug.cpp" />
<ClCompile Include="..\..\xbmc\dialogs\GUIDialogNumeric.cpp" />
@@ -1015,6 +1016,7 @@
<ClInclude Include="..\..\xbmc\cores\paplayer\PCMCodec.h" />
<ClInclude Include="..\..\xbmc\dialogs\GUIDialogKeyboardGeneric.h" />
<ClInclude Include="..\..\xbmc\DbUrl.h" />
+ <ClInclude Include="..\..\xbmc\dialogs\GUIDialogMediaFilter.h" />
<ClInclude Include="..\..\xbmc\filesystem\ImageFile.h" />
<ClInclude Include="..\..\xbmc\filesystem\VideoDatabaseDirectory\DirectoryNodeTags.h" />
<ClInclude Include="..\..\xbmc\filesystem\windows\WINFileSMB.h" />
diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters
index 749352eb36..b341508bce 100644
--- a/project/VS2010Express/XBMC.vcxproj.filters
+++ b/project/VS2010Express/XBMC.vcxproj.filters
@@ -2933,6 +2933,9 @@
<ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDDemuxers\DVDDemuxBXA.cpp">
<Filter>cores\dvdplayer\DVDDemuxers</Filter>
</ClCompile>
+ <ClCompile Include="..\..\xbmc\dialogs\GUIDialogMediaFilter.cpp">
+ <Filter>dialogs</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\xbmc\win32\pch.h">
@@ -5734,6 +5737,9 @@
<ClInclude Include="..\..\xbmc\utils\Screenshot.h">
<Filter>utils</Filter>
</ClInclude>
+ <ClInclude Include="..\..\xbmc\dialogs\GUIDialogMediaFilter.h">
+ <Filter>dialogs</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\..\xbmc\win32\XBMC_PC.rc">
diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp
index d29adc9feb..6dcceb3d52 100644
--- a/xbmc/Application.cpp
+++ b/xbmc/Application.cpp
@@ -286,6 +286,7 @@
#include "guilib/GUIControlFactory.h"
#include "dialogs/GUIDialogCache.h"
#include "dialogs/GUIDialogPlayEject.h"
+#include "dialogs/GUIDialogMediaFilter.h"
#include "utils/XMLUtils.h"
#include "addons/AddonInstaller.h"
@@ -1298,6 +1299,8 @@ bool CApplication::Initialize()
g_windowManager.Add(new CGUIDialogPeripheralManager);
g_windowManager.Add(new CGUIDialogPeripheralSettings);
+
+ g_windowManager.Add(new CGUIDialogMediaFilter); // window id = 151
g_windowManager.Add(new CGUIWindowMusicPlayList); // window id = 500
g_windowManager.Add(new CGUIWindowMusicSongs); // window id = 501
@@ -3475,6 +3478,7 @@ bool CApplication::Cleanup()
g_windowManager.Delete(WINDOW_DIALOG_ADDON_SETTINGS);
g_windowManager.Delete(WINDOW_DIALOG_ACCESS_POINTS);
g_windowManager.Delete(WINDOW_DIALOG_SLIDER);
+ g_windowManager.Delete(WINDOW_DIALOG_MEDIA_FILTER);
/* Delete PVR related windows and dialogs */
g_windowManager.Delete(WINDOW_PVR);
diff --git a/xbmc/DbUrl.h b/xbmc/DbUrl.h
index 4a70e59e21..c7d7b3289d 100644
--- a/xbmc/DbUrl.h
+++ b/xbmc/DbUrl.h
@@ -39,6 +39,7 @@ public:
const std::string& GetType() const { return m_type; }
void AppendPath(const std::string &subPath);
+ virtual void AddOption(const std::string &key, const char *value) { CUrlOptions::AddOption(key, value); updateOptions(); }
virtual void AddOption(const std::string &key, const std::string &value) { CUrlOptions::AddOption(key, value); updateOptions(); }
virtual void AddOption(const std::string &key, int value) { CUrlOptions::AddOption(key, value); updateOptions(); }
virtual void AddOption(const std::string &key, float value) { CUrlOptions::AddOption(key, value); updateOptions(); }
diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp
index f137f32c03..bb604b504d 100644
--- a/xbmc/GUIInfoManager.cpp
+++ b/xbmc/GUIInfoManager.cpp
@@ -431,7 +431,10 @@ const infomap container_bools[] ={{ "onnext", CONTAINER_MOVE_NEXT },
{ "currentpage", CONTAINER_CURRENT_PAGE },
{ "scrolling", CONTAINER_SCROLLING },
{ "hasnext", CONTAINER_HAS_NEXT },
- { "hasprevious", CONTAINER_HAS_PREVIOUS }};
+ { "hasprevious", CONTAINER_HAS_PREVIOUS },
+ { "canfilter", CONTAINER_CAN_FILTER },
+ { "canfilteradvanced",CONTAINER_CAN_FILTERADVANCED },
+ { "filtered", CONTAINER_FILTERED }};
const infomap container_ints[] = {{ "row", CONTAINER_ROW },
{ "column", CONTAINER_COLUMN },
@@ -2204,6 +2207,24 @@ bool CGUIInfoManager::GetBool(int condition1, int contextWindow, const CGUIListI
bReturn = control->GetCondition(condition, 0);
}
}
+ else if (condition == CONTAINER_CAN_FILTER)
+ {
+ CGUIWindow *window = GetWindowWithCondition(contextWindow, WINDOW_CONDITION_IS_MEDIA_WINDOW);
+ if (window)
+ bReturn = !((CGUIMediaWindow*)window)->CanFilterAdvanced();
+ }
+ else if (condition == CONTAINER_CAN_FILTERADVANCED)
+ {
+ CGUIWindow *window = GetWindowWithCondition(contextWindow, WINDOW_CONDITION_IS_MEDIA_WINDOW);
+ if (window)
+ bReturn = ((CGUIMediaWindow*)window)->CanFilterAdvanced();
+ }
+ else if (condition == CONTAINER_FILTERED)
+ {
+ CGUIWindow *window = GetWindowWithCondition(contextWindow, WINDOW_CONDITION_IS_MEDIA_WINDOW);
+ if (window)
+ bReturn = ((CGUIMediaWindow*)window)->IsFiltered();
+ }
else if (condition == VIDEOPLAYER_HAS_INFO)
bReturn = ((m_currentFile->HasVideoInfoTag() && !m_currentFile->GetVideoInfoTag()->IsEmpty()) ||
(m_currentFile->HasPVRChannelInfoTag() && !m_currentFile->GetPVRChannelInfoTag()->IsEmpty()));
diff --git a/xbmc/GUIInfoManager.h b/xbmc/GUIInfoManager.h
index 576a68b393..b6809380e3 100644
--- a/xbmc/GUIInfoManager.h
+++ b/xbmc/GUIInfoManager.h
@@ -289,6 +289,10 @@ namespace INFO
#define LASTFM_CANLOVE 331
#define LASTFM_CANBAN 332
+#define CONTAINER_CAN_FILTER 342
+#define CONTAINER_CAN_FILTERADVANCED 343
+#define CONTAINER_FILTERED 344
+
#define CONTAINER_SCROLL_PREVIOUS 345 // NOTE: These 5 must be kept in this consecutive order
#define CONTAINER_MOVE_PREVIOUS 346
#define CONTAINER_STATIC 347
diff --git a/xbmc/dbwrappers/Database.cpp b/xbmc/dbwrappers/Database.cpp
index c566e909b2..9773b35acd 100644
--- a/xbmc/dbwrappers/Database.cpp
+++ b/xbmc/dbwrappers/Database.cpp
@@ -203,6 +203,11 @@ CStdString CDatabase::GetSingleValue(const CStdString &strTable, const CStdStrin
return GetSingleValue(query, m_pDS);
}
+CStdString CDatabase::GetSingleValue(const CStdString &query)
+{
+ return GetSingleValue(query, m_pDS);
+}
+
bool CDatabase::DeleteValues(const CStdString &strTable, const CStdString &strWhereClause /* = CStdString() */)
{
bool bReturn = true;
diff --git a/xbmc/dbwrappers/Database.h b/xbmc/dbwrappers/Database.h
index ac9018273e..c04b13975e 100644
--- a/xbmc/dbwrappers/Database.h
+++ b/xbmc/dbwrappers/Database.h
@@ -83,6 +83,7 @@ public:
* @return The requested value or an empty string if it wasn't found.
*/
CStdString GetSingleValue(const CStdString &strTable, const CStdString &strColumn, const CStdString &strWhereClause = CStdString(), const CStdString &strOrderBy = CStdString());
+ CStdString GetSingleValue(const CStdString &query);
/*! \brief Get a single value from a query on a dataset.
\param query the query in question.
@@ -134,7 +135,7 @@ public:
*/
bool CommitInsertQueries();
- virtual bool GetFilter(const CDbUrl &dbUrl, Filter &filter) { return true; }
+ virtual bool GetFilter(CDbUrl &dbUrl, Filter &filter) { return true; }
virtual bool BuildSQL(const CStdString &strBaseDir, const CStdString &strQuery, Filter &filter, CStdString &strSQL, CDbUrl &dbUrl);
protected:
diff --git a/xbmc/dialogs/GUIDialogMediaFilter.cpp b/xbmc/dialogs/GUIDialogMediaFilter.cpp
new file mode 100644
index 0000000000..964fd7f138
--- /dev/null
+++ b/xbmc/dialogs/GUIDialogMediaFilter.cpp
@@ -0,0 +1,913 @@
+/*
+ * 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "GUIDialogMediaFilter.h"
+#include "FileItem.h"
+#include "GUIUserMessages.h"
+#include "XBDateTime.h"
+#include "dialogs/GUIDialogSelect.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "music/MusicDatabase.h"
+#include "playlists/SmartPlayList.h"
+#include "utils/MathUtils.h"
+#include "video/VideoDatabase.h"
+
+// list of controls
+#define CONTROL_HEADING 2
+// list of controls from CGUIDialogSettings
+#define CONTROL_GROUP_LIST 5
+#define CONTROL_DEFAULT_BUTTON 7
+#define CONTROL_DEFAULT_RADIOBUTTON 8
+#define CONTROL_DEFAULT_SPIN 9
+#define CONTROL_DEFAULT_SLIDER 10
+
+#define CONTROL_CLEAR_BUTTON 27
+#define CONTROL_OKAY_BUTTON 28
+#define CONTROL_CANCEL_BUTTON 29
+#define CONTROL_START 30
+
+#define CHECK_ALL -1
+#define CHECK_NO 0
+#define CHECK_YES 1
+#define CHECK_LABEL_ALL 593
+#define CHECK_LABEL_NO 106
+#define CHECK_LABEL_YES 107
+
+using namespace std;
+
+static const CGUIDialogMediaFilter::Filter filterList[] = {
+ { "movies", FieldTitle, 556, SettingInfo::EDIT, CSmartPlaylistRule::OPERATOR_CONTAINS },
+ { "movies", FieldRating, 563, SettingInfo::RANGE, CSmartPlaylistRule::OPERATOR_BETWEEN },
+ //{ "movies", FieldTime, 180, SettingInfo::TODO, CSmartPlaylistRule::TODO },
+ { "movies", FieldInProgress, 575, SettingInfo::CHECK, CSmartPlaylistRule::OPERATOR_FALSE },
+ { "movies", FieldYear, 562, SettingInfo::RANGE, CSmartPlaylistRule::OPERATOR_BETWEEN },
+ { "movies", FieldTag, 20459, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ { "movies", FieldGenre, 515, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ { "movies", FieldActor, 20337, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ { "movies", FieldDirector, 20339, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ { "movies", FieldStudio, 572, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ //{ "movies", FieldLastPlayed, 568, SettingInfo::TODO, CSmartPlaylistRule::TODO },
+ //{ "movies", FieldDateAdded, 570, SettingInfo::TODO, CSmartPlaylistRule::TODO },
+
+ { "tvshows", FieldTitle, 556, SettingInfo::EDIT, CSmartPlaylistRule::OPERATOR_CONTAINS },
+ //{ "tvshows", FieldTvShowStatus, 126, SettingInfo::TODO, CSmartPlaylistRule::TODO },
+ { "tvshows", FieldRating, 563, SettingInfo::RANGE, CSmartPlaylistRule::OPERATOR_BETWEEN },
+ { "tvshows", FieldInProgress, 575, SettingInfo::CHECK, CSmartPlaylistRule::OPERATOR_FALSE },
+ { "tvshows", FieldYear, 562, SettingInfo::RANGE, CSmartPlaylistRule::OPERATOR_BETWEEN },
+ { "tvshows", FieldGenre, 515, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ { "tvshows", FieldActor, 20337, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ { "tvshows", FieldDirector, 20339, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ { "tvshows", FieldStudio, 572, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ //{ "tvshows", FieldDateAdded, 570, SettingInfo::TODO, CSmartPlaylistRule::TODO },
+
+ { "episodes", FieldTitle, 556, SettingInfo::EDIT, CSmartPlaylistRule::OPERATOR_CONTAINS },
+ { "episodes", FieldRating, 563, SettingInfo::RANGE, CSmartPlaylistRule::OPERATOR_BETWEEN },
+ { "episodes", FieldAirDate, 20416, SettingInfo::RANGE, CSmartPlaylistRule::OPERATOR_BETWEEN },
+ { "episodes", FieldInProgress, 575, SettingInfo::CHECK, CSmartPlaylistRule::OPERATOR_FALSE },
+ { "episodes", FieldActor, 20337, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ { "episodes", FieldDirector, 20339, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ //{ "episodes", FieldLastPlayed, 568, SettingInfo::TODO, CSmartPlaylistRule::TODO },
+ //{ "episodes", FieldDateAdded, 570, SettingInfo::TODO, CSmartPlaylistRule::TODO },
+
+ { "musicvideos", FieldTitle, 556, SettingInfo::EDIT, CSmartPlaylistRule::OPERATOR_CONTAINS },
+ { "musicvideos", FieldArtist, 557, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ { "musicvideos", FieldAlbum, 558, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ //{ "musicvideos", FieldTime, 180, SettingInfo::TODO, CSmartPlaylistRule::TODO },
+ { "musicvideos", FieldYear, 562, SettingInfo::RANGE, CSmartPlaylistRule::OPERATOR_BETWEEN },
+ { "musicvideos", FieldGenre, 515, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ { "musicvideos", FieldDirector, 20339, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ { "musicvideos", FieldStudio, 572, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ //{ "musicvideos", FieldLastPlayed, 568, SettingInfo::TODO, CSmartPlaylistRule::TODO },
+ //{ "musicvideos", FieldDateAdded, 570, SettingInfo::TODO, CSmartPlaylistRule::TODO },
+
+ { "artists", FieldArtist, 557, SettingInfo::EDIT, CSmartPlaylistRule::OPERATOR_CONTAINS },
+ { "artists", FieldGenre, 515, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+
+ { "albums", FieldAlbum, 556, SettingInfo::EDIT, CSmartPlaylistRule::OPERATOR_CONTAINS },
+ { "albums", FieldArtist, 557, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ { "albums", FieldRating, 563, SettingInfo::RANGE, CSmartPlaylistRule::OPERATOR_BETWEEN },
+ { "albums", FieldAlbumType, 564, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ { "albums", FieldYear, 562, SettingInfo::RANGE, CSmartPlaylistRule::OPERATOR_BETWEEN },
+ { "albums", FieldGenre, 515, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ { "albums", FieldMusicLabel, 21899, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+
+ { "songs", FieldTitle, 556, SettingInfo::EDIT, CSmartPlaylistRule::OPERATOR_CONTAINS },
+ { "songs", FieldAlbum, 558, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ { "songs", FieldArtist, 557, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ { "songs", FieldTime, 180, SettingInfo::RANGE, CSmartPlaylistRule::OPERATOR_BETWEEN },
+ { "songs", FieldRating, 563, SettingInfo::RANGE, CSmartPlaylistRule::OPERATOR_BETWEEN },
+ { "songs", FieldYear, 562, SettingInfo::RANGE, CSmartPlaylistRule::OPERATOR_BETWEEN },
+ { "songs", FieldGenre, 515, SettingInfo::BUTTON, CSmartPlaylistRule::OPERATOR_EQUALS },
+ { "songs", FieldPlaycount, 567, SettingInfo::RANGE, CSmartPlaylistRule::OPERATOR_BETWEEN },
+ //{ "songs", FieldLastPlayed, 568, SettingInfo::TODO, CSmartPlaylistRule::TODO },
+ //{ "songs", FieldDateAdded, 570, SettingInfo::TODO, CSmartPlaylistRule::TODO },
+};
+
+#define NUM_FILTERS sizeof(filterList) / sizeof(CGUIDialogMediaFilter::Filter)
+
+CGUIDialogMediaFilter::CGUIDialogMediaFilter()
+ : CGUIDialogSettings(WINDOW_DIALOG_MEDIA_FILTER, "DialogMediaFilter.xml"),
+ m_dbUrl(NULL),
+ m_filter(NULL)
+{ }
+
+CGUIDialogMediaFilter::~CGUIDialogMediaFilter()
+{
+ Reset();
+}
+
+bool CGUIDialogMediaFilter::OnMessage(CGUIMessage& message)
+{
+ switch (message.GetMessage())
+ {
+ case GUI_MSG_CLICKED:
+ {
+ int control = message.GetSenderId();
+
+ if (control == CONTROL_CLEAR_BUTTON)
+ {
+ m_filter->Reset();
+ m_filter->SetType(m_mediaType);
+
+ for (map<uint32_t, Filter>::iterator filter = m_filters.begin(); filter != m_filters.end(); filter++)
+ {
+ filter->second.rule = NULL;
+
+ switch (filter->second.type)
+ {
+ case SettingInfo::STRING:
+ case SettingInfo::EDIT:
+ ((CStdString *)filter->second.data)->clear();
+ break;
+
+ case SettingInfo::CHECK:
+ *(int *)filter->second.data = CHECK_ALL;
+ break;
+
+ case SettingInfo::BUTTON:
+ ((CStdString *)filter->second.data)->clear();
+ SET_CONTROL_LABEL2(filter->second.controlIndex, *(CStdString *)filter->second.data);
+ break;
+
+ case SettingInfo::RANGE:
+ *(((float **)filter->second.data)[0]) = m_settings[filter->second.controlIndex - CONTROL_START].min;
+ *(((float **)filter->second.data)[1]) = m_settings[filter->second.controlIndex - CONTROL_START].max;
+ break;
+
+ default:
+ continue;
+ }
+
+ UpdateSetting(filter->first);
+ }
+
+ CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS, 10); // 10 for advanced
+ g_windowManager.SendMessage(message);
+ return true;
+ }
+ break;
+ }
+
+ case GUI_MSG_WINDOW_DEINIT:
+ {
+ Reset();
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return CGUIDialogSettings::OnMessage(message);
+}
+
+void CGUIDialogMediaFilter::ShowAndEditMediaFilter(const std::string &path, CSmartPlaylist &filter)
+{
+ CGUIDialogMediaFilter *dialog = (CGUIDialogMediaFilter *)g_windowManager.GetWindow(WINDOW_DIALOG_MEDIA_FILTER);
+ if (dialog == NULL)
+ return;
+
+ // initialize and show the dialog
+ dialog->Initialize();
+ dialog->m_filter = &filter;
+ // must be called after setting the filter/smartplaylist
+ if (!dialog->SetPath(path))
+ return;
+
+ dialog->DoModal();
+}
+
+void CGUIDialogMediaFilter::OnWindowLoaded()
+{
+ CGUIDialogSettings::OnWindowLoaded();
+ // we don't need the cancel button so let's hide it
+ SET_CONTROL_HIDDEN(CONTROL_CANCEL_BUTTON);
+}
+
+void CGUIDialogMediaFilter::CreateSettings()
+{
+ if (m_filter == NULL)
+ return;
+
+ m_settings.clear();
+ int handledRules = 0;
+ for (unsigned int index = 0; index < NUM_FILTERS; index++)
+ {
+ if (filterList[index].mediaType != m_mediaType)
+ continue;
+
+ Filter filter = filterList[index];
+ filter.controlIndex = CONTROL_START + m_settings.size();
+
+ // check the smartplaylist if it contains a matching rule
+ for (vector<CSmartPlaylistRule>::iterator rule = m_filter->m_ruleCombination.m_rules.begin(); rule != m_filter->m_ruleCombination.m_rules.end(); rule++)
+ {
+ if (rule->m_field == filter.field)
+ {
+ filter.rule = &(*rule);
+ handledRules++;
+ break;
+ }
+ }
+
+ switch (filter.type)
+ {
+ case SettingInfo::STRING:
+ case SettingInfo::EDIT:
+ {
+ if (filter.rule != NULL && filter.rule->m_parameter.size() == 1)
+ filter.data = new CStdString(filter.rule->m_parameter.at(0));
+ else
+ filter.data = new CStdString();
+
+ if (filter.type == SettingInfo::STRING)
+ AddString(filter.field, filter.label, (CStdString *)filter.data);
+ else
+ AddEdit(filter.field, filter.label, (CStdString *)filter.data);
+ break;
+ }
+
+ case SettingInfo::CHECK:
+ {
+ if (filter.rule == NULL)
+ filter.data = new int(CHECK_ALL);
+ else
+ filter.data = new int(filter.rule->m_operator == CSmartPlaylistRule::OPERATOR_TRUE ? CHECK_YES : CHECK_NO);
+
+ vector<pair<int, int> > entries;
+ entries.push_back(pair<int, int>(CHECK_ALL, CHECK_LABEL_ALL));
+ entries.push_back(pair<int, int>(CHECK_NO, CHECK_LABEL_NO));
+ entries.push_back(pair<int, int>(CHECK_YES, CHECK_LABEL_YES));
+ AddSpin(filter.field, filter.label, (int *)filter.data, entries);
+ break;
+ }
+
+ case SettingInfo::BUTTON:
+ {
+ CStdString *values = new CStdString();
+ if (filter.rule != NULL && filter.rule->m_parameter.size() > 0)
+ *values = filter.rule->GetLocalizedParameter(m_mediaType);
+ filter.data = values;
+
+ AddButton(filter.field, filter.label);
+ break;
+ }
+
+ case SettingInfo::RANGE:
+ {
+ float min, interval, max;
+ RANGEFORMATFUNCTION format;
+ GetRange(filter, min, interval, max, format);
+
+ // don't create the filter if there's no real range
+ if (min == max)
+ break;
+
+ float *valueLower = new float();
+ float *valueUpper = new float();
+ if (filter.rule != NULL && filter.rule->m_parameter.size() == 2)
+ {
+ *valueLower = (float)strtod(filter.rule->m_parameter.at(0), NULL);
+ *valueUpper = (float)strtod(filter.rule->m_parameter.at(1), NULL);
+ }
+ else
+ {
+ *valueLower = min;
+ *valueUpper = max;
+
+ if (filter.rule != NULL)
+ {
+ DeleteRule(filter.field);
+ filter.rule = NULL;
+ }
+ }
+
+ AddRangeSlider(filter.field, filter.label, valueLower, valueUpper, min, interval, max, format);
+ filter.data = m_settings[filter.controlIndex - CONTROL_START].data;
+ break;
+ }
+
+ default:
+ filter.controlIndex = -1;
+ if (filter.rule != NULL)
+ handledRules--;
+ continue;
+ }
+
+ m_filters[filter.field] = filter;
+ }
+
+ // make sure that no change in capacity size is needed when adding new rules
+ // which would copy around the rules and our pointers in the Filter struct
+ // wouldn't work anymore
+ m_filter->m_ruleCombination.m_rules.reserve(m_filters.size() + (m_filter->m_ruleCombination.m_rules.size() - handledRules));
+}
+
+void CGUIDialogMediaFilter::SetupPage()
+{
+ CGUIDialogSettings::SetupPage();
+
+ // set the heading label based on the media type
+ uint32_t localizedMediaId = 0;
+ if (m_mediaType == "movies")
+ localizedMediaId = 20342;
+ else if (m_mediaType == "tvshows")
+ localizedMediaId = 20343;
+ else if (m_mediaType == "episodes")
+ localizedMediaId = 20360;
+ else if (m_mediaType == "musicvideos")
+ localizedMediaId = 20389;
+ else if (m_mediaType == "artists")
+ localizedMediaId = 133;
+ else if (m_mediaType == "albums")
+ localizedMediaId = 132;
+ else if (m_mediaType == "songs")
+ localizedMediaId = 134;
+
+ CStdString format;
+ format.Format(g_localizeStrings.Get(1275).c_str(), g_localizeStrings.Get(localizedMediaId).c_str());
+ SET_CONTROL_LABEL(CONTROL_HEADING, format);
+
+ // now we can finally set the label/values of the button settings (genre, actors etc)
+ for (map<uint32_t, Filter>::const_iterator filter = m_filters.begin(); filter != m_filters.end(); filter++)
+ {
+ if (filter->second.type == SettingInfo::BUTTON &&
+ filter->second.controlIndex >= 0 && filter->second.data != NULL)
+ SET_CONTROL_LABEL2(filter->second.controlIndex, *(CStdString *)filter->second.data);
+ }
+
+ UpdateControls();
+}
+
+void CGUIDialogMediaFilter::OnSettingChanged(SettingInfo &setting)
+{
+ map<uint32_t, Filter>::iterator it = m_filters.find(setting.id);
+ if (it == m_filters.end())
+ return;
+
+ bool changed = true;
+ bool remove = false;
+ Filter& filter = it->second;
+
+ switch (filter.type)
+ {
+ case SettingInfo::STRING:
+ case SettingInfo::EDIT:
+ {
+ CStdString *str = static_cast<CStdString*>(filter.data);
+ if (!str->empty())
+ {
+ if (filter.rule == NULL)
+ filter.rule = AddRule(filter.field, filter.ruleOperator);
+ filter.rule->m_parameter.clear();
+ filter.rule->m_parameter.push_back(*str);
+ }
+ else
+ remove = true;
+
+ break;
+ }
+
+ case SettingInfo::CHECK:
+ {
+ int choice = *(int *)setting.data;
+ if (choice > CHECK_ALL)
+ {
+ CSmartPlaylistRule::SEARCH_OPERATOR ruleOperator = choice == CHECK_YES ? CSmartPlaylistRule::OPERATOR_TRUE : CSmartPlaylistRule::OPERATOR_FALSE;
+ if (filter.rule == NULL)
+ filter.rule = AddRule(filter.field, ruleOperator);
+ else
+ filter.rule->m_operator = ruleOperator;
+ }
+ else
+ remove = true;
+
+ break;
+ }
+
+ case SettingInfo::BUTTON:
+ {
+ CFileItemList items;
+ OnBrowse(filter, items);
+
+ if (items.Size() > 0)
+ {
+ if (filter.rule == NULL)
+ filter.rule = AddRule(filter.field, filter.ruleOperator);
+
+ filter.rule->m_parameter.clear();
+ for (int index = 0; index < items.Size(); index++)
+ filter.rule->m_parameter.push_back(items[index]->GetLabel());
+
+ *(CStdString *)filter.data = filter.rule->GetLocalizedParameter(m_mediaType);
+ }
+ else
+ {
+ remove = true;
+ *(CStdString *)filter.data = "";
+ }
+
+ SET_CONTROL_LABEL2(filter.controlIndex, *(CStdString *)filter.data);
+ break;
+ }
+
+ case SettingInfo::RANGE:
+ {
+ SettingInfo &setting = m_settings[filter.controlIndex - CONTROL_START];
+ float *valueLower = ((float **)filter.data)[0];
+ float *valueUpper = ((float **)filter.data)[1];
+
+ if (*valueLower > setting.min || *valueUpper < setting.max)
+ {
+ if (filter.rule == NULL)
+ filter.rule = AddRule(filter.field, filter.ruleOperator);
+
+ filter.rule->m_parameter.clear();
+ if (filter.field == FieldAirDate)
+ {
+ CDateTime lower = (time_t)*valueLower;
+ CDateTime upper = (time_t)*valueUpper;
+ filter.rule->m_parameter.push_back(lower.GetAsDBDate());
+ filter.rule->m_parameter.push_back(upper.GetAsDBDate());
+ }
+ else
+ {
+ CStdString tmp;
+ tmp.Format("%.1f", *valueLower);
+ filter.rule->m_parameter.push_back(tmp);
+ tmp.clear();
+ tmp.Format("%.1f", *valueUpper);
+ filter.rule->m_parameter.push_back(tmp);
+ }
+ }
+ else
+ {
+ remove = true;
+ *((float **)filter.data)[0] = setting.min;
+ *((float **)filter.data)[1] = setting.max;
+ }
+ break;
+ }
+
+ default:
+ changed = false;
+ break;
+ }
+
+ // we need to remove the existing rule for the title
+ if (remove && filter.rule != NULL)
+ {
+ DeleteRule(filter.field);
+ filter.rule = NULL;
+ }
+
+ if (changed)
+ {
+ CGUIMessage message(GUI_MSG_NOTIFY_ALL, GetID(), 0, GUI_MSG_FILTER_ITEMS, 10); // 10 for advanced
+ g_windowManager.SendMessage(message);
+
+ UpdateControls();
+ }
+}
+
+void CGUIDialogMediaFilter::Reset()
+{
+ delete m_dbUrl;
+ m_dbUrl = NULL;
+
+ // delete all the setting's data
+ for (map<uint32_t, Filter>::iterator filter = m_filters.begin(); filter != m_filters.end(); filter++)
+ delete filter->second.data;
+
+ m_filters.clear();
+}
+
+bool CGUIDialogMediaFilter::SetPath(const std::string &path)
+{
+ if (path.empty() || m_filter == NULL)
+ return false;
+
+ delete m_dbUrl;
+ bool video = false;
+ if (path.find("videodb://") == 0)
+ {
+ m_dbUrl = new CVideoDbUrl();
+ video = true;
+ }
+ else if (path.find("musicdb://") == 0)
+ m_dbUrl = new CMusicDbUrl();
+ else
+ return false;
+
+ if (!m_dbUrl->FromString(path) ||
+ (video && m_dbUrl->GetType() != "movies" && m_dbUrl->GetType() != "tvshows" && m_dbUrl->GetType() != "episodes" && m_dbUrl->GetType() != "musicvideos") ||
+ (!video && m_dbUrl->GetType() != "artists" && m_dbUrl->GetType() != "albums" && m_dbUrl->GetType() != "songs"))
+ return false;
+
+ // remove "filter" option
+ if (m_dbUrl->HasOption("filter"))
+ m_dbUrl->AddOption("filter", "");
+
+ if (video)
+ m_mediaType = ((CVideoDbUrl*)m_dbUrl)->GetItemType();
+ else
+ m_mediaType = m_dbUrl->GetType();
+
+ m_filter->SetType(m_mediaType);
+ return true;
+}
+
+void CGUIDialogMediaFilter::UpdateControls()
+{
+ for (map<uint32_t, Filter>::iterator itFilter = m_filters.begin(); itFilter != m_filters.end(); itFilter++)
+ {
+ if (itFilter->second.type == SettingInfo::BUTTON)
+ {
+ CFileItemList items;
+ OnBrowse(itFilter->second, items, true);
+
+ int size = items.Size();
+ if (items.Size() == 1 && items[0]->HasProperty("total"))
+ size = (int)items[0]->GetProperty("total").asInteger();
+
+ CStdString label = g_localizeStrings.Get(itFilter->second.label);
+ if (size <= 1)
+ CONTROL_DISABLE(itFilter->second.controlIndex);
+ else
+ {
+ CONTROL_ENABLE(itFilter->second.controlIndex);
+ label.Format("%s [%d]", label, size);
+ }
+ SET_CONTROL_LABEL(itFilter->second.controlIndex, label);
+ }
+ }
+}
+
+void CGUIDialogMediaFilter::OnBrowse(const Filter &filter, CFileItemList &items, bool countOnly /* = false */)
+{
+ CFileItemList selectItems;
+ if (m_mediaType == "movies" || m_mediaType == "tvshows" || m_mediaType == "episodes" || m_mediaType == "musicvideos")
+ {
+ CVideoDatabase videodb;
+ if (!videodb.Open())
+ return;
+
+ CSmartPlaylist tmpFilter = *m_filter;
+ for (vector<CSmartPlaylistRule>::iterator rule = tmpFilter.m_ruleCombination.m_rules.begin(); rule != tmpFilter.m_ruleCombination.m_rules.end(); rule++)
+ {
+ if (rule->m_field == filter.field)
+ {
+ tmpFilter.m_ruleCombination.m_rules.erase(rule);
+ break;
+ }
+ }
+
+ std::set<CStdString> playlists;
+ CDatabase::Filter dbfilter;
+ dbfilter.where = tmpFilter.GetWhereClause(videodb, playlists);
+
+ VIDEODB_CONTENT_TYPE type = VIDEODB_CONTENT_MOVIES;
+ if (m_mediaType == "tvshows")
+ type = VIDEODB_CONTENT_TVSHOWS;
+ else if (m_mediaType == "episodes")
+ type = VIDEODB_CONTENT_EPISODES;
+ else if (m_mediaType == "musicvideos")
+ type = VIDEODB_CONTENT_MUSICVIDEOS;
+
+ if (filter.field == FieldGenre)
+ videodb.GetGenresNav(m_dbUrl->ToString(), selectItems, type, dbfilter, countOnly);
+ else if (filter.field == FieldActor || filter.field == FieldArtist)
+ videodb.GetActorsNav(m_dbUrl->ToString(), selectItems, type, dbfilter, countOnly);
+ else if (filter.field == FieldDirector)
+ videodb.GetDirectorsNav(m_dbUrl->ToString(), selectItems, type, dbfilter, countOnly);
+ else if (filter.field == FieldStudio)
+ videodb.GetStudiosNav(m_dbUrl->ToString(), selectItems, type, dbfilter, countOnly);
+ else if (filter.field == FieldAlbum)
+ videodb.GetMusicVideoAlbumsNav(m_dbUrl->ToString(), selectItems, -1, dbfilter, countOnly);
+ else if (filter.field == FieldTag)
+ videodb.GetTagsNav(m_dbUrl->ToString(), selectItems, type, dbfilter, countOnly);
+ }
+ else if (m_mediaType == "artists" || m_mediaType == "albums" || m_mediaType == "songs")
+ {
+ CMusicDatabase musicdb;
+ if (!musicdb.Open())
+ return;
+
+ CSmartPlaylist tmpFilter = *m_filter;
+ for (vector<CSmartPlaylistRule>::iterator rule = tmpFilter.m_ruleCombination.m_rules.begin(); rule != tmpFilter.m_ruleCombination.m_rules.end(); rule++)
+ {
+ if (rule->m_field == filter.field)
+ {
+ tmpFilter.m_ruleCombination.m_rules.erase(rule);
+ break;
+ }
+ }
+
+ std::set<CStdString> playlists;
+ CDatabase::Filter dbfilter;
+ dbfilter.where = tmpFilter.GetWhereClause(musicdb, playlists);
+
+ if (filter.field == FieldGenre)
+ musicdb.GetGenresNav(m_dbUrl->ToString(), selectItems, dbfilter, countOnly);
+ else if (filter.field == FieldArtist)
+ musicdb.GetArtistsNav(m_dbUrl->ToString(), selectItems, m_mediaType == "albums", -1, -1, -1, dbfilter, SortDescription(), countOnly);
+ else if (filter.field == FieldAlbum)
+ musicdb.GetAlbumsNav(m_dbUrl->ToString(), selectItems, -1, -1, dbfilter, SortDescription(), countOnly);
+ else if (filter.field == FieldAlbumType)
+ musicdb.GetAlbumTypesNav(m_dbUrl->ToString(), selectItems, dbfilter, countOnly);
+ else if (filter.field == FieldMusicLabel)
+ musicdb.GetMusicLabelsNav(m_dbUrl->ToString(), selectItems, dbfilter, countOnly);
+ }
+
+ if (selectItems.Size() <= 0)
+ return;
+
+ if (countOnly)
+ {
+ items.Copy(selectItems);
+ return;
+ }
+
+ // sort the items
+ selectItems.Sort(SORT_METHOD_LABEL, SortOrderAscending);
+
+ CGUIDialogSelect* pDialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
+ pDialog->Reset();
+ pDialog->SetItems(&selectItems);
+ CStdString strHeading;
+ strHeading.Format(g_localizeStrings.Get(13401), g_localizeStrings.Get(filter.label));
+ pDialog->SetHeading(strHeading);
+ pDialog->SetMultiSelection(true);
+
+ if (filter.rule != NULL && !filter.rule->m_parameter.empty())
+ pDialog->SetSelected(filter.rule->m_parameter);
+
+ pDialog->DoModal();
+ if (pDialog->IsConfirmed())
+ items.Copy(pDialog->GetSelectedItems());
+ else
+ items.Clear();
+ pDialog->Reset();
+}
+
+CSmartPlaylistRule* CGUIDialogMediaFilter::AddRule(Field field, CSmartPlaylistRule::SEARCH_OPERATOR ruleOperator /* = CSmartPlaylistRule::OPERATOR_CONTAINS */)
+{
+ CSmartPlaylistRule rule;
+ rule.m_field = field;
+ rule.m_operator = ruleOperator;
+
+ m_filter->m_ruleCombination.m_rules.push_back(rule);
+ return &m_filter->m_ruleCombination.m_rules.at(m_filter->m_ruleCombination.m_rules.size() - 1);
+}
+
+void CGUIDialogMediaFilter::DeleteRule(Field field)
+{
+ for (vector<CSmartPlaylistRule>::iterator rule = m_filter->m_ruleCombination.m_rules.begin(); rule != m_filter->m_ruleCombination.m_rules.end(); rule++)
+ {
+ if (rule->m_field == field)
+ {
+ m_filter->m_ruleCombination.m_rules.erase(rule);
+ break;
+ }
+ }
+}
+
+void CGUIDialogMediaFilter::GetRange(const Filter &filter, float &min, float &interval, float &max, RANGEFORMATFUNCTION &formatFunction)
+{
+ if (filter.field == FieldRating)
+ {
+ if (m_mediaType == "movies" || m_mediaType == "tvshows" || m_mediaType == "episodes")
+ {
+ min = 0.0f;
+ interval = 0.1f;
+ max = 10.0f;
+ formatFunction = RangeAsFloat;
+ }
+ else if (m_mediaType == "albums" || m_mediaType == "songs")
+ {
+ min = 0.0f;
+ interval = 1.0f;
+ max = 5.0f;
+ formatFunction = RangeAsInt;
+ }
+ }
+ else if (filter.field == FieldYear)
+ {
+ formatFunction = RangeAsInt;
+ min = 0.0f;
+ interval = 1.0f;
+ max = 0.0f;
+
+ if (m_mediaType == "movies" || m_mediaType == "tvshows" || m_mediaType == "musicvideos")
+ {
+ CStdString table;
+ CStdString year;
+ if (m_mediaType == "movies")
+ {
+ table = "movieview";
+ year.Format("c%02d", VIDEODB_ID_YEAR);
+ }
+ else if (m_mediaType == "tvshows")
+ {
+ table = "tvshowview";
+ year.Format("strftime(\"%%Y\", c%02d)", VIDEODB_ID_TV_PREMIERED);
+ }
+ else if (m_mediaType == "musicvideos")
+ {
+ table = "musicvideoview";
+ year.Format("c%02d", VIDEODB_ID_MUSICVIDEO_YEAR);
+ }
+
+ GetMinMax(table, year, min, max);
+ }
+ else if (m_mediaType == "albums" || m_mediaType == "songs")
+ {
+ CStdString table;
+ if (m_mediaType == "albums")
+ table = "albumview";
+ else if (m_mediaType == "songs")
+ table = "songview";
+
+ CDatabase::Filter filter;
+ filter.where = "iYear > 0";
+ GetMinMax(table, "iYear", min, max, filter);
+ }
+ }
+ else if (filter.field == FieldAirDate)
+ {
+ formatFunction = RangeAsDate;
+ min = 0.0f;
+ interval = 1.0f;
+ max = 0.0f;
+
+ if (m_mediaType == "episodes")
+ {
+ CStdString field; field.Format("CAST(strftime(\"%%s\", c%02d) AS INTEGER)", VIDEODB_ID_EPISODE_AIRED);
+
+ GetMinMax("episodeview", field, min, max);
+ interval = 60 * 60 * 24 * 7; // 1 week
+ }
+ }
+ else if (filter.field == FieldTime)
+ {
+ formatFunction = RangeAsTime;
+ min = 0.0f;
+ interval = 10.0f;
+ max = 0.0f;
+
+ if (m_mediaType == "songs")
+ GetMinMax("songview", "iDuration", min, max);
+ }
+ else if (filter.field == FieldPlaycount)
+ {
+ formatFunction = RangeAsInt;
+ min = 0.0f;
+ interval = 1.0f;
+ max = 0.0f;
+
+ if (m_mediaType == "songs")
+ GetMinMax("songview", "iTimesPlayed", min, max);
+ }
+}
+
+bool CGUIDialogMediaFilter::GetMinMax(const CStdString &table, const CStdString &field, float &min, float &max, const CDatabase::Filter &filter /* = CDatabase::Filter() */)
+{
+ if (table.empty() || field.empty())
+ return false;
+
+ CDatabase *db = NULL;
+ CDbUrl *dbUrl = NULL;
+ if (m_mediaType == "movies" || m_mediaType == "tvshows" || m_mediaType == "episodes" || m_mediaType == "musicvideos")
+ {
+ CVideoDatabase *videodb = new CVideoDatabase();
+ if (!videodb->Open())
+ {
+ delete videodb;
+ return false;
+ }
+
+ db = videodb;
+ dbUrl = new CVideoDbUrl();
+ }
+ else if (m_mediaType == "artists" || m_mediaType == "albums" || m_mediaType == "songs")
+ {
+ CMusicDatabase *musicdb = new CMusicDatabase();
+ if (!musicdb->Open())
+ {
+ delete musicdb;
+ return false;
+ }
+
+ db = musicdb;
+ dbUrl = new CMusicDbUrl();
+ }
+
+ if (db == NULL || !db->IsOpen() || dbUrl == NULL)
+ {
+ delete db;
+ delete dbUrl;
+ return false;
+ }
+
+ CDatabase::Filter extFilter = filter;
+ CStdString strSQLExtra;
+ if (!db->BuildSQL(m_dbUrl->ToString(), strSQLExtra, extFilter, strSQLExtra, *dbUrl))
+ {
+ delete db;
+ delete dbUrl;
+ return false;
+ }
+
+ CStdString strSQL = "SELECT %s FROM %s ";
+
+ min = (float)strtod(db->GetSingleValue(db->PrepareSQL(strSQL, CStdString("MIN(" + field + ")").c_str(), table.c_str()) + strSQLExtra).c_str(), NULL);
+ max = (float)strtod(db->GetSingleValue(db->PrepareSQL(strSQL, CStdString("MAX(" + field + ")").c_str(), table.c_str()) + strSQLExtra).c_str(), NULL);
+
+ db->Close();
+ delete db;
+ delete dbUrl;
+
+ return true;
+}
+
+CStdString CGUIDialogMediaFilter::RangeAsFloat(float valueLower, float valueUpper, float minimum)
+{
+ CStdString text;
+ if (valueLower != valueUpper)
+ text.Format(g_localizeStrings.Get(21467).c_str(), valueLower, valueUpper);
+ else
+ text.Format("%.1f", valueLower);
+ return text;
+}
+
+CStdString CGUIDialogMediaFilter::RangeAsInt(float valueLower, float valueUpper, float minimum)
+{
+ CStdString text;
+ if (valueLower != valueUpper)
+ text.Format(g_localizeStrings.Get(21468).c_str(), MathUtils::round_int((double)valueLower), MathUtils::round_int((double)valueUpper));
+ else
+ text.Format("%d", MathUtils::round_int((double)valueLower));
+ return text;
+}
+
+CStdString CGUIDialogMediaFilter::RangeAsDate(float valueLower, float valueUpper, float minimum)
+{
+ CDateTime from = (time_t)valueLower;
+ CDateTime to = (time_t)valueUpper;
+ CStdString text;
+ if (valueLower != valueUpper)
+ text.Format(g_localizeStrings.Get(21469).c_str(), from.GetAsLocalizedDate(), to.GetAsLocalizedDate());
+ else
+ text.Format("%s", from.GetAsLocalizedDate());
+ return text;
+}
+
+CStdString CGUIDialogMediaFilter::RangeAsTime(float valueLower, float valueUpper, float minimum)
+{
+ CDateTime from = (time_t)valueLower;
+ CDateTime to = (time_t)valueUpper;
+ CStdString text;
+ if (valueLower != valueUpper)
+ text.Format(g_localizeStrings.Get(21469).c_str(), from.GetAsLocalizedTime("mm:ss"), to.GetAsLocalizedTime("mm:ss"));
+ else
+ text.Format("%s", from.GetAsLocalizedTime("mm:ss"));
+ return text;
+}
diff --git a/xbmc/dialogs/GUIDialogMediaFilter.h b/xbmc/dialogs/GUIDialogMediaFilter.h
new file mode 100644
index 0000000000..681f068488
--- /dev/null
+++ b/xbmc/dialogs/GUIDialogMediaFilter.h
@@ -0,0 +1,82 @@
+#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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <map>
+#include <string>
+
+#include "DbUrl.h"
+#include "dbwrappers/Database.h"
+#include "playlists/SmartPlayList.h"
+#include "settings/GUIDialogSettings.h"
+#include "utils/DatabaseUtils.h"
+#include "utils/StdString.h"
+
+class CFileItemList;
+
+class CGUIDialogMediaFilter : public CGUIDialogSettings
+{
+public:
+ CGUIDialogMediaFilter();
+ virtual ~CGUIDialogMediaFilter();
+
+ virtual bool OnMessage(CGUIMessage& message);
+
+ static void ShowAndEditMediaFilter(const std::string &path, CSmartPlaylist &filter);
+
+ typedef struct {
+ std::string mediaType;
+ Field field;
+ uint32_t label;
+ SettingInfo::SETTING_TYPE type;
+ CSmartPlaylistRule::SEARCH_OPERATOR ruleOperator;
+ void *data;
+ CSmartPlaylistRule *rule;
+ int controlIndex;
+ } Filter;
+
+protected:
+ virtual void OnWindowLoaded();
+
+ virtual void CreateSettings();
+ virtual void SetupPage();
+ virtual void OnSettingChanged(SettingInfo &setting);
+
+ void Reset();
+ bool SetPath(const std::string &path);
+ void UpdateControls();
+
+ void OnBrowse(const Filter &filter, CFileItemList &items, bool countOnly = false);
+ CSmartPlaylistRule* AddRule(Field field, CSmartPlaylistRule::SEARCH_OPERATOR ruleOperator = CSmartPlaylistRule::OPERATOR_CONTAINS);
+ void DeleteRule(Field field);
+ void GetRange(const Filter &filter, float &min, float &interval, float &max, RANGEFORMATFUNCTION &formatFunction);
+
+ bool GetMinMax(const CStdString &table, const CStdString &field, float &min, float &max, const CDatabase::Filter &filter = CDatabase::Filter());
+
+ static CStdString RangeAsFloat(float valueLower, float valueUpper, float minimum);
+ static CStdString RangeAsInt(float valueLower, float valueUpper, float minimum);
+ static CStdString RangeAsDate(float valueLower, float valueUpper, float minimum);
+ static CStdString RangeAsTime(float valueLower, float valueUpper, float minimum);
+
+ CDbUrl* m_dbUrl;
+ std::string m_mediaType;
+ CSmartPlaylist *m_filter;
+ std::map<uint32_t, Filter> m_filters;
+};
diff --git a/xbmc/dialogs/Makefile b/xbmc/dialogs/Makefile
index 50a2974f36..f286370a88 100644
--- a/xbmc/dialogs/Makefile
+++ b/xbmc/dialogs/Makefile
@@ -9,6 +9,7 @@ SRCS=GUIDialogBoxBase.cpp \
GUIDialogGamepad.cpp \
GUIDialogKaiToast.cpp \
GUIDialogKeyboardGeneric.cpp \
+ GUIDialogMediaFilter.cpp \
GUIDialogMediaSource.cpp \
GUIDialogMuteBug.cpp \
GUIDialogNumeric.cpp \
diff --git a/xbmc/filesystem/SmartPlaylistDirectory.cpp b/xbmc/filesystem/SmartPlaylistDirectory.cpp
index 5e7a7df959..ab2a3877b3 100644
--- a/xbmc/filesystem/SmartPlaylistDirectory.cpp
+++ b/xbmc/filesystem/SmartPlaylistDirectory.cpp
@@ -50,7 +50,7 @@ namespace XFILE
return GetDirectory(playlist, items);
}
- bool CSmartPlaylistDirectory::GetDirectory(const CSmartPlaylist &playlist, CFileItemList& items)
+ bool CSmartPlaylistDirectory::GetDirectory(const CSmartPlaylist &playlist, CFileItemList& items, const CStdString &strBaseDir /* = "" */, bool filter /* = false */)
{
bool success = false, success2 = false;
std::set<CStdString> playlists;
@@ -62,6 +62,8 @@ namespace XFILE
if (g_guiSettings.GetBool("filelists.ignorethewhensorting"))
sorting.sortAttributes = SortAttributeIgnoreArticle;
+ std::string option = !filter ? "xsp" : "filter";
+
if (playlist.GetType().Equals("movies") ||
playlist.GetType().Equals("tvshows") ||
playlist.GetType().Equals("episodes"))
@@ -71,32 +73,40 @@ namespace XFILE
{
MediaType mediaType = DatabaseUtils::MediaTypeFromString(playlist.GetType());
- CStdString strBaseDir;
- switch (mediaType)
+ CStdString baseDir = strBaseDir;
+ if (strBaseDir.empty())
{
- case MediaTypeTvShow:
- case MediaTypeEpisode:
- strBaseDir = "videodb://2/2/";
- break;
+ switch (mediaType)
+ {
+ case MediaTypeTvShow:
+ case MediaTypeEpisode:
+ baseDir = "videodb://2/2/";
+ break;
- case MediaTypeMovie:
- strBaseDir = "videodb://1/2/";
- break;
+ case MediaTypeMovie:
+ baseDir = "videodb://1/2/";
+ break;
- default:
- return false;
+ default:
+ return false;
+ }
}
CVideoDbUrl videoUrl;
- CStdString xsp;
- if (!videoUrl.FromString(strBaseDir) || !playlist.SaveAsJson(xsp, false))
+ if (!videoUrl.FromString(baseDir))
return false;
// store the smartplaylist as JSON in the URL as well
- videoUrl.AddOption("xsp", xsp);
+ CStdString xsp;
+ if (!playlist.IsEmpty())
+ {
+ if (!playlist.SaveAsJson(xsp, false))
+ return false;
+ }
+ videoUrl.AddOption(option, xsp);
- CDatabase::Filter filter;
- success = db.GetSortedVideos(mediaType, videoUrl.ToString(), sorting, items, filter, true);
+ CDatabase::Filter dbfilter;
+ success = db.GetSortedVideos(mediaType, videoUrl.ToString(), sorting, items, dbfilter, true);
db.Close();
}
}
@@ -106,15 +116,20 @@ namespace XFILE
if (db.Open())
{
CMusicDbUrl musicUrl;
- CStdString xsp;
- if (!musicUrl.FromString("musicdb://3/") || !playlist.SaveAsJson(xsp, false))
+ if (!musicUrl.FromString(!strBaseDir.empty() ? strBaseDir : "musicdb://3/"))
return false;
// store the smartplaylist as JSON in the URL as well
- musicUrl.AddOption("xsp", xsp);
+ CStdString xsp;
+ if (!playlist.IsEmpty())
+ {
+ if (!playlist.SaveAsJson(xsp, false))
+ return false;
+ }
+ musicUrl.AddOption(option, xsp);
- CDatabase::Filter filter;
- success = db.GetAlbumsByWhere(musicUrl.ToString(), filter, items, sorting);
+ CDatabase::Filter dbfilter;
+ success = db.GetAlbumsByWhere(musicUrl.ToString(), dbfilter, items, sorting);
items.SetContent("albums");
db.Close();
}
@@ -125,13 +140,17 @@ namespace XFILE
if (db.Open())
{
CMusicDbUrl musicUrl;
- CStdString xsp;
-
- if (!musicUrl.FromString("musicdb://2/") || !playlist.SaveAsJson(xsp, false))
+ if (!musicUrl.FromString("musicdb://2/"))
return false;
// store the smartplaylist as JSON in the URL as well
- musicUrl.AddOption("xsp", xsp);
+ CStdString xsp;
+ if (!playlist.IsEmpty())
+ {
+ if (!playlist.SaveAsJson(xsp, false))
+ return false;
+ }
+ musicUrl.AddOption(option, xsp);
CDatabase::Filter filter;
success = db.GetArtistsByWhere(musicUrl.ToString(), filter, items, sorting);
@@ -150,15 +169,20 @@ namespace XFILE
songPlaylist.SetType("songs");
CMusicDbUrl musicUrl;
- CStdString xsp;
- if (!musicUrl.FromString("musicdb://4/") || !songPlaylist.SaveAsJson(xsp, false))
+ if (!musicUrl.FromString(!strBaseDir.empty() ? strBaseDir : "musicdb://4/"))
return false;
// store the smartplaylist as JSON in the URL as well
- musicUrl.AddOption("xsp", xsp);
+ CStdString xsp;
+ if (!songPlaylist.IsEmpty())
+ {
+ if (!songPlaylist.SaveAsJson(xsp, false))
+ return false;
+ }
+ musicUrl.AddOption(option, xsp);
- CDatabase::Filter filter;
- success = db.GetSongsByWhere(musicUrl.ToString(), filter, items, sorting);
+ CDatabase::Filter dbfilter;
+ success = db.GetSongsByWhere(musicUrl.ToString(), dbfilter, items, sorting);
items.SetContent("songs");
db.Close();
}
@@ -173,12 +197,17 @@ namespace XFILE
mvidPlaylist.SetType("musicvideos");
CVideoDbUrl videoUrl;
- CStdString xsp;
- if (!videoUrl.FromString("videodb://3/2/") || !mvidPlaylist.SaveAsJson(xsp, false))
+ if (!videoUrl.FromString(!strBaseDir.empty() ? strBaseDir : "videodb://3/2/"))
return false;
// store the smartplaylist as JSON in the URL as well
- videoUrl.AddOption("xsp", xsp);
+ CStdString xsp;
+ if (!mvidPlaylist.IsEmpty())
+ {
+ if (!mvidPlaylist.SaveAsJson(xsp, false))
+ return false;
+ }
+ videoUrl.AddOption(option, xsp);
CFileItemList items2;
success2 = db.GetSortedVideos(MediaTypeMusicVideo, videoUrl.ToString(), sorting, items2);
diff --git a/xbmc/filesystem/SmartPlaylistDirectory.h b/xbmc/filesystem/SmartPlaylistDirectory.h
index 10a9a4235a..526d3a7aaf 100644
--- a/xbmc/filesystem/SmartPlaylistDirectory.h
+++ b/xbmc/filesystem/SmartPlaylistDirectory.h
@@ -36,7 +36,7 @@ namespace XFILE
virtual bool ContainsFiles(const CStdString& strPath);
virtual bool Remove(const char *strPath);
- static bool GetDirectory(const CSmartPlaylist &playlist, CFileItemList& items);
+ static bool GetDirectory(const CSmartPlaylist &playlist, CFileItemList& items, const CStdString &strBaseDir = "", bool filter = false);
static CStdString GetPlaylistByName(const CStdString& name, const CStdString& playlistType);
};
diff --git a/xbmc/guilib/GUISliderControl.cpp b/xbmc/guilib/GUISliderControl.cpp
index c2c3536d8f..be49d274b2 100644
--- a/xbmc/guilib/GUISliderControl.cpp
+++ b/xbmc/guilib/GUISliderControl.cpp
@@ -32,19 +32,26 @@ static const SliderAction actions[] = {
CGUISliderControl::CGUISliderControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& backGroundTexture, const CTextureInfo& nibTexture, const CTextureInfo& nibTextureFocus, int iType)
: CGUIControl(parentID, controlID, posX, posY, width, height)
, m_guiBackground(posX, posY, width, height, backGroundTexture)
- , m_guiMid(posX, posY, width, height, nibTexture)
- , m_guiMidFocus(posX, posY, width, height, nibTextureFocus)
+ , m_guiSelectorLower(posX, posY, width, height, nibTexture)
+ , m_guiSelectorUpper(posX, posY, width, height, nibTexture)
+ , m_guiSelectorLowerFocus(posX, posY, width, height, nibTextureFocus)
+ , m_guiSelectorUpperFocus(posX, posY, width, height, nibTextureFocus)
{
m_iType = iType;
- m_iPercent = 0;
+ m_rangeSelection = false;
+ m_currentSelector = RangeSelectorLower; // use lower selector by default
+ m_iPercent[0] = 0;
+ m_iPercent[1] = 100;
m_iStart = 0;
m_iEnd = 100;
m_iInterval = 1;
m_fStart = 0.0f;
m_fEnd = 1.0f;
m_fInterval = 0.1f;
- m_iValue = 0;
- m_fValue = 0.0;
+ m_iValue[0] = m_iStart;
+ m_iValue[1] = m_iEnd;
+ m_fValue[0] = m_fStart;
+ m_fValue[1] = m_fEnd;
ControlType = GUICONTROL_SLIDER;
m_iInfoCode = 0;
m_dragging = false;
@@ -76,35 +83,53 @@ void CGUISliderControl::Process(unsigned int currentTime, CDirtyRegionList &dirt
dirty |= m_guiBackground.SetWidth(m_width);
dirty |= m_guiBackground.Process(currentTime);
+ CGUITexture &nibLower = (m_bHasFocus && !IsDisabled() && m_currentSelector == RangeSelectorLower) ? m_guiSelectorLowerFocus : m_guiSelectorLower;
+ dirty |= ProcessSelector(nibLower, currentTime, fScaleY, RangeSelectorLower);
+ if (m_rangeSelection)
+ {
+ CGUITexture &nibUpper = (m_bHasFocus && !IsDisabled() && m_currentSelector == RangeSelectorUpper) ? m_guiSelectorUpperFocus : m_guiSelectorUpper;
+ dirty |= ProcessSelector(nibUpper, currentTime, fScaleY, RangeSelectorUpper);
+ }
+
+ if (dirty)
+ MarkDirtyRegion();
+
+ CGUIControl::Process(currentTime, dirtyregions);
+}
+
+bool CGUISliderControl::ProcessSelector(CGUITexture &nib, unsigned int currentTime, float fScaleY, RangeSelector selector)
+{
+ bool dirty = false;
// we render the nib centered at the appropriate percentage, except where the nib
// would overflow the background image
- CGUITexture &nib = (m_bHasFocus && !IsDisabled()) ? m_guiMidFocus : m_guiMid;
-
dirty |= nib.SetHeight(nib.GetTextureHeight() * fScaleY);
dirty |= nib.SetWidth(nib.GetHeight() * 2);
- CAspectRatio ratio(CAspectRatio::AR_KEEP); ratio.align = ASPECT_ALIGN_LEFT | ASPECT_ALIGNY_CENTER;
+ CAspectRatio ratio(CAspectRatio::AR_KEEP);
+ ratio.align = ASPECT_ALIGN_LEFT | ASPECT_ALIGNY_CENTER;
dirty |= nib.SetAspectRatio(ratio);
CRect rect = nib.GetRenderRect();
- float offset = GetProportion() * m_width - rect.Width() / 2;
+ float offset = GetProportion(selector) * m_width - rect.Width() / 2;
if (offset > m_width - rect.Width())
offset = m_width - rect.Width();
if (offset < 0)
offset = 0;
- dirty |= nib.SetPosition(m_guiBackground.GetXPosition() + offset, m_guiBackground.GetYPosition() );
+ dirty |= nib.SetPosition(m_guiBackground.GetXPosition() + offset, m_guiBackground.GetYPosition());
dirty |= nib.Process(currentTime);
- if (dirty)
- MarkDirtyRegion();
-
- CGUIControl::Process(currentTime, dirtyregions);
+ return dirty;
}
void CGUISliderControl::Render()
{
m_guiBackground.Render();
- CGUITexture &nib = (m_bHasFocus && !IsDisabled()) ? m_guiMidFocus : m_guiMid;
- nib.Render();
+ CGUITexture &nibLower = (m_bHasFocus && !IsDisabled() && m_currentSelector == RangeSelectorLower) ? m_guiSelectorLowerFocus : m_guiSelectorLower;
+ nibLower.Render();
+ if (m_rangeSelection)
+ {
+ CGUITexture &nibUpper = (m_bHasFocus && !IsDisabled() && m_currentSelector == RangeSelectorUpper) ? m_guiSelectorUpperFocus : m_guiSelectorUpper;
+ nibUpper.Render();
+ }
CGUIControl::Render();
}
@@ -121,7 +146,8 @@ bool CGUISliderControl::OnMessage(CGUIMessage& message)
case GUI_MSG_LABEL_RESET:
{
- SetPercentage(0);
+ SetPercentage(0, RangeSelectorLower);
+ SetPercentage(100, RangeSelectorUpper);
return true;
}
break;
@@ -137,42 +163,83 @@ bool CGUISliderControl::OnAction(const CAction &action)
{
case ACTION_MOVE_LEFT:
//case ACTION_OSD_SHOW_VALUE_MIN:
- Move( -1);
+ Move(-1);
return true;
- break;
case ACTION_MOVE_RIGHT:
//case ACTION_OSD_SHOW_VALUE_PLUS:
Move(1);
return true;
- break;
+
+ case ACTION_SELECT_ITEM:
+ // switch between the two sliders
+ SwitchRangeSelector();
+ return true;
+
default:
- return CGUIControl::OnAction(action);
+ break;
}
+
+ return CGUIControl::OnAction(action);
}
void CGUISliderControl::Move(int iNumSteps)
{
+ bool rangeSwap = false;
switch (m_iType)
{
case SPIN_CONTROL_TYPE_FLOAT:
- m_fValue += m_fInterval * iNumSteps;
- if (m_fValue < m_fStart) m_fValue = m_fStart;
- if (m_fValue > m_fEnd) m_fValue = m_fEnd;
- break;
+ {
+ float &value = m_fValue[m_currentSelector];
+ value += m_fInterval * iNumSteps;
+ if (value < m_fStart) value = m_fStart;
+ if (value > m_fEnd) value = m_fEnd;
+ if (m_fValue[0] > m_fValue[1])
+ {
+ float valueLower = m_fValue[0];
+ m_fValue[0] = m_fValue[1];
+ m_fValue[1] = valueLower;
+ rangeSwap = true;
+ }
+ break;
+ }
case SPIN_CONTROL_TYPE_INT:
- m_iValue += m_iInterval * iNumSteps;
- if (m_iValue < m_iStart) m_iValue = m_iStart;
- if (m_iValue > m_iEnd) m_iValue = m_iEnd;
- break;
+ {
+ int &value = m_iValue[m_currentSelector];
+ value += m_iInterval * iNumSteps;
+ if (value < m_iStart) value = m_iStart;
+ if (value > m_iEnd) value = m_iEnd;
+ if (m_iValue[0] > m_iValue[1])
+ {
+ int valueLower = m_iValue[0];
+ m_iValue[0] = m_iValue[1];
+ m_iValue[1] = valueLower;
+ rangeSwap = true;
+ }
+ break;
+ }
default:
- m_iPercent += m_iInterval * iNumSteps;
- if (m_iPercent < 0) m_iPercent = 0;
- if (m_iPercent > 100) m_iPercent = 100;
- break;
+ {
+ int &value = m_iPercent[m_currentSelector];
+ value += m_iInterval * iNumSteps;
+ if (value < 0) value = 0;
+ if (value > 100) value = 100;
+ if (m_iPercent[0] > m_iPercent[1])
+ {
+ int valueLower = m_iPercent[0];
+ m_iPercent[0] = m_iPercent[1];
+ m_iPercent[1] = valueLower;
+ rangeSwap = true;
+ }
+ break;
+ }
}
+
+ if (rangeSwap)
+ SwitchRangeSelector();
+
SendClick();
}
@@ -190,56 +257,130 @@ void CGUISliderControl::SendClick()
}
}
-void CGUISliderControl::SetPercentage(int iPercent)
+void CGUISliderControl::SetRangeSelection(bool rangeSelection)
+{
+ if (m_rangeSelection == rangeSelection)
+ return;
+
+ m_rangeSelection = rangeSelection;
+ SetRangeSelector(RangeSelectorLower);
+ SetInvalid();
+}
+
+void CGUISliderControl::SetRangeSelector(RangeSelector selector)
+{
+ if (m_currentSelector == selector)
+ return;
+
+ m_currentSelector = selector;
+ SetInvalid();
+}
+
+void CGUISliderControl::SwitchRangeSelector()
+{
+ if (m_currentSelector == RangeSelectorLower)
+ SetRangeSelector(RangeSelectorUpper);
+ else
+ SetRangeSelector(RangeSelectorLower);
+}
+
+void CGUISliderControl::SetPercentage(int iPercent, RangeSelector selector /* = RangeSelectorLower */)
{
if (iPercent > 100) iPercent = 100;
- if (iPercent < 0) iPercent = 0;
- m_iPercent = iPercent;
+ else if (iPercent < 0) iPercent = 0;
+
+ int iPercentLower = selector == RangeSelectorLower ? iPercent : m_iPercent[0];
+ int iPercentUpper = selector == RangeSelectorUpper ? iPercent : m_iPercent[1];
+
+ if (!m_rangeSelection || iPercentLower <= iPercentUpper)
+ {
+ m_iPercent[0] = iPercentLower;
+ m_iPercent[1] = iPercentUpper;
+ }
+ else
+ {
+ m_iPercent[0] = iPercentUpper;
+ m_iPercent[1] = iPercentLower;
+ }
}
-int CGUISliderControl::GetPercentage() const
+int CGUISliderControl::GetPercentage(RangeSelector selector /* = RangeSelectorLower */) const
{
- return m_iPercent;
+ return m_iPercent[selector];
}
-void CGUISliderControl::SetIntValue(int iValue)
+void CGUISliderControl::SetIntValue(int iValue, RangeSelector selector /* = RangeSelectorLower */)
{
if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
- m_fValue = (float)iValue;
+ SetFloatValue((float)iValue);
else if (m_iType == SPIN_CONTROL_TYPE_INT)
- m_iValue = iValue;
+ {
+ if (iValue > m_iEnd) iValue = m_iEnd;
+ else if (iValue < m_iStart) iValue = m_iStart;
+
+ int iValueLower = selector == RangeSelectorLower ? iValue : m_iValue[0];
+ int iValueUpper = selector == RangeSelectorUpper ? iValue : m_iValue[1];
+
+ if (!m_rangeSelection || iValueLower <= iValueUpper)
+ {
+ m_iValue[0] = iValueLower;
+ m_iValue[1] = iValueUpper;
+ }
+ else
+ {
+ m_iValue[0] = iValueUpper;
+ m_iValue[1] = iValueLower;
+ }
+ }
else
SetPercentage(iValue);
}
-int CGUISliderControl::GetIntValue() const
+int CGUISliderControl::GetIntValue(RangeSelector selector /* = RangeSelectorLower */) const
{
if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
- return (int)m_fValue;
+ return (int)m_fValue[selector];
else if (m_iType == SPIN_CONTROL_TYPE_INT)
- return m_iValue;
+ return m_iValue[selector];
else
- return m_iPercent;
+ return m_iPercent[selector];
}
-void CGUISliderControl::SetFloatValue(float fValue)
+void CGUISliderControl::SetFloatValue(float fValue, RangeSelector selector /* = RangeSelectorLower */)
{
if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
- m_fValue = fValue;
+ {
+ if (fValue > m_fEnd) fValue = m_fEnd;
+ else if (fValue < m_fStart) fValue = m_fStart;
+
+ float fValueLower = selector == RangeSelectorLower ? fValue : m_fValue[0];
+ float fValueUpper = selector == RangeSelectorUpper ? fValue : m_fValue[1];
+
+ if (!m_rangeSelection || fValueLower <= fValueUpper)
+ {
+ m_fValue[0] = fValueLower;
+ m_fValue[1] = fValueUpper;
+ }
+ else
+ {
+ m_fValue[0] = fValueUpper;
+ m_fValue[1] = fValueLower;
+ }
+ }
else if (m_iType == SPIN_CONTROL_TYPE_INT)
- m_iValue = (int)fValue;
+ SetIntValue((int)fValue);
else
SetPercentage((int)fValue);
}
-float CGUISliderControl::GetFloatValue() const
+float CGUISliderControl::GetFloatValue(RangeSelector selector /* = RangeSelectorLower */) const
{
if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
- return m_fValue;
+ return m_fValue[selector];
else if (m_iType == SPIN_CONTROL_TYPE_INT)
- return (float)m_iValue;
+ return (float)m_iValue[selector];
else
- return (float)m_iPercent;
+ return (float)m_iPercent[selector];
}
void CGUISliderControl::SetIntInterval(int iInterval)
@@ -264,8 +405,8 @@ void CGUISliderControl::SetRange(int iStart, int iEnd)
SetFloatRange((float)iStart,(float)iEnd);
else
{
- m_iStart = iStart;
- m_iEnd = iEnd;
+ m_iValue[0] = m_iStart = iStart;
+ m_iValue[1] = m_iEnd = iEnd;
}
}
@@ -275,8 +416,8 @@ void CGUISliderControl::SetFloatRange(float fStart, float fEnd)
SetRange((int)fStart, (int)fEnd);
else
{
- m_fStart = fStart;
- m_fEnd = fEnd;
+ m_fValue[0] = m_fStart = fStart;
+ m_fValue[1] = m_fEnd = fEnd;
}
}
@@ -284,38 +425,47 @@ void CGUISliderControl::FreeResources(bool immediately)
{
CGUIControl::FreeResources(immediately);
m_guiBackground.FreeResources(immediately);
- m_guiMid.FreeResources(immediately);
- m_guiMidFocus.FreeResources(immediately);
+ m_guiSelectorLower.FreeResources(immediately);
+ m_guiSelectorUpper.FreeResources(immediately);
+ m_guiSelectorLowerFocus.FreeResources(immediately);
+ m_guiSelectorUpperFocus.FreeResources(immediately);
}
void CGUISliderControl::DynamicResourceAlloc(bool bOnOff)
{
CGUIControl::DynamicResourceAlloc(bOnOff);
m_guiBackground.DynamicResourceAlloc(bOnOff);
- m_guiMid.DynamicResourceAlloc(bOnOff);
- m_guiMidFocus.DynamicResourceAlloc(bOnOff);
+ m_guiSelectorLower.DynamicResourceAlloc(bOnOff);
+ m_guiSelectorUpper.DynamicResourceAlloc(bOnOff);
+ m_guiSelectorLowerFocus.DynamicResourceAlloc(bOnOff);
+ m_guiSelectorUpperFocus.DynamicResourceAlloc(bOnOff);
}
void CGUISliderControl::AllocResources()
{
CGUIControl::AllocResources();
m_guiBackground.AllocResources();
- m_guiMid.AllocResources();
- m_guiMidFocus.AllocResources();
+ m_guiSelectorLower.AllocResources();
+ m_guiSelectorUpper.AllocResources();
+ m_guiSelectorLowerFocus.AllocResources();
+ m_guiSelectorUpperFocus.AllocResources();
}
void CGUISliderControl::SetInvalid()
{
CGUIControl::SetInvalid();
m_guiBackground.SetInvalid();
- m_guiMid.SetInvalid();
- m_guiMidFocus.SetInvalid();
+ m_guiSelectorLower.SetInvalid();
+ m_guiSelectorUpper.SetInvalid();
+ m_guiSelectorLowerFocus.SetInvalid();
+ m_guiSelectorUpperFocus.SetInvalid();
}
bool CGUISliderControl::HitTest(const CPoint &point) const
{
if (m_guiBackground.HitTest(point)) return true;
- if (m_guiMid.HitTest(point)) return true;
+ if (m_guiSelectorLower.HitTest(point)) return true;
+ if (m_rangeSelection && m_guiSelectorUpper.HitTest(point)) return true;
return false;
}
@@ -324,19 +474,39 @@ void CGUISliderControl::SetFromPosition(const CPoint &point)
float fPercent = (point.x - m_guiBackground.GetXPosition()) / m_guiBackground.GetWidth();
if (fPercent < 0) fPercent = 0;
if (fPercent > 1) fPercent = 1;
+
+ RangeSelector selector = RangeSelectorLower;
switch (m_iType)
{
case SPIN_CONTROL_TYPE_FLOAT:
- m_fValue = m_fStart + (m_fEnd - m_fStart) * fPercent;
- break;
+ {
+ float fValue = m_fStart + (m_fEnd - m_fStart) * fPercent;
+ if (m_rangeSelection && fValue >= m_fValue[1])
+ selector = RangeSelectorUpper;
+
+ SetFloatValue(fValue, selector);
+ break;
+ }
case SPIN_CONTROL_TYPE_INT:
- m_iValue = (int)(m_iStart + (float)(m_iEnd - m_iStart) * fPercent + 0.49f);
- break;
+ {
+ int iValue = (int)(m_iStart + (float)(m_iEnd - m_iStart) * fPercent + 0.49f);
+ if (m_rangeSelection && iValue >= m_iValue[1])
+ selector = RangeSelectorUpper;
+
+ SetIntValue(iValue, selector);
+ break;
+ }
default:
- m_iPercent = (int)(fPercent * 100 + 0.49f);
- break;
+ {
+ int iValue = (int)(fPercent * 100 + 0.49f);
+ if (m_rangeSelection && iValue >= m_iPercent[1])
+ selector = RangeSelectorUpper;
+
+ SetPercentage(iValue, selector);
+ break;
+ }
}
SendClick();
}
@@ -411,11 +581,26 @@ CStdString CGUISliderControl::GetDescription() const
return m_textValue;
CStdString description;
if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
- description.Format("%2.2f", m_fValue);
+ {
+ if (m_rangeSelection)
+ description.Format("[%2.2f, %2.2f]", m_fValue[0], m_fValue[1]);
+ else
+ description.Format("%2.2f", m_fValue[0]);
+ }
else if (m_iType == SPIN_CONTROL_TYPE_INT)
- description.Format("%i", m_iValue);
+ {
+ if (m_rangeSelection)
+ description.Format("[%i, %i]", m_iValue[0], m_iValue[1]);
+ else
+ description.Format("%i", m_iValue);
+ }
else
- description.Format("%i%%", m_iPercent);
+ {
+ if (m_rangeSelection)
+ description.Format("[%i%%, %i%%]", m_iPercent[0], m_iPercent[1]);
+ else
+ description.Format("%i%%", m_iPercent[0]);
+ }
return description;
}
@@ -423,19 +608,21 @@ bool CGUISliderControl::UpdateColors()
{
bool changed = CGUIControl::UpdateColors();
changed |= m_guiBackground.SetDiffuseColor(m_diffuseColor);
- changed |= m_guiMid.SetDiffuseColor(m_diffuseColor);
- changed |= m_guiMidFocus.SetDiffuseColor(m_diffuseColor);
+ changed |= m_guiSelectorLower.SetDiffuseColor(m_diffuseColor);
+ changed |= m_guiSelectorUpper.SetDiffuseColor(m_diffuseColor);
+ changed |= m_guiSelectorLowerFocus.SetDiffuseColor(m_diffuseColor);
+ changed |= m_guiSelectorUpperFocus.SetDiffuseColor(m_diffuseColor);
return changed;
}
-float CGUISliderControl::GetProportion() const
+float CGUISliderControl::GetProportion(RangeSelector selector /* = RangeSelectorLower */) const
{
if (m_iType == SPIN_CONTROL_TYPE_FLOAT)
- return (m_fValue - m_fStart) / (m_fEnd - m_fStart);
+ return (GetFloatValue(selector) - m_fStart) / (m_fEnd - m_fStart);
else if (m_iType == SPIN_CONTROL_TYPE_INT)
- return (float)(m_iValue - m_iStart) / (float)(m_iEnd - m_iStart);
- return 0.01f * m_iPercent;
+ return (float)(GetIntValue(selector) - m_iStart) / (float)(m_iEnd - m_iStart);
+ return 0.01f * GetPercentage(selector);
}
void CGUISliderControl::SetAction(const CStdString &action)
diff --git a/xbmc/guilib/GUISliderControl.h b/xbmc/guilib/GUISliderControl.h
index 6b8fbfa828..45809111ab 100644
--- a/xbmc/guilib/GUISliderControl.h
+++ b/xbmc/guilib/GUISliderControl.h
@@ -51,6 +51,11 @@ class CGUISliderControl :
public CGUIControl
{
public:
+ typedef enum {
+ RangeSelectorLower = 0,
+ RangeSelectorUpper = 1
+ } RangeSelector;
+
CGUISliderControl(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& backGroundTexture, const CTextureInfo& mibTexture, const CTextureInfo& nibTextureFocus, int iType);
virtual ~CGUISliderControl(void);
virtual CGUISliderControl *Clone() const { return new CGUISliderControl(*this); };
@@ -65,13 +70,17 @@ public:
virtual void SetRange(int iStart, int iEnd);
virtual void SetFloatRange(float fStart, float fEnd);
virtual bool OnMessage(CGUIMessage& message);
+ bool ProcessSelector(CGUITexture &nib, unsigned int currentTime, float fScaleY, RangeSelector selector);
+ void SetRangeSelection(bool rangeSelection);
+ void SetRangeSelector(RangeSelector selector);
+ void SwitchRangeSelector();
void SetInfo(int iInfo);
- void SetPercentage(int iPercent);
- int GetPercentage() const;
- void SetIntValue(int iValue);
- int GetIntValue() const;
- void SetFloatValue(float fValue);
- float GetFloatValue() const;
+ void SetPercentage(int iPercent, RangeSelector selector = RangeSelectorLower);
+ int GetPercentage(RangeSelector selector = RangeSelectorLower) const;
+ void SetIntValue(int iValue, RangeSelector selector = RangeSelectorLower);
+ int GetIntValue(RangeSelector selector = RangeSelectorLower) const;
+ void SetFloatValue(float fValue, RangeSelector selector = RangeSelectorLower);
+ float GetFloatValue(RangeSelector selector = RangeSelectorLower) const;
void SetIntInterval(int iInterval);
void SetFloatInterval(float fInterval);
void SetType(int iType) { m_iType = iType; };
@@ -87,25 +96,30 @@ protected:
/*! \brief Get the current position of the slider as a proportion
\return slider position in the range [0,1]
*/
- float GetProportion() const;
+ float GetProportion(RangeSelector selector = RangeSelectorLower) const;
/*! \brief Send a click message (and/or action) to the app in response to a slider move
*/
void SendClick();
CGUITexture m_guiBackground;
- CGUITexture m_guiMid;
- CGUITexture m_guiMidFocus;
+ CGUITexture m_guiSelectorLower;
+ CGUITexture m_guiSelectorUpper;
+ CGUITexture m_guiSelectorLowerFocus;
+ CGUITexture m_guiSelectorUpperFocus;
int m_iType;
- int m_iPercent;
+ bool m_rangeSelection;
+ RangeSelector m_currentSelector;
+
+ int m_iPercent[2];
- int m_iValue;
+ int m_iValue[2];
int m_iStart;
int m_iInterval;
int m_iEnd;
- float m_fValue;
+ float m_fValue[2];
float m_fStart;
float m_fInterval;
float m_fEnd;
diff --git a/xbmc/guilib/Key.h b/xbmc/guilib/Key.h
index 31c76a9280..32c3dfd668 100644
--- a/xbmc/guilib/Key.h
+++ b/xbmc/guilib/Key.h
@@ -309,6 +309,8 @@
#define ACTION_SUBTITLE_VSHIFT_DOWN 231 // shift down subtitles in DVDPlayer
#define ACTION_SUBTITLE_ALIGN 232 // toggle vertical alignment of subtitles
+#define ACTION_FILTER 233
+
// Window ID defines to make the code a bit more readable
#define WINDOW_INVALID 9999
#define WINDOW_HOME 10000
@@ -388,6 +390,7 @@
#define WINDOW_DIALOG_PERIPHERAL_MANAGER 10149
#define WINDOW_DIALOG_PERIPHERAL_SETTINGS 10150
#define WINDOW_DIALOG_EXT_PROGRESS 10151
+#define WINDOW_DIALOG_MEDIA_FILTER 10152
#define WINDOW_MUSIC_PLAYLIST 10500
#define WINDOW_MUSIC_FILES 10501
diff --git a/xbmc/input/ButtonTranslator.cpp b/xbmc/input/ButtonTranslator.cpp
index b49b94a457..2b29a1a945 100644
--- a/xbmc/input/ButtonTranslator.cpp
+++ b/xbmc/input/ButtonTranslator.cpp
@@ -189,6 +189,7 @@ static const ActionMapping actions[] =
{"jumpsms7" , ACTION_JUMP_SMS7},
{"jumpsms8" , ACTION_JUMP_SMS8},
{"jumpsms9" , ACTION_JUMP_SMS9},
+ {"filter" , ACTION_FILTER},
{"filterclear" , ACTION_FILTER_CLEAR},
{"filtersms2" , ACTION_FILTER_SMS2},
{"filtersms3" , ACTION_FILTER_SMS3},
@@ -335,7 +336,8 @@ static const ActionMapping windows[] =
{"startup" , WINDOW_STARTUP_ANIM},
{"peripherals" , WINDOW_DIALOG_PERIPHERAL_MANAGER},
{"peripheralsettings" , WINDOW_DIALOG_PERIPHERAL_SETTINGS},
- {"extendedprogressdialog" , WINDOW_DIALOG_EXT_PROGRESS}};
+ {"extendedprogressdialog" , WINDOW_DIALOG_EXT_PROGRESS},
+ {"mediafilter" , WINDOW_DIALOG_MEDIA_FILTER}};
static const ActionMapping mousecommands[] =
{
diff --git a/xbmc/interfaces/json-rpc/AudioLibrary.cpp b/xbmc/interfaces/json-rpc/AudioLibrary.cpp
index 13e5608a9d..0b52ee8b98 100644
--- a/xbmc/interfaces/json-rpc/AudioLibrary.cpp
+++ b/xbmc/interfaces/json-rpc/AudioLibrary.cpp
@@ -76,7 +76,7 @@ JSONRPC_STATUS CAudioLibrary::GetArtists(const CStdString &method, ITransportLay
return InvalidParams;
CFileItemList items;
- if (!musicdatabase.GetArtistsNav(musicUrl.ToString(), items, albumArtistsOnly, genreID, albumID, songID, sorting))
+ if (!musicdatabase.GetArtistsNav(musicUrl.ToString(), items, albumArtistsOnly, genreID, albumID, songID, CDatabase::Filter(), sorting))
return InternalError;
// Add "artist" to "properties" array by default
@@ -154,7 +154,7 @@ JSONRPC_STATUS CAudioLibrary::GetAlbums(const CStdString &method, ITransportLaye
return InvalidParams;
CFileItemList items;
- if (!musicdatabase.GetAlbumsNav(musicUrl.ToString(), items, genreID, artistID, sorting))
+ if (!musicdatabase.GetAlbumsNav(musicUrl.ToString(), items, genreID, artistID, CDatabase::Filter(), sorting))
return InternalError;
int size = items.Size();
diff --git a/xbmc/music/MusicDatabase.cpp b/xbmc/music/MusicDatabase.cpp
index eddf3ea788..d0f80affe7 100644
--- a/xbmc/music/MusicDatabase.cpp
+++ b/xbmc/music/MusicDatabase.cpp
@@ -2621,29 +2621,53 @@ void CMusicDatabase::Clean()
}
}
-bool CMusicDatabase::GetGenresNav(const CStdString& strBaseDir, CFileItemList& items)
+bool CMusicDatabase::GetGenresNav(const CStdString& strBaseDir, CFileItemList& items, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
{
try
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
+ // get primary genres for songs - could be simplified to just SELECT * FROM genre?
+ CStdString strSQL = "SELECT %s FROM genre ";
+
+ Filter extFilter = filter;
CMusicDbUrl musicUrl;
- if (!musicUrl.FromString(strBaseDir))
+ if (!musicUrl.FromString(strBaseDir) || !GetFilter(musicUrl, extFilter))
return false;
- // get primary genres for songs - could be simplified to just SELECT * FROM genre?
- CStdString strSQL="SELECT * "
- " FROM genre "
- " WHERE idGenre IN"
- "(SELECT song_genre.idGenre "
- " FROM song_genre) ";
+ // if there are extra WHERE conditions we might need access
+ // to songview or albumview for these conditions
+ if (extFilter.where.size() > 0)
+ {
+ if (extFilter.where.find("artistview") != string::npos)
+ extFilter.AppendJoin("JOIN song_genre ON song_genre.idGenre = genre.idGenre JOIN songview ON songview.idSong = song_genre.idSong "
+ "JOIN song_artist ON song_artist.idSong = songview.idSong JOIN artistview ON artistview.idArtist = song_artist.idArtist");
+ else if (extFilter.where.find("songview") != string::npos)
+ extFilter.AppendJoin("JOIN song_genre ON song_genre.idGenre = genre.idGenre JOIN songview ON songview.idSong = song_genre.idSong");
+ else if (extFilter.where.find("albumview") != string::npos)
+ extFilter.AppendJoin("JOIN album_genre ON album_genre.idGenre = genre.idGenre JOIN albumview ON albumview.idAlbum = album_genre.idAlbum");
+
+ extFilter.AppendGroup("genre.idGenre");
+ }
+ extFilter.AppendWhere("genre.strGenre != ''");
- // block null strings
- strSQL += " AND genre.strGenre != \"\"";
+ if (countOnly)
+ {
+ extFilter.fields = "COUNT(DISTINCT genre.idGenre)";
+ extFilter.group.clear();
+ extFilter.order.clear();
+ }
+
+ CStdString strSQLExtra;
+ if (!BuildSQL(strSQLExtra, extFilter, strSQLExtra))
+ return false;
+
+ strSQL = PrepareSQL(strSQL.c_str(), !extFilter.fields.empty() && extFilter.fields.compare("*") != 0 ? extFilter.fields.c_str() : "genre.*") + strSQLExtra;
// run query
CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());
+
if (!m_pDS->query(strSQL.c_str()))
return false;
int iRowsFound = m_pDS->num_rows();
@@ -2653,15 +2677,25 @@ bool CMusicDatabase::GetGenresNav(const CStdString& strBaseDir, CFileItemList& i
return true;
}
+ if (countOnly)
+ {
+ CFileItemPtr pItem(new CFileItem());
+ pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
+ items.Add(pItem);
+
+ m_pDS->close();
+ return true;
+ }
+
// get data from returned rows
while (!m_pDS->eof())
{
- CFileItemPtr pItem(new CFileItem(m_pDS->fv("strGenre").get_asString()));
- pItem->GetMusicInfoTag()->SetGenre(m_pDS->fv("strGenre").get_asString());
- pItem->GetMusicInfoTag()->SetDatabaseId(m_pDS->fv("idGenre").get_asInt(), "genre");
+ CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
+ pItem->GetMusicInfoTag()->SetGenre(m_pDS->fv("genre.strGenre").get_asString());
+ pItem->GetMusicInfoTag()->SetDatabaseId(m_pDS->fv("genre.idGenre").get_asInt(), "genre");
CMusicDbUrl itemUrl = musicUrl;
- CStdString strDir; strDir.Format("%ld/", m_pDS->fv("idGenre").get_asInt());
+ CStdString strDir; strDir.Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
itemUrl.AppendPath(strDir);
pItem->SetPath(itemUrl.ToString());
@@ -2751,7 +2785,99 @@ bool CMusicDatabase::GetAlbumsByYear(const CStdString& strBaseDir, CFileItemList
return GetAlbumsByWhere(musicUrl.ToString(), filter, items);
}
-bool CMusicDatabase::GetArtistsNav(const CStdString& strBaseDir, CFileItemList& items, bool albumArtistsOnly /* = false */, int idGenre /* = -1 */, int idAlbum /* = -1 */, int idSong /* = -1 */, const SortDescription &sortDescription /* = SortDescription() */)
+bool CMusicDatabase::GetCommonNav(const CStdString &strBaseDir, const CStdString &table, const CStdString &labelField, CFileItemList &items, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
+{
+ if (NULL == m_pDB.get()) return false;
+ if (NULL == m_pDS.get()) return false;
+
+ if (table.empty() || labelField.empty())
+ return false;
+
+ try
+ {
+ Filter extFilter = filter;
+ CStdString strSQL = "SELECT %s FROM " + table + " ";
+ extFilter.AppendGroup(labelField);
+ extFilter.AppendWhere(labelField + " != ''");
+
+ if (countOnly)
+ {
+ extFilter.fields = "COUNT(DISTINCT " + labelField + ")";
+ extFilter.group.clear();
+ extFilter.order.clear();
+ }
+
+ CMusicDbUrl musicUrl;
+ if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, musicUrl))
+ return false;
+
+ strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : labelField.c_str());
+
+ // run query
+ CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());
+ if (!m_pDS->query(strSQL.c_str()))
+ return false;
+
+ int iRowsFound = m_pDS->num_rows();
+ if (iRowsFound <= 0)
+ {
+ m_pDS->close();
+ return false;
+ }
+
+ if (countOnly)
+ {
+ CFileItemPtr pItem(new CFileItem());
+ pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
+ items.Add(pItem);
+
+ m_pDS->close();
+ return true;
+ }
+
+ // get data from returned rows
+ while (!m_pDS->eof())
+ {
+ string labelValue = m_pDS->fv(labelField).get_asString();
+ CFileItemPtr pItem(new CFileItem(labelValue));
+
+ CMusicDbUrl itemUrl = musicUrl;
+ CStdString strDir; strDir.Format("%s/", labelValue.c_str());
+ itemUrl.AppendPath(strDir);
+ pItem->SetPath(itemUrl.ToString());
+
+ pItem->m_bIsFolder = true;
+ items.Add(pItem);
+
+ m_pDS->next();
+ }
+ items.SetPath(musicUrl.ToString());
+
+ // cleanup
+ m_pDS->close();
+
+ return true;
+ }
+ catch (...)
+ {
+ m_pDS->close();
+ CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
+ }
+
+ return false;
+}
+
+bool CMusicDatabase::GetAlbumTypesNav(const CStdString &strBaseDir, CFileItemList &items, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
+{
+ return GetCommonNav(strBaseDir, "albumview", "albumview.strType", items, filter, countOnly);
+}
+
+bool CMusicDatabase::GetMusicLabelsNav(const CStdString &strBaseDir, CFileItemList &items, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
+{
+ return GetCommonNav(strBaseDir, "albumview", "albumview.strLabel", items, filter, countOnly);
+}
+
+bool CMusicDatabase::GetArtistsNav(const CStdString& strBaseDir, CFileItemList& items, bool albumArtistsOnly /* = false */, int idGenre /* = -1 */, int idAlbum /* = -1 */, int idSong /* = -1 */, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */, bool countOnly /* = false */)
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
@@ -2772,8 +2898,7 @@ bool CMusicDatabase::GetArtistsNav(const CStdString& strBaseDir, CFileItemList&
musicUrl.AddOption("albumartistsonly", albumArtistsOnly);
- Filter filter;
- bool result = GetArtistsByWhere(musicUrl.ToString(), filter, items, sortDescription);
+ bool result = GetArtistsByWhere(musicUrl.ToString(), filter, items, sortDescription, countOnly);
CLog::Log(LOGDEBUG,"Time to retrieve artists from dataset = %i", XbmcThreads::SystemClockMillis() - time);
return result;
@@ -2786,7 +2911,7 @@ bool CMusicDatabase::GetArtistsNav(const CStdString& strBaseDir, CFileItemList&
return false;
}
-bool CMusicDatabase::GetArtistsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
+bool CMusicDatabase::GetArtistsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */, bool countOnly /* = false */)
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
@@ -2796,7 +2921,7 @@ bool CMusicDatabase::GetArtistsByWhere(const CStdString& strBaseDir, const Filte
int total = -1;
CStdString strSQL = "SELECT %s FROM artistview ";
-
+
Filter extFilter = filter;
CMusicDbUrl musicUrl;
if (!musicUrl.FromString(strBaseDir) || !GetFilter(musicUrl, extFilter))
@@ -2822,6 +2947,13 @@ bool CMusicDatabase::GetArtistsByWhere(const CStdString& strBaseDir, const Filte
extFilter.AppendGroup("artistview.idArtist");
}
+ if (countOnly)
+ {
+ extFilter.fields = "COUNT(DISTINCT artistview.idArtist)";
+ extFilter.group.clear();
+ extFilter.order.clear();
+ }
+
CStdString strSQLExtra;
if (!BuildSQL(strSQLExtra, extFilter, strSQLExtra))
return false;
@@ -2847,6 +2979,16 @@ bool CMusicDatabase::GetArtistsByWhere(const CStdString& strBaseDir, const Filte
return true;
}
+ if (countOnly)
+ {
+ CFileItemPtr pItem(new CFileItem());
+ pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
+ items.Add(pItem);
+
+ m_pDS->close();
+ return true;
+ }
+
// store the total value of items as a property
if (total < iRowsFound)
total = iRowsFound;
@@ -2887,6 +3029,7 @@ bool CMusicDatabase::GetArtistsByWhere(const CStdString& strBaseDir, const Filte
CLog::Log(LOGERROR, "%s - out of memory getting listing (got %i)", __FUNCTION__, items.Size());
}
}
+ items.SetPath(musicUrl.ToString());
// cleanup
m_pDS->close();
@@ -2964,7 +3107,7 @@ bool CMusicDatabase::GetAlbumFromSong(const CSong &song, CAlbum &album)
return false;
}
-bool CMusicDatabase::GetAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre /* = -1 */, int idArtist /* = -1 */, const SortDescription &sortDescription /* = SortDescription() */)
+bool CMusicDatabase::GetAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre /* = -1 */, int idArtist /* = -1 */, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */, bool countOnly /* = false */)
{
CMusicDbUrl musicUrl;
if (!musicUrl.FromString(strBaseDir))
@@ -2977,11 +3120,10 @@ bool CMusicDatabase::GetAlbumsNav(const CStdString& strBaseDir, CFileItemList& i
if (idArtist > 0)
musicUrl.AddOption("artistid", idArtist);
- Filter filter;
- return GetAlbumsByWhere(musicUrl.ToString(), filter, items, sortDescription);
+ return GetAlbumsByWhere(musicUrl.ToString(), filter, items, sortDescription, countOnly);
}
-bool CMusicDatabase::GetAlbumsByWhere(const CStdString &baseDir, const Filter &filter, CFileItemList &items, const SortDescription &sortDescription /* = SortDescription() */)
+bool CMusicDatabase::GetAlbumsByWhere(const CStdString &baseDir, const Filter &filter, CFileItemList &items, const SortDescription &sortDescription /* = SortDescription() */, bool countOnly /* = false */)
{
if (m_pDB.get() == NULL || m_pDS.get() == NULL)
return false;
@@ -3039,6 +3181,16 @@ bool CMusicDatabase::GetAlbumsByWhere(const CStdString &baseDir, const Filter &f
if (total < iRowsFound)
total = iRowsFound;
items.SetProperty("total", total);
+
+ if (countOnly)
+ {
+ CFileItemPtr pItem(new CFileItem());
+ pItem->SetProperty("total", total);
+ items.Add(pItem);
+
+ m_pDS->close();
+ return true;
+ }
DatabaseResults results;
results.reserve(iRowsFound);
@@ -3069,6 +3221,7 @@ bool CMusicDatabase::GetAlbumsByWhere(const CStdString &baseDir, const Filter &f
CLog::Log(LOGERROR, "%s - out of memory getting listing (got %i)", __FUNCTION__, items.Size());
}
}
+ items.SetPath(musicUrl.ToString());
// cleanup
m_pDS->close();
@@ -3168,6 +3321,8 @@ bool CMusicDatabase::GetSongsByWhere(const CStdString &baseDir, const Filter &fi
return (items.Size() > 0);
}
}
+ items.SetPath(musicUrl.ToString());
+
// cleanup
m_pDS->close();
CLog::Log(LOGDEBUG, "%s(%s) - took %d ms", __FUNCTION__, filter.where.c_str(), XbmcThreads::SystemClockMillis() - time);
@@ -5107,7 +5262,7 @@ string CMusicDatabase::GetArtistArtForItem(int mediaId, const string &mediaType,
return GetSingleValue(query, m_pDS2);
}
-bool CMusicDatabase::GetFilter(const CDbUrl &musicUrl, Filter &filter)
+bool CMusicDatabase::GetFilter(CDbUrl &musicUrl, Filter &filter)
{
if (!musicUrl.IsValid())
return false;
@@ -5272,8 +5427,6 @@ bool CMusicDatabase::GetFilter(const CDbUrl &musicUrl, Filter &filter)
" OR songview.idSong IN (SELECT song.idSong FROM song JOIN album_artist ON song.idAlbum=album_artist.idAlbum JOIN artist ON artist.idArtist = album_artist.idArtist WHERE artist.strArtist like '%s')", // album artists
option->second.asString().c_str(), option->second.asString().c_str()));
}
- else
- return false;
option = options.find("xsp");
if (option != options.end())
@@ -5290,5 +5443,23 @@ bool CMusicDatabase::GetFilter(const CDbUrl &musicUrl, Filter &filter)
}
}
+ option = options.find("filter");
+ if (option != options.end())
+ {
+ CSmartPlaylist xspFilter;
+ if (!xspFilter.LoadFromJson(option->second.asString()))
+ return false;
+
+ // check if the filter playlist matches the item type
+ if (xspFilter.GetType() == type)
+ {
+ std::set<CStdString> playlists;
+ filter.AppendWhere(xspFilter.GetWhereClause(*this, playlists));
+ }
+ // remove the filter if it doesn't match the item type
+ else
+ musicUrl.AddOption("filter", "");
+ }
+
return true;
}
diff --git a/xbmc/music/MusicDatabase.h b/xbmc/music/MusicDatabase.h
index d030c21a8c..d11c265af0 100644
--- a/xbmc/music/MusicDatabase.h
+++ b/xbmc/music/MusicDatabase.h
@@ -154,16 +154,19 @@ public:
bool GetPaths(std::set<CStdString> &paths);
bool SetPathHash(const CStdString &path, const CStdString &hash);
bool GetPathHash(const CStdString &path, CStdString &hash);
- bool GetGenresNav(const CStdString& strBaseDir, CFileItemList& items);
+ bool GetGenresNav(const CStdString& strBaseDir, CFileItemList& items, const Filter &filter = Filter(), bool countOnly = false);
bool GetYearsNav(const CStdString& strBaseDir, CFileItemList& items);
- bool GetArtistsNav(const CStdString& strBaseDir, CFileItemList& items, bool albumArtistsOnly = false, int idGenre = -1, int idAlbum = -1, int idSong = -1, const SortDescription &sortDescription = SortDescription());
- bool GetAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre = -1, int idArtist = -1, const SortDescription &sortDescription = SortDescription());
+ bool GetArtistsNav(const CStdString& strBaseDir, CFileItemList& items, bool albumArtistsOnly = false, int idGenre = -1, int idAlbum = -1, int idSong = -1, const Filter &filter = Filter(), const SortDescription &sortDescription = SortDescription(), bool countOnly = false);
+ bool GetCommonNav(const CStdString &strBaseDir, const CStdString &table, const CStdString &labelField, CFileItemList &items, const Filter &filter /* = Filter() */, bool countOnly /* = false */);
+ bool GetAlbumTypesNav(const CStdString &strBaseDir, CFileItemList &items, const Filter &filter = Filter(), bool countOnly = false);
+ bool GetMusicLabelsNav(const CStdString &strBaseDir, CFileItemList &items, const Filter &filter = Filter(), bool countOnly = false);
+ bool GetAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre = -1, int idArtist = -1, const Filter &filter = Filter(), const SortDescription &sortDescription = SortDescription(), bool countOnly = false);
bool GetAlbumsByYear(const CStdString &strBaseDir, CFileItemList& items, int year);
bool GetSongsNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idArtist,int idAlbum, const SortDescription &sortDescription = SortDescription());
bool GetSongsByYear(const CStdString& baseDir, CFileItemList& items, int year);
bool GetSongsByWhere(const CStdString &baseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription = SortDescription());
- bool GetAlbumsByWhere(const CStdString &baseDir, const Filter &filter, CFileItemList &items, const SortDescription &sortDescription = SortDescription());
- bool GetArtistsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription = SortDescription());
+ bool GetAlbumsByWhere(const CStdString &baseDir, const Filter &filter, CFileItemList &items, const SortDescription &sortDescription = SortDescription(), bool countOnly = false);
+ bool GetArtistsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription = SortDescription(), bool countOnly = false);
bool GetRandomSong(CFileItem* item, int& idSong, const Filter &filter);
int GetKaraokeSongsCount();
int GetSongsCount(const Filter &filter = Filter());
@@ -269,7 +272,7 @@ public:
*/
std::string GetArtistArtForItem(int mediaId, const std::string &mediaType, const std::string &artType);
- virtual bool GetFilter(const CDbUrl &musicUrl, Filter &filter);
+ virtual bool GetFilter(CDbUrl &musicUrl, Filter &filter);
protected:
std::map<CStdString, int> m_artistCache;
diff --git a/xbmc/music/windows/GUIWindowMusicBase.cpp b/xbmc/music/windows/GUIWindowMusicBase.cpp
index 2e7a2e9af1..9e6fcf8054 100644
--- a/xbmc/music/windows/GUIWindowMusicBase.cpp
+++ b/xbmc/music/windows/GUIWindowMusicBase.cpp
@@ -1314,11 +1314,38 @@ void CGUIWindowMusicBase::OnRetrieveMusicInfo(CFileItemList& items)
bool CGUIWindowMusicBase::GetDirectory(const CStdString &strDirectory, CFileItemList &items)
{
+ CStdString directory = strDirectory;
+
+ // check if the path contains a filter and if so load it and
+ // remove it from the path to get proper GUI view states etc
+ CSmartPlaylist filterXsp;
+ CMusicDbUrl musicUrl;
+ if (musicUrl.FromString(strDirectory))
+ {
+ CVariant filter;
+ if (musicUrl.GetOption("filter", filter))
+ {
+ // load the filter and if it's type does not match the
+ // path's item type reset it
+ if (filterXsp.LoadFromJson(filter.asString()) && !filterXsp.GetType().Equals(musicUrl.GetType().c_str()))
+ filterXsp.Reset();
+
+ // remove the "filter" option from the path
+ musicUrl.AddOption("filter", "");
+ }
+ directory = musicUrl.ToString();
+ }
+
items.SetThumbnailImage("");
- bool bResult = CGUIMediaWindow::GetDirectory(strDirectory,items);
+ bool bResult = CGUIMediaWindow::GetDirectory(directory, items);
if (bResult)
CMusicThumbLoader::FillThumb(items);
+ // (re-)apply the previously retrieved filter
+ // because it was reset in CGUIMediaWindow::GetDirectory()
+ if (!filterXsp.IsEmpty())
+ m_filter = filterXsp;
+
// add in the "New Playlist" item if we're in the playlists folder
if ((items.GetPath() == "special://musicplaylists/") && !items.Contains("newplaylist://"))
{
@@ -1350,6 +1377,15 @@ void CGUIWindowMusicBase::OnPrepareFileItems(CFileItemList &items)
{
}
+bool CGUIWindowMusicBase::CheckFilterAdvanced(CFileItemList &items)
+{
+ CStdString content = items.GetContent();
+ if (items.IsMusicDb() && (content.Equals("artists") || content.Equals("albums") || content.Equals("songs")))
+ return true;
+
+ return false;
+}
+
void CGUIWindowMusicBase::OnInitWindow()
{
CGUIMediaWindow::OnInitWindow();
diff --git a/xbmc/music/windows/GUIWindowMusicBase.h b/xbmc/music/windows/GUIWindowMusicBase.h
index 1de6b29003..2b8fd67955 100644
--- a/xbmc/music/windows/GUIWindowMusicBase.h
+++ b/xbmc/music/windows/GUIWindowMusicBase.h
@@ -71,6 +71,8 @@ protected:
virtual void OnPrepareFileItems(CFileItemList &items);
virtual CStdString GetStartFolder(const CStdString &dir);
+ virtual bool CheckFilterAdvanced(CFileItemList &items);
+
// new methods
virtual void PlayItem(int iItem);
virtual bool OnPlayMedia(int iItem);
diff --git a/xbmc/music/windows/GUIWindowMusicNav.cpp b/xbmc/music/windows/GUIWindowMusicNav.cpp
index 88c55146d3..fb0948a7e8 100644
--- a/xbmc/music/windows/GUIWindowMusicNav.cpp
+++ b/xbmc/music/windows/GUIWindowMusicNav.cpp
@@ -272,7 +272,7 @@ bool CGUIWindowMusicNav::Update(const CStdString &strDirectory)
if (CGUIWindowMusicBase::Update(strDirectory))
{
- m_thumbLoader.Load(*m_vecItems);
+ m_thumbLoader.Load(*m_unfilteredItems);
return true;
}
diff --git a/xbmc/playlists/SmartPlayList.cpp b/xbmc/playlists/SmartPlayList.cpp
index ee78668059..739c0d4bfd 100644
--- a/xbmc/playlists/SmartPlayList.cpp
+++ b/xbmc/playlists/SmartPlayList.cpp
@@ -132,7 +132,8 @@ static const operatorField operators[] = { { "contains", CSmartPlaylistRule::OPE
{ "inthelast", CSmartPlaylistRule::OPERATOR_IN_THE_LAST, 21410 },
{ "notinthelast", CSmartPlaylistRule::OPERATOR_NOT_IN_THE_LAST, 21411 },
{ "true", CSmartPlaylistRule::OPERATOR_TRUE, 20122 },
- { "false", CSmartPlaylistRule::OPERATOR_FALSE, 20424 }
+ { "false", CSmartPlaylistRule::OPERATOR_FALSE, 20424 },
+ { "between", CSmartPlaylistRule::OPERATOR_BETWEEN, 21456 }
};
#define NUM_OPERATORS sizeof(operators) / sizeof(operatorField)
@@ -747,6 +748,21 @@ CStdString CSmartPlaylistRule::GetWhereClause(const CDatabase &db, const CStdStr
}
}
+ // The BETWEEN operator is handled special
+ if (op == OPERATOR_BETWEEN)
+ {
+ if (m_parameter.size() != 2)
+ return "";
+
+ FIELD_TYPE fieldType = GetFieldType(m_field);
+ if (fieldType == NUMERIC_FIELD || m_field == FieldYear)
+ return db.PrepareSQL("CAST(%s as DECIMAL(5,1)) BETWEEN %s AND %s", GetField(m_field, strType).c_str(), m_parameter[0].c_str(), m_parameter[1].c_str());
+ else if (fieldType == SECONDS_FIELD)
+ return db.PrepareSQL("CAST(%s as INTEGER) BETWEEN %s AND %s", GetField(m_field, strType).c_str(), m_parameter[0].c_str(), m_parameter[1].c_str());
+ else
+ return db.PrepareSQL("%s BETWEEN '%s' AND '%s'", GetField(m_field, strType).c_str(), m_parameter[0].c_str(), m_parameter[1].c_str());
+ }
+
// now the query parameter
CStdString wholeQuery;
for (vector<CStdString>::const_iterator it = m_parameter.begin(); it != m_parameter.end(); /* it++ is done further down */)
@@ -810,6 +826,13 @@ CStdString CSmartPlaylistRule::GetWhereClause(const CDatabase &db, const CStdStr
else if (m_field == FieldAlbumArtist)
query = GetField(FieldId, strType) + negate + " IN (SELECT album_artist.idAlbum FROM album_artist, artist WHERE album_artist.idArtist = artist.idArtist AND artist.strArtist" + parameter + ")";
}
+ else if (strType == "artists")
+ {
+ table = "artistview";
+
+ if (m_field == FieldGenre)
+ query = GetField(FieldId, strType) + negate + " IN (SELECT song_artist.idArtist FROM song_artist, song_genre, genre WHERE song_artist.idSong = song_genre.idSong AND song_genre.idGenre = genre.idGenre AND genre.strGenre" + parameter + ")";
+ }
else if (strType == "movies")
{
table = "movieview";
@@ -1093,11 +1116,7 @@ void CSmartPlaylistRuleCombination::AddCombination(const CSmartPlaylistRuleCombi
CSmartPlaylist::CSmartPlaylist()
{
- m_ruleCombination.SetType(CSmartPlaylistRuleCombination::CombinationAnd);
- m_limit = 0;
- m_orderField = SortByNone;
- m_orderAscending = true;
- m_playlistType = "songs"; // sane default
+ Reset();
}
TiXmlElement *CSmartPlaylist::OpenAndReadName(const CStdString &path)
@@ -1374,6 +1393,17 @@ bool CSmartPlaylist::SaveAsJson(CStdString &json, bool full /* = true */) const
return json.size() > 0;
}
+void CSmartPlaylist::Reset()
+{
+ m_ruleCombination.m_combinations.clear();
+ m_ruleCombination.m_rules.clear();
+ m_ruleCombination.SetType(CSmartPlaylistRuleCombination::CombinationAnd);
+ m_limit = 0;
+ m_orderField = SortByNone;
+ m_orderAscending = true;
+ m_playlistType = "songs"; // sane default
+}
+
void CSmartPlaylist::SetName(const CStdString &name)
{
m_playlistName = name;
diff --git a/xbmc/playlists/SmartPlayList.h b/xbmc/playlists/SmartPlayList.h
index b8a2374e08..190abd04b8 100644
--- a/xbmc/playlists/SmartPlayList.h
+++ b/xbmc/playlists/SmartPlayList.h
@@ -60,6 +60,7 @@ public:
OPERATOR_NOT_IN_THE_LAST,
OPERATOR_TRUE,
OPERATOR_FALSE,
+ OPERATOR_BETWEEN,
OPERATOR_END
};
@@ -137,6 +138,7 @@ public:
private:
friend class CSmartPlaylist;
friend class CGUIDialogSmartPlaylistEditor;
+ friend class CGUIDialogMediaFilter;
Combination m_type;
CSmartPlaylistRuleCombinations m_combinations;
@@ -159,6 +161,8 @@ public:
TiXmlElement *OpenAndReadName(const CStdString &path);
bool LoadFromXML(TiXmlElement *root, const CStdString &encoding = "UTF-8");
+ void Reset();
+
void SetName(const CStdString &name);
void SetType(const CStdString &type); // music, video, mixed
const CStdString& GetName() const { return m_playlistName; };
@@ -191,8 +195,10 @@ public:
static void GetAvailableFields(const std::string &type, std::vector<std::string> &fieldList);
static void GetAvailableOperators(std::vector<std::string> &operatorList);
+ bool IsEmpty() const { return m_ruleCombination.m_rules.empty() && m_ruleCombination.m_combinations.empty(); }
private:
friend class CGUIDialogSmartPlaylistEditor;
+ friend class CGUIDialogMediaFilter;
TiXmlElement* readName();
TiXmlElement *readNameFromXml(const CStdString &xml);
diff --git a/xbmc/settings/GUIDialogSettings.cpp b/xbmc/settings/GUIDialogSettings.cpp
index 91b1e3aa61..5aa16cffd4 100644
--- a/xbmc/settings/GUIDialogSettings.cpp
+++ b/xbmc/settings/GUIDialogSettings.cpp
@@ -198,7 +198,7 @@ void CGUIDialogSettings::UpdateSetting(unsigned int id)
{
float value = *(float *)setting.data;
pControl->SetFloatValue(value);
- if (setting.formatFunction) pControl->SetTextValue(setting.formatFunction(value, setting.interval));
+ if (setting.formatFunction.standard) pControl->SetTextValue(setting.formatFunction.standard(value, setting.interval));
}
}
else if (setting.type == SettingInfo::BUTTON_DIALOG)
@@ -209,8 +209,8 @@ void CGUIDialogSettings::UpdateSetting(unsigned int id)
}
else if (setting.type == SettingInfo::EDIT)
{
- CGUIEditControl *pControl = (CGUIEditControl *)GetControl(controlID);
- if (pControl && setting.data) pControl->SetLabel2(*(CStdString *)setting.data);
+ SET_CONTROL_LABEL(controlID, setting.name);
+ if (setting.data) SET_CONTROL_LABEL2(controlID, string(*(CStdString *)setting.data));
}
else if (setting.type == SettingInfo::EDIT_NUM)
{
@@ -229,6 +229,17 @@ void CGUIDialogSettings::UpdateSetting(unsigned int id)
strNewValue = "-";
SET_CONTROL_LABEL2(controlID, strNewValue);
}
+ else if (setting.type == SettingInfo::RANGE)
+ {
+ CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl *)GetControl(controlID);
+ float** value = (float **)setting.data;
+ if (pControl && setting.data)
+ {
+ pControl->SetFloatValue(*(value[0]), CGUISliderControl::RangeSelectorLower);
+ pControl->SetFloatValue(*(value[1]), CGUISliderControl::RangeSelectorUpper);
+ if (setting.formatFunction.range) pControl->SetTextValue(setting.formatFunction.range(*(value[0]), *(value[1]), setting.interval));
+ }
+ }
if (setting.enabled)
{
@@ -300,13 +311,13 @@ void CGUIDialogSettings::OnClick(int iID)
{
CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl *)GetControl(iID);
if (setting.data) *(float *)setting.data = pControl->GetFloatValue();
- if (setting.formatFunction) pControl->SetTextValue(setting.formatFunction(pControl->GetFloatValue(), setting.interval));
+ if (setting.formatFunction.standard) pControl->SetTextValue(setting.formatFunction.standard(pControl->GetFloatValue(), setting.interval));
}
else if (setting.type == SettingInfo::BUTTON && m_usePopupSliders && setting.data)
{ // we're using popup sliders
CGUIDialogSlider::ShowAndGetInput(setting.name, *(float *)setting.data, setting.min, setting.interval, setting.max, this, &setting);
- if (setting.formatFunction)
- SET_CONTROL_LABEL2(iID, setting.formatFunction(*(float *)setting.data, setting.interval));
+ if (setting.formatFunction.standard)
+ SET_CONTROL_LABEL2(iID, setting.formatFunction.standard(*(float *)setting.data, setting.interval));
}
else if (setting.type == SettingInfo::STRING)
{
@@ -316,6 +327,19 @@ void CGUIDialogSettings::OnClick(int iID)
strNewValue = "-";
SET_CONTROL_LABEL2(iID, strNewValue);
}
+ else if (setting.type == SettingInfo::RANGE)
+ {
+ CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl *)GetControl(iID);
+ if (setting.data)
+ {
+ *((float **)setting.data)[0] = pControl->GetFloatValue(CGUISliderControl::RangeSelectorLower);
+ *((float **)setting.data)[1] = pControl->GetFloatValue(CGUISliderControl::RangeSelectorUpper);
+ }
+ if (setting.formatFunction.range)
+ pControl->SetTextValue(setting.formatFunction.range(pControl->GetFloatValue(CGUISliderControl::RangeSelectorLower),
+ pControl->GetFloatValue(CGUISliderControl::RangeSelectorUpper),
+ setting.interval));
+ }
OnSettingChanged(setting);
}
@@ -346,8 +370,8 @@ void CGUIDialogSettings::AddSetting(SettingInfo &setting, float width, int iCont
pControl = new CGUIButtonControl(*m_pOriginalSettingsButton);
if (!pControl) return ;
((CGUIButtonControl *)pControl)->SetLabel(setting.name);
- if (setting.formatFunction)
- ((CGUIButtonControl *)pControl)->SetLabel2(setting.formatFunction(*(float *)setting.data, setting.interval));
+ if (setting.formatFunction.standard)
+ ((CGUIButtonControl *)pControl)->SetLabel2(setting.formatFunction.standard(*(float *)setting.data, setting.interval));
pControl->SetWidth(width);
}
else if (setting.type == SettingInfo::EDIT && m_pOriginalEdit)
@@ -404,14 +428,14 @@ void CGUIDialogSettings::AddSetting(SettingInfo &setting, float width, int iCont
if (!pControl) return ;
pControl->SetWidth(width);
((CGUISettingsSliderControl *)pControl)->SetText(setting.name);
- if (setting.formatFunction)
- ((CGUISettingsSliderControl *)pControl)->SetTextValue(setting.formatFunction(*(float *)setting.data, setting.interval));
+ if (setting.formatFunction.standard)
+ ((CGUISettingsSliderControl *)pControl)->SetTextValue(setting.formatFunction.standard(*(float *)setting.data, setting.interval));
((CGUISettingsSliderControl *)pControl)->SetType(SPIN_CONTROL_TYPE_FLOAT);
((CGUISettingsSliderControl *)pControl)->SetFloatRange(setting.min, setting.max);
((CGUISettingsSliderControl *)pControl)->SetFloatInterval(setting.interval);
if (setting.data) ((CGUISettingsSliderControl *)pControl)->SetFloatValue(*(float *)setting.data);
}
- if (setting.type == SettingInfo::STRING && m_pOriginalSettingsButton)
+ else if (setting.type == SettingInfo::STRING && m_pOriginalSettingsButton)
{
pControl = new CGUIButtonControl(*m_pOriginalSettingsButton);
if (!pControl) return ;
@@ -422,6 +446,25 @@ void CGUIDialogSettings::AddSetting(SettingInfo &setting, float width, int iCont
((CGUIButtonControl *)pControl)->SetLabel2(strValue);
pControl->SetWidth(width);
}
+ else if (setting.type == SettingInfo::RANGE)
+ {
+ if (!m_pOriginalSlider) return;
+ pControl = new CGUISettingsSliderControl(*m_pOriginalSlider);
+ if (!pControl) return ;
+ pControl->SetWidth(width);
+ ((CGUISettingsSliderControl *)pControl)->SetText(setting.name);
+ if (setting.formatFunction.range)
+ ((CGUISettingsSliderControl *)pControl)->SetTextValue(setting.formatFunction.range(*((float **)setting.data)[0], *((float **)setting.data)[1], setting.interval));
+ ((CGUISettingsSliderControl *)pControl)->SetType(SPIN_CONTROL_TYPE_FLOAT);
+ ((CGUISettingsSliderControl *)pControl)->SetRangeSelection(true);
+ ((CGUISettingsSliderControl *)pControl)->SetFloatRange(setting.min, setting.max);
+ ((CGUISettingsSliderControl *)pControl)->SetFloatInterval(setting.interval);
+ if (setting.data)
+ {
+ ((CGUISettingsSliderControl *)pControl)->SetFloatValue(*((float **)setting.data)[0], CGUISliderControl::RangeSelectorLower);
+ ((CGUISettingsSliderControl *)pControl)->SetFloatValue(*((float **)setting.data)[1], CGUISliderControl::RangeSelectorUpper);
+ }
+ }
if (!pControl) return;
pControl->SetID(iControlID);
@@ -469,7 +512,7 @@ void CGUIDialogSettings::AddButton(unsigned int id, int label, float *current, f
setting.min = min;
setting.max = max;
setting.interval = interval;
- setting.formatFunction = function;
+ setting.formatFunction.standard = function;
m_settings.push_back(setting);
}
@@ -583,7 +626,26 @@ void CGUIDialogSettings::AddSlider(unsigned int id, int label, float *current, f
setting.interval = interval;
setting.max = max;
setting.data = current;
- setting.formatFunction = function;
+ setting.formatFunction.standard = function;
+ m_settings.push_back(setting);
+}
+
+void CGUIDialogSettings::AddRangeSlider(unsigned int id, int label, float *currentLower, float* currentUpper, float min, float interval, float max, RANGEFORMATFUNCTION function)
+{
+ SettingInfo setting;
+ setting.id = id;
+ setting.name = g_localizeStrings.Get(label);
+ setting.type = SettingInfo::RANGE;
+ setting.min = min;
+ setting.interval = interval;
+ setting.max = max;
+
+ float** data = new float*[2];
+ data[0] = currentLower;
+ data[1] = currentUpper;
+ setting.data = data;
+
+ setting.formatFunction.range = function;
m_settings.push_back(setting);
}
@@ -612,8 +674,19 @@ void CGUIDialogSettings::OnSliderChange(void *data, CGUISliderControl *slider)
return;
SettingInfo *setting = (SettingInfo *)data;
- *(float *)setting->data = slider->GetFloatValue();
- OnSettingChanged(*setting);
- if (setting->formatFunction)
- slider->SetTextValue(setting->formatFunction(slider->GetFloatValue(), setting->interval));
+ if (setting->type == SettingInfo::SLIDER)
+ {
+ *(float *)setting->data = slider->GetFloatValue();
+ OnSettingChanged(*setting);
+ if (setting->formatFunction.standard)
+ slider->SetTextValue(setting->formatFunction.standard(slider->GetFloatValue(), setting->interval));
+ }
+ else if (setting->type == SettingInfo::RANGE)
+ {
+ *((float **)setting->data)[0] = slider->GetFloatValue(CGUISliderControl::RangeSelectorLower);
+ *((float **)setting->data)[1] = slider->GetFloatValue(CGUISliderControl::RangeSelectorUpper);
+ OnSettingChanged(*setting);
+ if (setting->formatFunction.range)
+ slider->SetTextValue(setting->formatFunction.range(slider->GetFloatValue(CGUISliderControl::RangeSelectorLower), slider->GetFloatValue(CGUISliderControl::RangeSelectorUpper), setting->interval));
+ }
}
diff --git a/xbmc/settings/GUIDialogSettings.h b/xbmc/settings/GUIDialogSettings.h
index dc6400f19f..4d89f5128f 100644
--- a/xbmc/settings/GUIDialogSettings.h
+++ b/xbmc/settings/GUIDialogSettings.h
@@ -29,14 +29,16 @@ class CGUIRadioButtonControl;
class CGUISettingsSliderControl;
class CGUIEditControl;
class CGUIImage;
+class CGUIEditControl;
typedef std::vector<CStdString> SETTINGSTRINGS;
typedef CStdString (*FORMATFUNCTION) (float value, float min);
+typedef CStdString (*RANGEFORMATFUNCTION) (float valueLower, float valueUpper, float min);
class SettingInfo
{
public:
- enum SETTING_TYPE { NONE=0, EDIT, EDIT_NUM, BUTTON, BUTTON_DIALOG, 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, RANGE };
SettingInfo()
{
id = 0;
@@ -46,7 +48,7 @@ public:
min = 0;
max = 0;
interval = 0;
- formatFunction = NULL;
+ formatFunction.standard = NULL;
};
SETTING_TYPE type;
CStdString name;
@@ -55,7 +57,11 @@ public:
float min;
float max;
float interval;
- FORMATFUNCTION formatFunction;
+ union
+ {
+ FORMATFUNCTION standard;
+ RANGEFORMATFUNCTION range;
+ } formatFunction;
std::vector<std::pair<int, CStdString> > entry;
bool enabled;
};
@@ -96,6 +102,7 @@ protected:
void AddSpin(unsigned int id, int label, int *current, std::vector<std::pair<int, CStdString> > &values);
void AddSpin(unsigned int id, int label, int *current, std::vector<std::pair<int, int> > &values);
void AddSlider(unsigned int id, int label, float *current, float min, float interval, float max, FORMATFUNCTION formatFunction, bool allowPopup = true);
+ void AddRangeSlider(unsigned int id, int label, float *currentLower, float* currentUpper, float min, float interval, float max, RANGEFORMATFUNCTION formatFunction);
void AddSeparator(unsigned int id);
CGUIEditControl *m_pOriginalEdit;
diff --git a/xbmc/utils/URIUtils.cpp b/xbmc/utils/URIUtils.cpp
index 8bcd48d595..5419f0da29 100644
--- a/xbmc/utils/URIUtils.cpp
+++ b/xbmc/utils/URIUtils.cpp
@@ -267,6 +267,22 @@ bool URIUtils::GetParentPath(const CStdString& strPath, CStdString& strParent)
strFile = url.GetHostName();
return GetParentPath(strFile, strParent);
}
+ else if ((url.GetProtocol() == "videodb" || url.GetProtocol() == "musicdb") && !url.GetOptions().empty())
+ {
+ CStdString options = url.GetOptions();
+ size_t filterStart = options.find("filter=");
+ if (filterStart != string::npos)
+ {
+ size_t filterEnd = options.find("&", filterStart);
+ options.erase(filterStart, filterEnd - filterStart);
+ if (options.Equals("?"))
+ options.clear();
+
+ url.SetOptions(options);
+ strParent = url.Get();
+ return true;
+ }
+ }
else if (url.GetProtocol() == "stack")
{
CStackDirectory dir;
diff --git a/xbmc/utils/UrlOptions.cpp b/xbmc/utils/UrlOptions.cpp
index c189a089a8..f383fa332d 100644
--- a/xbmc/utils/UrlOptions.cpp
+++ b/xbmc/utils/UrlOptions.cpp
@@ -28,6 +28,11 @@ using namespace std;
CUrlOptions::CUrlOptions()
{ }
+CUrlOptions::CUrlOptions(const std::string &options)
+{
+ AddOptions(options);
+}
+
CUrlOptions::~CUrlOptions()
{ }
@@ -45,6 +50,14 @@ std::string CUrlOptions::GetOptionsString() const
return options;
}
+void CUrlOptions::AddOption(const std::string &key, const char *value)
+{
+ if (key.empty() || value == NULL)
+ return;
+
+ return AddOption(key, string(value));
+}
+
void CUrlOptions::AddOption(const std::string &key, const std::string &value)
{
if (key.empty())
@@ -120,3 +133,29 @@ void CUrlOptions::AddOptions(const std::string &options)
AddOption(key, value);
}
}
+
+void CUrlOptions::AddOptions(const CUrlOptions &options)
+{
+ m_options.insert(options.m_options.begin(), options.m_options.end());
+}
+
+bool CUrlOptions::HasOption(const std::string &key)
+{
+ if (key.empty())
+ return false;
+
+ return m_options.find(key) != m_options.end();
+}
+
+bool CUrlOptions::GetOption(const std::string &key, CVariant &value)
+{
+ if (key.empty())
+ return false;
+
+ UrlOptions::const_iterator option = m_options.find(key);
+ if (option == m_options.end())
+ return false;
+
+ value = option->second;
+ return true;
+}
diff --git a/xbmc/utils/UrlOptions.h b/xbmc/utils/UrlOptions.h
index 47c6e511ef..b7c0fcbf29 100644
--- a/xbmc/utils/UrlOptions.h
+++ b/xbmc/utils/UrlOptions.h
@@ -29,17 +29,23 @@ public:
typedef std::map<std::string, CVariant> UrlOptions;
CUrlOptions();
+ CUrlOptions(const std::string &options);
virtual ~CUrlOptions();
virtual const UrlOptions& GetOptions() const { return m_options; }
virtual std::string GetOptionsString() const;
+ virtual void AddOption(const std::string &key, const char *value);
virtual void AddOption(const std::string &key, const std::string &value);
virtual void AddOption(const std::string &key, int value);
virtual void AddOption(const std::string &key, float value);
virtual void AddOption(const std::string &key, double value);
virtual void AddOption(const std::string &key, bool value);
virtual void AddOptions(const std::string &options);
+ virtual void AddOptions(const CUrlOptions &options);
+
+ virtual bool HasOption(const std::string &key);
+ virtual bool GetOption(const std::string &key, CVariant &value);
protected:
UrlOptions m_options;
diff --git a/xbmc/video/VideoDatabase.cpp b/xbmc/video/VideoDatabase.cpp
index fa6c4b7363..227f3f5194 100644
--- a/xbmc/video/VideoDatabase.cpp
+++ b/xbmc/video/VideoDatabase.cpp
@@ -4429,22 +4429,22 @@ void CVideoDatabase::EraseVideoSettings()
}
}
-bool CVideoDatabase::GetGenresNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
+bool CVideoDatabase::GetGenresNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
{
- return GetNavCommon(strBaseDir, items, "genre", idContent, filter);
+ return GetNavCommon(strBaseDir, items, "genre", idContent, filter, countOnly);
}
-bool CVideoDatabase::GetCountriesNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
+bool CVideoDatabase::GetCountriesNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
{
- return GetNavCommon(strBaseDir, items, "country", idContent, filter);
+ return GetNavCommon(strBaseDir, items, "country", idContent, filter, countOnly);
}
-bool CVideoDatabase::GetStudiosNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
+bool CVideoDatabase::GetStudiosNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
{
- return GetNavCommon(strBaseDir, items, "studio", idContent, filter);
+ return GetNavCommon(strBaseDir, items, "studio", idContent, filter, countOnly);
}
-bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
+bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
{
try
{
@@ -4457,19 +4457,22 @@ bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& i
{
if (idContent == VIDEODB_CONTENT_MOVIES)
{
- strSQL = PrepareSQL("select %s.id%s, %s.str%s, path.strPath, files.playCount from %s ", type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str());
+ strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
+ extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, path.strPath, files.playCount", type.c_str(), type.c_str(), type.c_str(), type.c_str());
extFilter.AppendJoin(PrepareSQL("join %slinkmovie on %s.id%s = %slinkmovie.id%s join movieview on %slinkmovie.idMovie = movieview.idMovie join files on files.idFile = movieview.idFile join path on path.idPath = files.idPath",
type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
}
else if (idContent == VIDEODB_CONTENT_TVSHOWS) //this will not get tvshows with 0 episodes
{
- strSQL = PrepareSQL("select %s.id%s, %s.str%s, path.strPath from %s ", type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str());
+ strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
+ extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, path.strPath", type.c_str(), type.c_str(), type.c_str(), type.c_str());
extFilter.AppendJoin(PrepareSQL("join %slinktvshow on %s.id%s = %slinktvshow.id%s join episodeview on %slinktvshow.idShow = episodeview.idShow join files on files.idFile = episodeview.idFile join path on path.idPath = files.idPath",
type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
}
else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
{
- strSQL = PrepareSQL("select %s.id%s, %s.str%s, path.strPath, files.playCount from %s ", type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str());
+ strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
+ extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, path.strPath, files.playCount", type.c_str(), type.c_str(), type.c_str(), type.c_str());
extFilter.AppendJoin(PrepareSQL("join %slinkmusicvideo on %s.id%s = %slinkmusicvideo.id%s join musicvideoview on %slinkmusicvideo.idMVideo = musicvideoview.idMVideo join files on files.idFile = musicvideoview.idFile join path on path.idPath = files.idPath",
type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
}
@@ -4480,20 +4483,23 @@ bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& i
{
if (idContent == VIDEODB_CONTENT_MOVIES)
{
- strSQL = PrepareSQL("select %s.id%s, %s.str%s, count(1), count(files.playCount) from %s ", type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str());
+ strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
+ extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, count(1), count(files.playCount)", type.c_str(), type.c_str(), type.c_str(), type.c_str());
extFilter.AppendJoin(PrepareSQL("join %slinkmovie on %s.id%s = %slinkmovie.id%s join movieview on %slinkmovie.idMovie = movieview.idMovie join files on files.idFile = movieview.idFile",
type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
}
else if (idContent == VIDEODB_CONTENT_TVSHOWS)
{
- strSQL = PrepareSQL("select distinct %s.id%s, %s.str%s from %s ", type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str());
+ strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
+ extFilter.fields = PrepareSQL("distinct %s.id%s, %s.str%s", type.c_str(), type.c_str(), type.c_str(), type.c_str());
extFilter.AppendJoin(PrepareSQL("join %slinktvshow on %s.id%s = %slinktvshow.id%s join tvshowview on %slinktvshow.idShow = tvshowview.idShow",
type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
}
else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
{
- strSQL = PrepareSQL("select %s.id%s, %s.str%s, count(1), count(files.playCount) from %s ", type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str());
+ strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
+ extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, count(1), count(files.playCount)", type.c_str(), type.c_str(), type.c_str(), type.c_str());
extFilter.AppendJoin(PrepareSQL("join %slinkmusicvideo on %s.id%s = %slinkmusicvideo.id%s join musicvideoview on %slinkmusicvideo.idMVideo = musicvideoview.idMVideo join files on files.idFile = musicvideoview.idFile",
type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
@@ -4502,6 +4508,14 @@ bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& i
return false;
}
+ if (countOnly)
+ {
+ extFilter.fields = PrepareSQL("COUNT(DISTINCT %s.id%s)", type.c_str(), type.c_str());
+ extFilter.group.clear();
+ extFilter.order.clear();
+ }
+ strSQL.Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
+
CVideoDbUrl videoUrl;
if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
return false;
@@ -4510,6 +4524,16 @@ bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& i
if (iRowsFound <= 0)
return iRowsFound == 0;
+ if (countOnly)
+ {
+ CFileItemPtr pItem(new CFileItem());
+ pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
+ items.Add(pItem);
+
+ m_pDS->close();
+ return true;
+ }
+
if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
{
map<int, pair<CStdString,int> > mapItems;
@@ -4591,7 +4615,7 @@ bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& i
return false;
}
-bool CVideoDatabase::GetTagsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
+bool CVideoDatabase::GetTagsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
{
CStdString mediaType;
if (idContent == VIDEODB_CONTENT_MOVIES)
@@ -4608,13 +4632,26 @@ bool CVideoDatabase::GetTagsNav(const CStdString& strBaseDir, CFileItemList& ite
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
- CStdString strSQL = "SELECT tag.idTag, tag.strTag FROM taglinks ";
+ CStdString strSQL = "SELECT %s FROM taglinks ";
Filter extFilter = filter;
+ extFilter.fields = "tag.idTag, tag.strTag";
extFilter.AppendJoin("JOIN tag ON tag.idTag = taglinks.idTag");
+
+ if (idContent == (int)VIDEODB_CONTENT_MOVIES)
+ extFilter.AppendJoin("JOIN movieview ON movieview.idMovie = taglinks.idMedia");
+
extFilter.AppendWhere(PrepareSQL("taglinks.media_type = '%s'", mediaType.c_str()));
extFilter.AppendGroup("taglinks.idTag");
+ if (countOnly)
+ {
+ extFilter.fields = "COUNT(DISTINCT taglinks.idTag)";
+ extFilter.group.clear();
+ extFilter.order.clear();
+ }
+ strSQL.Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
+
// parse the base path to get additional filters
CVideoDbUrl videoUrl;
if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
@@ -4624,6 +4661,16 @@ bool CVideoDatabase::GetTagsNav(const CStdString& strBaseDir, CFileItemList& ite
if (iRowsFound <= 0)
return iRowsFound == 0;
+ if (countOnly)
+ {
+ CFileItemPtr pItem(new CFileItem());
+ pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
+ items.Add(pItem);
+
+ m_pDS->close();
+ return true;
+ }
+
while (!m_pDS->eof())
{
int idTag = m_pDS->fv(0).get_asInt();
@@ -4791,23 +4838,23 @@ bool CVideoDatabase::GetSetsByWhere(const CStdString& strBaseDir, const Filter &
return false;
}
-bool CVideoDatabase::GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idArtist /* = -1 */, const Filter &filter /* = Filter() */)
+bool CVideoDatabase::GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idArtist /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
{
try
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
- CStdString strSQL;
+ CStdString strSQL = "select %s from musicvideoview ";
Filter extFilter = filter;
if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
{
- strSQL = PrepareSQL("select musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor, path.strPath from musicvideoview ", VIDEODB_ID_MUSICVIDEO_ALBUM);
+ extFilter.fields = PrepareSQL("musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor, path.strPath", VIDEODB_ID_MUSICVIDEO_ALBUM);
extFilter.AppendJoin("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = artistlinkmusicvideo.idArtist join files on files.idFile = musicvideoview.idFile join path on path.idPath = files.idPath");
}
else
{
- strSQL = PrepareSQL("select musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor from musicvideoview ", VIDEODB_ID_MUSICVIDEO_ALBUM);
+ extFilter.fields = PrepareSQL("musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor", VIDEODB_ID_MUSICVIDEO_ALBUM);
extFilter.AppendJoin("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = artistlinkmusicvideo.idArtist");
}
if (idArtist > -1)
@@ -4815,6 +4862,14 @@ bool CVideoDatabase::GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileI
extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM));
+ if (countOnly)
+ {
+ extFilter.fields = "COUNT(1)";
+ extFilter.group.clear();
+ extFilter.order.clear();
+ }
+ strSQL.Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
+
CVideoDbUrl videoUrl;
if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
return false;
@@ -4823,6 +4878,16 @@ bool CVideoDatabase::GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileI
if (iRowsFound <= 0)
return iRowsFound == 0;
+ if (countOnly)
+ {
+ CFileItemPtr pItem(new CFileItem());
+ pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
+ items.Add(pItem);
+
+ m_pDS->close();
+ return true;
+ }
+
if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
{
map<int, pair<CStdString,CStdString> > mapAlbums;
@@ -4900,21 +4965,21 @@ bool CVideoDatabase::GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileI
return false;
}
-bool CVideoDatabase::GetWritersNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
+bool CVideoDatabase::GetWritersNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
{
- return GetPeopleNav(strBaseDir, items, "writer", idContent, filter);
+ return GetPeopleNav(strBaseDir, items, "writer", idContent, filter, countOnly);
}
-bool CVideoDatabase::GetDirectorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
+bool CVideoDatabase::GetDirectorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
{
- return GetPeopleNav(strBaseDir, items, "director", idContent, filter);
+ return GetPeopleNav(strBaseDir, items, "director", idContent, filter, countOnly);
}
-bool CVideoDatabase::GetActorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
+bool CVideoDatabase::GetActorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
{
- if (GetPeopleNav(strBaseDir, items, (idContent == VIDEODB_CONTENT_MUSICVIDEOS) ? "artist" : "actor", idContent, filter))
+ if (GetPeopleNav(strBaseDir, items, (idContent == VIDEODB_CONTENT_MUSICVIDEOS) ? "artist" : "actor", idContent, filter, countOnly))
{ // set thumbs - ideally this should be in the normal thumb setting routines
- for (int i = 0; i < items.Size(); i++)
+ for (int i = 0; i < items.Size() && !countOnly; i++)
{
CFileItemPtr pItem = items[i];
if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
@@ -4927,7 +4992,7 @@ bool CVideoDatabase::GetActorsNav(const CStdString& strBaseDir, CFileItemList& i
return false;
}
-bool CVideoDatabase::GetPeopleNav(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
+bool CVideoDatabase::GetPeopleNav(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
@@ -4950,25 +5015,29 @@ bool CVideoDatabase::GetPeopleNav(const CStdString& strBaseDir, CFileItemList& i
{
if (idContent == VIDEODB_CONTENT_MOVIES)
{
- strSQL = PrepareSQL("select actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount from actors ");
+ strSQL = "select %s from actors ";
+ extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
extFilter.AppendJoin(PrepareSQL("join %slinkmovie on actors.idActor = %slinkmovie.id%s join movieview on %slinkmovie.idMovie = movieview.idMovie join files on files.idFile = movieview.idFile join path on path.idPath = files.idPath",
type.c_str(), type.c_str(), type.c_str(), type.c_str()));
}
else if (idContent == VIDEODB_CONTENT_TVSHOWS)
{
- strSQL = PrepareSQL("select actors.idActor, actors.strActor, actors.strThumb, path.strPath from actors ");
+ strSQL = "select %s from actors ";
+ extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath";
extFilter.AppendJoin(PrepareSQL("join %slinktvshow on actors.idActor = %slinktvshow.id%s join episodeview on %slinktvshow.idShow = episodeview.idShow join files on files.idFile = episodeview.idFile join path on path.idPath = files.idPath",
type.c_str(), type.c_str(), type.c_str(), type.c_str()));
}
else if (idContent == VIDEODB_CONTENT_EPISODES)
{
- strSQL = PrepareSQL("select actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount from actors ");
+ strSQL = "select %s from actors ";
+ extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
extFilter.AppendJoin(PrepareSQL("join %slinkepisode on actors.idActor = %slinkepisode.id%s join episodeview on %slinkepisode.idEpisode = episodeview.idEpisode join files on files.idFile = episodeview.idFile join path on path.idPath = files.idPath",
type.c_str(), type.c_str(), type.c_str(), type.c_str()));
}
else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
{
- strSQL = PrepareSQL("select actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount from actors ");
+ strSQL = "select %s from actors ";
+ extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
extFilter.AppendJoin(PrepareSQL("join %slinkmusicvideo on actors.idActor = %slinkmusicvideo.id%s join musicvideoview on %slinkmusicvideo.idMVideo = musicvideoview.idMVideo join files on files.idFile = musicvideoview.idFile join path on path.idPath = files.idPath",
type.c_str(), type.c_str(), type.c_str(), type.c_str()));
}
@@ -4979,27 +5048,31 @@ bool CVideoDatabase::GetPeopleNav(const CStdString& strBaseDir, CFileItemList& i
{
if (idContent == VIDEODB_CONTENT_MOVIES)
{
- strSQL = PrepareSQL("select actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount) from actors ");
+ strSQL ="select %s from actors ";
+ extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
extFilter.AppendJoin(PrepareSQL("join %slinkmovie on actors.idActor = %slinkmovie.id%s join movieview on %slinkmovie.idMovie = movieview.idMovie join files on files.idFile = movieview.idFile",
type.c_str(), type.c_str(), type.c_str(), type.c_str()));
extFilter.AppendGroup("actors.idActor");
}
else if (idContent == VIDEODB_CONTENT_TVSHOWS)
{
- strSQL = PrepareSQL("select distinct actors.idActor, actors.strActor, actors.strThumb from actors, %slinktvshow, tvshowview ", type.c_str());
+ strSQL = "select %s " + PrepareSQL("from actors, %slinktvshow, tvshowview ", type.c_str());
+ extFilter.fields = "distinct actors.idActor, actors.strActor, actors.strThumb";
extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinktvshow.id%s and %slinktvshow.idShow = tvshowview.idShow",
type.c_str(), type.c_str(), type.c_str()));
}
else if (idContent == VIDEODB_CONTENT_EPISODES)
{
- strSQL = PrepareSQL("select actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount) from %slinkepisode, actors, episodeview, files ", type.c_str());
+ strSQL = "select %s " + PrepareSQL("%slinkepisode, actors, episodeview, files ", type.c_str());
+ extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinkepisode.id%s and %slinkepisode.idEpisode = episodeview.idEpisode and files.idFile = episodeview.idFile",
type.c_str(), type.c_str(), type.c_str()));
extFilter.AppendGroup("actors.idActor");
}
else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
{
- strSQL = PrepareSQL("select actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount) from actors ");
+ strSQL = "select %s from actors ";
+ extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
extFilter.AppendJoin(PrepareSQL("join %slinkmusicvideo on actors.idActor = %slinkmusicvideo.id%s join musicvideoview on %slinkmusicvideo.idMVideo = musicvideoview.idMVideo join files on files.idFile = musicvideoview.idFile",
type.c_str(), type.c_str(), type.c_str(), type.c_str()));
extFilter.AppendGroup("actors.idActor");
@@ -5008,6 +5081,14 @@ bool CVideoDatabase::GetPeopleNav(const CStdString& strBaseDir, CFileItemList& i
return false;
}
+ if (countOnly)
+ {
+ extFilter.fields = "COUNT(1)";
+ extFilter.group.clear();
+ extFilter.order.clear();
+ }
+ strSQL.Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
+
CVideoDbUrl videoUrl;
if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
return false;
@@ -5024,6 +5105,16 @@ bool CVideoDatabase::GetPeopleNav(const CStdString& strBaseDir, CFileItemList& i
return true;
}
+ if (countOnly)
+ {
+ CFileItemPtr pItem(new CFileItem());
+ pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
+ items.Add(pItem);
+
+ m_pDS->close();
+ return true;
+ }
+
if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
{
map<int, CActor> mapActors;
@@ -5477,6 +5568,7 @@ bool CVideoDatabase::GetSeasonsNav(const CStdString& strBaseDir, CFileItemList&
}
m_pDS->close();
}
+ items.SetPath(videoUrl.ToString());
// now add any linked movies
Filter movieFilter;
@@ -5586,6 +5678,12 @@ bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter
if (!videoUrl.FromString(strBaseDir) || !GetFilter(videoUrl, extFilter))
return false;
+ // if we have a "setid" option we don't want to retrieve sets
+ CVariant setId;
+ if (fetchSets && videoUrl.GetOption("setid", setId) &&
+ setId.isInteger() && setId.asInteger() > 0)
+ fetchSets = false;
+
int total = -1;
CStdString strSQL = "select %s from movieview ";
@@ -5694,6 +5792,7 @@ bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter
items.Add(pItem);
}
}
+ items.SetPath(videoUrl.ToString());
// cleanup
m_pDS->close();
@@ -5809,6 +5908,7 @@ bool CVideoDatabase::GetTvShowsByWhere(const CStdString& strBaseDir, const Filte
}
Stack(items, VIDEODB_CONTENT_TVSHOWS, !filter.order.empty() || sortDescription.sortBy != SortByNone);
+ items.SetPath(videoUrl.ToString());
// cleanup
m_pDS->close();
@@ -6106,7 +6206,7 @@ bool CVideoDatabase::GetEpisodesByWhere(const CStdString& strBaseDir, const Filt
CVideoDbUrl itemUrl = videoUrl;
CStdString path;
- if (appendFullShowPath)
+ if (appendFullShowPath && videoUrl.GetItemType() != "episodes")
path.Format("%ld/%ld/%ld", record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt(), movie.m_iSeason, idEpisode);
else
path.Format("%ld", idEpisode);
@@ -6119,6 +6219,7 @@ bool CVideoDatabase::GetEpisodesByWhere(const CStdString& strBaseDir, const Filt
items.Add(pItem);
}
}
+ items.SetPath(videoUrl.ToString());
// cleanup
m_pDS->close();
@@ -6959,6 +7060,7 @@ bool CVideoDatabase::GetMusicVideosByWhere(const CStdString &baseDir, const Filt
items.Add(item);
}
}
+ items.SetPath(videoUrl.ToString());
// cleanup
m_pDS->close();
@@ -8879,7 +8981,7 @@ bool CVideoDatabase::GetItemsForPath(const CStdString &content, const CStdString
return items.Size() > 0;
}
-bool CVideoDatabase::GetFilter(const CDbUrl &videoUrl, Filter &filter)
+bool CVideoDatabase::GetFilter(CDbUrl &videoUrl, Filter &filter)
{
if (!videoUrl.IsValid())
return false;
@@ -9297,5 +9399,23 @@ bool CVideoDatabase::GetFilter(const CDbUrl &videoUrl, Filter &filter)
}
}
+ option = options.find("filter");
+ if (option != options.end())
+ {
+ CSmartPlaylist xspFilter;
+ if (!xspFilter.LoadFromJson(option->second.asString()))
+ return false;
+
+ // check if the filter playlist matches the item type
+ if (xspFilter.GetType() == itemType)
+ {
+ std::set<CStdString> playlists;
+ filter.AppendWhere(xspFilter.GetWhereClause(*this, playlists));
+ }
+ // remove the filter if it doesn't match the item type
+ else
+ videoUrl.AddOption("filter", "");
+ }
+
return true;
}
diff --git a/xbmc/video/VideoDatabase.h b/xbmc/video/VideoDatabase.h
index 616403a4b1..3dc45c1f6d 100644
--- a/xbmc/video/VideoDatabase.h
+++ b/xbmc/video/VideoDatabase.h
@@ -578,16 +578,16 @@ public:
bool ArbitraryExec(const CStdString& strExec);
// general browsing
- bool GetGenresNav(const CStdString& strBaseDir, CFileItemList& items, int idContent=-1, const Filter &filter = Filter());
- bool GetCountriesNav(const CStdString& strBaseDir, CFileItemList& items, int idContent=-1, const Filter &filter = Filter());
- bool GetStudiosNav(const CStdString& strBaseDir, CFileItemList& items, int idContent=-1, const Filter &filter = Filter());
+ bool GetGenresNav(const CStdString& strBaseDir, CFileItemList& items, int idContent=-1, const Filter &filter = Filter(), bool countOnly = false);
+ bool GetCountriesNav(const CStdString& strBaseDir, CFileItemList& items, int idContent=-1, const Filter &filter = Filter(), bool countOnly = false);
+ bool GetStudiosNav(const CStdString& strBaseDir, CFileItemList& items, int idContent=-1, const Filter &filter = Filter(), bool countOnly = false);
bool GetYearsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent=-1, const Filter &filter = Filter());
- bool GetActorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent=-1, const Filter &filter = Filter());
- bool GetDirectorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent=-1, const Filter &filter = Filter());
- bool GetWritersNav(const CStdString& strBaseDir, CFileItemList& items, int idContent=-1, const Filter &filter = Filter());
+ bool GetActorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent=-1, const Filter &filter = Filter(), bool countOnly = false);
+ bool GetDirectorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent=-1, const Filter &filter = Filter(), bool countOnly = false);
+ bool GetWritersNav(const CStdString& strBaseDir, CFileItemList& items, int idContent=-1, const Filter &filter = Filter(), bool countOnly = false);
bool GetSetsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent=-1);
- bool GetTagsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent=-1, const Filter &filter = Filter());
- bool GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idArtist, const Filter &filter = Filter());
+ bool GetTagsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent=-1, const Filter &filter = Filter(), bool countOnly = false);
+ bool GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idArtist, const Filter &filter = Filter(), bool countOnly = false);
bool GetMoviesNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre=-1, int idYear=-1, int idActor=-1, int idDirector=-1, int idStudio=-1, int idCountry=-1, int idSet=-1, int idTag=-1, const SortDescription &sortDescription = SortDescription());
bool GetTvShowsNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre=-1, int idYear=-1, int idActor=-1, int idDirector=-1, int idStudio=-1, int idTag=-1, const SortDescription &sortDescription = SortDescription());
@@ -687,7 +687,7 @@ public:
void AddTagToItem(int idItem, int idTag, const std::string &type);
void RemoveTagFromItem(int idItem, int idTag, const std::string &type);
- virtual bool GetFilter(const CDbUrl &videoUrl, Filter &filter);
+ virtual bool GetFilter(CDbUrl &videoUrl, Filter &filter);
protected:
int GetMovieId(const CStdString& strFilenameAndPath);
@@ -756,8 +756,8 @@ protected:
CVideoInfoTag GetDetailsForEpisode(const dbiplus::sql_record* const record, bool needsCast = false);
CVideoInfoTag GetDetailsForMusicVideo(std::auto_ptr<dbiplus::Dataset> &pDS);
CVideoInfoTag GetDetailsForMusicVideo(const dbiplus::sql_record* const record);
- bool GetPeopleNav(const CStdString& strBaseDir, CFileItemList& items, const CStdString& type, int idContent=-1, const Filter &filter = Filter());
- bool GetNavCommon(const CStdString& strBaseDir, CFileItemList& items, const CStdString& type, int idContent=-1, const Filter &filter = Filter());
+ bool GetPeopleNav(const CStdString& strBaseDir, CFileItemList& items, const CStdString& type, int idContent=-1, const Filter &filter = Filter(), bool countOnly = false);
+ bool GetNavCommon(const CStdString& strBaseDir, CFileItemList& items, const CStdString& type, int idContent=-1, const Filter &filter = Filter(), bool countOnly = false);
void GetCast(const CStdString &table, const CStdString &table_id, int type_id, std::vector<SActorInfo> &cast);
void GetDetailsFromDB(std::auto_ptr<dbiplus::Dataset> &pDS, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset = 2);
diff --git a/xbmc/video/windows/GUIWindowVideoBase.cpp b/xbmc/video/windows/GUIWindowVideoBase.cpp
index e0a8350f76..92b77618ad 100644
--- a/xbmc/video/windows/GUIWindowVideoBase.cpp
+++ b/xbmc/video/windows/GUIWindowVideoBase.cpp
@@ -1783,7 +1783,34 @@ bool CGUIWindowVideoBase::Update(const CStdString &strDirectory)
bool CGUIWindowVideoBase::GetDirectory(const CStdString &strDirectory, CFileItemList &items)
{
- bool bResult = CGUIMediaWindow::GetDirectory(strDirectory,items);
+ CStdString directory = strDirectory;
+
+ // check if the path contains a filter and if so load it and
+ // remove it from the path to get proper GUI view states etc
+ CSmartPlaylist filterXsp;
+ CVideoDbUrl videoUrl;
+ if (videoUrl.FromString(strDirectory))
+ {
+ CVariant filter;
+ if (videoUrl.GetOption("filter", filter))
+ {
+ // load the filter and if it's type does not match the
+ // path's item type reset it
+ if (filterXsp.LoadFromJson(filter.asString()) && !filterXsp.GetType().Equals(videoUrl.GetItemType().c_str()))
+ filterXsp.Reset();
+
+ // remove the "filter" option from the path
+ videoUrl.AddOption("filter", "");
+ }
+ directory = videoUrl.ToString();
+ }
+
+ bool bResult = CGUIMediaWindow::GetDirectory(directory, items);
+
+ // (re-)apply the previously retrieved filter
+ // because it was reset in CGUIMediaWindow::GetDirectory()
+ if (!filterXsp.IsEmpty())
+ m_filter = filterXsp;
// add in the "New Playlist" item if we're in the playlists folder
if ((items.GetPath() == "special://videoplaylists/") && !items.Contains("newplaylist://"))
@@ -1809,7 +1836,7 @@ bool CGUIWindowVideoBase::GetDirectory(const CStdString &strDirectory, CFileItem
// we may also be in a tvshow files listing
// (ideally this should be removed, and our stack regexps tidied up if necessary
// No "normal" episodes should stack, and multi-parts should be supported)
- ADDON::ScraperPtr info = m_database.GetScraperForPath(strDirectory);
+ ADDON::ScraperPtr info = m_database.GetScraperForPath(directory);
if (info && info->Content() == CONTENT_TVSHOWS)
m_stackingAvailable = false;
@@ -1830,6 +1857,15 @@ void CGUIWindowVideoBase::OnPrepareFileItems(CFileItemList &items)
{
}
+bool CGUIWindowVideoBase::CheckFilterAdvanced(CFileItemList &items)
+{
+ CStdString content = items.GetContent();
+ if (items.IsVideoDb() && (content.Equals("movies") || content.Equals("tvshows") || content.Equals("episodes") || content.Equals("musicvideos")))
+ return true;
+
+ return false;
+}
+
void CGUIWindowVideoBase::AddToDatabase(int iItem)
{
if (iItem < 0 || iItem >= m_vecItems->Size())
diff --git a/xbmc/video/windows/GUIWindowVideoBase.h b/xbmc/video/windows/GUIWindowVideoBase.h
index 9deadafe12..78d94b5822 100644
--- a/xbmc/video/windows/GUIWindowVideoBase.h
+++ b/xbmc/video/windows/GUIWindowVideoBase.h
@@ -89,6 +89,8 @@ protected:
virtual void OnItemLoaded(CFileItem* pItem) {};
virtual void OnPrepareFileItems(CFileItemList &items);
+ virtual bool CheckFilterAdvanced(CFileItemList &items);
+
virtual void GetContextButtons(int itemNumber, CContextButtons &buttons);
void GetNonContextButtons(int itemNumber, CContextButtons &buttons);
virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button);
diff --git a/xbmc/video/windows/GUIWindowVideoNav.cpp b/xbmc/video/windows/GUIWindowVideoNav.cpp
index c28348d064..eac61e05a3 100644
--- a/xbmc/video/windows/GUIWindowVideoNav.cpp
+++ b/xbmc/video/windows/GUIWindowVideoNav.cpp
@@ -527,8 +527,24 @@ void CGUIWindowVideoNav::UpdateButtons()
bool CGUIWindowVideoNav::GetFilteredItems(const CStdString &filter, CFileItemList &items)
{
- bool listchanged = CGUIMediaWindow::GetFilteredItems(filter, items);
+ bool listchanged = false;
+ bool updateItems = false;
+ if (!m_canFilterAdvanced)
+ listchanged = CGUIMediaWindow::GetFilteredItems(filter, items);
+ else
+ listchanged = CGUIMediaWindow::GetAdvanceFilteredItems(items, updateItems);
+
listchanged |= ApplyWatchedFilter(items);
+
+ // there are new items so we need to run the thumbloader
+ if (updateItems)
+ {
+ if (m_thumbLoader.IsLoading())
+ m_thumbLoader.StopThread();
+
+ m_thumbLoader.Load(items);
+ }
+
return listchanged;
}
diff --git a/xbmc/windows/GUIMediaWindow.cpp b/xbmc/windows/GUIMediaWindow.cpp
index 6cbe81c22b..25f791a8c3 100644
--- a/xbmc/windows/GUIMediaWindow.cpp
+++ b/xbmc/windows/GUIMediaWindow.cpp
@@ -64,16 +64,18 @@
#include "interfaces/python/XBPython.h"
#endif
#include "interfaces/Builtins.h"
+#include "dialogs/GUIDialogMediaFilter.h"
+#include "filesystem/SmartPlaylistDirectory.h"
#if defined(TARGET_ANDROID)
#include "xbmc/android/activity/XBMCApp.h"
#endif
-#define CONTROL_BTNVIEWASICONS 2
-#define CONTROL_BTNSORTBY 3
-#define CONTROL_BTNSORTASC 4
-#define CONTROL_BTN_FILTER 19
+#define CONTROL_BTNVIEWASICONS 2
+#define CONTROL_BTNSORTBY 3
+#define CONTROL_BTNSORTASC 4
+#define CONTROL_BTN_FILTER 19
-#define CONTROL_LABELFILES 12
+#define CONTROL_LABELFILES 12
using namespace std;
using namespace ADDON;
@@ -87,6 +89,7 @@ CGUIMediaWindow::CGUIMediaWindow(int id, const char *xmlFile)
m_vecItems->SetPath("?");
m_iLastControl = -1;
m_iSelectedItem = -1;
+ m_canFilterAdvanced = false;
m_guiState.reset(CGUIViewState::GetViewState(GetID(), *m_vecItems));
}
@@ -175,6 +178,9 @@ bool CGUIMediaWindow::OnAction(const CAction &action)
if (CGUIWindow::OnAction(action))
return true;
+ if (action.GetID() == ACTION_FILTER)
+ return Filter();
+
// live filtering
if (action.GetID() == ACTION_FILTER_CLEAR)
{
@@ -226,6 +232,13 @@ bool CGUIMediaWindow::OnMessage(CGUIMessage& message)
CGUIDialogContextMenu* pDlg = (CGUIDialogContextMenu*)g_windowManager.GetWindow(WINDOW_DIALOG_CONTEXT_MENU);
if (pDlg && pDlg->IsActive())
pDlg->Close();
+
+ // get rid of any active filtering
+ if (m_canFilterAdvanced)
+ {
+ m_canFilterAdvanced = false;
+ m_filter.Reset();
+ }
// Call ClearFileItems() after our window has finished doing any WindowClose
// animations
@@ -273,22 +286,10 @@ bool CGUIMediaWindow::OnMessage(CGUIMessage& message)
}
else if (iControl == CONTROL_BTN_FILTER)
{
- if (GetControl(iControl)->GetControlType() == CGUIControl::GUICONTROL_EDIT)
- { // filter updated
- CGUIMessage selected(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTN_FILTER);
- OnMessage(selected);
- OnFilterItems(selected.GetLabel());
+ if (m_canFilterAdvanced)
return true;
- }
- if (GetProperty("filter").empty())
- {
- CStdString filter = GetProperty("filter").asString();
- CGUIKeyboardFactory::ShowAndGetFilter(filter, false);
- SetProperty("filter", filter);
- }
- else
- OnFilterItems("");
- return true;
+
+ return Filter();
}
else if (m_viewControl.HasControl(iControl)) // list/thumb control
{
@@ -415,16 +416,21 @@ bool CGUIMediaWindow::OnMessage(CGUIMessage& message)
}
else if (message.GetParam1() == GUI_MSG_FILTER_ITEMS && IsActive())
{
- CStdString filter(GetProperty("filter").asString());
- if (message.GetParam2() == 1) // append
- filter += message.GetStringParam();
- else if (message.GetParam2() == 2)
- { // delete
- if (filter.size())
- filter = filter.Left(filter.size() - 1);
+ CStdString filter;
+ // check if this is meant for advanced filtering
+ if (message.GetParam2() != 10)
+ {
+ filter = GetProperty("filter").asString();
+ if (message.GetParam2() == 1) // append
+ filter += message.GetStringParam();
+ else if (message.GetParam2() == 2)
+ { // delete
+ if (filter.size())
+ filter = filter.Left(filter.size() - 1);
+ }
+ else
+ filter = message.GetStringParam();
}
- else
- filter = message.GetStringParam();
OnFilterItems(filter);
return true;
}
@@ -561,10 +567,8 @@ void CGUIMediaWindow::UpdateButtons()
items.Format("%i %s", m_vecItems->GetObjectCount(), g_localizeStrings.Get(127).c_str());
SET_CONTROL_LABEL(CONTROL_LABELFILES, items);
- //#ifdef PRE_SKIN_VERSION_3
- SET_CONTROL_SELECTED(GetID(),CONTROL_BTN_FILTER, !GetProperty("filter").empty());
- SET_CONTROL_LABEL2(CONTROL_BTN_FILTER, GetProperty("filter").asString());
- //#endif
+ if (!m_canFilterAdvanced)
+ SET_CONTROL_LABEL2(CONTROL_BTN_FILTER, GetProperty("filter").asString());
}
void CGUIMediaWindow::ClearFileItems()
@@ -637,7 +641,7 @@ bool CGUIMediaWindow::GetDirectory(const CStdString &strDirectory, CFileItemList
if (items.Size())
items.Clear();
- CStdString strParentPath=m_history.GetParentPath();
+ CStdString strParentPath = m_history.GetParentPath();
CLog::Log(LOGDEBUG,"CGUIMediaWindow::GetDirectory (%s)", strDirectory.c_str());
CLog::Log(LOGDEBUG," ParentPath = [%s]", strParentPath.c_str());
@@ -700,6 +704,8 @@ bool CGUIMediaWindow::GetDirectory(const CStdString &strDirectory, CFileItemList
// clear the filter
SetProperty("filter", "");
+ m_canFilterAdvanced = false;
+ m_filter.Reset();
return true;
}
@@ -723,10 +729,10 @@ bool CGUIMediaWindow::Update(const CStdString &strDirectory)
GetDirectoryHistoryString(pItem.get(), strSelectedItem);
}
}
+
+ CStdString strCurrentDirectory = m_vecItems->GetPath();
- CStdString strOldDirectory = m_vecItems->GetPath();
-
- m_history.SetSelectedItem(strSelectedItem, strOldDirectory);
+ m_history.SetSelectedItem(strSelectedItem, strCurrentDirectory);
CFileItemList items;
if (!GetDirectory(strDirectory, items))
@@ -734,7 +740,7 @@ bool CGUIMediaWindow::Update(const CStdString &strDirectory)
CLog::Log(LOGERROR,"CGUIMediaWindow::GetDirectory(%s) failed", strDirectory.c_str());
// if the directory is the same as the old directory, then we'll return
// false. Else, we assume we can get the previous directory
- if (strDirectory.Equals(strOldDirectory))
+ if (strDirectory.Equals(strCurrentDirectory))
return false;
// We assume, we can get the parent
@@ -781,6 +787,11 @@ bool CGUIMediaWindow::Update(const CStdString &strDirectory)
}
m_iLastControl = GetFocusedControlID();
+ // Check whether to enabled advanced filtering based on the content type
+ m_canFilterAdvanced = CheckFilterAdvanced(*m_vecItems);
+ if (m_canFilterAdvanced)
+ m_filter.SetType(m_vecItems->GetContent());
+
// Ask the derived class if it wants to load additional info
// for the fileitems like media info or additional
// filtering on the items, setting thumbs.
@@ -943,6 +954,13 @@ bool CGUIMediaWindow::OnClick(int iItem)
if (!items.AlwaysCache())
items.RemoveDiscCache(GetID());
+ // if we have a filtered list, we need to add the filtered
+ // path to be able to come back to the filtered view
+ CStdString strCurrentDirectory = m_vecItems->GetPath();
+ if (m_canFilterAdvanced && !m_filter.IsEmpty() &&
+ !m_unfilteredItems->GetPath().Equals(strCurrentDirectory))
+ m_history.AddPath(strCurrentDirectory);
+
CFileItem directory(*pItem);
if (!Update(directory.GetPath()))
ShowShareErrorMessage(&directory);
@@ -1095,7 +1113,6 @@ void CGUIMediaWindow::GoParentFolder()
// if vector is not empty, pop parent
// if vector is empty, parent is root source listing
- CStdString strOldPath(m_vecItems->GetPath());
strParent = m_history.RemoveParentPath();
Update(strParent);
}
@@ -1543,7 +1560,34 @@ void CGUIMediaWindow::OnFilterItems(const CStdString &filter)
{
m_vecItems->ClearItems();
m_vecItems->Append(items);
- SetProperty("filter", filter);
+
+ if (!m_canFilterAdvanced)
+ SetProperty("filter", filter);
+ else
+ {
+ m_vecItems->SetPath(items.GetPath());
+
+ // to be able to select the same item as before we need to adjust
+ // the path of the item i.e. add or remove the "filter=" URL option
+ CURL curUrl(currentItem), newUrl(items.GetPath());
+ CUrlOptions curOptions(curUrl.GetOptions()), newOptions(newUrl.GetOptions());
+
+ if (newOptions.HasOption("filter"))
+ {
+ CVariant filter;
+ if (newOptions.GetOption("filter", filter) && filter.isString())
+ curOptions.AddOption("filter", filter.asString());
+ }
+ else if (curOptions.HasOption("filter"))
+ curOptions.AddOption("filter", "");
+
+ string options = curOptions.GetOptionsString();
+ if (!options.empty())
+ curUrl.SetOptions("?" + options);
+ else
+ curUrl.SetOptions("");
+ currentItem = curUrl.Get();
+ }
}
// and update our view control + buttons
@@ -1554,6 +1598,12 @@ void CGUIMediaWindow::OnFilterItems(const CStdString &filter)
bool CGUIMediaWindow::GetFilteredItems(const CStdString &filter, CFileItemList &items)
{
+ if (m_canFilterAdvanced)
+ {
+ bool hasNewItems;
+ return GetAdvanceFilteredItems(items, hasNewItems);
+ }
+
CStdString trimmedFilter(filter);
trimmedFilter.TrimLeft().ToLower();
@@ -1593,7 +1643,112 @@ bool CGUIMediaWindow::GetFilteredItems(const CStdString &filter, CFileItemList &
items.ClearItems();
items.Append(filteredItems);
- return (items.GetObjectCount() > 0);
+
+ return items.GetObjectCount() > 0;
+}
+
+bool CGUIMediaWindow::GetAdvanceFilteredItems(CFileItemList &items, bool &hasNewItems)
+{
+ hasNewItems = false;
+
+ CFileItemList resultItems;
+ XFILE::CSmartPlaylistDirectory::GetDirectory(m_filter, resultItems, m_vecItems->GetPath(), true);
+
+ // put together a lookup map for faster path comparison
+ map<CStdString, CFileItemPtr> lookup;
+ for (int j = 0; j < resultItems.Size(); j++)
+ {
+ CStdString itemPath = resultItems[j]->GetPath();
+ size_t pos = itemPath.find('?');
+ if (pos != string::npos)
+ itemPath.erase(pos);
+ itemPath.ToLower();
+
+ lookup[itemPath] = resultItems[j];
+ }
+
+ // loop through all the original items and find
+ // those which are still part of the filter
+ CFileItemList filteredItems;
+ for (int i = 0; i < items.Size(); i++)
+ {
+ CFileItemPtr item = items.Get(i);
+ if (item->IsParentFolder())
+ {
+ filteredItems.Add(item);
+ continue;
+ }
+
+ // check if the item is part of the resultItems list
+ // by comparing their paths (but ignoring any special
+ // options because they differ from filter to filter)
+ CStdString path = item->GetPath();
+ size_t pos = path.find('?');
+ if (pos != string::npos)
+ path.erase(pos);
+ path.ToLower();
+
+ map<CStdString, CFileItemPtr>::iterator itItem = lookup.find(path);
+ if (itItem != lookup.end())
+ {
+ // we need to copy the path of the filtered
+ // item to be able to keep the applied filters
+ item->SetPath(itItem->second->GetPath());
+
+ // add the item to the list of filtered items
+ filteredItems.Add(item);
+
+ // remove the item from the lists
+ resultItems.Remove(itItem->second.get());
+ lookup.erase(itItem);
+ }
+ }
+
+ if (resultItems.Size() > 0)
+ {
+ filteredItems.Append(resultItems);
+ hasNewItems = true;
+ }
+
+ items.ClearItems();
+ items.Append(filteredItems);
+ items.SetPath(resultItems.GetPath());
+ return true;
+}
+
+bool CGUIMediaWindow::IsFiltered()
+{
+ return (!m_canFilterAdvanced && !GetProperty("filter").empty()) ||
+ (m_canFilterAdvanced && !m_filter.IsEmpty());
+}
+
+bool CGUIMediaWindow::Filter()
+{
+ // basic filtering
+ if (!m_canFilterAdvanced)
+ {
+ const CGUIControl *btnFilter = GetControl(CONTROL_BTN_FILTER);
+ if (btnFilter != NULL && btnFilter->GetControlType() == CGUIControl::GUICONTROL_EDIT)
+ { // filter updated
+ CGUIMessage selected(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTN_FILTER);
+ OnMessage(selected);
+ OnFilterItems(selected.GetLabel());
+ return true;
+ }
+ if (GetProperty("filter").empty())
+ {
+ CStdString filter = GetProperty("filter").asString();
+ CGUIKeyboardFactory::ShowAndGetFilter(filter, false);
+ SetProperty("filter", filter);
+ }
+ else
+ OnFilterItems("");
+ }
+ // advanced filtering
+ else
+ CGUIDialogMediaFilter::ShowAndEditMediaFilter(m_vecItems->GetPath(), m_filter);
+
+ return true;
}
CStdString CGUIMediaWindow::GetStartFolder(const CStdString &dir)
diff --git a/xbmc/windows/GUIMediaWindow.h b/xbmc/windows/GUIMediaWindow.h
index 4421aff442..8dc7521e0a 100644
--- a/xbmc/windows/GUIMediaWindow.h
+++ b/xbmc/windows/GUIMediaWindow.h
@@ -25,6 +25,7 @@
#include "filesystem/DirectoryHistory.h"
#include "GUIViewControl.h"
#include "dialogs/GUIDialogContextMenu.h"
+#include "playlists/SmartPlayList.h"
class CFileItemList;
@@ -47,6 +48,9 @@ public:
virtual CFileItemPtr GetCurrentListItem(int offset = 0);
const CGUIViewState *GetViewState() const;
+ virtual bool CanFilterAdvanced() { return m_canFilterAdvanced; }
+ virtual bool IsFiltered();
+
protected:
virtual void LoadAdditionalTags(TiXmlElement *root);
CGUIControl *GetFirstFocusableControl(int id);
@@ -73,6 +77,9 @@ protected:
void ClearFileItems();
virtual void SortItems(CFileItemList &items);
+ virtual bool CheckFilterAdvanced(CFileItemList &items) { return false; }
+ virtual bool Filter();
+
/* \brief Called on response to a GUI_MSG_FILTER_ITEMS message
Filters the current list with the given filter using FilterItems()
\param filter the filter to use.
@@ -87,6 +94,14 @@ protected:
*/
virtual bool GetFilteredItems(const CStdString &filter, CFileItemList &items);
+ /* \brief Retrieve the advance filtered item list
+ \param items CFileItemList to filter
+ \param hasNewItems Whether the filtered item list contains new items
+ which were not present in the original list
+ \sa GetFilteredItems
+ */
+ virtual bool GetAdvanceFilteredItems(CFileItemList &items, bool &hasNewItems);
+
// check for a disc or connection
virtual bool HaveDiscOrConnection(const CStdString& strPath, int iDriveType);
void ShowShareErrorMessage(CFileItem* pItem);
@@ -121,4 +136,7 @@ protected:
int m_iLastControl;
int m_iSelectedItem;
CStdString m_startDirectory;
+
+ CSmartPlaylist m_filter;
+ bool m_canFilterAdvanced;
};