summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorUwe Klotz <uklotz@mixxx.org>2018-11-16 00:14:10 +0100
committerUwe Klotz <uklotz@mixxx.org>2018-11-16 00:14:10 +0100
commitd0a441d9cee780282eb4ce3e42d20e9412050ea7 (patch)
tree30f2f321d1b264f4904f64e89c3c3e244067aa35 /src
parent54261f3b8f3221578aa95c928ca5217852eeac9b (diff)
parent3a609d743841d5cebd883b66325cea66909986b6 (diff)
Merge branch '2.2' of git@github.com:mixxxdj/mixxx.git
# Conflicts: # src/dialog/dlgabout.cpp
Diffstat (limited to 'src')
-rw-r--r--src/dialog/dlgabout.cpp3
-rw-r--r--src/engine/bpmcontrol.cpp6
-rw-r--r--src/engine/enginebuffer.cpp2
-rw-r--r--src/library/banshee/bansheefeature.cpp1
-rw-r--r--src/library/banshee/bansheeplaylistmodel.cpp73
-rw-r--r--src/library/banshee/bansheeplaylistmodel.h4
-rw-r--r--src/library/baseexternallibraryfeature.cpp11
-rw-r--r--src/library/baseexternalplaylistmodel.cpp9
-rw-r--r--src/library/baseexternalplaylistmodel.h1
-rw-r--r--src/library/crate/cratetablemodel.cpp4
-rw-r--r--src/library/librarytablemodel.cpp4
-rw-r--r--src/library/playlisttablemodel.cpp4
-rw-r--r--src/library/searchquery.cpp26
-rw-r--r--src/library/searchquery.h6
-rw-r--r--src/library/searchqueryparser.cpp16
-rw-r--r--src/library/trackmodel.h4
-rw-r--r--src/test/searchqueryparsertest.cpp74
-rw-r--r--src/util/db/dbconnection.cpp25
-rw-r--r--src/util/db/dbconnection.h2
-rw-r--r--src/widget/wtracktableview.cpp84
20 files changed, 253 insertions, 106 deletions
diff --git a/src/dialog/dlgabout.cpp b/src/dialog/dlgabout.cpp
index 390449957f..59c940dc3e 100644
--- a/src/dialog/dlgabout.cpp
+++ b/src/dialog/dlgabout.cpp
@@ -100,7 +100,8 @@ DlgAbout::DlgAbout(QWidget* parent) : QDialog(parent), Ui::DlgAboutDlg() {
<< "Pawe&#322; Goli&#324;ski"
<< "beenisss"
<< "Bernd Binder"
- << "Pradyuman";
+ << "Pradyuman"
+ << "Nikolaus Einhauser";
QStringList specialThanks;
specialThanks
diff --git a/src/engine/bpmcontrol.cpp b/src/engine/bpmcontrol.cpp
index 66bd011d00..db9bb5cd29 100644
--- a/src/engine/bpmcontrol.cpp
+++ b/src/engine/bpmcontrol.cpp
@@ -219,10 +219,14 @@ void BpmControl::slotTapFilter(double averageLength, int numSamples) {
if (numSamples < 4)
return;
+ auto pBeats = m_pBeats;
+ if (!pBeats)
+ return;
+
// (60 seconds per minute) * (1000 milliseconds per second) / (X millis per
// beat) = Y beats/minute
double averageBpm = 60.0 * 1000.0 / averageLength / calcRateRatio();
- m_pBeats->setBpm(averageBpm);
+ pBeats->setBpm(averageBpm);
}
void BpmControl::slotControlBeatSyncPhase(double v) {
diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp
index eac1beed1b..79b8911c8d 100644
--- a/src/engine/enginebuffer.cpp
+++ b/src/engine/enginebuffer.cpp
@@ -182,7 +182,7 @@ EngineBuffer::EngineBuffer(QString group, UserSettingsPointer pConfig,
this, SLOT(slotEjectTrack(double)),
Qt::DirectConnection);
- m_pTrackLoaded = new ControlObject(ConfigKey(m_group, "track_loaded"));
+ m_pTrackLoaded = new ControlObject(ConfigKey(m_group, "track_loaded"), false);
m_pTrackLoaded->setReadOnly();
// Quantization Controller for enabling and disabling the
diff --git a/src/library/banshee/bansheefeature.cpp b/src/library/banshee/bansheefeature.cpp
index e0a6b4c75a..bdb16b58e1 100644
--- a/src/library/banshee/bansheefeature.cpp
+++ b/src/library/banshee/bansheefeature.cpp
@@ -139,6 +139,7 @@ void BansheeFeature::appendTrackIdsFromRightClickIndex(QList<TrackId>* trackIds,
if (playlistID > 0) {
BansheePlaylistModel* pPlaylistModelToAdd = new BansheePlaylistModel(this, m_pTrackCollection, &m_connection);
pPlaylistModelToAdd->setTableModel(playlistID);
+ pPlaylistModelToAdd->select();
// Copy Tracks
int rows = pPlaylistModelToAdd->rowCount();
diff --git a/src/library/banshee/bansheeplaylistmodel.cpp b/src/library/banshee/bansheeplaylistmodel.cpp
index c871095bd3..d46661b965 100644
--- a/src/library/banshee/bansheeplaylistmodel.cpp
+++ b/src/library/banshee/bansheeplaylistmodel.cpp
@@ -11,6 +11,7 @@
#include "mixer/playermanager.h"
#define BANSHEE_TABLE "banshee"
+#define CLM_TRACK_ID "track_id"
#define CLM_VIEW_ORDER "position"
#define CLM_ARTIST "artist"
#define CLM_TITLE "title"
@@ -31,39 +32,50 @@
#define CLM_COMPOSER "composer"
#define CLM_PREVIEW "preview"
+namespace {
+QAtomicInt sTableNumber;
+}
+
BansheePlaylistModel::BansheePlaylistModel(QObject* pParent, TrackCollection* pTrackCollection, BansheeDbConnection* pConnection)
: BaseSqlTableModel(pParent, pTrackCollection, "mixxx.db.model.banshee_playlist"),
m_pConnection(pConnection),
m_playlistId(-1) {
+ m_tempTableName = BANSHEE_TABLE + QString::number(sTableNumber.fetchAndAddAcquire(1));
}
BansheePlaylistModel::~BansheePlaylistModel() {
+ dropTempTable();
}
-void BansheePlaylistModel::setTableModel(int playlistId) {
- //qDebug() << "BansheePlaylistModel::setTableModel" << playlistId;
- if (m_playlistId == playlistId) {
- qDebug() << "Already focused on playlist " << playlistId;
- return;
- }
-
+void BansheePlaylistModel::dropTempTable() {
if (m_playlistId >= 0) {
// Clear old playlist
m_playlistId = -1;
QSqlQuery query(m_pTrackCollection->database());
- QString strQuery("DELETE FROM " BANSHEE_TABLE);
- if (!query.exec(strQuery)) {
+ QString strQuery("DROP TABLE IF EXISTS %1");
+ if (!query.exec(strQuery.arg(m_tempTableName))) {
LOG_FAILED_QUERY(query);
}
}
+}
+
+void BansheePlaylistModel::setTableModel(int playlistId) {
+ //qDebug() << "BansheePlaylistModel::setTableModel" << this << playlistId;
+ if (m_playlistId == playlistId) {
+ qDebug() << "Already focused on playlist " << playlistId;
+ return;
+ }
+
+ dropTempTable();
if (playlistId >= 0) {
// setup new playlist
m_playlistId = playlistId;
QSqlQuery query(m_pTrackCollection->database());
- QString strQuery("CREATE TEMP TABLE IF NOT EXISTS " BANSHEE_TABLE
- " (" CLM_VIEW_ORDER " INTEGER, "
+ QString strQuery("CREATE TEMP TABLE IF NOT EXISTS %1"
+ " (" CLM_TRACK_ID " INTEGER, "
+ CLM_VIEW_ORDER " INTEGER, "
CLM_ARTIST " TEXT, "
CLM_TITLE " TEXT, "
CLM_DURATION " INTEGER, "
@@ -82,12 +94,13 @@ void BansheePlaylistModel::setTableModel(int playlistId) {
CLM_PLAYCOUNT" INTEGER, "
CLM_COMPOSER " TEXT, "
CLM_PREVIEW " TEXT)");
- if (!query.exec(strQuery)) {
+ if (!query.exec(strQuery.arg(m_tempTableName))) {
LOG_FAILED_QUERY(query);
}
- query.prepare("INSERT INTO " BANSHEE_TABLE
- " (" CLM_VIEW_ORDER ", "
+ QString strQuery2("INSERT INTO %1"
+ " (" CLM_TRACK_ID ", "
+ CLM_VIEW_ORDER ", "
CLM_ARTIST ", "
CLM_TITLE ", "
CLM_DURATION ", "
@@ -106,6 +119,7 @@ void BansheePlaylistModel::setTableModel(int playlistId) {
CLM_PLAYCOUNT ", "
CLM_COMPOSER ") "
"VALUES (:"
+ CLM_TRACK_ID ", :"
CLM_VIEW_ORDER ", :"
CLM_ARTIST ", :"
CLM_TITLE ", :"
@@ -125,6 +139,7 @@ void BansheePlaylistModel::setTableModel(int playlistId) {
CLM_PLAYCOUNT ", :"
CLM_COMPOSER ") ");
+ query.prepare(strQuery2.arg(m_tempTableName));
QList<struct BansheeDbConnection::PlaylistEntry> list =
m_pConnection->getPlaylistEntries(playlistId);
@@ -133,6 +148,9 @@ void BansheePlaylistModel::setTableModel(int playlistId) {
beginInsertRows(QModelIndex(), 0, list.size() - 1);
foreach (struct BansheeDbConnection::PlaylistEntry entry, list) {
+ query.bindValue(":" CLM_TRACK_ID, entry.trackId);
+ // Note: entry.viewOrder is 0 for all tracks if they have
+ // never been sorted by the user
query.bindValue(":" CLM_VIEW_ORDER, entry.viewOrder + 1);
query.bindValue(":" CLM_ARTIST, entry.pArtist->name);
query.bindValue(":" CLM_TITLE, entry.pTrack->title);
@@ -165,11 +183,14 @@ void BansheePlaylistModel::setTableModel(int playlistId) {
}
QStringList tableColumns;
- tableColumns << CLM_VIEW_ORDER // 0
- << CLM_PREVIEW;
+ tableColumns
+ << CLM_TRACK_ID // 0
+ << CLM_VIEW_ORDER
+ << CLM_PREVIEW; // 3
QStringList trackSourceColumns;
- trackSourceColumns << CLM_VIEW_ORDER // 0
+ trackSourceColumns
+ << CLM_TRACK_ID // 0
<< CLM_ARTIST
<< CLM_TITLE
<< CLM_DURATION
@@ -189,10 +210,10 @@ void BansheePlaylistModel::setTableModel(int playlistId) {
<< CLM_COMPOSER;
QSharedPointer<BaseTrackCache> trackSource(
- new BaseTrackCache(m_pTrackCollection, BANSHEE_TABLE, CLM_VIEW_ORDER,
+ new BaseTrackCache(m_pTrackCollection, m_tempTableName, CLM_TRACK_ID,
trackSourceColumns, false));
- setTable(BANSHEE_TABLE, CLM_VIEW_ORDER, tableColumns, trackSource);
+ setTable(m_tempTableName, CLM_TRACK_ID, tableColumns, trackSource);
setSearch("");
setDefaultSort(fieldIndex(PLAYLISTTRACKSTABLE_POSITION), Qt::AscendingOrder);
setSort(defaultSortColumn(), defaultSortOrder());
@@ -327,6 +348,15 @@ TrackPointer BansheePlaylistModel::getTrack(const QModelIndex& index) const {
return pTrack;
}
+TrackId BansheePlaylistModel::getTrackId(const QModelIndex& index) const {
+ const auto track = getTrack(index);
+ if (track) {
+ return track->getId();
+ } else {
+ return TrackId();
+ }
+}
+
// Gets the on-disk location of the track at the given location.
QString BansheePlaylistModel::getTrackLocation(const QModelIndex& index) const {
if (!index.isValid()) {
@@ -365,6 +395,7 @@ QString BansheePlaylistModel::getTrackLocation(const QModelIndex& index) const {
}
bool BansheePlaylistModel::isColumnInternal(int column) {
- Q_UNUSED(column);
- return false;
+ return (column == fieldIndex(ColumnCache::COLUMN_PLAYLISTTRACKSTABLE_TRACKID) ||
+ (PlayerManager::numPreviewDecks() == 0 &&
+ column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_PREVIEW)));
}
diff --git a/src/library/banshee/bansheeplaylistmodel.h b/src/library/banshee/bansheeplaylistmodel.h
index efda77a878..d89fc20a8a 100644
--- a/src/library/banshee/bansheeplaylistmodel.h
+++ b/src/library/banshee/bansheeplaylistmodel.h
@@ -20,6 +20,8 @@ class BansheePlaylistModel : public BaseSqlTableModel {
void setTableModel(int playlistId);
TrackPointer getTrack(const QModelIndex& index) const final;
+ TrackId getTrackId(const QModelIndex& index) const final;
+
QString getTrackLocation(const QModelIndex& index) const final;
bool isColumnInternal(int column) final;
@@ -41,9 +43,11 @@ class BansheePlaylistModel : public BaseSqlTableModel {
private:
QString getFieldString(const QModelIndex& index, const QString& fieldName) const;
QVariant getFieldVariant(const QModelIndex& index, const QString& fieldName) const;
+ void dropTempTable();
BansheeDbConnection* m_pConnection;
int m_playlistId;
+ QString m_tempTableName;
};
#endif // BANSHEEPLAYLISTMODEL_H
diff --git a/src/library/baseexternallibraryfeature.cpp b/src/library/baseexternallibraryfeature.cpp
index 87807a62e7..b527c119d3 100644
--- a/src/library/baseexternallibraryfeature.cpp
+++ b/src/library/baseexternallibraryfeature.cpp
@@ -56,7 +56,7 @@ void BaseExternalLibraryFeature::slotAddToAutoDJTop() {
}
void BaseExternalLibraryFeature::addToAutoDJ(bool bTop) {
- // qDebug() << "slotAddToAutoDJ() row:" << m_lastRightClickedIndex.data();
+ //qDebug() << "slotAddToAutoDJ() row:" << m_lastRightClickedIndex.data();
QList<TrackId> trackIds;
QString playlist;
@@ -96,15 +96,14 @@ void BaseExternalLibraryFeature::slotImportAsMixxxPlaylist() {
}
// This is a common function for all external Librarys copied to Mixxx DB
-void BaseExternalLibraryFeature::appendTrackIdsFromRightClickIndex(QList<TrackId>* trackIds, QString* pPlaylist) {
+void BaseExternalLibraryFeature::appendTrackIdsFromRightClickIndex(
+ QList<TrackId>* trackIds, QString* pPlaylist) {
if (!m_lastRightClickedIndex.isValid()) {
return;
}
- // Qt::UserRole asks TreeItemModel for the TreeItem's data. We need to
- // use the data because models with nested playlists need to use the
- // full path/name of the playlist.
- *pPlaylist = m_lastRightClickedIndex.data(Qt::UserRole).toString();
+ DEBUG_ASSERT(pPlaylist);
+ *pPlaylist = m_lastRightClickedIndex.data().toString();
QScopedPointer<BaseSqlTableModel> pPlaylistModelToAdd(
getPlaylistModelForPlaylist(*pPlaylist));
diff --git a/src/library/baseexternalplaylistmodel.cpp b/src/library/baseexternalplaylistmodel.cpp
index 7529523896..2ba94fe30b 100644
--- a/src/library/baseexternalplaylistmodel.cpp
+++ b/src/library/baseexternalplaylistmodel.cpp
@@ -65,6 +65,15 @@ TrackPointer BaseExternalPlaylistModel::getTrack(const QModelIndex& index) const
return pTrack;
}
+TrackId BaseExternalPlaylistModel::getTrackId(const QModelIndex& index) const {
+ const auto track = getTrack(index);
+ if (track) {
+ return track->getId();
+ } else {
+ return TrackId();
+ }
+}
+
bool BaseExternalPlaylistModel::isColumnInternal(int column) {
if (column == fieldIndex(ColumnCache::COLUMN_PLAYLISTTRACKSTABLE_TRACKID) ||
(PlayerManager::numPreviewDecks() == 0 &&
diff --git a/src/library/baseexternalplaylistmodel.h b/src/library/baseexternalplaylistmodel.h
index 0b8cd3e7b4..51cbb9c94f 100644
--- a/src/library/baseexternalplaylistmodel.h
+++ b/src/library/baseexternalplaylistmodel.h
@@ -25,6 +25,7 @@ class BaseExternalPlaylistModel : public BaseSqlTableModel {
void setPlaylist(QString path_name);
TrackPointer getTrack(const QModelIndex& index) const override;
+ TrackId getTrackId(const QModelIndex& index) const override;
bool isColumnInternal(int column) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
void trackLoaded(QString group, TrackPointer pTrack) override;
diff --git a/src/library/crate/cratetablemodel.cpp b/src/library/crate/cratetablemodel.cpp
index 661ac606a4..ca059ffe10 100644
--- a/src/library/crate/cratetablemodel.cpp
+++ b/src/library/crate/cratetablemodel.cpp
@@ -107,13 +107,11 @@ TrackModel::CapabilitiesFlags CrateTableModel::getCapabilities() const {
| TRACKMODELCAPS_ADDTOPLAYLIST
| TRACKMODELCAPS_ADDTOCRATE
| TRACKMODELCAPS_ADDTOAUTODJ
- | TRACKMODELCAPS_IMPORTMETADATA
+ | TRACKMODELCAPS_EDITMETADATA
| TRACKMODELCAPS_LOADTODECK
| TRACKMODELCAPS_LOADTOSAMPLER
| TRACKMODELCAPS_LOADTOPREVIEWDECK
| TRACKMODELCAPS_REMOVE_CRATE
- | TRACKMODELCAPS_MANIPULATEBEATS
- | TRACKMODELCAPS_CLEAR_BEATS
| TRACKMODELCAPS_RESETPLAYED;
if (m_selectedCrate.isValid()) {
Crate crate;
diff --git a/src/library/librarytablemodel.cpp b/src/library/librarytablemodel.cpp
index c50a99867b..3d79457bea 100644
--- a/src/library/librarytablemodel.cpp
+++ b/src/library/librarytablemodel.cpp
@@ -101,12 +101,10 @@ TrackModel::CapabilitiesFlags LibraryTableModel::getCapabilities() const {
| TRACKMODELCAPS_ADDTOPLAYLIST
| TRACKMODELCAPS_ADDTOCRATE
| TRACKMODELCAPS_ADDTOAUTODJ
- | TRACKMODELCAPS_IMPORTMETADATA
+ | TRACKMODELCAPS_EDITMETADATA
| TRACKMODELCAPS_LOADTODECK
| TRACKMODELCAPS_LOADTOSAMPLER
| TRACKMODELCAPS_LOADTOPREVIEWDECK
| TRACKMODELCAPS_HIDE
- | TRACKMODELCAPS_MANIPULATEBEATS
- | TRACKMODELCAPS_CLEAR_BEATS
| TRACKMODELCAPS_RESETPLAYED;
}
diff --git a/src/library/playlisttablemodel.cpp b/src/library/playlisttablemodel.cpp
index 8c659f9b93..e1bc686c7d 100644
--- a/src/library/playlisttablemodel.cpp
+++ b/src/library/playlisttablemodel.cpp
@@ -242,12 +242,10 @@ TrackModel::CapabilitiesFlags PlaylistTableModel::getCapabilities() const {
| TRACKMODELCAPS_REORDER
| TRACKMODELCAPS_ADDTOCRATE
| TRACKMODELCAPS_ADDTOPLAYLIST
- | TRACKMODELCAPS_IMPORTMETADATA
+ | TRACKMODELCAPS_EDITMETADATA
| TRACKMODELCAPS_LOADTODECK
| TRACKMODELCAPS_LOADTOSAMPLER
| TRACKMODELCAPS_LOADTOPREVIEWDECK
- | TRACKMODELCAPS_MANIPULATEBEATS
- | TRACKMODELCAPS_CLEAR_BEATS
| TRACKMODELCAPS_RESETPLAYED;
if (m_iPlaylistId != m_pTrackCollection->getPlaylistDAO().getPlaylistIdFromName(AUTODJ_TABLE)) {
diff --git a/src/library/searchquery.cpp b/src/library/searchquery.cpp
index 13dee24ef8..04e6814e03 100644
--- a/src/library/searchquery.cpp
+++ b/src/library/searchquery.cpp
@@ -6,6 +6,7 @@
#include "track/keyutils.h"
#include "library/dao/trackschema.h"
#include "util/db/sqllikewildcards.h"
+#include "util/db/dbconnection.h"
QVariant getTrackValueForColumn(const TrackPointer& pTrack, const QString& column) {
if (column == LIBRARYTABLE_ARTIST) {
@@ -141,6 +142,15 @@ QString NotNode::toSql() const {
}
}
+TextFilterNode::TextFilterNode(const QSqlDatabase& database,
+ const QStringList& sqlColumns,
+ const QString& argument)
+ : m_database(database),
+ m_sqlColumns(sqlColumns),
+ m_argument(argument) {
+ mixxx::DbConnection::makeStringLatinLow(&m_argument);
+}
+
bool TextFilterNode::match(const TrackPointer& pTrack) const {
for (const auto& sqlColumn: m_sqlColumns) {
QVariant value = getTrackValueForColumn(pTrack, sqlColumn);
@@ -148,7 +158,9 @@ bool TextFilterNode::match(const TrackPointer& pTrack) const {
continue;
}
- if (value.toString().contains(m_argument, Qt::CaseInsensitive)) {
+ QString strValue = value.toString();
+ mixxx::DbConnection::makeStringLatinLow(&strValue);
+ if (strValue.contains(m_argument)) {
return true;
}
}
@@ -157,8 +169,16 @@ bool TextFilterNode::match(const TrackPointer& pTrack) const {
QString TextFilterNode::toSql() const {
FieldEscaper escaper(m_database);
- QString escapedArgument = escaper.escapeString(kSqlLikeMatchAll + m_argument + kSqlLikeMatchAll);
-
+ QString argument = m_argument;
+ if (argument.size() > 0) {
+ if (argument[argument.size() - 1].isSpace()) {
+ // LIKE eats a trailing space. This can be avoided by adding a '_'
+ // as a delimiter that matches any following character.
+ argument.append('_');
+ }
+ }
+ QString escapedArgument = escaper.escapeString(
+ kSqlLikeMatchAll + argument + kSqlLikeMatchAll);
QStringList searchClauses;
for (const auto& sqlColumn: m_sqlColumns) {
searchClauses << QString("%1 LIKE %2").arg(sqlColumn, escapedArgument);
diff --git a/src/library/searchquery.h b/src/library/searchquery.h
index c36f1ad400..20241dec5a 100644
--- a/src/library/searchquery.h
+++ b/src/library/searchquery.h
@@ -76,11 +76,7 @@ class TextFilterNode : public QueryNode {
public:
TextFilterNode(const QSqlDatabase& database,
const QStringList& sqlColumns,
- const QString& argument)
- : m_database(database),
- m_sqlColumns(sqlColumns),
- m_argument(argument) {
- }
+ const QString& argument);
bool match(const TrackPointer& pTrack) const override;
QString toSql() const override;
diff --git a/src/library/searchqueryparser.cpp b/src/library/searchqueryparser.cpp
index 7ab0c83cdd..c8b0a47a30 100644
--- a/src/library/searchqueryparser.cpp
+++ b/src/library/searchqueryparser.cpp
@@ -136,15 +136,16 @@ void SearchQueryParser::parseTokens(QStringList tokens,
} else if (m_textFilterMatcher.indexIn(token) != -1) {
QString field = m_textFilterMatcher.cap(1);
QString argument = getTextArgument(
- m_textFilterMatcher.cap(2), &tokens).trimmed();
+ m_textFilterMatcher.cap(2), &tokens);
if (!argument.isEmpty()) {
if (field == "crate") {
pNode = std::make_unique<CrateFilterNode>(
- &m_pTrackCollection->crates(), argument);
+ &m_pTrackCollection->crates(), argument);
} else {
pNode = std::make_unique<TextFilterNode>(
- m_pTrackCollection->database(), m_fieldToSqlColumns[field], argument);
+ m_pTrackCollection->database(),
+ m_fieldToSqlColumns[field], argument);
}
}
} else if (m_numericFilterMatcher.indexIn(token) != -1) {
@@ -154,7 +155,7 @@ void SearchQueryParser::parseTokens(QStringList tokens,
if (!argument.isEmpty()) {
pNode = std::make_unique<NumericFilterNode>(
- m_fieldToSqlColumns[field], argument);
+ m_fieldToSqlColumns[field], argument);
}
} else if (m_specialFilterMatcher.indexIn(token) != -1) {
bool fuzzy = token.startsWith(kFuzzyPrefix);
@@ -190,6 +191,7 @@ void SearchQueryParser::parseTokens(QStringList tokens,
}
// Don't trigger on a lone minus sign.
if (!token.isEmpty()) {
+ QString argument = getTextArgument(token, &tokens);
// For untagged strings we search the track fields as well
// as the crate names the track is in. This allows the user
// to use crates like tags
@@ -197,14 +199,14 @@ void SearchQueryParser::parseTokens(QStringList tokens,
std::unique_ptr<OrNode> gNode = std::make_unique<OrNode>();
gNode->addNode(std::make_unique<CrateFilterNode>(
- &m_pTrackCollection->crates(), token));
+ &m_pTrackCollection->crates(), argument));
gNode->addNode(std::make_unique<TextFilterNode>(
- m_pTrackCollection->database(), queryColumns, token));
+ m_pTrackCollection->database(), queryColumns, argument));
pNode = std::move(gNode);
} else {
pNode = std::make_unique<TextFilterNode>(
- m_pTrackCollection->database(), queryColumns, token);
+ m_pTrackCollection->database(), queryColumns, argument);
}
}
}
diff --git a/src/library/trackmodel.h b/src/library/trackmodel.h
index a877be7ed5..1c4a17fb49 100644
--- a/src/library/trackmodel.h
+++ b/src/library/trackmodel.h
@@ -35,13 +35,11 @@ class TrackModel {
TRACKMODELCAPS_ADDTOCRATE = 0x00008,
TRACKMODELCAPS_ADDTOAUTODJ = 0x00010,
TRACKMODELCAPS_LOCKED = 0x00020,
- TRACKMODELCAPS_IMPORTMETADATA = 0x00040,
+ TRACKMODELCAPS_EDITMETADATA = 0x00040,
TRACKMODELCAPS_LOADTODECK = 0x00080,
TRACKMODELCAPS_LOADTOSAMPLER = 0x00100,
TRACKMODELCAPS_LOADTOPREVIEWDECK = 0x00200,
TRACKMODELCAPS_REMOVE = 0x00400,
- TRACKMODELCAPS_MANIPULATEBEATS = 0x00800,
- TRACKMODELCAPS_CLEAR_BEATS = 0x01000,
TRACKMODELCAPS_RESETPLAYED = 0x02000,
TRACKMODELCAPS_HIDE = 0x04000,
TRACKMODELCAPS_UNHIDE = 0x08000,
diff --git a/src/test/searchqueryparsertest.cpp b/src/test/searchqueryparsertest.cpp
index f848ad0119..31c696375d 100644
--- a/src/test/searchqueryparsertest.cpp
+++ b/src/test/searchqueryparsertest.cpp
@@ -260,6 +260,78 @@ TEST_F(SearchQueryParserTest, TextFilterAllowsSpace) {
qPrintable(pQuery->toSql()));
}
+TEST_F(SearchQueryParserTest, TextFilterQuotes) {
+ QStringList searchColumns;
+ searchColumns << "artist"
+ << "album";
+
+ auto pQuery(
+ m_parser.parseQuery("comment:\"asdf ewe\"", searchColumns, ""));
+
+ TrackPointer pTrack(Track::newTemporary());
+ pTrack->setArtist("asdf");
+ EXPECT_FALSE(pQuery->match(pTrack));
+ pTrack->setComment("test ASDF ewetest");
+ EXPECT_TRUE(pQuery->match(pTrack));
+
+ EXPECT_STREQ(
+ qPrintable(QString("comment LIKE '%asdf ewe%'")),
+ qPrintable(pQuery->toSql()));
+}
+
+TEST_F(SearchQueryParserTest, TextFilterDecoration) {
+ QStringList searchColumns;
+ searchColumns << "artist"
+ << "album";
+
+ auto pQuery(
+ m_parser.parseQuery(QString::fromUtf8("comment:\"asdf\xC2\xB0 ewe\""), searchColumns, "")); // with ˚
+
+ TrackPointer pTrack(Track::newTemporary());
+ pTrack->setArtist("asdf");
+ EXPECT_FALSE(pQuery->match(pTrack));
+ pTrack->setComment("test ASDF ewetest");
+ EXPECT_FALSE(pQuery->match(pTrack));
+
+ pTrack->setComment(QString::fromUtf8("comment:\"asdf\xC2\xB0 ewe\""));
+ EXPECT_TRUE(pQuery->match(pTrack));
+
+ qDebug() << pQuery->toSql();
+
+ EXPECT_STREQ(
+ qPrintable(QString::fromUtf8("comment LIKE '%asdf\xC2\xB0 ewe%'")),
+ qPrintable(pQuery->toSql()));
+}
+
+TEST_F(SearchQueryParserTest, TextFilterTrailingSpace) {
+ QStringList searchColumns;
+ searchColumns << "artist"
+ << "album";
+
+ auto pQuery(
+ m_parser.parseQuery("comment:\"asdf \"", searchColumns, ""));
+
+ TrackPointer pTrack(Track::newTemporary());
+ pTrack->setArtist("asdf");
+ EXPECT_FALSE(pQuery->match(pTrack));
+ pTrack->setComment("test ASDF test");
+ EXPECT_TRUE(pQuery->match(pTrack));
+
+ EXPECT_STREQ(
+ qPrintable(QString("comment LIKE '%asdf _%'")),
+ qPrintable(pQuery->toSql()));
+
+ // We allow to search for two consequitve spaces
+ auto pQuery2(
+ m_parser.parseQuery("comment:\" \"", searchColumns, ""));
+
+ EXPECT_FALSE(pQuery2->match(pTrack));
+
+ EXPECT_STREQ(
+ qPrintable(QString("comment LIKE '% _%'")),
+ qPrintable(pQuery2->toSql()));
+}
+
TEST_F(SearchQueryParserTest, TextFilterNegation) {
QStringList searchColumns;
searchColumns << "artist"
@@ -447,7 +519,7 @@ TEST_F(SearchQueryParserTest, MultipleFilters) {
EXPECT_STREQ(
qPrintable(QString("((bpm >= 127.12) AND (bpm <= 129)) AND "
"((artist LIKE '%com truise%') OR (album_artist LIKE '%com truise%')) AND "
- "((artist LIKE '%Colorvision%') OR (title LIKE '%Colorvision%'))")),
+ "((artist LIKE '%colorvision%') OR (title LIKE '%colorvision%'))")),
qPrintable(pQuery->toSql()));
}
diff --git a/src/util/db/dbconnection.cpp b/src/util/db/dbconnection.cpp
index ee78dfec0f..4a66fa9ff4 100644
--- a/src/util/db/dbconnection.cpp
+++ b/src/util/db/dbconnection.cpp
@@ -73,7 +73,14 @@ inline int compareLocaleAwareCaseInsensitive(
void makeLatinLow(QChar* c, int count) {
for (int i = 0; i < count; ++i) {
if (c[i].decompositionTag() != QChar::NoDecomposition) {
- c[i] = c[i].decomposition()[0];
+ QString decomposition = c[i].decomposition();
+ if (!decomposition[0].isSpace()) {
+ // here we remove the decoration brom all characters.
+ // We want "o" matching "ó" and all other variants but we
+ // do not decompose decoration only characters like "˚" where
+ // the base character is a space
+ c[i] = c[i].decomposition()[0];
+ }
}
if (c[i].isUpper()) {
c[i] = c[i].toLower();
@@ -88,12 +95,11 @@ const QChar kSqlLikeEscapeDefault = '\0';
// false (0) if they are different.
// This is the original sqlite3 icuLikeCompare rewritten for QChar
int likeCompareInner(
- const QChar* pattern, // LIKE pattern
- int patternSize,
- const QChar* string, // The string to compare against
- int stringSize,
- const QChar esc) { // The escape character
-
+ const QChar* pattern, // LIKE pattern
+ int patternSize,
+ const QChar* string, // The string to compare against
+ int stringSize,
+ const QChar esc) { // The escape character
int iPattern = 0; // Current index in pattern
int iString = 0; // Current index in string
@@ -376,6 +382,11 @@ int DbConnection::likeCompareLatinLow(
esc);
}
+//static
+void DbConnection::makeStringLatinLow(QString* string) {
+ makeLatinLow(string->data(), string->length());
+}
+
QDebug operator<<(QDebug debug, const DbConnection& connection) {
return debug
<< connection.name()
diff --git a/src/util/db/dbconnection.h b/src/util/db/dbconnection.h
index b0f080533c..1b344ac8ff 100644
--- a/src/util/db/dbconnection.h
+++ b/src/util/db/dbconnection.h
@@ -22,6 +22,8 @@ class DbConnection final {
QString* string,
QChar esc);
+ static void makeStringLatinLow(QString* string);
+
struct Params {
QString type;
QString hostName;
diff --git a/src/widget/wtracktableview.cpp b/src/widget/wtracktableview.cpp
index fd92079f1b..d78e273309 100644
--- a/src/widget/wtracktableview.cpp
+++ b/src/widget/wtracktableview.cpp
@@ -898,16 +898,14 @@ void WTrackTableView::contextMenuEvent(QContextMenuEvent* event) {
m_pMenu->addSeparator();
m_pMetadataMenu->clear();
- if (modelHasCapabilities(TrackModel::TRACKMODELCAPS_IMPORTMETADATA)) {
+ if (modelHasCapabilities(TrackModel::TRACKMODELCAPS_EDITMETADATA)) {
m_pMetadataMenu->addAction(m_pImportMetadataFromFileAct);
m_pImportMetadataFromMusicBrainzAct->setEnabled(oneSongSelected);
m_pMetadataMenu->addAction(m_pImportMetadataFromMusicBrainzAct);
m_pMetadataMenu->addAction(m_pExportMetadataAct);
- }
- m_pClearMetadataMenu->clear();
+ m_pClearMetadataMenu->clear();
- if (modelHasCapabilities(TrackModel::TRACKMODELCAPS_CLEAR_BEATS)) {
if (trackModel == nullptr) {
return;
}
@@ -928,38 +926,38 @@ void WTrackTableView::contextMenuEvent(QContextMenuEvent* event) {
m_pClearMetadataMenu-