[videodb] remove unused seasons table from episode_view
[xbmc.git] / xbmc / dialogs / GUIDialogSmartPlaylistRule.cpp
blob396bea02cfdb31df7b413ab32afd52d446205117
1 /*
2 * Copyright (C) 2005-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 "GUIDialogSmartPlaylistRule.h"
11 #include "FileItem.h"
12 #include "FileItemList.h"
13 #include "GUIDialogFileBrowser.h"
14 #include "GUIDialogSelect.h"
15 #include "ServiceBroker.h"
16 #include "filesystem/Directory.h"
17 #include "guilib/GUIComponent.h"
18 #include "guilib/GUIEditControl.h"
19 #include "guilib/GUIWindowManager.h"
20 #include "guilib/LocalizeStrings.h"
21 #include "music/MusicDatabase.h"
22 #include "settings/MediaSourceSettings.h"
23 #include "settings/Settings.h"
24 #include "settings/SettingsComponent.h"
25 #include "storage/MediaManager.h"
26 #include "utils/LabelFormatter.h"
27 #include "utils/StringUtils.h"
28 #include "utils/Variant.h"
29 #include "video/VideoDatabase.h"
31 #include <utility>
33 using namespace KODI;
35 #define CONTROL_FIELD 15
36 #define CONTROL_OPERATOR 16
37 #define CONTROL_VALUE 17
38 #define CONTROL_OK 18
39 #define CONTROL_CANCEL 19
40 #define CONTROL_BROWSE 20
42 CGUIDialogSmartPlaylistRule::CGUIDialogSmartPlaylistRule(void)
43 : CGUIDialog(WINDOW_DIALOG_SMART_PLAYLIST_RULE, "SmartPlaylistRule.xml")
45 m_cancelled = false;
46 m_loadType = KEEP_IN_MEMORY;
49 CGUIDialogSmartPlaylistRule::~CGUIDialogSmartPlaylistRule() = default;
51 bool CGUIDialogSmartPlaylistRule::OnBack(int actionID)
53 m_cancelled = true;
54 return CGUIDialog::OnBack(actionID);
57 bool CGUIDialogSmartPlaylistRule::OnMessage(CGUIMessage& message)
59 switch ( message.GetMessage() )
61 case GUI_MSG_CLICKED:
63 int iControl = message.GetSenderId();
64 if (iControl == CONTROL_OK)
65 OnOK();
66 else if (iControl == CONTROL_CANCEL)
67 OnCancel();
68 else if (iControl == CONTROL_VALUE)
70 std::string parameter;
71 OnEditChanged(iControl, parameter);
72 m_rule.SetParameter(parameter);
74 else if (iControl == CONTROL_OPERATOR)
75 OnOperator();
76 else if (iControl == CONTROL_FIELD)
77 OnField();
78 else if (iControl == CONTROL_BROWSE)
79 OnBrowse();
80 return true;
82 break;
84 case GUI_MSG_VALIDITY_CHANGED:
85 CONTROL_ENABLE_ON_CONDITION(CONTROL_OK, message.GetParam1());
86 break;
88 return CGUIDialog::OnMessage(message);
91 void CGUIDialogSmartPlaylistRule::OnOK()
93 m_cancelled = false;
94 Close();
97 void CGUIDialogSmartPlaylistRule::OnBrowse()
99 CFileItemList items;
100 CMusicDatabase database;
101 database.Open();
102 CVideoDatabase videodatabase;
103 videodatabase.Open();
105 std::string basePath;
106 if (PLAYLIST::CSmartPlaylist::IsMusicType(m_type))
107 basePath = "musicdb://";
108 else
109 basePath = "videodb://";
111 VideoDbContentType type = VideoDbContentType::MOVIES;
112 if (m_type == "movies")
113 basePath += "movies/";
114 else if (m_type == "tvshows")
116 type = VideoDbContentType::TVSHOWS;
117 basePath += "tvshows/";
119 else if (m_type == "musicvideos")
121 type = VideoDbContentType::MUSICVIDEOS;
122 basePath += "musicvideos/";
124 else if (m_type == "episodes")
126 if (m_rule.m_field == FieldGenre || m_rule.m_field == FieldYear ||
127 m_rule.m_field == FieldStudio)
128 type = VideoDbContentType::TVSHOWS;
129 else
130 type = VideoDbContentType::EPISODES;
131 basePath += "tvshows/";
134 int iLabel = 0;
135 if (m_rule.m_field == FieldGenre)
137 if (m_type == "tvshows" ||
138 m_type == "episodes" ||
139 m_type == "movies")
140 videodatabase.GetGenresNav(basePath + "genres/", items, type);
141 else if (m_type == "songs" ||
142 m_type == "albums" ||
143 m_type == "artists" ||
144 m_type == "mixed")
145 database.GetGenresNav("musicdb://genres/",items);
146 if (m_type == "musicvideos" ||
147 m_type == "mixed")
149 CFileItemList items2;
150 videodatabase.GetGenresNav("videodb://musicvideos/genres/", items2,
151 VideoDbContentType::MUSICVIDEOS);
152 items.Append(items2);
154 iLabel = 515;
156 else if (m_rule.m_field == FieldSource)
158 if (m_type == "songs" ||
159 m_type == "albums" ||
160 m_type == "artists" ||
161 m_type == "mixed")
163 database.GetSourcesNav("musicdb://sources/", items);
164 iLabel = 39030;
167 else if (m_rule.m_field == FieldRole)
169 if (m_type == "artists" || m_type == "mixed")
171 database.GetRolesNav("musicdb://songs/", items);
172 iLabel = 38033;
175 else if (m_rule.m_field == FieldCountry)
177 videodatabase.GetCountriesNav(basePath, items, type);
178 iLabel = 574;
180 else if (m_rule.m_field == FieldArtist || m_rule.m_field == FieldAlbumArtist)
182 if (PLAYLIST::CSmartPlaylist::IsMusicType(m_type))
183 database.GetArtistsNav("musicdb://artists/", items, m_rule.m_field == FieldAlbumArtist, -1);
184 if (m_type == "musicvideos" ||
185 m_type == "mixed")
187 CFileItemList items2;
188 videodatabase.GetMusicVideoArtistsByName("", items2);
189 items.Append(items2);
191 iLabel = 557;
193 else if (m_rule.m_field == FieldAlbum)
195 if (PLAYLIST::CSmartPlaylist::IsMusicType(m_type))
196 database.GetAlbumsNav("musicdb://albums/", items);
197 if (m_type == "musicvideos" ||
198 m_type == "mixed")
200 CFileItemList items2;
201 videodatabase.GetMusicVideoAlbumsByName("", items2);
202 items.Append(items2);
204 iLabel = 558;
206 else if (m_rule.m_field == FieldActor)
208 videodatabase.GetActorsNav(basePath + "actors/",items,type);
209 iLabel = 20337;
211 else if (m_rule.m_field == FieldYear)
213 if (PLAYLIST::CSmartPlaylist::IsMusicType(m_type))
214 database.GetYearsNav("musicdb://years/", items);
215 if (PLAYLIST::CSmartPlaylist::IsVideoType(m_type))
217 CFileItemList items2;
218 videodatabase.GetYearsNav(basePath + "years/", items2, type);
219 items.Append(items2);
221 iLabel = 562;
223 else if (m_rule.m_field == FieldOrigYear)
225 database.GetYearsNav("musicdb://originalyears/", items);
226 iLabel = 38078;
228 else if (m_rule.m_field == FieldDirector)
230 videodatabase.GetDirectorsNav(basePath + "directors/", items, type);
231 iLabel = 20339;
233 else if (m_rule.m_field == FieldStudio)
235 videodatabase.GetStudiosNav(basePath + "studios/", items, type);
236 iLabel = 572;
238 else if (m_rule.m_field == FieldWriter)
240 videodatabase.GetWritersNav(basePath, items, type);
241 iLabel = 20417;
243 else if (m_rule.m_field == FieldTvShowTitle ||
244 (m_type == "tvshows" && m_rule.m_field == FieldTitle))
246 videodatabase.GetTvShowsNav(basePath + "titles/", items);
247 iLabel = 20343;
249 else if (m_rule.m_field == FieldTitle)
251 if (m_type == "songs" || m_type == "mixed")
253 database.GetSongsNav("musicdb://songs/", items, -1, -1, -1);
254 iLabel = 134;
256 if (m_type == "movies")
258 videodatabase.GetMoviesNav(basePath + "titles/", items);
259 iLabel = 20342;
261 if (m_type == "episodes")
263 videodatabase.GetEpisodesNav(basePath + "titles/-1/-1/", items);
264 // we need to replace the db label (<season>x<episode> <title>) with the title only
265 CLabelFormatter format("%T", "");
266 for (int i = 0; i < items.Size(); i++)
267 format.FormatLabel(items[i].get());
268 iLabel = 20360;
270 if (m_type == "musicvideos" || m_type == "mixed")
272 videodatabase.GetMusicVideosNav(basePath + "titles/", items);
273 iLabel = 20389;
276 else if (m_rule.m_field == FieldPlaylist || m_rule.m_field == FieldVirtualFolder)
278 // use filebrowser to grab another smart playlist
280 // Note: This can cause infinite loops (playlist that refers to the same playlist) but I don't
281 // think there's any decent way to deal with this, as the infinite loop may be an arbitrary
282 // number of playlists deep, eg playlist1 -> playlist2 -> playlist3 ... -> playlistn -> playlist1
283 if (PLAYLIST::CSmartPlaylist::IsVideoType(m_type))
284 XFILE::CDirectory::GetDirectory("special://videoplaylists/", items, ".xsp", XFILE::DIR_FLAG_NO_FILE_DIRS);
285 if (PLAYLIST::CSmartPlaylist::IsMusicType(m_type))
287 CFileItemList items2;
288 XFILE::CDirectory::GetDirectory("special://musicplaylists/", items2, ".xsp", XFILE::DIR_FLAG_NO_FILE_DIRS);
289 items.Append(items2);
292 for (int i = 0; i < items.Size(); i++)
294 CFileItemPtr item = items[i];
295 PLAYLIST::CSmartPlaylist playlist;
296 // don't list unloadable smartplaylists or any referenceable smartplaylists
297 // which do not match the type of the current smartplaylist
298 if (!playlist.Load(item->GetPath()) ||
299 (m_rule.m_field == FieldPlaylist &&
300 (!PLAYLIST::CSmartPlaylist::CheckTypeCompatibility(m_type, playlist.GetType()) ||
301 (!playlist.GetGroup().empty() || playlist.IsGroupMixed()))))
303 items.Remove(i);
304 i -= 1;
305 continue;
308 if (!playlist.GetName().empty())
309 item->SetLabel(playlist.GetName());
311 iLabel = 559;
313 else if (m_rule.m_field == FieldPath)
315 VECSOURCES sources;
316 if (m_type == "songs" || m_type == "mixed")
317 sources = *CMediaSourceSettings::GetInstance().GetSources("music");
318 if (PLAYLIST::CSmartPlaylist::IsVideoType(m_type))
320 VECSOURCES sources2 = *CMediaSourceSettings::GetInstance().GetSources("video");
321 sources.insert(sources.end(),sources2.begin(),sources2.end());
323 CServiceBroker::GetMediaManager().GetLocalDrives(sources);
325 std::string path = m_rule.GetParameter();
326 CGUIDialogFileBrowser::ShowAndGetDirectory(sources, g_localizeStrings.Get(657), path, false);
327 if (!m_rule.m_parameter.empty())
328 m_rule.m_parameter.clear();
329 if (!path.empty())
330 m_rule.m_parameter.emplace_back(std::move(path));
332 UpdateButtons();
333 return;
335 else if (m_rule.m_field == FieldSet)
337 videodatabase.GetSetsNav("videodb://movies/sets/", items, VideoDbContentType::MOVIES);
338 iLabel = 20434;
340 else if (m_rule.m_field == FieldTag)
342 VideoDbContentType type = VideoDbContentType::MOVIES;
343 if (m_type == "tvshows" ||
344 m_type == "episodes")
345 type = VideoDbContentType::TVSHOWS;
346 else if (m_type == "musicvideos")
347 type = VideoDbContentType::MUSICVIDEOS;
348 else if (m_type != "movies")
349 return;
351 videodatabase.GetTagsNav(basePath + "tags/", items, type);
352 iLabel = 20459;
354 else
355 { //! @todo Add browseability in here.
356 assert(false);
359 // sort the items
360 items.Sort(SortByLabel, SortOrderAscending, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING) ? SortAttributeIgnoreArticle : SortAttributeNone);
362 CGUIDialogSelect* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
363 pDialog->Reset();
364 pDialog->SetItems(items);
365 std::string strHeading =
366 StringUtils::Format(g_localizeStrings.Get(13401), g_localizeStrings.Get(iLabel));
367 pDialog->SetHeading(CVariant{std::move(strHeading)});
368 pDialog->SetMultiSelection(m_rule.m_field != FieldPlaylist && m_rule.m_field != FieldVirtualFolder);
370 if (!m_rule.m_parameter.empty())
371 pDialog->SetSelected(m_rule.m_parameter);
373 pDialog->Open();
374 if (pDialog->IsConfirmed())
376 m_rule.m_parameter.clear();
377 for (int i : pDialog->GetSelectedItems())
378 m_rule.m_parameter.push_back(items.Get(i)->GetLabel());
380 UpdateButtons();
382 pDialog->Reset();
385 std::pair<std::string, int> OperatorLabel(CDatabaseQueryRule::SEARCH_OPERATOR op)
387 return std::make_pair(PLAYLIST::CSmartPlaylistRule::GetLocalizedOperator(op), op);
390 std::vector<std::pair<std::string, int>> CGUIDialogSmartPlaylistRule::GetValidOperators(
391 const PLAYLIST::CSmartPlaylistRule& rule)
393 std::vector< std::pair<std::string, int> > labels;
394 switch (rule.GetFieldType(rule.m_field))
396 case CDatabaseQueryRule::TEXT_FIELD:
397 // text fields - add the usual comparisons
398 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_EQUALS));
399 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_DOES_NOT_EQUAL));
400 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_CONTAINS));
401 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_DOES_NOT_CONTAIN));
402 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_STARTS_WITH));
403 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_ENDS_WITH));
404 break;
406 case CDatabaseQueryRule::REAL_FIELD:
407 case CDatabaseQueryRule::NUMERIC_FIELD:
408 case CDatabaseQueryRule::SECONDS_FIELD:
409 // numerical fields - less than greater than
410 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_EQUALS));
411 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_DOES_NOT_EQUAL));
412 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_GREATER_THAN));
413 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_LESS_THAN));
414 break;
416 case CDatabaseQueryRule::DATE_FIELD:
417 // date field
418 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_AFTER));
419 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_BEFORE));
420 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_IN_THE_LAST));
421 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_NOT_IN_THE_LAST));
422 break;
424 case CDatabaseQueryRule::PLAYLIST_FIELD:
425 CONTROL_ENABLE(CONTROL_BROWSE);
426 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_EQUALS));
427 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_DOES_NOT_EQUAL));
428 break;
430 case CDatabaseQueryRule::BOOLEAN_FIELD:
431 CONTROL_DISABLE(CONTROL_VALUE);
432 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_TRUE));
433 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_FALSE));
434 break;
436 case CDatabaseQueryRule::TEXTIN_FIELD:
437 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_EQUALS));
438 labels.push_back(OperatorLabel(CDatabaseQueryRule::OPERATOR_DOES_NOT_EQUAL));
439 break;
441 return labels;
444 void CGUIDialogSmartPlaylistRule::OnCancel()
446 m_cancelled = true;
447 Close();
450 void CGUIDialogSmartPlaylistRule::OnField()
452 const auto fields = PLAYLIST::CSmartPlaylistRule::GetFields(m_type);
453 CGUIDialogSelect* dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
454 dialog->Reset();
455 dialog->SetHeading(CVariant{20427});
456 int selected = -1;
457 for (auto field = fields.begin(); field != fields.end(); field++)
459 dialog->Add(PLAYLIST::CSmartPlaylistRule::GetLocalizedField(*field));
460 if (*field == m_rule.m_field)
461 selected = std::distance(fields.begin(), field);
463 if (selected > -1)
464 dialog->SetSelected(selected);
465 dialog->Open();
466 int newSelected = dialog->GetSelectedItem();
467 // check if selection has changed
468 if (!dialog->IsConfirmed() || newSelected < 0 || newSelected == selected)
469 return;
471 m_rule.m_field = fields[newSelected];
472 // check if operator is still valid. if not, reset to first valid one
473 std::vector< std::pair<std::string, int> > validOperators = GetValidOperators(m_rule);
474 bool isValid = false;
475 for (auto op : validOperators)
476 if (std::get<0>(op) == std::get<0>(OperatorLabel(m_rule.m_operator)))
477 isValid = true;
478 if (!isValid)
479 m_rule.m_operator = (CDatabaseQueryRule::SEARCH_OPERATOR)std::get<1>(validOperators[0]);
481 m_rule.SetParameter("");
482 UpdateButtons();
485 void CGUIDialogSmartPlaylistRule::OnOperator()
487 const auto labels = GetValidOperators(m_rule);
488 CGUIDialogSelect* dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
489 dialog->Reset();
490 dialog->SetHeading(CVariant{ 16023 });
491 for (auto label : labels)
492 dialog->Add(std::get<0>(label));
493 dialog->SetSelected(PLAYLIST::CSmartPlaylistRule::GetLocalizedOperator(m_rule.m_operator));
494 dialog->Open();
495 int newSelected = dialog->GetSelectedItem();
496 // check if selection has changed
497 if (!dialog->IsConfirmed() || newSelected < 0)
498 return;
500 m_rule.m_operator = (CDatabaseQueryRule::SEARCH_OPERATOR)std::get<1>(labels[newSelected]);
501 UpdateButtons();
504 void CGUIDialogSmartPlaylistRule::UpdateButtons()
506 if (m_rule.m_field == 0)
507 m_rule.m_field = PLAYLIST::CSmartPlaylistRule::GetFields(m_type)[0];
508 SET_CONTROL_LABEL(CONTROL_FIELD, PLAYLIST::CSmartPlaylistRule::GetLocalizedField(m_rule.m_field));
510 CONTROL_ENABLE(CONTROL_VALUE);
511 if (PLAYLIST::CSmartPlaylistRule::IsFieldBrowseable(m_rule.m_field))
512 CONTROL_ENABLE(CONTROL_BROWSE);
513 else
514 CONTROL_DISABLE(CONTROL_BROWSE);
515 SET_CONTROL_LABEL(CONTROL_OPERATOR, std::get<0>(OperatorLabel(m_rule.m_operator)));
517 // update label2 appropriately
518 SET_CONTROL_LABEL2(CONTROL_VALUE, m_rule.GetParameter());
519 CGUIEditControl::INPUT_TYPE type = CGUIEditControl::INPUT_TYPE_TEXT;
520 CDatabaseQueryRule::FIELD_TYPE fieldType = m_rule.GetFieldType(m_rule.m_field);
521 switch (fieldType)
523 case CDatabaseQueryRule::TEXT_FIELD:
524 case CDatabaseQueryRule::PLAYLIST_FIELD:
525 case CDatabaseQueryRule::TEXTIN_FIELD:
526 case CDatabaseQueryRule::REAL_FIELD:
527 case CDatabaseQueryRule::NUMERIC_FIELD:
528 type = CGUIEditControl::INPUT_TYPE_TEXT;
529 break;
530 case CDatabaseQueryRule::DATE_FIELD:
531 if (m_rule.m_operator == CDatabaseQueryRule::OPERATOR_IN_THE_LAST ||
532 m_rule.m_operator == CDatabaseQueryRule::OPERATOR_NOT_IN_THE_LAST)
533 type = CGUIEditControl::INPUT_TYPE_TEXT;
534 else
535 type = CGUIEditControl::INPUT_TYPE_DATE;
536 break;
537 case CDatabaseQueryRule::SECONDS_FIELD:
538 type = CGUIEditControl::INPUT_TYPE_SECONDS;
539 break;
540 case CDatabaseQueryRule::BOOLEAN_FIELD:
541 type = CGUIEditControl::INPUT_TYPE_NUMBER;
542 break;
544 SendMessage(GUI_MSG_SET_TYPE, CONTROL_VALUE, type, 21420);
547 void CGUIDialogSmartPlaylistRule::OnInitWindow()
549 CGUIDialog::OnInitWindow();
551 UpdateButtons();
553 CGUIEditControl *editControl = dynamic_cast<CGUIEditControl*>(GetControl(CONTROL_VALUE));
554 if (editControl != NULL)
555 editControl->SetInputValidation(PLAYLIST::CSmartPlaylistRule::Validate, &m_rule);
558 void CGUIDialogSmartPlaylistRule::OnDeinitWindow(int nextWindowID)
560 CGUIDialog::OnDeinitWindow(nextWindowID);
562 // reset field spincontrolex
563 SendMessage(GUI_MSG_LABEL_RESET, CONTROL_FIELD);
564 // reset operator spincontrolex
565 SendMessage(GUI_MSG_LABEL_RESET, CONTROL_OPERATOR);
568 bool CGUIDialogSmartPlaylistRule::EditRule(PLAYLIST::CSmartPlaylistRule& rule,
569 const std::string& type)
571 CGUIDialogSmartPlaylistRule *editor = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSmartPlaylistRule>(WINDOW_DIALOG_SMART_PLAYLIST_RULE);
572 if (!editor) return false;
574 editor->m_rule = rule;
575 editor->m_type = type;
576 editor->Open();
577 rule = editor->m_rule;
578 return !editor->m_cancelled;