2 * Copyright (C) 2012-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "GUIDialogMediaFilter.h"
13 #include "FileItemList.h"
14 #include "GUIUserMessages.h"
15 #include "ServiceBroker.h"
16 #include "XBDateTime.h"
17 #include "guilib/GUIComponent.h"
18 #include "guilib/GUIWindowManager.h"
19 #include "guilib/LocalizeStrings.h"
20 #include "music/MusicDatabase.h"
21 #include "music/MusicDbUrl.h"
22 #include "playlists/SmartPlayList.h"
23 #include "settings/SettingUtils.h"
24 #include "settings/lib/Setting.h"
25 #include "settings/lib/SettingDefinitions.h"
26 #include "settings/windows/GUIControlSettings.h"
27 #include "utils/SortUtils.h"
28 #include "utils/StringUtils.h"
29 #include "utils/Variant.h"
30 #include "utils/log.h"
31 #include "video/VideoDatabase.h"
32 #include "video/VideoDbUrl.h"
36 #define CONTROL_HEADING 2
38 #define CONTROL_OKAY_BUTTON 28
39 #define CONTROL_CANCEL_BUTTON 29
40 #define CONTROL_CLEAR_BUTTON 30
45 #define CHECK_LABEL_ALL 593
46 #define CHECK_LABEL_NO 106
47 #define CHECK_LABEL_YES 107
49 static const CGUIDialogMediaFilter::Filter filterList
[] = {
50 { "movies", FieldTitle
, 556, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
51 { "movies", FieldRating
, 563, SettingType::Number
, "range", "number", CDatabaseQueryRule::OPERATOR_BETWEEN
},
52 { "movies", FieldUserRating
, 38018, SettingType::Integer
, "range", "integer", CDatabaseQueryRule::OPERATOR_BETWEEN
},
53 //{ "movies", FieldTime, 180, SettingType::Integer, "range", "time", CDatabaseQueryRule::OPERATOR_BETWEEN },
54 { "movies", FieldInProgress
, 575, SettingType::Integer
, "toggle", "", CDatabaseQueryRule::OPERATOR_FALSE
},
55 { "movies", FieldYear
, 562, SettingType::Integer
, "range", "integer", CDatabaseQueryRule::OPERATOR_BETWEEN
},
56 { "movies", FieldTag
, 20459, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
57 { "movies", FieldGenre
, 515, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
58 { "movies", FieldActor
, 20337, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
59 { "movies", FieldDirector
, 20339, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
60 { "movies", FieldStudio
, 572, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
62 { "tvshows", FieldTitle
, 556, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
63 //{ "tvshows", FieldTvShowStatus, 126, SettingType::List, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS },
64 { "tvshows", FieldRating
, 563, SettingType::Number
, "range", "number", CDatabaseQueryRule::OPERATOR_BETWEEN
},
65 { "tvshows", FieldUserRating
, 38018, SettingType::Integer
, "range", "integer", CDatabaseQueryRule::OPERATOR_BETWEEN
},
66 { "tvshows", FieldInProgress
, 575, SettingType::Integer
, "toggle", "", CDatabaseQueryRule::OPERATOR_FALSE
},
67 { "tvshows", FieldYear
, 562, SettingType::Integer
, "range", "integer", CDatabaseQueryRule::OPERATOR_BETWEEN
},
68 { "tvshows", FieldTag
, 20459, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
69 { "tvshows", FieldGenre
, 515, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
70 { "tvshows", FieldActor
, 20337, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
71 { "tvshows", FieldDirector
, 20339, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
72 { "tvshows", FieldStudio
, 572, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
74 { "episodes", FieldTitle
, 556, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
75 { "episodes", FieldRating
, 563, SettingType::Number
, "range", "number", CDatabaseQueryRule::OPERATOR_BETWEEN
},
76 { "episodes", FieldUserRating
, 38018, SettingType::Integer
, "range", "integer", CDatabaseQueryRule::OPERATOR_BETWEEN
},
77 { "episodes", FieldAirDate
, 20416, SettingType::Integer
, "range", "date", CDatabaseQueryRule::OPERATOR_BETWEEN
},
78 { "episodes", FieldInProgress
, 575, SettingType::Integer
, "toggle", "", CDatabaseQueryRule::OPERATOR_FALSE
},
79 { "episodes", FieldActor
, 20337, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
80 { "episodes", FieldDirector
, 20339, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
82 { "musicvideos", FieldTitle
, 556, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
83 { "musicvideos", FieldRating
, 563, SettingType::Number
, "range", "number", CDatabaseQueryRule::OPERATOR_BETWEEN
},
84 { "musicvideos", FieldUserRating
, 38018, SettingType::Integer
, "range", "integer", CDatabaseQueryRule::OPERATOR_BETWEEN
},
85 { "musicvideos", FieldArtist
, 557, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
86 { "musicvideos", FieldAlbum
, 558, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
87 //{ "musicvideos", FieldTime, 180, SettingType::Integer, "range", "time", CDatabaseQueryRule::OPERATOR_BETWEEN },
88 { "musicvideos", FieldYear
, 562, SettingType::Integer
, "range", "integer", CDatabaseQueryRule::OPERATOR_BETWEEN
},
89 { "musicvideos", FieldTag
, 20459, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
90 { "musicvideos", FieldGenre
, 515, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
91 { "musicvideos", FieldDirector
, 20339, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
92 { "musicvideos", FieldStudio
, 572, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
94 { "artists", FieldArtist
, 557, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
95 { "artists", FieldSource
, 39030, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
96 { "artists", FieldGenre
, 515, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
97 { "artists", FieldMoods
, 175, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
98 { "artists", FieldStyles
, 176, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
99 { "artists", FieldInstruments
, 21892, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
100 { "artists", FieldArtistType
, 564, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
101 { "artists", FieldGender
, 39025, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
102 { "artists", FieldDisambiguation
, 39026, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
103 { "artists", FieldBiography
, 21887, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
104 { "artists", FieldBorn
, 21893, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
105 { "artists", FieldBandFormed
, 21894, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
106 { "artists", FieldDisbanded
, 21896, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
107 { "artists", FieldDied
, 21897, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
109 { "albums", FieldAlbum
, 556, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
110 // { "albums", FieldArtist, 557, SettingType::List, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS },
111 { "albums", FieldDiscTitle
, 38076, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
112 { "albums", FieldAlbumArtist
, 566, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
113 { "albums", FieldSource
, 39030, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
114 { "albums", FieldRating
, 563, SettingType::Number
, "range", "number", CDatabaseQueryRule::OPERATOR_BETWEEN
},
115 { "albums", FieldUserRating
, 38018, SettingType::Integer
, "range", "integer", CDatabaseQueryRule::OPERATOR_BETWEEN
},
116 { "albums", FieldAlbumType
, 564, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
117 { "albums", FieldYear
, 562, SettingType::Integer
, "range", "integer", CDatabaseQueryRule::OPERATOR_BETWEEN
},
118 { "albums", FieldGenre
, 515, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
119 { "albums", FieldMusicLabel
, 21899, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
120 { "albums", FieldCompilation
, 204, SettingType::Boolean
, "toggle", "", CDatabaseQueryRule::OPERATOR_FALSE
},
121 { "albums", FieldIsBoxset
, 38074, SettingType::Boolean
, "toggle", "", CDatabaseQueryRule::OPERATOR_FALSE
},
122 { "albums", FieldOrigYear
, 38078, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
124 { "songs", FieldTitle
, 556, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
125 { "songs", FieldAlbum
, 558, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
126 { "songs", FieldDiscTitle
, 38076, SettingType::String
, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS
},
127 { "songs", FieldArtist
, 557, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
128 { "songs", FieldTime
, 180, SettingType::Integer
, "range", "time", CDatabaseQueryRule::OPERATOR_BETWEEN
},
129 { "songs", FieldRating
, 563, SettingType::Number
, "range", "number", CDatabaseQueryRule::OPERATOR_BETWEEN
},
130 { "songs", FieldUserRating
, 38018, SettingType::Integer
, "range", "integer", CDatabaseQueryRule::OPERATOR_BETWEEN
},
131 { "songs", FieldYear
, 562, SettingType::Integer
, "range", "integer", CDatabaseQueryRule::OPERATOR_BETWEEN
},
132 { "songs", FieldGenre
, 515, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
},
133 { "songs", FieldPlaycount
, 567, SettingType::Integer
, "range", "integer", CDatabaseQueryRule::OPERATOR_BETWEEN
},
134 { "songs", FieldSource
, 39030, SettingType::List
, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS
}
137 CGUIDialogMediaFilter::CGUIDialogMediaFilter()
138 : CGUIDialogSettingsManualBase(WINDOW_DIALOG_MEDIA_FILTER
, "DialogSettings.xml"),
143 CGUIDialogMediaFilter::~CGUIDialogMediaFilter()
148 bool CGUIDialogMediaFilter::OnMessage(CGUIMessage
& message
)
150 switch (message
.GetMessage())
152 case GUI_MSG_CLICKED
:
154 if (message
.GetSenderId()== CONTROL_CLEAR_BUTTON
)
157 m_filter
->SetType(m_mediaType
);
159 for (auto& filter
: m_filters
)
161 filter
.second
.rule
= nullptr;
162 filter
.second
.setting
->Reset();
171 case GUI_MSG_REFRESH_LIST
:
178 case GUI_MSG_WINDOW_DEINIT
:
188 return CGUIDialogSettingsManualBase::OnMessage(message
);
191 void CGUIDialogMediaFilter::ShowAndEditMediaFilter(const std::string
& path
,
192 PLAYLIST::CSmartPlaylist
& filter
)
194 CGUIDialogMediaFilter
*dialog
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogMediaFilter
>(WINDOW_DIALOG_MEDIA_FILTER
);
198 // initialize and show the dialog
199 dialog
->Initialize();
200 dialog
->m_filter
= &filter
;
202 // must be called after setting the filter/smartplaylist
203 if (!dialog
->SetPath(path
))
209 void CGUIDialogMediaFilter::OnWindowLoaded()
211 CGUIDialogSettingsManualBase::OnWindowLoaded();
213 // we don't need the cancel button so let's hide it
214 SET_CONTROL_HIDDEN(CONTROL_CANCEL_BUTTON
);
217 void CGUIDialogMediaFilter::OnInitWindow()
219 CGUIDialogSettingsManualBase::OnInitWindow();
224 void CGUIDialogMediaFilter::OnSettingChanged(const std::shared_ptr
<const CSetting
>& setting
)
226 CGUIDialogSettingsManualBase::OnSettingChanged(setting
);
228 std::map
<std::string
, Filter
>::iterator it
= m_filters
.find(setting
->GetId());
229 if (it
== m_filters
.end())
233 Filter
& filter
= it
->second
;
235 if (filter
.controlType
== "edit")
237 std::string value
= setting
->ToString();
240 if (filter
.rule
== NULL
)
241 filter
.rule
= AddRule(filter
.field
, filter
.ruleOperator
);
242 filter
.rule
->m_parameter
.clear();
243 filter
.rule
->m_parameter
.push_back(value
);
248 else if (filter
.controlType
== "toggle")
250 int choice
= std::static_pointer_cast
<const CSettingInt
>(setting
)->GetValue();
251 if (choice
> CHECK_ALL
)
253 CDatabaseQueryRule::SEARCH_OPERATOR ruleOperator
= choice
== CHECK_YES
? CDatabaseQueryRule::OPERATOR_TRUE
: CDatabaseQueryRule::OPERATOR_FALSE
;
254 if (filter
.rule
== NULL
)
255 filter
.rule
= AddRule(filter
.field
, ruleOperator
);
257 filter
.rule
->m_operator
= ruleOperator
;
262 else if (filter
.controlType
== "list")
264 std::vector
<CVariant
> values
= CSettingUtils::GetList(std::static_pointer_cast
<const CSettingList
>(setting
));
267 if (filter
.rule
== NULL
)
268 filter
.rule
= AddRule(filter
.field
, filter
.ruleOperator
);
270 filter
.rule
->m_parameter
.clear();
271 for (const auto& itValue
: values
)
272 filter
.rule
->m_parameter
.push_back(itValue
.asString());
277 else if (filter
.controlType
== "range")
279 const std::shared_ptr
<const CSettingList
> settingList
= std::static_pointer_cast
<const CSettingList
>(setting
);
280 std::vector
<CVariant
> values
= CSettingUtils::GetList(settingList
);
281 if (values
.size() != 2)
284 std::string strValueLower
, strValueUpper
;
286 SettingConstPtr definition
= settingList
->GetDefinition();
287 if (definition
->GetType() == SettingType::Integer
)
289 const std::shared_ptr
<const CSettingInt
> definitionInt
= std::static_pointer_cast
<const CSettingInt
>(definition
);
290 int valueLower
= static_cast<int>(values
.at(0).asInteger());
291 int valueUpper
= static_cast<int>(values
.at(1).asInteger());
293 if (valueLower
> definitionInt
->GetMinimum() ||
294 valueUpper
< definitionInt
->GetMaximum())
296 if (filter
.controlFormat
== "date")
298 strValueLower
= CDateTime(static_cast<time_t>(valueLower
)).GetAsDBDate();
299 strValueUpper
= CDateTime(static_cast<time_t>(valueUpper
)).GetAsDBDate();
303 strValueLower
= values
.at(0).asString();
304 strValueUpper
= values
.at(1).asString();
308 else if (definition
->GetType() == SettingType::Number
)
310 const std::shared_ptr
<const CSettingNumber
> definitionNumber
= std::static_pointer_cast
<const CSettingNumber
>(definition
);
311 float valueLower
= values
.at(0).asFloat();
312 float valueUpper
= values
.at(1).asFloat();
314 if (static_cast<double>(valueLower
) > definitionNumber
->GetMinimum() ||
315 static_cast<double>(valueUpper
) < definitionNumber
->GetMaximum())
317 strValueLower
= values
.at(0).asString();
318 strValueUpper
= values
.at(1).asString();
324 if (!strValueLower
.empty() && !strValueUpper
.empty())
326 // prepare the filter rule
327 if (filter
.rule
== NULL
)
328 filter
.rule
= AddRule(filter
.field
, filter
.ruleOperator
);
329 filter
.rule
->m_parameter
.clear();
331 filter
.rule
->m_parameter
.push_back(strValueLower
);
332 filter
.rule
->m_parameter
.push_back(strValueUpper
);
340 // we need to remove the existing rule for the title
341 if (remove
&& filter
.rule
!= NULL
)
343 DeleteRule(filter
.field
);
347 CGUIMessage
msg(GUI_MSG_REFRESH_LIST
, GetID(), 0);
348 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg
, WINDOW_DIALOG_MEDIA_FILTER
);
351 void CGUIDialogMediaFilter::SetupView()
353 CGUIDialogSettingsManualBase::SetupView();
355 // set the heading label based on the media type
356 uint32_t localizedMediaId
= 0;
357 if (m_mediaType
== "movies")
358 localizedMediaId
= 20342;
359 else if (m_mediaType
== "tvshows")
360 localizedMediaId
= 20343;
361 else if (m_mediaType
== "episodes")
362 localizedMediaId
= 20360;
363 else if (m_mediaType
== "musicvideos")
364 localizedMediaId
= 20389;
365 else if (m_mediaType
== "artists")
366 localizedMediaId
= 133;
367 else if (m_mediaType
== "albums")
368 localizedMediaId
= 132;
369 else if (m_mediaType
== "songs")
370 localizedMediaId
= 134;
373 SET_CONTROL_LABEL(CONTROL_HEADING
, StringUtils::Format(g_localizeStrings
.Get(1275),
374 g_localizeStrings
.Get(localizedMediaId
)));
376 SET_CONTROL_LABEL(CONTROL_OKAY_BUTTON
, 186);
377 SET_CONTROL_LABEL(CONTROL_CLEAR_BUTTON
, 192);
380 void CGUIDialogMediaFilter::InitializeSettings()
382 CGUIDialogSettingsManualBase::InitializeSettings();
384 if (m_filter
== NULL
)
389 int handledRules
= 0;
391 const std::shared_ptr
<CSettingCategory
> category
= AddCategory("filter", -1);
392 if (category
== NULL
)
394 CLog::Log(LOGERROR
, "CGUIDialogMediaFilter: unable to setup filters");
398 const std::shared_ptr
<CSettingGroup
> group
= AddGroup(category
);
401 CLog::Log(LOGERROR
, "CGUIDialogMediaFilter: unable to setup filters");
405 for (const Filter
& f
: filterList
)
407 if (f
.mediaType
!= m_mediaType
)
412 // check the smartplaylist if it contains a matching rule
413 for (auto& rule
: m_filter
->m_ruleCombination
.m_rules
)
415 if (rule
->m_field
== filter
.field
)
417 filter
.rule
= static_cast<PLAYLIST::CSmartPlaylistRule
*>(rule
.get());
423 std::string settingId
= StringUtils::Format("filter.{}.{}", filter
.mediaType
, filter
.field
);
424 if (filter
.controlType
== "edit")
427 if (filter
.rule
!= NULL
&& filter
.rule
->m_parameter
.size() == 1)
428 data
= filter
.rule
->m_parameter
.at(0);
430 if (filter
.settingType
== SettingType::String
)
431 filter
.setting
= AddEdit(group
, settingId
, filter
.label
, SettingLevel::Basic
, data
.asString(), true, false, filter
.label
, true);
432 else if (filter
.settingType
== SettingType::Integer
)
433 filter
.setting
= AddEdit(group
, settingId
, filter
.label
, SettingLevel::Basic
, static_cast<int>(data
.asInteger()), 0, 1, 0, false, static_cast<int>(filter
.label
), true);
434 else if (filter
.settingType
== SettingType::Number
)
435 filter
.setting
= AddEdit(group
, settingId
, filter
.label
, SettingLevel::Basic
, data
.asFloat(), 0.0f
, 1.0f
, 0.0f
, false, filter
.label
, true);
437 else if (filter
.controlType
== "toggle")
439 int value
= CHECK_ALL
;
440 if (filter
.rule
!= NULL
)
441 value
= filter
.rule
->m_operator
== CDatabaseQueryRule::OPERATOR_TRUE
? CHECK_YES
: CHECK_NO
;
443 TranslatableIntegerSettingOptions entries
;
444 entries
.emplace_back(CHECK_LABEL_ALL
, CHECK_ALL
);
445 entries
.emplace_back(CHECK_LABEL_NO
, CHECK_NO
);
446 entries
.emplace_back(CHECK_LABEL_YES
, CHECK_YES
);
448 filter
.setting
= AddSpinner(group
, settingId
, filter
.label
, SettingLevel::Basic
, value
, entries
, true);
450 else if (filter
.controlType
== "list")
452 std::vector
<std::string
> values
;
453 if (filter
.rule
!= NULL
&& !filter
.rule
->m_parameter
.empty())
455 values
= StringUtils::Split(filter
.rule
->GetParameter(), DATABASEQUERY_RULE_VALUE_SEPARATOR
);
456 if (values
.size() == 1 && values
.at(0).empty())
457 values
.erase(values
.begin());
460 filter
.setting
= AddList(group
, settingId
, filter
.label
, SettingLevel::Basic
, values
, GetStringListOptions
, filter
.label
);
462 else if (filter
.controlType
== "range")
464 CVariant valueLower
, valueUpper
;
465 if (filter
.rule
!= NULL
)
467 if (filter
.rule
->m_parameter
.size() == 2)
469 valueLower
= filter
.rule
->m_parameter
.at(0);
470 valueUpper
= filter
.rule
->m_parameter
.at(1);
474 DeleteRule(filter
.field
);
479 if (filter
.settingType
== SettingType::Integer
)
484 GetRange(filter
, min
, interval
, max
);
486 // don't create the filter if there's no real range
490 int iValueLower
= valueLower
.isNull() ? min
: static_cast<int>(valueLower
.asInteger());
491 int iValueUpper
= valueUpper
.isNull() ? max
: static_cast<int>(valueUpper
.asInteger());
493 if (filter
.controlFormat
== "integer")
494 filter
.setting
= AddRange(group
, settingId
, filter
.label
, SettingLevel::Basic
, iValueLower
, iValueUpper
, min
, interval
, max
, -1, 21469, true);
495 else if (filter
.controlFormat
== "percentage")
496 filter
.setting
= AddPercentageRange(group
, settingId
, filter
.label
, SettingLevel::Basic
, iValueLower
, iValueUpper
, -1, 1, 21469, true);
497 else if (filter
.controlFormat
== "date")
498 filter
.setting
= AddDateRange(group
, settingId
, filter
.label
, SettingLevel::Basic
, iValueLower
, iValueUpper
, min
, interval
, max
, -1, 21469, true);
499 else if (filter
.controlFormat
== "time")
500 filter
.setting
= AddTimeRange(group
, settingId
, filter
.label
, SettingLevel::Basic
, iValueLower
, iValueUpper
, min
, interval
, max
, -1, 21469, true);
502 else if (filter
.settingType
== SettingType::Number
)
507 GetRange(filter
, min
, interval
, max
);
509 // don't create the filter if there's no real range
513 float fValueLower
= valueLower
.isNull() ? min
: valueLower
.asFloat();
514 float fValueUpper
= valueUpper
.isNull() ? max
: valueUpper
.asFloat();
516 filter
.setting
= AddRange(group
, settingId
, filter
.label
, SettingLevel::Basic
, fValueLower
, fValueUpper
, min
, interval
, max
, -1, 21469, true);
521 if (filter
.rule
!= NULL
)
524 CLog::Log(LOGWARNING
,
525 "CGUIDialogMediaFilter: filter {} of media type {} with unknown control type '{}'",
526 filter
.field
, filter
.mediaType
, filter
.controlType
);
530 if (filter
.setting
== NULL
)
532 if (filter
.rule
!= NULL
)
535 CLog::Log(LOGWARNING
,
536 "CGUIDialogMediaFilter: failed to create filter {} of media type {} with control "
538 filter
.field
, filter
.mediaType
, filter
.controlType
);
542 m_filters
.insert(make_pair(settingId
, filter
));
545 // make sure that no change in capacity size is needed when adding new rules
546 // which would copy around the rules and our pointers in the Filter struct
547 // wouldn't work anymore
548 m_filter
->m_ruleCombination
.m_rules
.reserve(m_filters
.size() + (m_filter
->m_ruleCombination
.m_rules
.size() - handledRules
));
551 bool CGUIDialogMediaFilter::SetPath(const std::string
&path
)
553 if (path
.empty() || m_filter
== NULL
)
555 CLog::Log(LOGWARNING
, "CGUIDialogMediaFilter::SetPath({}): invalid path or filter", path
);
561 if (path
.find("videodb://") == 0)
563 m_dbUrl
= new CVideoDbUrl();
566 else if (path
.find("musicdb://") == 0)
567 m_dbUrl
= new CMusicDbUrl();
572 "CGUIDialogMediaFilter::SetPath({}): invalid path (neither videodb:// nor musicdb://)",
577 if (!m_dbUrl
->FromString(path
) ||
578 (video
&& m_dbUrl
->GetType() != "movies" && m_dbUrl
->GetType() != "tvshows" && m_dbUrl
->GetType() != "episodes" && m_dbUrl
->GetType() != "musicvideos") ||
579 (!video
&& m_dbUrl
->GetType() != "artists" && m_dbUrl
->GetType() != "albums" && m_dbUrl
->GetType() != "songs"))
581 CLog::Log(LOGWARNING
, "CGUIDialogMediaFilter::SetPath({}): invalid media type", path
);
585 // remove "filter" option
586 if (m_dbUrl
->HasOption("filter"))
587 m_dbUrl
->RemoveOption("filter");
590 m_mediaType
= ((CVideoDbUrl
*)m_dbUrl
)->GetItemType();
592 m_mediaType
= m_dbUrl
->GetType();
594 m_filter
->SetType(m_mediaType
);
598 void CGUIDialogMediaFilter::UpdateControls()
600 for (const auto& itFilter
: m_filters
)
602 if (itFilter
.second
.controlType
!= "list")
605 std::vector
<std::string
> items
;
606 int size
= GetItems(itFilter
.second
, items
, true);
608 std::string label
= g_localizeStrings
.Get(itFilter
.second
.label
);
609 BaseSettingControlPtr control
= GetSettingControl(itFilter
.second
.setting
->GetId());
614 (size
== 1 && itFilter
.second
.field
!= FieldSet
&& itFilter
.second
.field
!= FieldTag
))
615 CONTROL_DISABLE(control
->GetID());
618 CONTROL_ENABLE(control
->GetID());
619 label
= StringUtils::Format(g_localizeStrings
.Get(21470), label
, size
);
621 SET_CONTROL_LABEL(control
->GetID(), label
);
625 void CGUIDialogMediaFilter::TriggerFilter() const
627 if (m_filter
== NULL
)
630 CGUIMessage
message(GUI_MSG_NOTIFY_ALL
, GetID(), 0, GUI_MSG_FILTER_ITEMS
, 10); // 10 for advanced
631 CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message
);
634 void CGUIDialogMediaFilter::Reset(bool filtersOnly
/* = false */)
645 int CGUIDialogMediaFilter::GetItems(const Filter
&filter
, std::vector
<std::string
> &items
, bool countOnly
/* = false */)
647 CFileItemList selectItems
;
649 // remove the rule for the field of the filter we want to retrieve items for
650 PLAYLIST::CSmartPlaylist tmpFilter
= *m_filter
;
651 for (CDatabaseQueryRules::iterator rule
= tmpFilter
.m_ruleCombination
.m_rules
.begin();
652 rule
!= tmpFilter
.m_ruleCombination
.m_rules
.end(); ++rule
)
654 if ((*rule
)->m_field
== filter
.field
)
656 tmpFilter
.m_ruleCombination
.m_rules
.erase(rule
);
661 if (m_mediaType
== "movies" || m_mediaType
== "tvshows" || m_mediaType
== "episodes" || m_mediaType
== "musicvideos")
663 CVideoDatabase videodb
;
667 std::set
<std::string
> playlists
;
668 CDatabase::Filter dbfilter
;
669 dbfilter
.where
= tmpFilter
.GetWhereClause(videodb
, playlists
);
671 VideoDbContentType type
= VideoDbContentType::MOVIES
;
672 if (m_mediaType
== "tvshows")
673 type
= VideoDbContentType::TVSHOWS
;
674 else if (m_mediaType
== "episodes")
675 type
= VideoDbContentType::EPISODES
;
676 else if (m_mediaType
== "musicvideos")
677 type
= VideoDbContentType::MUSICVIDEOS
;
679 if (filter
.field
== FieldGenre
)
680 videodb
.GetGenresNav(m_dbUrl
->ToString(), selectItems
, type
, dbfilter
, countOnly
);
681 else if (filter
.field
== FieldActor
|| filter
.field
== FieldArtist
)
682 videodb
.GetActorsNav(m_dbUrl
->ToString(), selectItems
, type
, dbfilter
, countOnly
);
683 else if (filter
.field
== FieldDirector
)
684 videodb
.GetDirectorsNav(m_dbUrl
->ToString(), selectItems
, type
, dbfilter
, countOnly
);
685 else if (filter
.field
== FieldStudio
)
686 videodb
.GetStudiosNav(m_dbUrl
->ToString(), selectItems
, type
, dbfilter
, countOnly
);
687 else if (filter
.field
== FieldAlbum
)
688 videodb
.GetMusicVideoAlbumsNav(m_dbUrl
->ToString(), selectItems
, -1, dbfilter
, countOnly
);
689 else if (filter
.field
== FieldTag
)
690 videodb
.GetTagsNav(m_dbUrl
->ToString(), selectItems
, type
, dbfilter
, countOnly
);
692 else if (m_mediaType
== "artists" || m_mediaType
== "albums" || m_mediaType
== "songs")
694 CMusicDatabase musicdb
;
698 std::set
<std::string
> playlists
;
699 CDatabase::Filter dbfilter
;
700 dbfilter
.where
= tmpFilter
.GetWhereClause(musicdb
, playlists
);
702 if (filter
.field
== FieldGenre
)
703 musicdb
.GetGenresNav(m_dbUrl
->ToString(), selectItems
, dbfilter
, countOnly
);
704 else if (filter
.field
== FieldArtist
|| filter
.field
== FieldAlbumArtist
)
705 musicdb
.GetArtistsNav(m_dbUrl
->ToString(), selectItems
, m_mediaType
== "albums", -1, -1, -1, dbfilter
, SortDescription(), countOnly
);
706 else if (filter
.field
== FieldAlbum
)
707 musicdb
.GetAlbumsNav(m_dbUrl
->ToString(), selectItems
, -1, -1, dbfilter
, SortDescription(), countOnly
);
708 else if (filter
.field
== FieldAlbumType
)
709 musicdb
.GetAlbumTypesNav(m_dbUrl
->ToString(), selectItems
, dbfilter
, countOnly
);
710 else if (filter
.field
== FieldMusicLabel
)
711 musicdb
.GetMusicLabelsNav(m_dbUrl
->ToString(), selectItems
, dbfilter
, countOnly
);
712 if (filter
.field
== FieldSource
)
713 musicdb
.GetSourcesNav(m_dbUrl
->ToString(), selectItems
, dbfilter
, countOnly
);
716 int size
= selectItems
.Size();
722 if (size
== 1 && selectItems
.Get(0)->HasProperty("total"))
723 return (int)selectItems
.Get(0)->GetProperty("total").asInteger();
728 selectItems
.Sort(SortByLabel
, SortOrderAscending
);
730 for (int index
= 0; index
< size
; ++index
)
731 items
.push_back(selectItems
.Get(index
)->GetLabel());
736 PLAYLIST::CSmartPlaylistRule
* CGUIDialogMediaFilter::AddRule(
738 CDatabaseQueryRule::SEARCH_OPERATOR ruleOperator
/* = CDatabaseQueryRule::OPERATOR_CONTAINS */)
740 PLAYLIST::CSmartPlaylistRule rule
;
741 rule
.m_field
= field
;
742 rule
.m_operator
= ruleOperator
;
744 m_filter
->m_ruleCombination
.AddRule(rule
);
745 return static_cast<PLAYLIST::CSmartPlaylistRule
*>(
746 m_filter
->m_ruleCombination
.m_rules
.back().get());
749 void CGUIDialogMediaFilter::DeleteRule(Field field
)
751 for (CDatabaseQueryRules::iterator rule
= m_filter
->m_ruleCombination
.m_rules
.begin();
752 rule
!= m_filter
->m_ruleCombination
.m_rules
.end(); ++rule
)
754 if ((*rule
)->m_field
== field
)
756 m_filter
->m_ruleCombination
.m_rules
.erase(rule
);
762 void CGUIDialogMediaFilter::GetStringListOptions(const SettingConstPtr
& setting
,
763 std::vector
<StringSettingOption
>& list
,
764 std::string
& current
,
767 if (setting
== NULL
|| data
== NULL
)
770 CGUIDialogMediaFilter
*mediaFilter
= static_cast<CGUIDialogMediaFilter
*>(data
);
772 std::map
<std::string
, Filter
>::const_iterator itFilter
= mediaFilter
->m_filters
.find(setting
->GetId());
773 if (itFilter
== mediaFilter
->m_filters
.end())
776 std::vector
<std::string
> items
;
777 if (mediaFilter
->GetItems(itFilter
->second
, items
, false) <= 0)
780 for (const auto& item
: items
)
781 list
.emplace_back(item
, item
);
784 void CGUIDialogMediaFilter::GetRange(const Filter
&filter
, int &min
, int &interval
, int &max
)
786 if (filter
.field
== FieldUserRating
&&
787 (m_mediaType
== "movies" || m_mediaType
== "tvshows" || m_mediaType
== "episodes"|| m_mediaType
== "musicvideos" || m_mediaType
== "albums" || m_mediaType
== "songs"))
793 else if (filter
.field
== FieldYear
)
799 if (m_mediaType
== "movies" || m_mediaType
== "tvshows" || m_mediaType
== "musicvideos")
803 if (m_mediaType
== "movies")
805 table
= "movie_view";
806 year
= DatabaseUtils::GetField(FieldYear
, MediaTypeMovie
, DatabaseQueryPartWhere
);
808 else if (m_mediaType
== "tvshows")
810 table
= "tvshow_view";
811 year
= StringUtils::Format(
812 "strftime(\"%%Y\", {})",
813 DatabaseUtils::GetField(FieldYear
, MediaTypeTvShow
, DatabaseQueryPartWhere
));
815 else if (m_mediaType
== "musicvideos")
817 table
= "musicvideo_view";
818 year
= DatabaseUtils::GetField(FieldYear
, MediaTypeMusicVideo
, DatabaseQueryPartWhere
);
821 CDatabase::Filter filter
;
822 filter
.where
= year
+ " > 0";
823 GetMinMax(table
, year
, min
, max
, filter
);
825 else if (m_mediaType
== "albums" || m_mediaType
== "songs")
828 if (m_mediaType
== "albums")
830 else if (m_mediaType
== "songs")
835 CDatabase::Filter filter
;
836 filter
.where
= DatabaseUtils::GetField(FieldYear
, CMediaTypes::FromString(m_mediaType
), DatabaseQueryPartWhere
) + " > 0";
837 GetMinMax(table
, DatabaseUtils::GetField(FieldYear
, CMediaTypes::FromString(m_mediaType
), DatabaseQueryPartSelect
), min
, max
, filter
);
840 else if (filter
.field
== FieldAirDate
)
846 if (m_mediaType
== "episodes")
848 std::string field
= StringUtils::Format("CAST(strftime(\"%%s\", c{:02}) AS INTEGER)",
849 VIDEODB_ID_EPISODE_AIRED
);
851 GetMinMax("episode_view", field
, min
, max
);
852 interval
= 60 * 60 * 24 * 7; // 1 week
855 else if (filter
.field
== FieldTime
)
861 if (m_mediaType
== "songs")
862 GetMinMax("songview", "iDuration", min
, max
);
864 else if (filter
.field
== FieldPlaycount
)
870 if (m_mediaType
== "songs")
871 GetMinMax("songview", "iTimesPlayed", min
, max
);
875 void CGUIDialogMediaFilter::GetRange(const Filter
&filter
, float &min
, float &interval
, float &max
)
877 if (filter
.field
== FieldRating
&&
878 (m_mediaType
== "movies" || m_mediaType
== "tvshows" || m_mediaType
== "episodes" || m_mediaType
== "musicvideos" || m_mediaType
== "albums" || m_mediaType
== "songs"))
886 bool CGUIDialogMediaFilter::GetMinMax(const std::string
&table
, const std::string
&field
, int &min
, int &max
, const CDatabase::Filter
&filter
/* = CDatabase::Filter() */)
888 if (table
.empty() || field
.empty())
891 CDatabase
*db
= NULL
;
892 CDbUrl
*dbUrl
= NULL
;
893 if (m_mediaType
== "movies" || m_mediaType
== "tvshows" || m_mediaType
== "episodes" || m_mediaType
== "musicvideos")
895 CVideoDatabase
*videodb
= new CVideoDatabase();
896 if (!videodb
->Open())
903 dbUrl
= new CVideoDbUrl();
905 else if (m_mediaType
== "artists" || m_mediaType
== "albums" || m_mediaType
== "songs")
907 CMusicDatabase
*musicdb
= new CMusicDatabase();
908 if (!musicdb
->Open())
915 dbUrl
= new CMusicDbUrl();
918 if (db
== NULL
|| !db
->IsOpen() || dbUrl
== NULL
)
925 CDatabase::Filter extFilter
= filter
;
926 std::string strSQLExtra
;
927 if (!db
->BuildSQL(m_dbUrl
->ToString(), strSQLExtra
, extFilter
, strSQLExtra
, *dbUrl
))
934 std::string strSQL
= "SELECT %s FROM %s ";
936 min
= static_cast<int>(strtol(db
->GetSingleValue(db
->PrepareSQL(strSQL
, ("MIN(" + field
+ ")").c_str(), table
.c_str()) + strSQLExtra
).c_str(), NULL
, 0));
937 max
= static_cast<int>(strtol(db
->GetSingleValue(db
->PrepareSQL(strSQL
, ("MAX(" + field
+ ")").c_str(), table
.c_str()) + strSQLExtra
).c_str(), NULL
, 0));