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