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