[cstdstring] demise Format, replacing with StringUtils::Format
[vuplus_xbmc] / xbmc / dialogs / GUIDialogSmartPlaylistRule.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "GUIDialogSmartPlaylistRule.h"
22 #include "GUIDialogFileBrowser.h"
23 #include "music/MusicDatabase.h"
24 #include "video/VideoDatabase.h"
25 #include "guilib/GUIWindowManager.h"
26 #include "GUIDialogSelect.h"
27 #include "filesystem/Directory.h"
28 #include "FileItem.h"
29 #include "guilib/GUIEditControl.h"
30 #include "guilib/LocalizeStrings.h"
31 #include "settings/MediaSourceSettings.h"
32 #include "storage/MediaManager.h"
33 #include "utils/LabelFormatter.h"
34 #include "utils/StringUtils.h"
35
36 #define CONTROL_FIELD           15
37 #define CONTROL_OPERATOR        16
38 #define CONTROL_VALUE           17
39 #define CONTROL_OK              18
40 #define CONTROL_CANCEL          19
41 #define CONTROL_BROWSE          20
42
43 using namespace std;
44
45 CGUIDialogSmartPlaylistRule::CGUIDialogSmartPlaylistRule(void)
46     : CGUIDialog(WINDOW_DIALOG_SMART_PLAYLIST_RULE, "SmartPlaylistRule.xml")
47 {
48   m_cancelled = false;
49   m_loadType = KEEP_IN_MEMORY;
50 }
51
52 CGUIDialogSmartPlaylistRule::~CGUIDialogSmartPlaylistRule()
53 {
54 }
55
56 bool CGUIDialogSmartPlaylistRule::OnBack(int actionID)
57 {
58   m_cancelled = true;
59   return CGUIDialog::OnBack(actionID);
60 }
61
62 bool CGUIDialogSmartPlaylistRule::OnMessage(CGUIMessage& message)
63 {
64   switch ( message.GetMessage() )
65   {
66   case GUI_MSG_CLICKED:
67     {
68       int iControl = message.GetSenderId();
69       if (iControl == CONTROL_OK)
70         OnOK();
71       else if (iControl == CONTROL_CANCEL)
72         OnCancel();
73       else if (iControl == CONTROL_VALUE)
74       {
75         CStdString parameter;
76         OnEditChanged(iControl, parameter);
77         m_rule.SetParameter(parameter);
78       }
79       else if (iControl == CONTROL_OPERATOR)
80         OnOperator();
81       else if (iControl == CONTROL_FIELD)
82         OnField();
83       else if (iControl == CONTROL_BROWSE)
84         OnBrowse();
85       return true;
86     }
87     break;
88
89   case GUI_MSG_VALIDITY_CHANGED:
90     CONTROL_ENABLE_ON_CONDITION(CONTROL_OK, message.GetParam1());
91     break;
92   }
93   return CGUIDialog::OnMessage(message);
94 }
95
96 void CGUIDialogSmartPlaylistRule::OnOK()
97 {
98   m_cancelled = false;
99   Close();
100 }
101
102 void CGUIDialogSmartPlaylistRule::OnBrowse()
103 {
104   CFileItemList items;
105   CMusicDatabase database;
106   database.Open();
107   CVideoDatabase videodatabase;
108   videodatabase.Open();
109
110   std::string basePath;
111   if (CSmartPlaylist::IsMusicType(m_type))
112     basePath = "musicdb://";
113   else
114     basePath = "videodb://";
115
116   VIDEODB_CONTENT_TYPE type = VIDEODB_CONTENT_MOVIES;
117   if (m_type.Equals("movies"))
118     basePath += "movies/";
119   else if (m_type.Equals("tvshows"))
120   {
121     type = VIDEODB_CONTENT_TVSHOWS;
122     basePath += "tvshows/";
123   }
124   else if (m_type.Equals("musicvideos"))
125   {
126     type = VIDEODB_CONTENT_MUSICVIDEOS;
127     basePath += "musicvideos/";
128   }
129   else if (m_type.Equals("episodes"))
130   {
131     if (m_rule.m_field == FieldGenre || m_rule.m_field == FieldYear ||
132         m_rule.m_field == FieldStudio)
133       type = VIDEODB_CONTENT_TVSHOWS;
134     else
135       type = VIDEODB_CONTENT_EPISODES;
136     basePath += "tvshows/";
137   }
138
139   int iLabel = 0;
140   if (m_rule.m_field == FieldGenre)
141   {
142     if (m_type.Equals("tvshows") || m_type.Equals("episodes") || m_type.Equals("movies"))
143       videodatabase.GetGenresNav(basePath + "genres/", items, type);
144     else if (m_type.Equals("songs") || m_type.Equals("albums") || m_type.Equals("artists") || m_type.Equals("mixed"))
145       database.GetGenresNav("musicdb://genres/",items);
146     if (m_type.Equals("musicvideos") || m_type.Equals("mixed"))
147     {
148       CFileItemList items2;
149       videodatabase.GetGenresNav("videodb://musicvideos/genres/",items2,VIDEODB_CONTENT_MUSICVIDEOS);
150       items.Append(items2);
151     }
152     iLabel = 515;
153   }
154   else if (m_rule.m_field == FieldCountry)
155   {
156     videodatabase.GetCountriesNav(basePath, items, type);
157     iLabel = 574;
158   }
159   else if (m_rule.m_field == FieldArtist || m_rule.m_field == FieldAlbumArtist)
160   {
161     if (CSmartPlaylist::IsMusicType(m_type))
162       database.GetArtistsNav("musicdb://artists/", items, m_rule.m_field == FieldAlbumArtist, -1);
163     if (m_type.Equals("musicvideos") || m_type.Equals("mixed"))
164     {
165       CFileItemList items2;
166       videodatabase.GetMusicVideoArtistsByName("", items2);
167       items.Append(items2);
168     }
169     iLabel = 557;
170   }
171   else if (m_rule.m_field == FieldAlbum)
172   {
173     if (CSmartPlaylist::IsMusicType(m_type))
174       database.GetAlbumsNav("musicdb://albums/", items);
175     if (m_type.Equals("musicvideos") || m_type.Equals("mixed"))
176     {
177       CFileItemList items2;
178       videodatabase.GetMusicVideoAlbumsByName("", items2);
179       items.Append(items2);
180     }
181     iLabel = 558;
182   }
183   else if (m_rule.m_field == FieldActor)
184   {
185     videodatabase.GetActorsNav(basePath + "actors/",items,type);
186     iLabel = 20337;
187   }
188   else if (m_rule.m_field == FieldYear)
189   {
190     if (CSmartPlaylist::IsMusicType(m_type))
191       database.GetYearsNav("musicdb://years/", items);
192     if (!m_type.Equals("songs") && !m_type.Equals("albums") && !m_type.Equals("artists"))
193     {
194       CFileItemList items2;
195       videodatabase.GetYearsNav(basePath + "years/", items2, type);
196       items.Append(items2);
197     }
198     iLabel = 562;
199   }
200   else if (m_rule.m_field == FieldDirector)
201   {
202     videodatabase.GetDirectorsNav(basePath + "directors/", items, type);
203     iLabel = 20339;
204   }
205   else if (m_rule.m_field == FieldStudio)
206   {
207     videodatabase.GetStudiosNav(basePath + "studios/", items, type);
208     iLabel = 572;
209   }
210   else if (m_rule.m_field == FieldWriter)
211   {
212     videodatabase.GetWritersNav(basePath, items, type);
213     iLabel = 20417;
214   }
215   else if (m_rule.m_field == FieldTvShowTitle ||
216           (m_type.Equals("tvshows") && m_rule.m_field == FieldTitle))
217   {
218     videodatabase.GetTvShowsNav(basePath + "titles/", items);
219     iLabel = 20343;
220   }
221   else if (m_rule.m_field == FieldTitle)
222   {
223     if (m_type.Equals("songs"))
224     {
225       database.GetSongsNav("musicdb://songs/", items, -1, -1, -1);
226       iLabel = 134;
227     }
228     else if (m_type.Equals("movies"))
229     {
230       videodatabase.GetMoviesNav(basePath + "titles/", items);
231       iLabel = 20342;
232     }
233     else if (m_type.Equals("episodes"))
234     {
235       videodatabase.GetEpisodesNav(basePath + "titles/-1/-1/", items);
236       // we need to replace the db label (<season>x<episode> <title>) with the title only
237       CLabelFormatter format("%T", "");
238       for (int i = 0; i < items.Size(); i++)
239         format.FormatLabel(items[i].get());
240       iLabel = 20360;
241     }
242     else if (m_type.Equals("musicvideos"))
243     {
244       videodatabase.GetMusicVideosNav(basePath + "titles/", items);
245       iLabel = 20389;
246     }
247     else
248       assert(false);
249   }
250   else if (m_rule.m_field == FieldPlaylist || m_rule.m_field == FieldVirtualFolder)
251   {
252     // use filebrowser to grab another smart playlist
253
254     // Note: This can cause infinite loops (playlist that refers to the same playlist) but I don't
255     //       think there's any decent way to deal with this, as the infinite loop may be an arbitrary
256     //       number of playlists deep, eg playlist1 -> playlist2 -> playlist3 ... -> playlistn -> playlist1
257     CStdString path = "special://videoplaylists/";
258     if (m_type.Equals("songs") || m_type.Equals("albums") || m_type.Equals("artists"))
259       path = "special://musicplaylists/";
260     XFILE::CDirectory::GetDirectory(path, items, ".xsp", XFILE::DIR_FLAG_NO_FILE_DIRS);
261     for (int i = 0; i < items.Size(); i++)
262     {
263       CFileItemPtr item = items[i];
264       CSmartPlaylist playlist;
265       // don't list unloadable smartplaylists or any referencable smartplaylists
266       // which do not match the type of the current smartplaylist
267       if (!playlist.Load(item->GetPath()) ||
268          (m_rule.m_field == FieldPlaylist &&
269          (!CSmartPlaylist::CheckTypeCompatibility(m_type, playlist.GetType()) ||
270          (!playlist.GetGroup().empty() || playlist.IsGroupMixed()))))
271       {
272         items.Remove(i);
273         i -= 1;
274         continue;
275       }
276
277       if (!playlist.GetName().empty())
278         item->SetLabel(playlist.GetName());
279     }
280     iLabel = 559;
281   }
282   else if (m_rule.m_field == FieldPath)
283   {
284     VECSOURCES sources;
285     if (m_type == "songs" || m_type == "mixed")
286       sources = *CMediaSourceSettings::Get().GetSources("music");
287     if (m_type != "songs")
288     {
289       VECSOURCES sources2 = *CMediaSourceSettings::Get().GetSources("video");
290       sources.insert(sources.end(),sources2.begin(),sources2.end());
291     }
292     g_mediaManager.GetLocalDrives(sources);
293     
294     CStdString path = m_rule.GetParameter();
295     CGUIDialogFileBrowser::ShowAndGetDirectory(sources, g_localizeStrings.Get(657), path, false);
296     if (m_rule.m_parameter.size() > 0)
297       m_rule.m_parameter.clear();
298     if (!path.empty())
299       m_rule.m_parameter.push_back(path);
300
301     UpdateButtons();
302     return;
303   }
304   else if (m_rule.m_field == FieldSet)
305   {
306     videodatabase.GetSetsNav("videodb://movies/sets/", items, VIDEODB_CONTENT_MOVIES);
307     iLabel = 20434;
308   }
309   else if (m_rule.m_field == FieldTag)
310   {
311     VIDEODB_CONTENT_TYPE type = VIDEODB_CONTENT_MOVIES;
312     if (m_type == "tvshows")
313       type = VIDEODB_CONTENT_TVSHOWS;
314     else if (m_type == "musicvideos")
315       type = VIDEODB_CONTENT_MUSICVIDEOS;
316     else if (m_type != "movies")
317       return;
318
319     videodatabase.GetTagsNav(basePath + "tags/", items, type);
320     iLabel = 20459;
321   }
322   else
323   { // TODO: Add browseability in here.
324     assert(false);
325   }
326
327   // sort the items
328   items.Sort(SortByLabel, SortOrderAscending);
329
330   CGUIDialogSelect* pDialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
331   pDialog->Reset();
332   pDialog->SetItems(&items);
333   CStdString strHeading = StringUtils::Format(g_localizeStrings.Get(13401).c_str(), g_localizeStrings.Get(iLabel).c_str());
334   pDialog->SetHeading(strHeading);
335   pDialog->SetMultiSelection(m_rule.m_field != FieldPlaylist && m_rule.m_field != FieldVirtualFolder);
336
337   if (!m_rule.m_parameter.empty())
338     pDialog->SetSelected(m_rule.m_parameter);
339
340   pDialog->DoModal();
341   if (pDialog->IsConfirmed())
342   {
343     const CFileItemList &items = pDialog->GetSelectedItems();
344     m_rule.m_parameter.clear();
345     for (int index = 0; index < items.Size(); index++)
346       m_rule.m_parameter.push_back(items[index]->GetLabel());
347
348     UpdateButtons();
349   }
350   pDialog->Reset();
351 }
352
353 void CGUIDialogSmartPlaylistRule::OnCancel()
354 {
355   m_cancelled = true;
356   Close();
357 }
358
359 void CGUIDialogSmartPlaylistRule::OnField()
360 {
361   CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_FIELD);
362   OnMessage(msg);
363   m_rule.m_field = (Field)msg.GetParam1();
364
365   UpdateButtons();
366 }
367
368 void CGUIDialogSmartPlaylistRule::OnOperator()
369 {
370   CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_OPERATOR);
371   OnMessage(msg);
372   m_rule.m_operator = (CDatabaseQueryRule::SEARCH_OPERATOR)msg.GetParam1();
373
374   UpdateButtons();
375 }
376
377 void CGUIDialogSmartPlaylistRule::UpdateButtons()
378 {
379   // update the field control
380   SendMessage(GUI_MSG_ITEM_SELECT, CONTROL_FIELD, m_rule.m_field);
381   CGUIMessage msg2(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_FIELD);
382   OnMessage(msg2);
383   m_rule.m_field = (Field)msg2.GetParam1();
384
385   // and now update the operator set
386   SendMessage(GUI_MSG_LABEL_RESET, CONTROL_OPERATOR);
387
388   CONTROL_ENABLE(CONTROL_VALUE);
389   if (CSmartPlaylistRule::IsFieldBrowseable(m_rule.m_field))
390     CONTROL_ENABLE(CONTROL_BROWSE);
391   else
392     CONTROL_DISABLE(CONTROL_BROWSE);
393
394   switch (m_rule.GetFieldType(m_rule.m_field))
395   {
396   case CDatabaseQueryRule::TEXT_FIELD:
397     // text fields - add the usual comparisons
398     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_EQUALS);
399     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_DOES_NOT_EQUAL);
400     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_CONTAINS);
401     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_DOES_NOT_CONTAIN);
402     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_STARTS_WITH);
403     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_ENDS_WITH);
404     break;
405
406   case CDatabaseQueryRule::NUMERIC_FIELD:
407   case CDatabaseQueryRule::SECONDS_FIELD:
408     // numerical fields - less than greater than
409     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_EQUALS);
410     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_DOES_NOT_EQUAL);
411     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_GREATER_THAN);
412     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_LESS_THAN);
413     break;
414
415   case CDatabaseQueryRule::DATE_FIELD:
416     // date field
417     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_AFTER);
418     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_BEFORE);
419     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_IN_THE_LAST);
420     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_NOT_IN_THE_LAST);
421     break;
422
423   case CDatabaseQueryRule::PLAYLIST_FIELD:
424     CONTROL_ENABLE(CONTROL_BROWSE);
425     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_EQUALS);
426     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_DOES_NOT_EQUAL);
427     break;
428
429   case CDatabaseQueryRule::BOOLEAN_FIELD:
430     CONTROL_DISABLE(CONTROL_VALUE);
431     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_TRUE);
432     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_FALSE);
433     break;
434
435   case CDatabaseQueryRule::TEXTIN_FIELD:
436     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_EQUALS);
437     AddOperatorLabel(CDatabaseQueryRule::OPERATOR_DOES_NOT_EQUAL);
438     break;
439   }
440
441   // check our operator is valid, and update if not
442   SendMessage(GUI_MSG_ITEM_SELECT, CONTROL_OPERATOR, m_rule.m_operator);
443   CGUIMessage selected(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_OPERATOR);
444   OnMessage(selected);
445   m_rule.m_operator = (CDatabaseQueryRule::SEARCH_OPERATOR)selected.GetParam1();
446
447   // update the parameter edit control appropriately
448   SET_CONTROL_LABEL2(CONTROL_VALUE, m_rule.GetParameter());
449   CGUIEditControl::INPUT_TYPE type = CGUIEditControl::INPUT_TYPE_TEXT;
450   CDatabaseQueryRule::FIELD_TYPE fieldType = m_rule.GetFieldType(m_rule.m_field);
451   switch (fieldType)
452   {
453   case CDatabaseQueryRule::TEXT_FIELD:
454   case CDatabaseQueryRule::PLAYLIST_FIELD:
455   case CDatabaseQueryRule::TEXTIN_FIELD:
456   case CDatabaseQueryRule::NUMERIC_FIELD:
457     type = CGUIEditControl::INPUT_TYPE_TEXT;
458     break;
459   case CDatabaseQueryRule::DATE_FIELD:
460     if (m_rule.m_operator == CDatabaseQueryRule::OPERATOR_IN_THE_LAST ||
461         m_rule.m_operator == CDatabaseQueryRule::OPERATOR_NOT_IN_THE_LAST)
462       type = CGUIEditControl::INPUT_TYPE_TEXT;
463     else
464       type = CGUIEditControl::INPUT_TYPE_DATE;
465     break;
466   case CDatabaseQueryRule::SECONDS_FIELD:
467     type = CGUIEditControl::INPUT_TYPE_SECONDS;
468     break;
469   case CDatabaseQueryRule::BOOLEAN_FIELD:
470     type = CGUIEditControl::INPUT_TYPE_NUMBER;
471     break;
472   }
473   SendMessage(GUI_MSG_SET_TYPE, CONTROL_VALUE, type, 21420);
474 }
475
476 void CGUIDialogSmartPlaylistRule::AddOperatorLabel(CDatabaseQueryRule::SEARCH_OPERATOR op)
477 {
478   CGUIMessage select(GUI_MSG_LABEL_ADD, GetID(), CONTROL_OPERATOR, op);
479   select.SetLabel(CSmartPlaylistRule::GetLocalizedOperator(op));
480   OnMessage(select);
481 }
482
483 void CGUIDialogSmartPlaylistRule::OnWindowLoaded()
484 {
485   CGUIWindow::OnWindowLoaded();
486   ChangeButtonToEdit(CONTROL_VALUE, true); // true for single label
487 }
488
489 void CGUIDialogSmartPlaylistRule::OnInitWindow()
490 {
491   CGUIDialog::OnInitWindow();
492
493   SendMessage(GUI_MSG_LABEL_RESET, CONTROL_FIELD);
494   // add the fields to the field spincontrol
495   vector<Field> fields = CSmartPlaylistRule::GetFields(m_type);
496   for (unsigned int i = 0; i < fields.size(); i++)
497   {
498     CGUIMessage msg(GUI_MSG_LABEL_ADD, GetID(), CONTROL_FIELD, fields[i]);
499     msg.SetLabel(CSmartPlaylistRule::GetLocalizedField(fields[i]));
500     OnMessage(msg);
501   }
502   UpdateButtons();
503
504   CGUIEditControl *editControl = (CGUIEditControl*)GetControl(CONTROL_VALUE);
505   if (editControl != NULL)
506     editControl->SetInputValidation(CSmartPlaylistRule::Validate, &m_rule);
507 }
508
509 void CGUIDialogSmartPlaylistRule::OnDeinitWindow(int nextWindowID)
510 {
511   CGUIDialog::OnDeinitWindow(nextWindowID);
512
513   // reset field spincontrolex
514   SendMessage(GUI_MSG_LABEL_RESET, CONTROL_FIELD);
515   // reset operator spincontrolex
516   SendMessage(GUI_MSG_LABEL_RESET, CONTROL_OPERATOR);
517 }
518
519 bool CGUIDialogSmartPlaylistRule::EditRule(CSmartPlaylistRule &rule, const CStdString& type)
520 {
521   CGUIDialogSmartPlaylistRule *editor = (CGUIDialogSmartPlaylistRule *)g_windowManager.GetWindow(WINDOW_DIALOG_SMART_PLAYLIST_RULE);
522   if (!editor) return false;
523
524   editor->m_rule = rule;
525   editor->m_type = type == "mixed" ? "songs" : type;
526   editor->DoModal(g_windowManager.GetActiveWindow());
527   rule = editor->m_rule;
528   return !editor->m_cancelled;
529 }
530