44 { .m_name=
"", .m_sqlName=
"" },
45 { .m_name=
"Artist", .m_sqlName=
"music_artists.artist_name" },
46 { .m_name=
"Album", .m_sqlName=
"music_albums.album_name" },
47 { .m_name=
"Title", .m_sqlName=
"music_songs.name" },
48 { .m_name=
"Genre", .m_sqlName=
"music_genres.genre" },
49 { .m_name=
"Year", .m_sqlName=
"music_songs.year",
51 .m_minValue=1900, .m_maxValue=2099, .m_defaultValue=2000 },
52 { .m_name=
"Track No.", .m_sqlName=
"music_songs.track",
54 { .m_name=
"Rating", .m_sqlName=
"music_songs.rating",
56 { .m_name=
"Play Count", .m_sqlName=
"music_songs.numplays",
58 { .m_name=
"Compilation", .m_sqlName=
"music_albums.compilation",
60 { .m_name=
"Comp. Artist", .m_sqlName=
"music_comp_artists.artist_name",
62 { .m_name=
"Last Play", .m_sqlName=
"FROM_DAYS(TO_DAYS(music_songs.lastplay))",
64 { .m_name=
"Date Imported", .m_sqlName=
"FROM_DAYS(TO_DAYS(music_songs.date_entered))",
78 { .m_name=
"is equal to", .m_validForBoolean=
true },
79 { .m_name=
"is not equal to", .m_validForBoolean=
true },
80 { .m_name=
"is greater than" },
81 { .m_name=
"is less than" },
82 { .m_name=
"starts with", .m_stringOnly=
true },
83 { .m_name=
"ends with", .m_stringOnly=
true },
84 { .m_name=
"contains", .m_stringOnly=
true },
85 { .m_name=
"does not contain", .m_stringOnly=
true },
86 { .m_name=
"is between", .m_noOfArguments=2 },
87 { .m_name=
"is set", .m_noOfArguments=0 },
88 { .m_name=
"is not set", .m_noOfArguments=0 },
95 if (oper.m_name == name)
105 if (field.m_name == name)
113#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
114 QSqlField field(
"", value.type());
116 QSqlField field(
"", value.metaType());
121 field.setValue(value);
124 QString result = QString::fromUtf8(query.
driver()->formatValue(field).toLatin1().data());
130 if (sDate.startsWith(
"$DATE"))
134 if (sDate.length() > 9)
136 bool bNegative =
false;
140 if (sDate.endsWith(
" days"))
141 sDate = sDate.left(sDate.length() - 5);
143#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
144 int nDays = sDate.midRef(8).toInt();
146 int nDays = QStringView(sDate).mid(8).toInt();
151 date = date.addDays(nDays);
161 QString value1, QString value2)
165 if (fieldName.isEmpty())
186 value1 = (value1 ==
"Yes") ?
"1":
"0";
187 value2 = (value2 ==
"Yes") ?
"1":
"0";
195 if (Operator->
m_name ==
"is equal to")
199 else if (Operator->
m_name ==
"is not equal to")
203 else if (Operator->
m_name ==
"is greater than")
207 else if (Operator->
m_name ==
"is less than")
211 else if (Operator->
m_name ==
"starts with")
215 else if (Operator->
m_name ==
"ends with")
219 else if (Operator->
m_name ==
"contains")
223 else if (Operator->
m_name ==
"does not contain")
227 else if (Operator->
m_name ==
"is between")
232 else if (Operator->
m_name ==
"is set")
234 result = result +
" IS NOT NULL";
236 else if (Operator->
m_name ==
"is not set")
238 result = result +
" IS NULL";
243 LOG(VB_GENERAL, LOG_ERR,
244 QString(
"getCriteriaSQL(): invalid operator '%1'")
253 if (orderByFields.isEmpty())
256 QStringList list = orderByFields.split(
",");
262 for (
int x = 0; x < list.count(); x++)
264 fieldName = list[x].trimmed();
268 if (fieldName.right(3) ==
"(D)")
276 result =
" ORDER BY " + Field->
m_sqlName + order;
280 result +=
", " + Field->
m_sqlName + order;
324 query.
prepare(
"INSERT INTO music_smartplaylist_items (smartplaylistid, field, operator,"
326 "VALUES (:SMARTPLAYLISTID, :FIELD, :OPERATOR, :VALUE1, :VALUE2);");
327 query.
bindValue(
":SMARTPLAYLISTID", smartPlaylistID);
359 result +=
" " + tr(
"and") +
" " +
m_value2;
407 LOG(VB_GENERAL, LOG_ERR,
"Cannot load screen 'smartplaylisteditor'");
419 if (field.m_name ==
"")
447 for (
int i = 0; i < actions.size() && !handled; i++)
449 const QString&
action = actions[i];
481 if (dce->GetResult() < 0)
484 QString resultid = dce->GetId();
485 QString resulttext = dce->GetResultText();
486 if (resultid ==
"categorymenu")
488 if (resulttext == tr(
"New Category"))
491 QString label = tr(
"Enter Name Of New Category");
503 else if (resulttext == tr(
"Delete Category"))
507 else if (resulttext == tr(
"Rename Category"))
548 if (!editor->Create())
610 if (!editor->Create())
649 item->
SetText(row->toString());
657 QString label = tr(
"Category Actions");
669 menu->SetReturnEvent(
this,
"categorymenu");
671 menu->AddButton(tr(
"New Category"),
nullptr);
672 menu->AddButton(tr(
"Delete Category"),
nullptr);
673 menu->AddButton(tr(
"Rename Category"),
nullptr);
680 QString label = tr(
"Criteria Actions");
692 menu->SetReturnEvent(
this,
"criteriamenu");
717 "LEFT JOIN music_artists ON "
718 " music_songs.artist_id=music_artists.artist_id "
719 "LEFT JOIN music_albums ON music_songs.album_id=music_albums.album_id "
720 "LEFT JOIN music_artists AS music_comp_artists ON "
721 " music_albums.artist_id=music_comp_artists.artist_id "
722 "LEFT JOIN music_genres ON music_songs.genre_id=music_genres.genre_id ";
729 if (!query.
exec(sql))
731 else if (query.
next())
762 query.
prepare(
"INSERT INTO music_smartplaylists (name, categoryid, matchtype, orderby, limitto) "
763 "VALUES (:NAME, :CATEGORYID, :MATCHTYPE, :ORDERBY, :LIMIT);");
765 query.
bindValue(
":CATEGORYID", categoryid);
766 query.
bindValue(
":MATCHTYPE", matchType);
778 query.
prepare(
"SELECT smartplaylistid FROM music_smartplaylists "
779 "WHERE categoryid = :CATEGORYID AND name = :NAME;");
780 query.
bindValue(
":CATEGORYID", categoryid);
787 ID = query.
value(0).toInt();
791 LOG(VB_GENERAL, LOG_ERR,
792 QString(
"Failed to find ID for smartplaylist: %1").arg(name));
804 row->saveToDatabase(ID);
840 query.
prepare(
"SELECT smartplaylistid, name, categoryid, matchtype, orderby, limitto "
841 "FROM music_smartplaylists WHERE name = :NAME AND categoryid = :CATEGORYID;");
843 query.
bindValue(
":CATEGORYID", categoryid);
849 ID = query.
value(0).toInt();
852 if (query.
value(3).toString() ==
"All")
857 QString orderBy = query.
value(4).toString();
869 LOG(VB_GENERAL, LOG_ERR,
870 QString(
"Cannot find smartplaylist: %1").arg(name));
882 query.
prepare(
"SELECT field, operator, value1, value2 "
883 "FROM music_smartplaylist_items WHERE smartplaylistid = :ID "
884 "ORDER BY smartplaylistitemid;");
889 if (query.
size() > 0)
893 QString Field = query.
value(0).toString();
894 QString Operator = query.
value(1).toString();
895 QString Value1 = query.
value(2).toString();
896 QString Value2 = query.
value(3).toString();
906 LOG(VB_GENERAL, LOG_WARNING,
907 QString(
"Got no smartplaylistitems for ID: ").arg(ID));
916 query.
prepare(
"INSERT INTO music_smartplaylist_categories (name) "
932 if (category.isEmpty())
939 tr(
"Are you sure you want to delete this Category?")
940 +
"\n\n\"" + category +
"\"\n\n"
941 + tr(
"It will also delete any Smart Playlists belonging to this category."),
958 query.
prepare(
"UPDATE music_smartplaylist_categories SET name = :NEW_CATEGORY "
959 "WHERE name = :OLD_CATEGORY;");
961 query.
bindValue(
":NEW_CATEGORY", category);
977 QString orderByClause;
979 sql =
"SELECT " + fields +
" FROM music_songs "
980 "LEFT JOIN music_artists ON music_songs.artist_id=music_artists.artist_id "
981 "LEFT JOIN music_albums ON music_songs.album_id=music_albums.album_id "
982 "LEFT JOIN music_artists AS music_comp_artists ON music_albums.artist_id=music_comp_artists.artist_id "
983 "LEFT JOIN music_genres ON music_songs.genre_id=music_genres.genre_id ";
990 sql = sql + whereClause + orderByClause + limitClause;
1006 QString sql =
"WHERE ";
1010 QString criteria = row->getSQL();
1011 if (criteria.isEmpty())
1022 sql +=
" OR " + criteria;
1024 sql +=
" AND " + criteria;
1033 QString sql =
getSQL(
"song_id, music_artists.artist_name, album_name, "
1034 "name, genre, music_songs.year, track");
1040 if (!resultViewer->Create())
1042 delete resultViewer;
1046 resultViewer->setSQL(sql);
1057 if (!orderByDialog->Create())
1059 delete orderByDialog;
1086 if (query.
exec(
"SELECT name FROM music_smartplaylist_categories ORDER BY name;"))
1090 while (query.
next())
1095 LOG(VB_GENERAL, LOG_ERR,
1096 "Could not find any smartplaylist categories");
1115 query.
prepare(
"SELECT smartplaylistid FROM music_smartplaylists WHERE name = :NAME "
1116 "AND categoryid = :CATEGORYID;");
1118 query.
bindValue(
":CATEGORYID", categoryid);
1124 ID = query.
value(0).toInt();
1140 query.
prepare(
"DELETE FROM music_smartplaylist_items WHERE smartplaylistid = :ID;");
1146 query.
prepare(
"DELETE FROM music_smartplaylists WHERE smartplaylistid = :ID;");
1162 query.
prepare(
"SELECT name FROM music_smartplaylists "
1163 "WHERE categoryid = :CATEGORYID;");
1164 query.
bindValue(
":CATEGORYID", categoryid);
1173 while (query.
next())
1180 query.
prepare(
"DELETE FROM music_smartplaylist_categories WHERE categoryid = :ID;");
1193 query.
prepare(
"SELECT categoryid FROM music_smartplaylist_categories "
1194 "WHERE name = :CATEGORY;");
1202 ID = query.
value(0).toInt();
1206 LOG(VB_GENERAL, LOG_ERR,
1207 QString(
"Failed to find smart playlist category: %1")
1253 LOG(VB_GENERAL, LOG_ERR,
"Cannot load screen 'criteriaroweditor'");
1356 bool enabled =
false;
1362 if (Field && Operator)
1469 if (currentValue < Field->m_minValue || currentValue > Field->
m_maxValue)
1479 if (currentValue < Field->m_minValue || currentValue > Field->
m_maxValue)
1529 if (fieldType !=
ftString && oper.m_stringOnly)
1533 if (fieldType ==
ftBoolean && !oper.m_validForBoolean)
1546 QStringList searchList;
1551 msg = tr(
"Select an Artist");
1556 msg = tr(
"Select a Compilation Artist");
1561 msg = tr(
"Select an Album");
1566 msg = tr(
"Select a Genre");
1571 msg = tr(
"Select a Title");
1584 if (!searchDlg->Create())
1609 if (!dateDlg->Create())
1615 dateDlg->setDate(date);
1661 LOG(VB_GENERAL, LOG_ERR,
"Cannot load screen 'smartplresultviewer'");
1680 QStringList actions;
1683 for (
int i = 0; i < actions.size() && !handled; i++)
1685 const QString&
action = actions[i];
1711 if (artFile.isEmpty())
1714 item->
SetImage(mdata->getAlbumArtFile());
1760 if (query.
exec(sql))
1762 while (query.
next())
1768 mdata->
toMap(metadataMap);
1771 item->SetTextFromMap(metadataMap);
1804 LOG(VB_GENERAL, LOG_ERR,
"Cannot load screen 'orderbydialog'");
1855 QStringList list = fieldList.split(
",");
1857 for (
int x = 0; x < list.count(); x++)
1860 QString state = list[x].contains(
"(A)") ?
"ascending" :
"descending";
1861 item->DisplayState(state,
"sortstate");
1902 item->DisplayState(
"ascending",
"sortstate");
2022 LOG(VB_GENERAL, LOG_ERR,
"Cannot load screen 'dateeditordialog'");
2065 month =
"0" + month;
2079 if (date.startsWith(
"$DATE"))
2084 if (date.length() > 9)
2086 bool bNegative =
false;
2090 if (date.endsWith(
" days"))
2091 date = date.left(date.length() - 5);
2093#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2094 int nDays = date.midRef(8).toInt();
2096 int nDays = QStringView(date).mid(8).toInt();
2112#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
2113 int nYear = date.midRef(0, 4).toInt();
2114 int nMonth = date.midRef(5, 2).toInt();
2115 int nDay = date.midRef(8, 2).toInt();
2117 int nYear = QStringView(date).mid(0, 4).toInt();
2118 int nMonth = QStringView(date).mid(5, 2).toInt();
2119 int nDay = QStringView(date).mid(8, 2).toInt();
2178 bool bValidDate =
true;
2188 month =
"0" + month;
2208 days = QString(
"$DATE");
2210 days = QString(
"$DATE - %1 days").arg(
2224#include "moc_smartplaylist.cpp"
MusicMetadata * getMetadata(int an_id)
MythUIButton * m_cancelButton
MythUIButtonList * m_fieldSelector
void setDate(const QString &date)
MythUIButtonList * m_value1Selector
MythUIButton * m_value1Button
void valueEditChanged(void)
MythUITextEdit * m_value1Edit
MythUIButton * m_value2Button
void getOperatorList(SmartPLFieldType fieldType)
MythUISpinBox * m_value1Spinbox
MythUIButtonList * m_value2Selector
void enableSaveButton(void)
bool Create(void) override
void setValue(const QString &value)
void operatorChanged(void)
MythUIButton * m_saveButton
SmartPLCriteriaRow * m_criteriaRow
MythUISpinBox * m_value2Spinbox
MythUITextEdit * m_value2Edit
void updateOperators(void)
MythUIButtonList * m_operatorSelector
void valueButtonClicked(void)
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
QSqlQuery wrapper that fetches a DB connection from the connection pool.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
bool first(void)
Wrap QSqlQuery::first() so we can display the query results.
QVariant value(int i) const
bool isActive(void) const
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
const QSqlDriver * driver(void) const
static void DBError(const QString &where, const MSqlQuery &query)
Basic menu dialog, message and a list of options.
MythScreenStack * GetMainStack()
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
MythScreenStack * GetStack(const QString &Stackname)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
void BuildFocusList(void)
MythUIType * GetFocusWidget(void) const
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
bool SetFocusWidget(MythUIType *widget=nullptr)
Dialog prompting the user to enter a text string.
void SetCheckState(MythUIStateType::StateType state)
bool GetBooleanCheckState(void) const
Provide a dialog to quickly find an entry in a list.
void SetRange(int low, int high, int step, uint pageMultiple=5)
Set the lower and upper bounds of the spinbox, the interval and page amount.
void SetValue(int val) override
QString GetValue(void) const override
int GetIntValue(void) const override
QString GetText(void) const
void SetText(const QString &text, bool moveCursor=true)
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
QString GetText(void) const
void SetFontState(const QString &state)
virtual void SetText(const QString &text)
bool IsEnabled(void) const
void SetEnabled(bool enable)
bool saveToDatabase(int smartPlaylistID) const
QString toString(void) const
QString getSQL(void) const
void setDate(QString date)
MythUICheckBox * m_fixedRadio
MythUICheckBox * m_nowRadio
MythUISpinBox * m_daySpin
MythUIText * m_statusText
MythUIButton * m_okButton
MythUISpinBox * m_monthSpin
MythUIButton * m_cancelButton
MythUISpinBox * m_yearSpin
MythUISpinBox * m_addDaysSpin
void fixedCheckToggled(bool on)
void nowCheckToggled(bool on)
bool Create(void) override
void dateChanged(QString date)
void getOrderByFields(void)
MythUIButtonList * m_orderSelector
QString getFieldList(void)
bool Create(void) override
MythUIButton * m_addButton
MythUIButtonList * m_fieldList
MythUIButton * m_descendingButton
void setFieldList(const QString &fieldList)
MythUIButton * m_moveUpButton
void descendingPressed(void)
void fieldListSelectionChanged(MythUIButtonListItem *item)
void orderByChanged(void)
void moveDownPressed(void)
void ascendingPressed(void)
MythUIButton * m_cancelButton
MythUIButton * m_ascendingButton
MythUIButton * m_okButton
MythUIButton * m_moveDownButton
MythUIButton * m_deleteButton
void setSQL(const QString &sql)
MythUIText * m_positionText
MythUIButtonList * m_trackList
void trackSelected(MythUIButtonListItem *item)
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
static void trackVisible(MythUIButtonListItem *item)
bool Create(void) override
void startDeleteCategory(const QString &category)
void renameCategory(const QString &category)
MythUIButton * m_orderByButton
void editSmartPlaylist(const QString &category, const QString &name)
MythUIButton * m_cancelButton
QString getWhereClause(void)
void customEvent(QEvent *event) override
MythUIButton * m_saveButton
void loadFromDatabase(const QString &category, const QString &name)
void showCategoryMenu(void)
void doDeleteCriteria(bool doit)
void showResultsClicked(void)
void smartPLChanged(const QString &category, const QString &name)
QString getOrderByClause(void)
MythUIButton * m_showResultsButton
MythUIButtonList * m_categorySelector
void showCriteriaMenu(void)
void deleteCriteria(void)
QString getSQL(const QString &fields)
void newCategory(const QString &category)
static bool deleteCategory(const QString &category)
MythUIButtonList * m_matchSelector
void orderByClicked(void)
MythUIButtonList * m_orderBySelector
void getSmartPlaylistCategories(void)
MythUIText * m_matchesText
bool Create(void) override
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
void orderByChanged(const QString &orderBy)
void getCategoryAndName(QString &category, QString &name)
QList< SmartPLCriteriaRow * > m_criteriaRows
static int lookupCategoryID(const QString &category)
MythUITextEdit * m_titleEdit
MythUIButton * m_categoryButton
MythUIButtonList * m_criteriaList
~SmartPlaylistEditor(void) override
MythUISpinBox * m_limitSpin
QString m_originalCategory
void newSmartPlaylist(const QString &category)
static bool deleteSmartPlaylist(const QString &category, const QString &name)
SmartPLCriteriaRow * m_tempCriteriaRow
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
MythMainWindow * GetMythMainWindow(void)
static MythThemedMenu * menu
QHash< QString, QString > InfoMap
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
static const SmartPLOperator * lookupOperator(const QString &name)
QString getOrderBySQL(const QString &orderByFields)
static const std::array< const SmartPLOperator, 11 > SmartPLOperators
QString getSQLFieldName(const QString &fieldName)
QString getCriteriaSQL(const QString &fieldName, const QString &operatorName, QString value1, QString value2)
static const std::array< const SmartPLField, 13 > SmartPLFields
QString formattedFieldValue(const QVariant &value)
static QString evaluateDateValue(QString sDate)
static const SmartPLField * lookupField(const QString &name)
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)