MythTV  master
smartplaylist.cpp
Go to the documentation of this file.
1 // c/c++
2 #include <unistd.h>
3 #include <iostream>
4 using namespace std;
5 
6 // qt
7 #include <QKeyEvent>
8 #include <QSqlDriver>
9 #include <QSqlField>
10 #include <utility>
11 
12 // mythtv
13 #include <mythcontext.h>
14 #include <mythmainwindow.h>
15 #include <mythdb.h>
16 #include <mythuihelper.h>
17 #include <mythscreentype.h>
18 #include <mythuitext.h>
19 #include <mythuitextedit.h>
20 #include <mythuibuttonlist.h>
21 #include <mythuibutton.h>
22 #include <mythuispinbox.h>
23 #include <mythuicheckbox.h>
24 #include <mythdialogbox.h>
25 #include <mythdate.h>
26 #include <musicmetadata.h>
27 
28 // mythmusic
29 #include "musicdata.h"
30 #include "smartplaylist.h"
31 #include "musiccommon.h"
32 
34 {
35  QString name;
36  QString sqlName;
38  int minValue;
39  int maxValue;
41 };
42 
44 {
45  { "", "", ftString, 0, 0, 0 },
46  { "Artist", "music_artists.artist_name", ftString, 0, 0, 0 },
47  { "Album", "music_albums.album_name", ftString, 0, 0, 0 },
48  { "Title", "music_songs.name", ftString, 0, 0, 0 },
49  { "Genre", "music_genres.genre", ftString, 0, 0, 0 },
50  { "Year", "music_songs.year", ftNumeric, 1900, 2099, 2000 },
51  { "Track No.", "music_songs.track", ftNumeric, 0, 99, 0 },
52  { "Rating", "music_songs.rating", ftNumeric, 0, 10, 0 },
53  { "Play Count", "music_songs.numplays", ftNumeric, 0, 9999, 0 },
54  { "Compilation", "music_albums.compilation", ftBoolean, 0, 0, 0 },
55  { "Comp. Artist", "music_comp_artists.artist_name", ftString, 0, 0, 0 },
56  { "Last Play", "FROM_DAYS(TO_DAYS(music_songs.lastplay))",
57  ftDate, 0, 0, 0 },
58  { "Date Imported", "FROM_DAYS(TO_DAYS(music_songs.date_entered))",
59  ftDate, 0, 0, 0 },
60 };
61 
63 {
64  QString name;
66  bool stringOnly;
68 };
69 
71 {
72  { "is equal to", 1, false, true },
73  { "is not equal to", 1, false, true },
74  { "is greater than", 1, false, false },
75  { "is less than", 1, false, false },
76  { "starts with", 1, true, false },
77  { "ends with", 1, true, false },
78  { "contains", 1, true, false },
79  { "does not contain", 1, true, false },
80  { "is between", 2, false, false },
81  { "is set", 0, false, false },
82  { "is not set", 0, false, false },
83 };
84 
85 static int SmartPLOperatorsCount = sizeof(SmartPLOperators) / sizeof(SmartPLOperators[0]);
86 static int SmartPLFieldsCount = sizeof(SmartPLFields) / sizeof(SmartPLFields[0]);
87 
88 static SmartPLOperator *lookupOperator(const QString& name)
89 {
90  for (int x = 0; x < SmartPLOperatorsCount; x++)
91  {
92  if (SmartPLOperators[x].name == name)
93  return &SmartPLOperators[x];
94  }
95  return nullptr;
96 }
97 
98 static SmartPLField *lookupField(const QString& name)
99 {
100  for (int x = 0; x < SmartPLFieldsCount; x++)
101  {
102  if (SmartPLFields[x].name == name)
103  return &SmartPLFields[x];
104  }
105  return nullptr;
106 }
107 
108 QString formattedFieldValue(const QVariant &value)
109 {
110  QSqlField field("", value.type());
111  if (value.isNull())
112  field.clear();
113  else
114  field.setValue(value);
115 
116  MSqlQuery query(MSqlQuery::InitCon());
117  QString result = QString::fromUtf8(query.driver()->formatValue(field).toLatin1().data());
118  return result;
119 }
120 
121 static QString evaluateDateValue(QString sDate)
122 {
123  if (sDate.startsWith("$DATE"))
124  {
125  QDate date = MythDate::current().toLocalTime().date();
126 
127  if (sDate.length() > 9)
128  {
129  bool bNegative = false;
130  if (sDate[6] == '-')
131  bNegative = true;
132 
133  if (sDate.endsWith(" days"))
134  sDate = sDate.left(sDate.length() - 5);
135 
136  int nDays = sDate.mid(8).toInt();
137  if (bNegative)
138  nDays = -nDays;
139 
140  date = date.addDays(nDays);
141  }
142 
143  return date.toString(Qt::ISODate);
144  }
145 
146  return sDate;
147 }
148 
149 QString getCriteriaSQL(const QString& fieldName, QString operatorName,
150  QString value1, QString value2)
151 {
152  QString result;
153 
154  if (fieldName.isEmpty())
155  return result;
156 
157  SmartPLField *Field;
158  Field = lookupField(fieldName);
159  if (!Field)
160  {
161  return "";
162  }
163 
164  result = Field->sqlName;
165 
166  SmartPLOperator *Operator;
167  Operator = lookupOperator(std::move(operatorName));
168  if (!Operator)
169  {
170  return QString();
171  }
172 
173  // convert boolean and date values
174  if (Field->type == ftBoolean)
175  {
176  // compilation field uses 0 = false; 1 = true
177  value1 = (value1 == "Yes") ? "1":"0";
178  value2 = (value2 == "Yes") ? "1":"0";
179  }
180  else if (Field->type == ftDate)
181  {
182  value1 = evaluateDateValue(value1);
183  value2 = evaluateDateValue(value2);
184  }
185 
186  if (Operator->name == "is equal to")
187  {
188  result = result + " = " + formattedFieldValue(value1);
189  }
190  else if (Operator->name == "is not equal to")
191  {
192  result = result + " != " + formattedFieldValue(value1);
193  }
194  else if (Operator->name == "is greater than")
195  {
196  result = result + " > " + formattedFieldValue(value1);
197  }
198  else if (Operator->name == "is less than")
199  {
200  result = result + " < " + formattedFieldValue(value1);
201  }
202  else if (Operator->name == "starts with")
203  {
204  result = result + " LIKE " + formattedFieldValue(value1 + QString("%"));
205  }
206  else if (Operator->name == "ends with")
207  {
208  result = result + " LIKE " + formattedFieldValue(QString("%") + value1);
209  }
210  else if (Operator->name == "contains")
211  {
212  result = result + " LIKE " + formattedFieldValue(QString("%") + value1 + "%");
213  }
214  else if (Operator->name == "does not contain")
215  {
216  result = result + " NOT LIKE " + formattedFieldValue(QString("%") + value1 + "%");
217  }
218  else if (Operator->name == "is between")
219  {
220  result = result + " BETWEEN " + formattedFieldValue(value1) +
221  " AND " + formattedFieldValue(value2);
222  }
223  else if (Operator->name == "is set")
224  {
225  result = result + " IS NOT NULL";
226  }
227  else if (Operator->name == "is not set")
228  {
229  result = result + " IS NULL";
230  }
231  else
232  {
233  result.clear();
234  LOG(VB_GENERAL, LOG_ERR,
235  QString("getCriteriaSQL(): invalid operator '%1'")
236  .arg(Operator->name));
237  }
238 
239  return result;
240 }
241 
242 QString getOrderBySQL(const QString& orderByFields)
243 {
244  if (orderByFields.isEmpty())
245  return QString();
246 
247  QStringList list = orderByFields.split(",");
248  QString fieldName, result, order;
249  bool bFirst = true;
250 
251  for (int x = 0; x < list.count(); x++)
252  {
253  fieldName = list[x].trimmed();
254  SmartPLField *Field;
255  Field = lookupField(fieldName.left(fieldName.length() - 4));
256  if (Field)
257  {
258  if (fieldName.right(3) == "(D)")
259  order = " DESC";
260  else
261  order = " ASC";
262 
263  if (bFirst)
264  {
265  bFirst = false;
266  result = " ORDER BY " + Field->sqlName + order;
267  }
268  else
269  result += ", " + Field->sqlName + order;
270  }
271  }
272 
273  return result;
274 }
275 
276 QString getSQLFieldName(QString fieldName)
277 {
278  SmartPLField *Field;
279  Field = lookupField(std::move(fieldName));
280  if (!Field)
281  {
282  return "";
283  }
284 
285  return Field->sqlName;
286 }
287 
288 /*
290 */
291 
293 {
294  if (m_field.isEmpty())
295  return QString();
296 
297  QString result;
298 
299  result = getCriteriaSQL(m_field, m_operator, m_value1, m_value2);
300 
301  return result;
302 }
303 
304 // return false on error
305 bool SmartPLCriteriaRow::saveToDatabase(int smartPlaylistID)
306 {
307  // save playlistitem to database
308 
309  if (m_field.isEmpty())
310  return true;
311 
312  MSqlQuery query(MSqlQuery::InitCon());
313  query.prepare("INSERT INTO music_smartplaylist_items (smartplaylistid, field, operator,"
314  " value1, value2)"
315  "VALUES (:SMARTPLAYLISTID, :FIELD, :OPERATOR, :VALUE1, :VALUE2);");
316  query.bindValue(":SMARTPLAYLISTID", smartPlaylistID);
317  query.bindValueNoNull(":FIELD", m_field);
318  query.bindValueNoNull(":OPERATOR", m_operator);
319  query.bindValueNoNull(":VALUE1", m_value1);
320  query.bindValueNoNull(":VALUE2", m_value2);
321 
322  if (!query.exec())
323  {
324  MythDB::DBError("Inserting new smartplaylist item", query);
325  return false;
326  }
327 
328  return true;
329 }
330 
332 {
333  SmartPLOperator *PLOperator = lookupOperator(m_operator);
334  if (PLOperator)
335  {
336  QString result;
337  if (PLOperator->noOfArguments == 0)
338  result = m_field + " " + m_operator;
339  else if (PLOperator->noOfArguments == 1)
340  result = m_field + " " + m_operator + " " + m_value1;
341  else
342  {
343  result = m_field + " " + m_operator + " " + m_value1;
344  result += " " + tr("and") + " " + m_value2;
345  }
346 
347  return result;
348  }
349 
350  return QString();
351 }
352 
353 /*
354 ---------------------------------------------------------------------
355 */
356 
358 {
359  while (!m_criteriaRows.empty())
360  {
361  delete m_criteriaRows.back();
362  m_criteriaRows.pop_back();
363  }
364 
365  delete m_tempCriteriaRow;
366 }
367 
368 
370 {
371  if (!LoadWindowFromXML("music-ui.xml", "smartplaylisteditor", this))
372  return false;
373 
374  bool err = false;
375 
376  UIUtilE::Assign(this, m_categorySelector, "categoryselector", &err);
377  UIUtilE::Assign(this, m_categoryButton, "categorybutton", &err);
378  UIUtilE::Assign(this, m_titleEdit, "titleedit", &err);
379  UIUtilE::Assign(this, m_matchSelector, "matchselector", &err);
380  UIUtilE::Assign(this, m_criteriaList, "criterialist", &err);
381  UIUtilE::Assign(this, m_orderBySelector, "orderbyselector", &err);
382  UIUtilE::Assign(this, m_orderByButton, "orderbybutton", &err);
383  UIUtilE::Assign(this, m_matchesText, "matchestext", &err);
384  UIUtilE::Assign(this, m_limitSpin, "limitspin", &err);
385 
386  UIUtilE::Assign(this, m_cancelButton, "cancelbutton", &err);
387  UIUtilE::Assign(this, m_saveButton, "savebutton", &err);
388  UIUtilE::Assign(this, m_showResultsButton, "showresultsbutton", &err);
389 
390  if (err)
391  {
392  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'smartplaylisteditor'");
393  return false;
394  }
395 
396  getSmartPlaylistCategories();
397 
398  new MythUIButtonListItem(m_matchSelector, tr("All"));
399  new MythUIButtonListItem(m_matchSelector, tr("Any"));
400  connect(m_matchSelector, SIGNAL(itemSelected(MythUIButtonListItem*)), SLOT(updateMatches()));
401 
402  for (int x = 0; x < SmartPLFieldsCount; x++)
403  {
404  if (SmartPLFields[x].name == "")
405  new MythUIButtonListItem(m_orderBySelector, SmartPLFields[x].name);
406  else
407  new MythUIButtonListItem(m_orderBySelector, SmartPLFields[x].name + " (A)");
408  }
409 
410  m_limitSpin->SetRange(0, 9999, 10);
411 
412  connect(m_orderByButton, SIGNAL(Clicked()), SLOT(orderByClicked()));
413  connect(m_saveButton, SIGNAL(Clicked()), SLOT(saveClicked()));
414  connect(m_cancelButton, SIGNAL(Clicked()), SLOT(Close()));
415  connect(m_categoryButton, SIGNAL(Clicked()), SLOT(showCategoryMenu()));
416  connect(m_showResultsButton, SIGNAL(Clicked()), SLOT(showResultsClicked()));
417  connect(m_criteriaList, SIGNAL(itemClicked(MythUIButtonListItem*)), SLOT(editCriteria()));
418 
419  BuildFocusList();
420 
421  return true;
422 }
423 
425 {
426  if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
427  return true;
428 
429  QStringList actions;
430  bool handled = GetMythMainWindow()->TranslateKeyPress("Music", event, actions);
431 
432  for (int i = 0; i < actions.size() && !handled; i++)
433  {
434  QString action = actions[i];
435  handled = true;
436 
437  if (action == "MENU")
438  {
439  showCriteriaMenu();
440  }
441  else if (action == "DELETE" && GetFocusWidget() == m_criteriaList)
442  {
443  deleteCriteria();
444  }
445  else if (action == "EDIT" && GetFocusWidget() == m_criteriaList)
446  {
447  editCriteria();
448  }
449  else
450  handled = false;
451  }
452 
453  if (!handled && MythScreenType::keyPressEvent(event))
454  handled = true;
455 
456  return handled;
457 }
458 
460 {
461  if (event->type() == DialogCompletionEvent::kEventType)
462  {
464 
465  // make sure the user didn't ESCAPE out of the menu
466  if (dce->GetResult() < 0)
467  return;
468 
469  QString resultid = dce->GetId();
470  QString resulttext = dce->GetResultText();
471  if (resultid == "categorymenu")
472  {
473  if (resulttext == tr("New Category"))
474  {
475  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
476  QString label = tr("Enter Name Of New Category");
477 
478  MythTextInputDialog *input = new MythTextInputDialog(popupStack, label);
479 
480  connect(input, SIGNAL(haveResult(QString)),
481  SLOT(newCategory(QString)));
482 
483  if (input->Create())
484  popupStack->AddScreen(input);
485  else
486  delete input;
487  }
488  else if (resulttext == tr("Delete Category"))
489  startDeleteCategory(m_categorySelector->GetValue());
490  else if (resulttext == tr("Rename Category"))
491  {
492  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
493  QString label = tr("Enter New Name For Category: %1").arg(m_categorySelector->GetValue());
494 
495  MythTextInputDialog *input = new MythTextInputDialog(popupStack, label);
496 
497  connect(input, SIGNAL(haveResult(QString)),
498  SLOT(renameCategory(QString)));
499 
500  if (input->Create())
501  popupStack->AddScreen(input);
502  else
503  delete input;
504  }
505  }
506  }
507 }
508 
510 {
511  if (m_tempCriteriaRow)
512  {
513  delete m_tempCriteriaRow;
514  m_tempCriteriaRow = nullptr;
515  }
516 
517  MythUIButtonListItem *item = m_criteriaList->GetItemCurrent();
518 
519  if (!item)
520  return;
521 
522  SmartPLCriteriaRow *row = item->GetData().value<SmartPLCriteriaRow*>();
523 
524  if (!row)
525  return;
526 
527  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
528 
529  CriteriaRowEditor *editor = new CriteriaRowEditor(popupStack, row);
530 
531  if (!editor->Create())
532  {
533  delete editor;
534  return;
535  }
536 
537  connect(editor, SIGNAL(criteriaChanged()), SLOT(criteriaChanged()));
538 
539  popupStack->AddScreen(editor);
540 }
541 
543 {
544  // make sure we have something to delete
545  MythUIButtonListItem *item = m_criteriaList->GetItemCurrent();
546 
547  if (!item)
548  return;
549 
550  ShowOkPopup(tr("Delete Criteria?"), this, SLOT(doDeleteCriteria(bool)), true);
551 }
552 
554 {
555  if (doit)
556  {
557  MythUIButtonListItem *item = m_criteriaList->GetItemCurrent();
558  if (!item)
559  return;
560 
561  SmartPLCriteriaRow *row = item->GetData().value<SmartPLCriteriaRow*>();
562 
563  if (!row)
564  return;
565 
566  m_criteriaRows.removeAll(row);
567  m_criteriaList->RemoveItem(item);
568 
569  criteriaChanged();
570  }
571 }
572 
574 {
575  /*
576  SmartPLCriteriaRow *row = new SmartPLCriteriaRow();
577  m_criteriaRows.append(row);
578 
579  MythUIButtonListItem *item = new MythUIButtonListItem(m_criteriaList, row->toString(), qVariantFromValue(row));
580 
581  m_criteriaList->SetItemCurrent(item);
582 
583  editCriteria();
584  */
585 
586  delete m_tempCriteriaRow;
587  m_tempCriteriaRow = new SmartPLCriteriaRow();
588 
589  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
590 
591  CriteriaRowEditor *editor = new CriteriaRowEditor(popupStack, m_tempCriteriaRow);
592 
593  if (!editor->Create())
594  {
595  delete editor;
596  return;
597  }
598 
599  connect(editor, SIGNAL(criteriaChanged()), SLOT(criteriaChanged()));
600 
601  popupStack->AddScreen(editor);
602 }
603 
605 {
606  MythUIButtonListItem *item = nullptr;
607 
608  if (m_tempCriteriaRow)
609  {
610  // this is a new row so add it to the list
611  m_criteriaRows.append(m_tempCriteriaRow);
612 
613  item = new MythUIButtonListItem(m_criteriaList, m_tempCriteriaRow->toString(),
614  qVariantFromValue(m_tempCriteriaRow));
615 
616  m_criteriaList->SetItemCurrent(item);
617 
618  m_tempCriteriaRow = nullptr;
619  }
620  else
621  {
622  // update the existing row
623  item = m_criteriaList->GetItemCurrent();
624  if (!item)
625  return;
626 
627  SmartPLCriteriaRow *row = item->GetData().value<SmartPLCriteriaRow*>();
628 
629  if (!row)
630  return;
631 
632  item->SetText(row->toString());
633  }
634 
635  updateMatches();
636 }
637 
639 {
640  QString label = tr("Category Actions");
641 
642  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
643 
644  MythDialogBox *menu = new MythDialogBox(label, popupStack, "actionmenu");
645 
646  if (!menu->Create())
647  {
648  delete menu;
649  return;
650  }
651 
652  menu->SetReturnEvent(this, "categorymenu");
653 
654  menu->AddButton(tr("New Category"), nullptr);
655  menu->AddButton(tr("Delete Category"), nullptr);
656  menu->AddButton(tr("Rename Category"), nullptr);
657 
658  popupStack->AddScreen(menu);
659 }
660 
662 {
663  QString label = tr("Criteria Actions");
664 
665  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
666 
667  MythDialogBox *menu = new MythDialogBox(label, popupStack, "actionmenu");
668 
669  if (!menu->Create())
670  {
671  delete menu;
672  return;
673  }
674 
675  menu->SetReturnEvent(this, "criteriamenu");
676 
677  MythUIButtonListItem *item = m_criteriaList->GetItemCurrent();
678 
679  if (item)
680  menu->AddButton(tr("Edit Criteria"), SLOT(editCriteria()));
681 
682  menu->AddButton(tr("Add Criteria"), SLOT(addCriteria()));
683 
684  if (item)
685  menu->AddButton(tr("Delete Criteria"), SLOT(deleteCriteria()));
686 
687  popupStack->AddScreen(menu);
688 }
689 
691 {
692  m_saveButton->SetEnabled((m_playlistIsValid && !m_titleEdit->GetText().isEmpty()));
693 }
694 
696 {
697  QString sql =
698  "SELECT count(*) "
699  "FROM music_songs "
700  "LEFT JOIN music_artists ON "
701  " music_songs.artist_id=music_artists.artist_id "
702  "LEFT JOIN music_albums ON music_songs.album_id=music_albums.album_id "
703  "LEFT JOIN music_artists AS music_comp_artists ON "
704  " music_albums.artist_id=music_comp_artists.artist_id "
705  "LEFT JOIN music_genres ON music_songs.genre_id=music_genres.genre_id ";
706 
707  sql += getWhereClause();
708 
709  m_matchesCount = 0;
710 
711  MSqlQuery query(MSqlQuery::InitCon());
712  if (!query.exec(sql))
713  MythDB::DBError("SmartPlaylistEditor::updateMatches", query);
714  else if (query.next())
715  m_matchesCount = query.value(0).toInt();
716 
717  m_matchesText->SetText(QString::number(m_matchesCount));
718 
719  m_playlistIsValid = !m_criteriaRows.empty();
720  m_showResultsButton->SetEnabled((m_matchesCount > 0));
721  titleChanged();
722 }
723 
725 {
726  // save smartplaylist to database
727 
728  QString name = m_titleEdit->GetText();
729  QString category = m_categorySelector->GetValue();
730  QString matchType = (m_matchSelector->GetValue() == tr("All") ? "All" : "Any");
731  QString orderBy = m_orderBySelector->GetValue();
732  QString limit = m_limitSpin->GetValue();
733 
734  // lookup categoryid
735  int categoryid = SmartPlaylistEditor::lookupCategoryID(category);
736 
737  // easier to delete any existing smartplaylist and recreate a new one
738  if (!m_newPlaylist)
739  SmartPlaylistEditor::deleteSmartPlaylist(m_originalCategory, m_originalName);
740  else
742 
743  MSqlQuery query(MSqlQuery::InitCon());
744  // insert new smartplaylist
745  query.prepare("INSERT INTO music_smartplaylists (name, categoryid, matchtype, orderby, limitto) "
746  "VALUES (:NAME, :CATEGORYID, :MATCHTYPE, :ORDERBY, :LIMIT);");
747  query.bindValue(":NAME", name);
748  query.bindValue(":CATEGORYID", categoryid);
749  query.bindValue(":MATCHTYPE", matchType);
750  query.bindValue(":ORDERBY", orderBy);
751  query.bindValue(":LIMIT", limit);
752 
753  if (!query.exec())
754  {
755  MythDB::DBError("Inserting new playlist", query);
756  return;
757  }
758 
759  // get smartplaylistid
760  int ID;
761  query.prepare("SELECT smartplaylistid FROM music_smartplaylists "
762  "WHERE categoryid = :CATEGORYID AND name = :NAME;");
763  query.bindValue(":CATEGORYID", categoryid);
764  query.bindValue(":NAME", name);
765  if (query.exec())
766  {
767  if (query.isActive() && query.size() > 0)
768  {
769  query.first();
770  ID = query.value(0).toInt();
771  }
772  else
773  {
774  LOG(VB_GENERAL, LOG_ERR,
775  QString("Failed to find ID for smartplaylist: %1").arg(name));
776  return;
777  }
778  }
779  else
780  {
781  MythDB::DBError("Getting smartplaylist ID", query);
782  return;
783  }
784 
785  // save smartplaylist items
786  for (int x = 0; x < m_criteriaRows.size(); x++)
787  m_criteriaRows[x]->saveToDatabase(ID);
788 
789  emit smartPLChanged(category, name);
790 
791  Close();
792 }
793 
794 void SmartPlaylistEditor::newSmartPlaylist(const QString& category)
795 {
796  m_categorySelector->SetValue(category);
797  m_titleEdit->Reset();
798  m_originalCategory = category;
799  m_originalName.clear();
800 
801  m_newPlaylist = true;
802 
803  updateMatches();
804 }
805 
806 void SmartPlaylistEditor::editSmartPlaylist(const QString& category, const QString& name)
807 {
808  m_originalCategory = category;
809  m_originalName = name;
810  m_newPlaylist = false;
811  loadFromDatabase(category, name);
812  updateMatches();
813 }
814 
815 void SmartPlaylistEditor::loadFromDatabase(const QString& category, const QString& name)
816 {
817  // load smartplaylist from database
818  int categoryid = SmartPlaylistEditor::lookupCategoryID(category);
819 
820  MSqlQuery query(MSqlQuery::InitCon());
821  int ID;
822 
823  query.prepare("SELECT smartplaylistid, name, categoryid, matchtype, orderby, limitto "
824  "FROM music_smartplaylists WHERE name = :NAME AND categoryid = :CATEGORYID;");
825  query.bindValue(":NAME", name);
826  query.bindValue(":CATEGORYID", categoryid);
827  if (query.exec())
828  {
829  if (query.isActive() && query.size() > 0)
830  {
831  query.first();
832  ID = query.value(0).toInt();
833  m_titleEdit->SetText(name);
834  m_categorySelector->SetValue(category);
835  if (query.value(3).toString() == "All")
836  m_matchSelector->SetValue(tr("All"));
837  else
838  m_matchSelector->SetValue(tr("Any"));
839 
840  QString orderBy = query.value(4).toString();
841  if (!m_orderBySelector->Find(orderBy))
842  {
843  // not found so add it to the selector
844  new MythUIButtonListItem(m_orderBySelector, orderBy);
845  m_orderBySelector->SetValue(orderBy);
846  }
847 
848  m_limitSpin->SetValue(query.value(5).toInt());
849  }
850  else
851  {
852  LOG(VB_GENERAL, LOG_ERR,
853  QString("Cannot find smartplaylist: %1").arg(name));
854  return;
855  }
856  }
857  else
858  {
859  MythDB::DBError("Load smartplaylist", query);
860  return;
861  }
862 
863  m_criteriaList->Reset();
864 
865  query.prepare("SELECT field, operator, value1, value2 "
866  "FROM music_smartplaylist_items WHERE smartplaylistid = :ID "
867  "ORDER BY smartplaylistitemid;");
868  query.bindValue(":ID", ID);
869  if (!query.exec())
870  MythDB::DBError("Load smartplaylist items", query);
871 
872  if (query.size() > 0)
873  {
874  while (query.next())
875  {
876  QString Field = query.value(0).toString();
877  QString Operator = query.value(1).toString();
878  QString Value1 = query.value(2).toString();
879  QString Value2 = query.value(3).toString();
880  // load smartplaylist items
881  SmartPLCriteriaRow *row =
882  new SmartPLCriteriaRow(Field, Operator, Value1, Value2);
883  m_criteriaRows.append(row);
884 
885  new MythUIButtonListItem(m_criteriaList, row->toString(), qVariantFromValue(row));
886  }
887  }
888  else
889  {
890  LOG(VB_GENERAL, LOG_WARNING,
891  QString("Got no smartplaylistitems for ID: ").arg(ID));
892  }
893 }
894 
895 void SmartPlaylistEditor::newCategory(const QString &category)
896 {
897  // insert new smartplaylistcategory
898 
899  MSqlQuery query(MSqlQuery::InitCon());
900  query.prepare("INSERT INTO music_smartplaylist_categories (name) "
901  "VALUES (:NAME);");
902  query.bindValue(":NAME", category);
903 
904  if (!query.exec())
905  {
906  MythDB::DBError("Inserting new smartplaylist category", query);
907  return;
908  }
909 
910  getSmartPlaylistCategories();
911  m_categorySelector->SetValue(category);
912 }
913 
914 void SmartPlaylistEditor::startDeleteCategory(const QString &category)
915 {
916  if (category.isEmpty())
917  return;
918 
919 //FIXME::
920 #if 0
921  if (!MythPopupBox::showOkCancelPopup(GetMythMainWindow(),
922  "Delete Category",
923  tr("Are you sure you want to delete this Category?")
924  + "\n\n\"" + category + "\"\n\n"
925  + tr("It will also delete any Smart Playlists belonging to this category."),
926  false))
927  return;
928 
930 #endif
931  getSmartPlaylistCategories();
932  m_titleEdit->Reset();
933 }
934 
935 void SmartPlaylistEditor::renameCategory(const QString &category)
936 {
937  if (m_categorySelector->GetValue() == category)
938  return;
939 
940  // change the category
941  MSqlQuery query(MSqlQuery::InitCon());
942  query.prepare("UPDATE music_smartplaylist_categories SET name = :NEW_CATEGORY "
943  "WHERE name = :OLD_CATEGORY;");
944  query.bindValue(":OLD_CATEGORY", m_categorySelector->GetValue());
945  query.bindValue(":NEW_CATEGORY", category);
946 
947  if (!query.exec())
948  MythDB::DBError("Rename smartplaylist", query);
949 
950  if (!m_newPlaylist)
951  m_originalCategory = m_categorySelector->GetValue();
952 
953  getSmartPlaylistCategories();
954  m_categorySelector->SetValue(category);
955 }
956 
957 QString SmartPlaylistEditor::getSQL(const QString& fields)
958 {
959  QString sql, whereClause, orderByClause, limitClause;
960  sql = "SELECT " + fields + " FROM music_songs "
961  "LEFT JOIN music_artists ON music_songs.artist_id=music_artists.artist_id "
962  "LEFT JOIN music_albums ON music_songs.album_id=music_albums.album_id "
963  "LEFT JOIN music_artists AS music_comp_artists ON music_albums.artist_id=music_comp_artists.artist_id "
964  "LEFT JOIN music_genres ON music_songs.genre_id=music_genres.genre_id ";
965 
966  whereClause = getWhereClause();
967  orderByClause = getOrderByClause();
968  if (m_limitSpin->GetIntValue() > 0)
969  limitClause = " LIMIT " + m_limitSpin->GetValue();
970 
971  sql = sql + whereClause + orderByClause + limitClause;
972 
973  return sql;
974 }
975 
977 {
978  return getOrderBySQL(m_orderBySelector->GetValue());
979 }
980 
982 {
983  if (m_criteriaRows.empty())
984  return QString();
985 
986  bool bFirst = true;
987  QString sql = "WHERE ";
988 
989  for (int x = 0; x < m_criteriaRows.size(); x++)
990  {
991  QString criteria = m_criteriaRows[x]->getSQL();
992  if (criteria.isEmpty())
993  continue;
994 
995  if (bFirst)
996  {
997  sql += criteria;
998  bFirst = false;
999  }
1000  else
1001  {
1002  if (m_matchSelector->GetValue() == tr("Any"))
1003  sql += " OR " + criteria;
1004  else
1005  sql += " AND " + criteria;
1006  }
1007  }
1008 
1009  return sql;
1010 }
1011 
1013 {
1014  QString sql = getSQL("song_id, music_artists.artist_name, album_name, "
1015  "name, genre, music_songs.year, track");
1016 
1018 
1019  SmartPLResultViewer *resultViewer = new SmartPLResultViewer(mainStack);
1020 
1021  if (!resultViewer->Create())
1022  {
1023  delete resultViewer;
1024  return;
1025  }
1026 
1027  resultViewer->setSQL(sql);
1028 
1029  mainStack->AddScreen(resultViewer);
1030 }
1031 
1033 {
1034  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1035 
1036  SmartPLOrderByDialog *orderByDialog = new SmartPLOrderByDialog(popupStack);
1037 
1038  if (!orderByDialog->Create())
1039  {
1040  delete orderByDialog;
1041  return;
1042  }
1043 
1044  orderByDialog->setFieldList(m_orderBySelector->GetValue());
1045 
1046  connect(orderByDialog, SIGNAL(orderByChanged(QString)), SLOT(orderByChanged(QString)));
1047 
1048  popupStack->AddScreen(orderByDialog);
1049 }
1050 
1051 void SmartPlaylistEditor::orderByChanged(const QString& orderBy)
1052 {
1053  if (m_orderBySelector->MoveToNamedPosition(orderBy))
1054  return;
1055 
1056  // not found so add it to the selector
1057  new MythUIButtonListItem(m_orderBySelector, orderBy);
1058  m_orderBySelector->SetValue(orderBy);
1059 }
1060 
1062 {
1063  m_categorySelector->Reset();
1064  MSqlQuery query(MSqlQuery::InitCon());
1065 
1066  if (query.exec("SELECT name FROM music_smartplaylist_categories ORDER BY name;"))
1067  {
1068  if (query.isActive() && query.size() > 0)
1069  {
1070  while (query.next())
1071  new MythUIButtonListItem(m_categorySelector, query.value(0).toString());
1072  }
1073  else
1074  {
1075  LOG(VB_GENERAL, LOG_ERR,
1076  "Could not find any smartplaylist categories");
1077  }
1078  }
1079  else
1080  {
1081  MythDB::DBError("Load smartplaylist categories", query);
1082  }
1083 }
1084 
1085 // static function to delete a smartplaylist and any associated smartplaylist items
1086 bool SmartPlaylistEditor::deleteSmartPlaylist(QString category, const QString& name)
1087 {
1088  // get categoryid
1089  int categoryid = SmartPlaylistEditor::lookupCategoryID(std::move(category));
1090 
1091  MSqlQuery query(MSqlQuery::InitCon());
1092 
1093  // get playlist ID
1094  int ID;
1095  query.prepare("SELECT smartplaylistid FROM music_smartplaylists WHERE name = :NAME "
1096  "AND categoryid = :CATEGORYID;");
1097  query.bindValue(":NAME", name);
1098  query.bindValue(":CATEGORYID", categoryid);
1099  if (query.exec())
1100  {
1101  if (query.isActive() && query.size() > 0)
1102  {
1103  query.first();
1104  ID = query.value(0).toInt();
1105  }
1106  else
1107  {
1108  // not always an error maybe we are trying to delete a playlist
1109  // that does not exist
1110  return true;
1111  }
1112  }
1113  else
1114  {
1115  MythDB::DBError("Delete smartplaylist", query);
1116  return false;
1117  }
1118 
1119  //delete smartplaylist items
1120  query.prepare("DELETE FROM music_smartplaylist_items WHERE smartplaylistid = :ID;");
1121  query.bindValue(":ID", ID);
1122  if (!query.exec())
1123  MythDB::DBError("Delete smartplaylist items", query);
1124 
1125  //delete smartplaylist
1126  query.prepare("DELETE FROM music_smartplaylists WHERE smartplaylistid = :ID;");
1127  query.bindValue(":ID", ID);
1128  if (!query.exec())
1129  MythDB::DBError("Delete smartplaylist", query);
1130 
1131  return true;
1132 }
1133 
1134 // static function to delete all smartplaylists belonging to the given category
1135 // will also delete any associated smartplaylist items
1136 bool SmartPlaylistEditor::deleteCategory(const QString& category)
1137 {
1138  int categoryid = SmartPlaylistEditor::lookupCategoryID(category);
1139  MSqlQuery query(MSqlQuery::InitCon());
1140 
1141  //delete all smartplaylists with the selected category
1142  query.prepare("SELECT name FROM music_smartplaylists "
1143  "WHERE categoryid = :CATEGORYID;");
1144  query.bindValue(":CATEGORYID", categoryid);
1145  if (!query.exec())
1146  {
1147  MythDB::DBError("Delete SmartPlaylist Category", query);
1148  return false;
1149  }
1150 
1151  if (query.isActive() && query.size() > 0)
1152  {
1153  while (query.next())
1154  {
1155  SmartPlaylistEditor::deleteSmartPlaylist(category, query.value(0).toString());
1156  }
1157  }
1158 
1159  // delete the category
1160  query.prepare("DELETE FROM music_smartplaylist_categories WHERE categoryid = :ID;");
1161  query.bindValue(":ID", categoryid);
1162  if (!query.exec())
1163  MythDB::DBError("Delete smartplaylist category", query);
1164 
1165  return true;
1166 }
1167 
1168 // static function to lookup the categoryid given its name
1169 int SmartPlaylistEditor::lookupCategoryID(const QString& category)
1170 {
1171  int ID;
1172  MSqlQuery query(MSqlQuery::InitCon());
1173  query.prepare("SELECT categoryid FROM music_smartplaylist_categories "
1174  "WHERE name = :CATEGORY;");
1175  query.bindValue(":CATEGORY", category);
1176 
1177  if (query.exec())
1178  {
1179  if (query.isActive() && query.size() > 0)
1180  {
1181  query.first();
1182  ID = query.value(0).toInt();
1183  }
1184  else
1185  {
1186  LOG(VB_GENERAL, LOG_ERR,
1187  QString("Failed to find smart playlist category: %1")
1188  .arg(category));
1189  ID = -1;
1190  }
1191  }
1192  else
1193  {
1194  MythDB::DBError("Getting category ID", query);
1195  ID = -1;
1196  }
1197 
1198  return ID;
1199 }
1200 
1201 void SmartPlaylistEditor::getCategoryAndName(QString &category, QString &name)
1202 {
1203  category = m_categorySelector->GetValue();
1204  name = m_titleEdit->GetText();
1205 }
1206 
1207 /*
1208 ---------------------------------------------------------------------
1209 */
1210 
1212 {
1213  if (!LoadWindowFromXML("music-ui.xml", "criteriaroweditor", this))
1214  return false;
1215 
1216  bool err = false;
1217 
1218  UIUtilE::Assign(this, m_fieldSelector, "fieldselector", &err);
1219  UIUtilE::Assign(this, m_operatorSelector, "operatorselector", &err);
1220  UIUtilE::Assign(this, m_value1Edit, "value1edit", &err);
1221  UIUtilE::Assign(this, m_value2Edit, "value2edit", &err);
1222  UIUtilE::Assign(this, m_value1Selector, "value1selector", &err);
1223  UIUtilE::Assign(this, m_value2Selector, "value2selector", &err);
1224  UIUtilE::Assign(this, m_value1Spinbox, "value1spinbox", &err);
1225  UIUtilE::Assign(this, m_value2Spinbox, "value2spinbox", &err);
1226  UIUtilE::Assign(this, m_value1Button, "value1button", &err);
1227  UIUtilE::Assign(this, m_value2Button, "value2button", &err);
1228  UIUtilE::Assign(this, m_cancelButton, "cancelbutton", &err);
1229  UIUtilE::Assign(this, m_saveButton, "savebutton", &err);
1230 
1231  if (err)
1232  {
1233  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'criteriaroweditor'");
1234  return false;
1235  }
1236 
1237  updateFields();
1238  updateOperators();
1239  updateValues();
1240 
1241  connect(m_fieldSelector, SIGNAL(itemSelected(MythUIButtonListItem*)), SLOT(fieldChanged()));
1242  connect(m_operatorSelector, SIGNAL(itemSelected(MythUIButtonListItem*)), SLOT(operatorChanged()));
1243 
1244  connect(m_value1Edit, SIGNAL(valueChanged()), SLOT(valueEditChanged()));
1245  connect(m_value2Edit, SIGNAL(valueChanged()), SLOT(valueEditChanged()));
1246  connect(m_value1Selector, SIGNAL(itemSelected(MythUIButtonListItem*)), SLOT(valueEditChanged()));
1247  connect(m_value2Selector, SIGNAL(itemSelected(MythUIButtonListItem*)), SLOT(valueEditChanged()));
1248 
1249  connect(m_value1Button, SIGNAL(Clicked()), SLOT(valueButtonClicked()));
1250  connect(m_value2Button, SIGNAL(Clicked()), SLOT(valueButtonClicked()));
1251 
1252  connect(m_cancelButton, SIGNAL(Clicked()), SLOT(Close()));
1253  connect(m_saveButton, SIGNAL(Clicked()), SLOT(saveClicked()));
1254 
1255  BuildFocusList();
1256 
1257  return true;
1258 }
1259 
1261 {
1262  for (int x = 0; x < SmartPLFieldsCount; x++)
1263  new MythUIButtonListItem(m_fieldSelector, SmartPLFields[x].name);
1264 
1265  m_fieldSelector->SetValue(m_criteriaRow->m_field);
1266 }
1267 
1269 {
1270  for (int x = 0; x < SmartPLOperatorsCount; x++)
1271  new MythUIButtonListItem(m_operatorSelector, SmartPLOperators[x].name);
1272 
1273  m_operatorSelector->SetValue(m_criteriaRow->m_operator);
1274 }
1275 
1277 {
1278  enableSaveButton();
1279 }
1280 
1282 {
1283  m_value1Edit->SetText(m_criteriaRow->m_value1);
1284  m_value2Edit->SetText(m_criteriaRow->m_value2);
1285  m_value1Spinbox->SetValue(m_criteriaRow->m_value1);
1286  m_value2Spinbox->SetValue(m_criteriaRow->m_value2);
1287 
1288  if (!m_value1Selector->MoveToNamedPosition(m_criteriaRow->m_value1))
1289  {
1290  // not found so add it to the selector
1291  new MythUIButtonListItem(m_value1Selector, m_criteriaRow->m_value1);
1292  m_value1Selector->SetValue(m_criteriaRow->m_value1);
1293  }
1294 
1295  if (!m_value2Selector->MoveToNamedPosition(m_criteriaRow->m_value2))
1296  {
1297  // not found so add it to the selector
1298  new MythUIButtonListItem(m_value2Selector, m_criteriaRow->m_value2);
1299  m_value2Selector->SetValue(m_criteriaRow->m_value2);
1300  }
1301 }
1302 
1304 {
1305  SmartPLField *Field;
1306  Field = lookupField(m_fieldSelector->GetValue());
1307  if (!Field)
1308  return;
1309 
1310  m_criteriaRow->m_field = m_fieldSelector->GetValue();
1311  m_criteriaRow->m_operator = m_operatorSelector->GetValue();
1312 
1313  if (Field->type == ftNumeric)
1314  {
1315  m_criteriaRow->m_value1 = m_value1Spinbox->GetValue();
1316  m_criteriaRow->m_value2 = m_value2Spinbox->GetValue();
1317  }
1318  else if (Field->type == ftBoolean || Field->type == ftDate)
1319  {
1320  m_criteriaRow->m_value1 = m_value1Selector->GetValue();
1321  m_criteriaRow->m_value2 = m_value2Selector->GetValue();
1322  }
1323  else // ftString
1324  {
1325  m_criteriaRow->m_value1 = m_value1Edit->GetText();
1326  m_criteriaRow->m_value2 = m_value2Edit->GetText();
1327  }
1328 
1329  emit criteriaChanged();
1330 
1331  Close();
1332 }
1333 
1335 {
1336  bool enabled = false;
1337 
1338  SmartPLField *Field;
1339  Field = lookupField(m_fieldSelector->GetValue());
1340 
1341  SmartPLOperator *Operator;
1342  Operator = lookupOperator(m_operatorSelector->GetValue());
1343 
1344  if (Field && Operator)
1345  {
1346  if (Field->type == ftNumeric || Field->type == ftBoolean)
1347  enabled = true;
1348  else if (Field->type == ftDate)
1349  {
1350  if (Operator->noOfArguments == 0)
1351  enabled = true;
1352  else if (Operator->noOfArguments == 1 && !m_value1Selector->GetValue().isEmpty())
1353  enabled = true;
1354  else if (Operator->noOfArguments == 2 && !m_value1Selector->GetValue().isEmpty()
1355  && !m_value2Selector->GetValue().isEmpty())
1356  enabled = true;
1357  }
1358  else // ftString
1359  {
1360  if (Operator->noOfArguments == 0)
1361  enabled = true;
1362  else if (Operator->noOfArguments == 1 && !m_value1Edit->GetText().isEmpty())
1363  enabled = true;
1364  else if (Operator->noOfArguments == 2 && !m_value1Edit->GetText().isEmpty()
1365  && !m_value2Edit->GetText().isEmpty())
1366  enabled = true;
1367  }
1368  }
1369 
1370  m_saveButton->SetEnabled(enabled);
1371 }
1372 
1374 {
1375  SmartPLField *Field;
1376  Field = lookupField(m_fieldSelector->GetValue());
1377  if (!Field)
1378  return;
1379 
1380  if (Field->type == ftBoolean)
1381  {
1382  // add yes / no items to combo
1383  m_value1Selector->Reset();
1384  new MythUIButtonListItem(m_value1Selector, "No");
1385  new MythUIButtonListItem(m_value1Selector, "Yes");
1386  m_value2Selector->Reset();
1387  new MythUIButtonListItem(m_value2Selector, "No");
1388  new MythUIButtonListItem(m_value2Selector, "Yes");
1389  }
1390  else if (Field->type == ftDate)
1391  {
1392  // add a couple of date values to the combo
1393  m_value1Selector->Reset();
1394  new MythUIButtonListItem(m_value1Selector, "$DATE");
1395  new MythUIButtonListItem(m_value1Selector, "$DATE - 30 days");
1396  new MythUIButtonListItem(m_value1Selector, "$DATE - 60 days");
1397 
1398  if (!m_value1Selector->MoveToNamedPosition(m_criteriaRow->m_value1))
1399  {
1400  // not found so add it to the selector
1401  new MythUIButtonListItem(m_value1Selector, m_criteriaRow->m_value1);
1402  m_value1Selector->SetValue(m_criteriaRow->m_value1);
1403  }
1404 
1405 
1406  m_value2Selector->Reset();
1407  new MythUIButtonListItem(m_value2Selector, "$DATE");
1408  new MythUIButtonListItem(m_value2Selector, "$DATE - 30 days");
1409  new MythUIButtonListItem(m_value2Selector, "$DATE - 60 days");
1410 
1411  if (!m_value2Selector->MoveToNamedPosition(m_criteriaRow->m_value2))
1412  {
1413  // not found so add it to the selector
1414  new MythUIButtonListItem(m_value2Selector, m_criteriaRow->m_value2);
1415  m_value2Selector->SetValue(m_criteriaRow->m_value2);
1416  }
1417  }
1418 
1419  // get list of operators valid for this field type
1420  getOperatorList(Field->type);
1421 
1422  enableSaveButton();
1423 }
1424 
1426 {
1427  SmartPLField *Field;
1428  Field = lookupField(m_fieldSelector->GetValue());
1429  if (!Field)
1430  return;
1431 
1432  SmartPLOperator *Operator;
1433  Operator = lookupOperator(m_operatorSelector->GetValue());
1434  if (!Operator)
1435  return;
1436 
1437  // hide all widgets
1438  m_value1Edit->Hide();
1439  m_value2Edit->Hide();
1440  m_value1Button->Hide();
1441  m_value2Button->Hide();
1442  m_value1Selector->Hide();
1443  m_value2Selector->Hide();
1444  m_value1Spinbox->Hide();
1445  m_value2Spinbox->Hide();
1446 
1447  // show spin edits
1448  if (Field->type == ftNumeric)
1449  {
1450  if (Operator->noOfArguments >= 1)
1451  {
1452  m_value1Spinbox->Show();
1453  int currentValue = m_value1Spinbox->GetIntValue();
1454  m_value1Spinbox->SetRange(Field->minValue, Field->maxValue, 1);
1455 
1456  if (currentValue < Field->minValue || currentValue > Field->maxValue)
1457  m_value1Spinbox->SetValue(Field->defaultValue);
1458  }
1459 
1460  if (Operator->noOfArguments == 2)
1461  {
1462  m_value2Spinbox->Show();
1463  int currentValue = m_value2Spinbox->GetIntValue();
1464  m_value2Spinbox->SetRange(Field->minValue, Field->maxValue, 1);
1465 
1466  if (currentValue < Field->minValue || currentValue > Field->maxValue)
1467  m_value2Spinbox->SetValue(Field->defaultValue);
1468  }
1469  }
1470  else if (Field->type == ftBoolean)
1471  {
1472  // only show value1combo
1473  m_value1Selector->Show();
1474  }
1475  else if (Field->type == ftDate)
1476  {
1477  if (Operator->noOfArguments >= 1)
1478  {
1479  m_value1Selector->Show();
1480  m_value1Button->Show();
1481  }
1482 
1483  if (Operator->noOfArguments == 2)
1484  {
1485  m_value2Selector->Show();
1486  m_value2Button->Show();
1487  }
1488  }
1489  else // ftString
1490  {
1491  if (Operator->noOfArguments >= 1)
1492  {
1493  m_value1Edit->Show();
1494  m_value1Button->Show();
1495  }
1496 
1497  if (Operator->noOfArguments == 2)
1498  {
1499  m_value2Edit->Show();
1500  m_value2Button->Show();
1501  }
1502  }
1503 
1504  enableSaveButton();
1505 }
1506 
1508 {
1509  QString currentOperator = m_operatorSelector->GetValue();
1510 
1511  m_operatorSelector->Reset();
1512 
1513  for (int x = 0; x < SmartPLOperatorsCount; x++)
1514  {
1515  // don't add operators that only work with string fields
1516  if (fieldType != ftString && SmartPLOperators[x].stringOnly)
1517  continue;
1518 
1519  // don't add operators that only work with boolean fields
1520  if (fieldType == ftBoolean && !SmartPLOperators[x].validForBoolean)
1521  continue;
1522 
1523  new MythUIButtonListItem(m_operatorSelector, SmartPLOperators[x].name);
1524  }
1525 
1526  // try to set the operatorCombo to the same operator or else the first item
1527  m_operatorSelector->SetValue(currentOperator);
1528 }
1529 
1531 {
1532  QString msg;
1533  QStringList searchList;
1534  QString s = GetFocusWidget() == m_value1Button ? m_value1Edit->GetText() : m_value2Edit->GetText();
1535 
1536  if (m_fieldSelector->GetValue() == "Artist")
1537  {
1538  msg = tr("Select an Artist");
1539  searchList = MusicMetadata::fillFieldList("artist");
1540  }
1541  else if (m_fieldSelector->GetValue() == "Comp. Artist")
1542  {
1543  msg = tr("Select a Compilation Artist");
1544  searchList = MusicMetadata::fillFieldList("compilation_artist");
1545  }
1546  else if (m_fieldSelector->GetValue() == "Album")
1547  {
1548  msg = tr("Select an Album");
1549  searchList = MusicMetadata::fillFieldList("album");
1550  }
1551  else if (m_fieldSelector->GetValue() == "Genre")
1552  {
1553  msg = tr("Select a Genre");
1554  searchList = MusicMetadata::fillFieldList("genre");
1555  }
1556  else if (m_fieldSelector->GetValue() == "Title")
1557  {
1558  msg = tr("Select a Title");
1559  searchList = MusicMetadata::fillFieldList("title");
1560  }
1561  else if (m_fieldSelector->GetValue() == "Last Play")
1562  {
1563  editDate();
1564  return;
1565  }
1566  else if (m_fieldSelector->GetValue() == "Date Imported")
1567  {
1568  editDate();
1569  return;
1570  }
1571 
1572  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1573  MythUISearchDialog *searchDlg = new MythUISearchDialog(popupStack, msg, searchList, false, s);
1574 
1575  if (!searchDlg->Create())
1576  {
1577  delete searchDlg;
1578  return;
1579  }
1580 
1581  connect(searchDlg, SIGNAL(haveResult(QString)), SLOT(setValue(QString)));
1582 
1583  popupStack->AddScreen(searchDlg);
1584 }
1585 
1586 void CriteriaRowEditor::setValue(const QString& value)
1587 {
1588  if (GetFocusWidget() && GetFocusWidget() == m_value1Button)
1589  m_value1Edit->SetText(value);
1590  else
1591  m_value2Edit->SetText(value);
1592 }
1593 
1595 {
1596  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1597  SmartPLDateDialog *dateDlg = new SmartPLDateDialog(popupStack);
1598  QString date = GetFocusWidget() == m_value1Button ? m_value1Selector->GetValue() : m_value2Selector->GetValue();
1599 
1600  if (!dateDlg->Create())
1601  {
1602  delete dateDlg;
1603  return;
1604  }
1605 
1606  dateDlg->setDate(date);
1607 
1608  connect(dateDlg, SIGNAL(dateChanged(QString)), SLOT(setDate(QString)));
1609 
1610  popupStack->AddScreen(dateDlg);
1611 }
1612 
1613 void CriteriaRowEditor::setDate(const QString& date)
1614 {
1615  if (GetFocusWidget() && GetFocusWidget() == m_value1Button)
1616  {
1617  if (m_value1Selector->MoveToNamedPosition(date))
1618  return;
1619 
1620  // not found so add it to the selector
1621  new MythUIButtonListItem(m_value1Selector, date);
1622  m_value1Selector->SetValue(date);
1623  }
1624  else
1625  {
1626  if (m_value2Selector->MoveToNamedPosition(date))
1627  return;
1628 
1629  // not found so add it to the selector
1630  new MythUIButtonListItem(m_value2Selector, date);
1631  m_value2Selector->SetValue(date);
1632  }
1633 }
1634 
1635 /*
1636 ---------------------------------------------------------------------
1637 */
1638 
1639 
1641 {
1642  if (!LoadWindowFromXML("music-ui.xml", "smartplresultviewer", this))
1643  return false;
1644 
1645  bool err = false;
1646 
1647  UIUtilE::Assign(this, m_trackList, "tracklist", &err);
1648  UIUtilW::Assign(this, m_positionText, "position", &err);
1649 
1650  if (err)
1651  {
1652  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'smartplresultviewer'");
1653  return false;
1654  }
1655 
1656  connect(m_trackList, SIGNAL(itemVisible(MythUIButtonListItem*)),
1657  this, SLOT(trackVisible(MythUIButtonListItem*)));
1658  connect(m_trackList, SIGNAL(itemSelected(MythUIButtonListItem*)),
1659  this, SLOT(trackSelected(MythUIButtonListItem*)));
1660 
1661  BuildFocusList();
1662 
1663  return true;
1664 }
1665 
1667 {
1668  if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
1669  return true;
1670 
1671  QStringList actions;
1672  bool handled = GetMythMainWindow()->TranslateKeyPress("Music", event, actions);
1673 
1674  for (int i = 0; i < actions.size() && !handled; i++)
1675  {
1676  QString action = actions[i];
1677  handled = true;
1678 
1679  if (action == "INFO")
1680  showTrackInfo();
1681  else
1682  handled = false;
1683  }
1684 
1685  if (!handled && MythScreenType::keyPressEvent(event))
1686  handled = true;
1687 
1688  return handled;
1689 }
1690 
1692 {
1693  if (!item)
1694  return;
1695 
1696  if (item->GetImageFilename().isEmpty())
1697  {
1698  MusicMetadata *mdata = item->GetData().value<MusicMetadata *>();
1699  if (mdata)
1700  {
1701  QString artFile = mdata->getAlbumArtFile();
1702  if (artFile.isEmpty())
1703  item->SetImage("mm_nothumb.png");
1704  else
1705  item->SetImage(mdata->getAlbumArtFile());
1706  }
1707  else
1708  item->SetImage("mm_nothumb.png");
1709  }
1710 }
1711 
1713 {
1714  if (!item || !m_positionText)
1715  return;
1716 
1717  m_positionText->SetText(tr("%1 of %2").arg(m_trackList->IsEmpty() ? 0 : m_trackList->GetCurrentPos() + 1)
1718  .arg(m_trackList->GetCount()));
1719 }
1721 {
1722  MythUIButtonListItem *item = m_trackList->GetItemCurrent();
1723  if (!item)
1724  return;
1725 
1726  MusicMetadata *mdata = item->GetData().value<MusicMetadata *>();
1727  if (!mdata)
1728  return;
1729 
1730  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1731 
1732  TrackInfoDialog *dlg = new TrackInfoDialog(popupStack, mdata, "trackinfopopup");
1733 
1734  if (!dlg->Create())
1735  {
1736  delete dlg;
1737  return;
1738  }
1739 
1740  popupStack->AddScreen(dlg);
1741 }
1742 
1743 void SmartPLResultViewer::setSQL(const QString& sql)
1744 {
1745  m_trackList->Reset();;
1746 
1747  MSqlQuery query(MSqlQuery::InitCon());
1748 
1749  if (query.exec(sql))
1750  {
1751  while (query.next())
1752  {
1753  MusicMetadata *mdata = gMusicData->m_all_music->getMetadata(query.value(0).toInt());
1754  if (mdata)
1755  {
1756  InfoMap metadataMap;
1757  mdata->toMap(metadataMap);
1758 
1759  MythUIButtonListItem *item = new MythUIButtonListItem(m_trackList, "", qVariantFromValue(mdata));
1760  item->SetTextFromMap(metadataMap);
1761  }
1762  }
1763  }
1764 
1765  trackSelected(m_trackList->GetItemCurrent());
1766 }
1767 
1768 
1769 /*
1770 ---------------------------------------------------------------------
1771 */
1772 
1774 {
1775  if (!LoadWindowFromXML("music-ui.xml", "orderbydialog", this))
1776  return false;
1777 
1778  bool err = false;
1779 
1780  UIUtilE::Assign(this, m_fieldList, "fieldlist", &err);
1781  UIUtilE::Assign(this, m_orderSelector, "fieldselector", &err);
1782  UIUtilE::Assign(this, m_addButton, "addbutton", &err);
1783  UIUtilE::Assign(this, m_deleteButton, "deletebutton", &err);
1784  UIUtilE::Assign(this, m_moveUpButton, "moveupbutton", &err);
1785  UIUtilE::Assign(this, m_moveDownButton, "movedownbutton", &err);
1786  UIUtilE::Assign(this, m_ascendingButton, "ascendingbutton", &err);
1787  UIUtilE::Assign(this, m_descendingButton, "descendingbutton", &err);
1788  UIUtilE::Assign(this, m_cancelButton, "cancelbutton", &err);
1789  UIUtilE::Assign(this, m_okButton, "okbutton", &err);
1790 
1791  if (err)
1792  {
1793  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'orderbydialog'");
1794  return false;
1795  }
1796 
1797  connect(m_addButton, SIGNAL(Clicked()), this, SLOT(addPressed()));
1798  connect(m_deleteButton, SIGNAL(Clicked()), this, SLOT(deletePressed()));
1799  connect(m_moveUpButton, SIGNAL(Clicked()), this, SLOT(moveUpPressed()));
1800  connect(m_moveDownButton, SIGNAL(Clicked()), this, SLOT(moveDownPressed()));
1801  connect(m_ascendingButton, SIGNAL(Clicked()), this, SLOT(ascendingPressed()));
1802  connect(m_descendingButton, SIGNAL(Clicked()), this, SLOT(descendingPressed()));
1803  connect(m_cancelButton, SIGNAL(Clicked()), this, SLOT(Close()));
1804  connect(m_okButton, SIGNAL(Clicked()), this, SLOT(okPressed()));
1805 
1806  connect(m_orderSelector, SIGNAL(itemSelected(MythUIButtonListItem*)),
1807  this, SLOT(orderByChanged(void)));
1808  connect(m_fieldList, SIGNAL(itemSelected(MythUIButtonListItem*)),
1809  this, SLOT(fieldListSelectionChanged(MythUIButtonListItem*)));
1810 
1811  getOrderByFields();
1812 
1813  orderByChanged();
1814 
1815  BuildFocusList();
1816 
1817  return true;
1818 }
1819 
1821 {
1822  QString result;
1823  bool bFirst = true;
1824 
1825  for (int i = 0; i < m_fieldList->GetCount(); i++)
1826  {
1827  if (bFirst)
1828  {
1829  bFirst = false;
1830  result = m_fieldList->GetItemAt(i)->GetText();
1831  }
1832  else
1833  result += ", " + m_fieldList->GetItemAt(i)->GetText();
1834  }
1835 
1836  return result;
1837 }
1838 
1839 void SmartPLOrderByDialog::setFieldList(const QString &fieldList)
1840 {
1841  m_fieldList->Reset();
1842  QStringList list = fieldList.split(",");
1843 
1844  for (int x = 0; x < list.count(); x++)
1845  {
1846  MythUIButtonListItem *item = new MythUIButtonListItem(m_fieldList, list[x].trimmed());
1847  QString state = list[x].contains("(A)") ? "ascending" : "descending";
1848  item->DisplayState(state, "sortstate");
1849  }
1850 
1851  orderByChanged();
1852 }
1853 
1855 {
1856  if (!item)
1857  return;
1858 
1859  m_orderSelector->SetValue(item->GetText().left(item->GetText().length() - 4));
1860 }
1861 
1863 {
1864  if (!m_fieldList->GetItemCurrent())
1865  return;
1866 
1867  m_fieldList->GetItemCurrent()->SetText(m_orderSelector->GetValue() + " (A)");
1868  m_fieldList->GetItemCurrent()->DisplayState("ascending", "sortstate");
1869 
1870  orderByChanged();
1871  SetFocusWidget(m_descendingButton);
1872 }
1873 
1875 {
1876  if (!m_fieldList->GetItemCurrent())
1877  return;
1878 
1879  m_fieldList->GetItemCurrent()->SetText(m_orderSelector->GetValue() + " (D)");
1880  m_fieldList->GetItemCurrent()->DisplayState("descending", "sortstate");
1881 
1882  orderByChanged();
1883  SetFocusWidget(m_ascendingButton);
1884 }
1885 
1887 {
1888  MythUIButtonListItem *item = new MythUIButtonListItem(m_fieldList, m_orderSelector->GetValue() + " (A)");
1889  item->DisplayState("ascending", "sortstate");
1890 
1891  orderByChanged();
1892  SetFocusWidget(m_orderSelector);
1893 }
1894 
1896 {
1897  m_fieldList->RemoveItem(m_fieldList->GetItemCurrent());
1898  orderByChanged();
1899 
1900  if (!m_deleteButton->IsEnabled())
1901  SetFocusWidget(m_addButton);
1902  else
1903  SetFocusWidget(m_deleteButton);
1904 }
1905 
1907 {
1908  MythUIButtonListItem *item = m_fieldList->GetItemCurrent();
1909 
1910  if (item)
1911  item->MoveUpDown(true);
1912 
1913  orderByChanged();
1914 
1915  if (!m_moveUpButton->IsEnabled())
1916  SetFocusWidget(m_moveDownButton);
1917  else
1918  SetFocusWidget(m_moveUpButton);
1919 }
1920 
1922 {
1923  MythUIButtonListItem *item = m_fieldList->GetItemCurrent();
1924 
1925  if (item)
1926  item->MoveUpDown(false);
1927 
1928  orderByChanged();
1929 
1930  if (!m_moveDownButton->IsEnabled())
1931  SetFocusWidget(m_moveUpButton);
1932  else
1933  SetFocusWidget(m_moveDownButton);
1934 }
1935 
1937 {
1938  emit orderByChanged(getFieldList());
1939  Close();
1940 }
1941 
1943 {
1944  bool found = false;
1945  for (int i = 0 ; i < m_fieldList->GetCount() ; ++i)
1946  {
1947  if (m_fieldList->GetItemAt(i)->GetText().startsWith(m_orderSelector->GetValue()))
1948  {
1949  m_fieldList->SetItemCurrent(i);
1950  found = true;
1951  }
1952  }
1953 
1954  if (found)
1955  {
1956  m_addButton->SetEnabled(false);
1957  m_deleteButton->SetEnabled(true);
1958  m_moveUpButton->SetEnabled((m_fieldList->GetCurrentPos() != 0));
1959  m_moveDownButton->SetEnabled((m_fieldList->GetCurrentPos() != m_fieldList->GetCount() - 1) );
1960  m_ascendingButton->SetEnabled((m_fieldList->GetValue().right(3) == "(D)") );
1961  m_descendingButton->SetEnabled((m_fieldList->GetValue().right(3) == "(A)"));
1962  }
1963  else
1964  {
1965  m_addButton->SetEnabled(true);
1966  m_deleteButton->SetEnabled(false);
1967  m_moveUpButton->SetEnabled(false);
1968  m_moveDownButton->SetEnabled(false);
1969  m_ascendingButton->SetEnabled(false);
1970  m_descendingButton->SetEnabled(false);
1971  }
1972 }
1973 
1975 {
1976  m_orderSelector->Reset();
1977  for (int x = 1; x < SmartPLFieldsCount; x++)
1978  new MythUIButtonListItem(m_orderSelector, SmartPLFields[x].name);
1979 }
1980 
1981 /*
1982 ---------------------------------------------------------------------
1983 */
1984 
1986 {
1987  if (!LoadWindowFromXML("music-ui.xml", "dateeditordialog", this))
1988  return false;
1989 
1990  bool err = false;
1991 
1992  UIUtilE::Assign(this, m_fixedRadio, "fixeddatecheck", &err);
1993  UIUtilE::Assign(this, m_daySpin, "dayspinbox", &err);
1994  UIUtilE::Assign(this, m_monthSpin, "monthspinbox", &err);
1995  UIUtilE::Assign(this, m_yearSpin, "yearspinbox", &err);
1996  UIUtilE::Assign(this, m_nowRadio, "nowcheck", &err);
1997  UIUtilE::Assign(this, m_addDaysSpin, "adddaysspinbox", &err);
1998  UIUtilE::Assign(this, m_statusText, "statustext", &err);
1999  UIUtilE::Assign(this, m_cancelButton, "cancelbutton", &err);
2000  UIUtilE::Assign(this, m_okButton, "okbutton", &err);
2001 
2002  if (err)
2003  {
2004  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'dateeditordialog'");
2005  return false;
2006  }
2007 
2008  m_daySpin->SetRange(1, 31, 1);
2009  m_monthSpin->SetRange(1, 12, 1);
2010  m_yearSpin->SetRange(1900, 2099, 1);
2011  m_addDaysSpin->SetRange(-9999, 9999, 1);
2012 
2013 
2014  connect(m_fixedRadio, SIGNAL(toggled(bool)), this, SLOT(fixedCheckToggled(bool)));
2015  connect(m_nowRadio, SIGNAL(toggled(bool)), this, SLOT(nowCheckToggled(bool)));
2016  //connect(addDaysCheck, SIGNAL(toggled(bool)), this, SLOT(addDaysCheckToggled(bool)));
2017  connect(m_addDaysSpin, SIGNAL(itemSelected(MythUIButtonListItem*)),
2018  this, SLOT(valueChanged(void)));
2019  connect(m_daySpin, SIGNAL(itemSelected(MythUIButtonListItem*)),
2020  this, SLOT(valueChanged(void)));
2021  connect(m_monthSpin, SIGNAL(itemSelected(MythUIButtonListItem*)),
2022  this, SLOT(valueChanged(void)));
2023  connect(m_yearSpin, SIGNAL(itemSelected(MythUIButtonListItem*)),
2024  this, SLOT(valueChanged(void)));
2025 
2026  connect(m_cancelButton, SIGNAL(Clicked()), this, SLOT(Close()));
2027  connect(m_okButton, SIGNAL(Clicked()), this, SLOT(okPressed()));
2028 
2029  valueChanged();
2030 
2031  BuildFocusList();
2032 
2033  return true;
2034 }
2035 
2037 {
2038  QString sResult;
2039 
2040  if (m_fixedRadio->GetBooleanCheckState())
2041  {
2042  QString day = m_daySpin->GetValue();
2043  if (m_daySpin->GetIntValue() < 10)
2044  day = "0" + day;
2045 
2046  QString month = m_monthSpin->GetValue();
2047  if (m_monthSpin->GetIntValue() < 10)
2048  month = "0" + month;
2049 
2050  sResult = m_yearSpin->GetValue() + "-" + month + "-" + day;
2051  }
2052  else
2053  sResult = m_statusText->GetText();
2054 
2055  return sResult;
2056 }
2057 
2058 void SmartPLDateDialog::setDate(QString date)
2059 {
2060  if (date.startsWith("$DATE"))
2061  {
2062  m_nowRadio->SetCheckState(true);
2063  m_fixedRadio->SetCheckState(false);
2064 
2065  if (date.length() > 9)
2066  {
2067  bool bNegative = false;
2068  if (date[6] == '-')
2069  bNegative = true;
2070 
2071  if (date.endsWith(" days"))
2072  date = date.left(date.length() - 5);
2073 
2074  int nDays = date.mid(8).toInt();
2075  if (bNegative)
2076  nDays = -nDays;
2077 
2078  m_addDaysSpin->SetValue(nDays);
2079  }
2080  else
2081  m_addDaysSpin->SetValue(0);
2082 
2083  nowCheckToggled(true);
2084  }
2085  else
2086  {
2087  int nYear = date.mid(0, 4).toInt();
2088  int nMonth = date.mid(5, 2).toInt();
2089  int nDay = date.mid(8, 2).toInt();
2090 
2091  m_daySpin->SetValue(nDay);
2092  m_monthSpin->SetValue(nMonth);
2093  m_yearSpin->SetValue(nYear);
2094 
2095  fixedCheckToggled(true);
2096  }
2097 }
2098 
2100 {
2101  if (m_updating)
2102  return;
2103 
2104  m_updating = true;
2105  m_daySpin->SetEnabled(on);
2106  m_monthSpin->SetEnabled(on);
2107  m_yearSpin->SetEnabled(on);
2108 
2109  m_nowRadio->SetCheckState(!on);
2110  m_addDaysSpin->SetEnabled(!on);
2111 
2112  valueChanged();
2113 
2114  m_updating = false;
2115 }
2116 
2118 {
2119  if (m_updating)
2120  return;
2121 
2122  m_updating = true;
2123 
2124  m_fixedRadio->SetCheckState(!on);
2125  m_daySpin->SetEnabled(!on);
2126  m_monthSpin->SetEnabled(!on);
2127  m_yearSpin->SetEnabled(!on);
2128 
2129  m_addDaysSpin->SetEnabled(on);
2130 
2131  valueChanged();
2132 
2133  m_updating = false;
2134 }
2135 
2137 {
2138  QString date = getDate();
2139 
2140  emit dateChanged(date);
2141 
2142  Close();
2143 }
2144 
2146 {
2147  bool bValidDate = true;
2148 
2149  if (m_fixedRadio->GetBooleanCheckState())
2150  {
2151  QString day = m_daySpin->GetValue();
2152  if (m_daySpin->GetIntValue() < 10)
2153  day = "0" + day;
2154 
2155  QString month = m_monthSpin->GetValue();
2156  if (m_monthSpin->GetIntValue() < 10)
2157  month = "0" + month;
2158 
2159  QString sDate = m_yearSpin->GetValue() + "-" + month + "-" + day;
2160  QDate date = QDate::fromString(sDate, Qt::ISODate);
2161  if (date.isValid())
2162  m_statusText->SetText(date.toString("dddd, d MMMM yyyy"));
2163  else
2164  {
2165  bValidDate = false;
2166  m_statusText->SetText(tr("Invalid Date"));
2167  }
2168  }
2169  else if (m_nowRadio->GetBooleanCheckState())
2170  {
2171  QString days;
2172  if (m_addDaysSpin->GetIntValue() > 0)
2173  days = QString("$DATE + %1 days").arg(m_addDaysSpin->GetIntValue());
2174  else if (m_addDaysSpin->GetIntValue() == 0)
2175  days = QString("$DATE");
2176  else
2177  days = QString("$DATE - %1 days").arg(
2178  m_addDaysSpin->GetValue().right(m_addDaysSpin->GetValue().length() - 1));
2179 
2180  m_statusText->SetText(days);
2181  }
2182 
2183  if (bValidDate)
2184  m_statusText->SetFontState("valid");
2185  else
2186  m_statusText->SetFontState("error");
2187 
2188  m_okButton->SetEnabled(bValidDate);
2189 }
2190 
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:782
void setDate(const QString &date)
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:863
QString getSQL(const QString &fields)
void valueEditChanged(void)
void operatorChanged(void)
QString getCriteriaSQL(const QString &fieldName, QString operatorName, QString value1, QString value2)
bool TranslateKeyPress(const QString &context, QKeyEvent *e, QStringList &actions, bool allowJumps=true)
Get a list of actions for a keypress in the given context.
void orderByChanged(const QString &orderBy)
MythConfirmationDialog * ShowOkPopup(const QString &message, QObject *parent, const char *slot, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
QString getOrderBySQL(const QString &orderByFields)
QString toString(void)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
QString getSQL(void)
Basic menu dialog, message and a list of options.
QString GetImageFilename(const QString &name="") const
int size(void) const
Definition: mythdbcon.h:203
QString getFieldList(void)
static bool deleteSmartPlaylist(QString category, const QString &name)
SmartPLFieldType
Definition: smartplaylist.h:21
static QStringList fillFieldList(const QString &field)
MythScreenStack * GetStack(const QString &stackname)
void startDeleteCategory(const QString &category)
MythScreenStack * GetMainStack()
bool Create(void) override
static MythThemedMenu * menu
static SmartPLOperator SmartPLOperators[]
void setSQL(const QString &sql)
static Type kEventType
Definition: mythdialogbox.h:50
void SetImage(MythImage *image, const QString &name="")
Sets an image directly, should only be used in special circumstances since it bypasses the cache.
void getSmartPlaylistCategories(void)
SmartPLFieldType type
QString getOrderByClause(void)
void newCategory(const QString &category)
QVariant value(int i) const
Definition: mythdbcon.h:198
QString getDate(void)
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
bool Create(void) override
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
static bool deleteCategory(const QString &category)
static SmartPLOperator * lookupOperator(const QString &name)
void enableSaveButton(void)
void customEvent(QEvent *event) override
static int SmartPLOperatorsCount
MusicMetadata * getMetadata(int an_id)
bool Create(void) override
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
void setDate(QString date)
void getCategoryAndName(QString &category, QString &name)
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
void showCategoryMenu(void)
bool Create(void) override
void setFieldList(const QString &fieldList)
bool isActive(void) const
Definition: mythdbcon.h:204
QString getAlbumArtFile(void)
void trackVisible(MythUIButtonListItem *item)
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
bool Create(void) override
void SetText(const QString &text, const QString &name="", const QString &state="")
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
const char * name
Definition: ParseText.cpp:328
bool MoveUpDown(bool flag)
static QString evaluateDateValue(QString sDate)
void setValue(const QString &value)
QString getWhereClause(void)
void fixedCheckToggled(bool on)
void editSmartPlaylist(const QString &category, const QString &name)
MythMainWindow * GetMythMainWindow(void)
QString GetText(const QString &name="") const
bool first(void)
Wrap QSqlQuery::first() so we can display the query results.
Definition: mythdbcon.cpp:792
void showCriteriaMenu(void)
QString getSQLFieldName(QString fieldName)
Dialog prompting the user to enter a text string.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:807
bool keyPressEvent(QKeyEvent *) override
Key event handler.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static int SmartPLFieldsCount
static SmartPLField * lookupField(const QString &name)
void getOperatorList(SmartPLFieldType fieldType)
bool Create(void) override
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
Definition: mythdbcon.cpp:868
bool Create(void) override
bool saveToDatabase(int smartPlaylistID)
static SmartPLField SmartPLFields[]
void doDeleteCriteria(bool doit)
MusicData * gMusicData
Definition: musicdata.cpp:20
static int lookupCategoryID(const QString &category)
const QSqlDriver * driver(void) const
Definition: mythdbcon.h:209
void newSmartPlaylist(const QString &category)
QString formattedFieldValue(const QVariant &value)
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
void updateOperators(void)
void showResultsClicked(void)
void valueButtonClicked(void)
void fieldListSelectionChanged(MythUIButtonListItem *item)
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:37
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
void DisplayState(const QString &state, const QString &name)
void SetTextFromMap(const InfoMap &infoMap, const QString &state="")
void nowCheckToggled(bool on)
Default UTC.
Definition: mythdate.h:14
void loadFromDatabase(const QString &category, const QString &name)
void toMap(InfoMap &metadataMap, const QString &prefix="")
bool Create(void) override
AllMusic * m_all_music
Definition: musicdata.h:54
bool Create(void) override
Provide a dialog to quickly find an entry in a list.
void renameCategory(const QString &category)
void trackSelected(MythUIButtonListItem *item)