Merge pull request #26350 from jjd-uk/estuary_media_align
[xbmc.git] / xbmc / dialogs / GUIDialogMediaFilter.cpp
blobfb85c6910221b82ecb9eaa953eb5f2b2dd488add
1 /*
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.
7 */
9 #include "GUIDialogMediaFilter.h"
11 #include "DbUrl.h"
12 #include "FileItem.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"
34 using namespace KODI;
36 #define CONTROL_HEADING 2
38 #define CONTROL_OKAY_BUTTON 28
39 #define CONTROL_CANCEL_BUTTON 29
40 #define CONTROL_CLEAR_BUTTON 30
42 #define CHECK_ALL -1
43 #define CHECK_NO 0
44 #define CHECK_YES 1
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"),
139 m_dbUrl(NULL),
140 m_filter(NULL)
143 CGUIDialogMediaFilter::~CGUIDialogMediaFilter()
145 Reset();
148 bool CGUIDialogMediaFilter::OnMessage(CGUIMessage& message)
150 switch (message.GetMessage())
152 case GUI_MSG_CLICKED:
154 if (message.GetSenderId()== CONTROL_CLEAR_BUTTON)
156 m_filter->Reset();
157 m_filter->SetType(m_mediaType);
159 for (auto& filter : m_filters)
161 filter.second.rule = nullptr;
162 filter.second.setting->Reset();
165 TriggerFilter();
166 return true;
168 break;
171 case GUI_MSG_REFRESH_LIST:
173 TriggerFilter();
174 UpdateControls();
175 break;
178 case GUI_MSG_WINDOW_DEINIT:
180 Reset();
181 break;
184 default:
185 break;
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);
195 if (dialog == NULL)
196 return;
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))
204 return;
206 dialog->Open();
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();
221 UpdateControls();
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())
230 return;
232 bool remove = false;
233 Filter& filter = it->second;
235 if (filter.controlType == "edit")
237 std::string value = setting->ToString();
238 if (!value.empty())
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);
245 else
246 remove = true;
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);
256 else
257 filter.rule->m_operator = ruleOperator;
259 else
260 remove = true;
262 else if (filter.controlType == "list")
264 std::vector<CVariant> values = CSettingUtils::GetList(std::static_pointer_cast<const CSettingList>(setting));
265 if (!values.empty())
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());
274 else
275 remove = true;
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)
282 return;
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();
301 else
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();
321 else
322 return;
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);
334 else
335 remove = true;
337 else
338 return;
340 // we need to remove the existing rule for the title
341 if (remove && filter.rule != NULL)
343 DeleteRule(filter.field);
344 filter.rule = NULL;
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;
372 // set the heading
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)
385 return;
387 Reset(true);
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");
395 return;
398 const std::shared_ptr<CSettingGroup> group = AddGroup(category);
399 if (group == NULL)
401 CLog::Log(LOGERROR, "CGUIDialogMediaFilter: unable to setup filters");
402 return;
405 for (const Filter& f : filterList)
407 if (f.mediaType != m_mediaType)
408 continue;
410 Filter filter = f;
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());
418 handledRules++;
419 break;
423 std::string settingId = StringUtils::Format("filter.{}.{}", filter.mediaType, filter.field);
424 if (filter.controlType == "edit")
426 CVariant data;
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);
472 else
474 DeleteRule(filter.field);
475 filter.rule = NULL;
479 if (filter.settingType == SettingType::Integer)
481 int min = 0;
482 int interval = 0;
483 int max = 0;
484 GetRange(filter, min, interval, max);
486 // don't create the filter if there's no real range
487 if (min == max)
488 continue;
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)
504 float min = 0;
505 float interval = 0;
506 float max = 0;
507 GetRange(filter, min, interval, max);
509 // don't create the filter if there's no real range
510 if (min == max)
511 continue;
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);
519 else
521 if (filter.rule != NULL)
522 handledRules--;
524 CLog::Log(LOGWARNING,
525 "CGUIDialogMediaFilter: filter {} of media type {} with unknown control type '{}'",
526 filter.field, filter.mediaType, filter.controlType);
527 continue;
530 if (filter.setting == NULL)
532 if (filter.rule != NULL)
533 handledRules--;
535 CLog::Log(LOGWARNING,
536 "CGUIDialogMediaFilter: failed to create filter {} of media type {} with control "
537 "type '{}'",
538 filter.field, filter.mediaType, filter.controlType);
539 continue;
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);
556 return false;
559 delete m_dbUrl;
560 bool video = false;
561 if (path.starts_with("videodb://"))
563 m_dbUrl = new CVideoDbUrl();
564 video = true;
566 else if (path.starts_with("musicdb://"))
567 m_dbUrl = new CMusicDbUrl();
568 else
570 CLog::Log(
571 LOGWARNING,
572 "CGUIDialogMediaFilter::SetPath({}): invalid path (neither videodb:// nor musicdb://)",
573 path);
574 return false;
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);
582 return false;
585 // remove "filter" option
586 if (m_dbUrl->HasOption("filter"))
587 m_dbUrl->RemoveOption("filter");
589 if (video)
590 m_mediaType = ((CVideoDbUrl*)m_dbUrl)->GetItemType();
591 else
592 m_mediaType = m_dbUrl->GetType();
594 m_filter->SetType(m_mediaType);
595 return true;
598 void CGUIDialogMediaFilter::UpdateControls()
600 for (const auto& itFilter : m_filters)
602 if (itFilter.second.controlType != "list")
603 continue;
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());
610 if (control == NULL)
611 continue;
613 if (size <= 0 ||
614 (size == 1 && itFilter.second.field != FieldSet && itFilter.second.field != FieldTag))
615 CONTROL_DISABLE(control->GetID());
616 else
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)
628 return;
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 */)
636 if (!filtersOnly)
638 delete m_dbUrl;
639 m_dbUrl = NULL;
642 m_filters.clear();
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);
657 break;
661 if (m_mediaType == "movies" || m_mediaType == "tvshows" || m_mediaType == "episodes" || m_mediaType == "musicvideos")
663 CVideoDatabase videodb;
664 if (!videodb.Open())
665 return -1;
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;
695 if (!musicdb.Open())
696 return -1;
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();
717 if (size <= 0)
718 return 0;
720 if (countOnly)
722 if (size == 1 && selectItems.Get(0)->HasProperty("total"))
723 return (int)selectItems.Get(0)->GetProperty("total").asInteger();
724 return 0;
727 // sort the items
728 selectItems.Sort(SortByLabel, SortOrderAscending);
730 for (int index = 0; index < size; ++index)
731 items.push_back(selectItems.Get(index)->GetLabel());
733 return items.size();
736 PLAYLIST::CSmartPlaylistRule* CGUIDialogMediaFilter::AddRule(
737 Field field,
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);
757 break;
762 void CGUIDialogMediaFilter::GetStringListOptions(const SettingConstPtr& setting,
763 std::vector<StringSettingOption>& list,
764 std::string& current,
765 void* data)
767 if (setting == NULL || data == NULL)
768 return;
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())
774 return;
776 std::vector<std::string> items;
777 if (mediaFilter->GetItems(itFilter->second, items, false) <= 0)
778 return;
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"))
789 min = 0;
790 interval = 1;
791 max = 10;
793 else if (filter.field == FieldYear)
795 min = 0;
796 interval = 1;
797 max = 0;
799 if (m_mediaType == "movies" || m_mediaType == "tvshows" || m_mediaType == "musicvideos")
801 std::string table;
802 std::string year;
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")
827 std::string table;
828 if (m_mediaType == "albums")
829 table = "albumview";
830 else if (m_mediaType == "songs")
831 table = "songview";
832 else
833 return;
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)
842 min = 0;
843 interval = 1;
844 max = 0;
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)
857 min = 0;
858 interval = 10;
859 max = 0;
861 if (m_mediaType == "songs")
862 GetMinMax("songview", "iDuration", min, max);
864 else if (filter.field == FieldPlaycount)
866 min = 0;
867 interval = 1;
868 max = 0;
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"))
880 min = 0.0f;
881 interval = 0.1f;
882 max = 10.0f;
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())
889 return false;
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())
898 delete videodb;
899 return false;
902 db = videodb;
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())
910 delete musicdb;
911 return false;
914 db = musicdb;
915 dbUrl = new CMusicDbUrl();
918 if (db == NULL || !db->IsOpen() || dbUrl == NULL)
920 delete db;
921 delete dbUrl;
922 return false;
925 CDatabase::Filter extFilter = filter;
926 std::string strSQLExtra;
927 if (!db->BuildSQL(m_dbUrl->ToString(), strSQLExtra, extFilter, strSQLExtra, *dbUrl))
929 delete db;
930 delete dbUrl;
931 return false;
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));
939 db->Close();
940 delete db;
941 delete dbUrl;
943 return true;