Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Library track selection tweaks #4536

Merged
merged 4 commits into from
Jan 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/library/autodj/dlgautodj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,6 @@ void DlgAutoDJ::saveCurrentViewState() {
m_pTrackTableView->saveCurrentViewState();
}

void DlgAutoDJ::restoreCurrentViewState() {
m_pTrackTableView->restoreCurrentViewState();
bool DlgAutoDJ::restoreCurrentViewState() {
return m_pTrackTableView->restoreCurrentViewState();
}
2 changes: 1 addition & 1 deletion src/library/autodj/dlgautodj.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class DlgAutoDJ : public QWidget, public Ui::DlgAutoDJ, public LibraryView {
void loadSelectedTrackToGroup(const QString& group, bool play) override;
void moveSelection(int delta) override;
void saveCurrentViewState() override;
void restoreCurrentViewState() override;
bool restoreCurrentViewState() override;

public slots:
void shufflePlaylistButton(bool buttonChecked);
Expand Down
4 changes: 2 additions & 2 deletions src/library/dlganalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,6 @@ void DlgAnalysis::saveCurrentViewState() {
m_pAnalysisLibraryTableView->saveCurrentViewState();
}

void DlgAnalysis::restoreCurrentViewState() {
m_pAnalysisLibraryTableView->restoreCurrentViewState();
bool DlgAnalysis::restoreCurrentViewState() {
return m_pAnalysisLibraryTableView->restoreCurrentViewState();
}
2 changes: 1 addition & 1 deletion src/library/dlganalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class DlgAnalysis : public QWidget, public Ui::DlgAnalysis, public virtual Libra
return m_pAnalysisLibraryTableModel->currentSearch();
}
void saveCurrentViewState() override;
void restoreCurrentViewState() override;
bool restoreCurrentViewState() override;

public slots:
void tableSelectionChanged(const QItemSelection& selected,
Expand Down
4 changes: 2 additions & 2 deletions src/library/dlghidden.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ void DlgHidden::saveCurrentViewState() {
m_pTrackTableView->saveCurrentViewState();
}

void DlgHidden::restoreCurrentViewState() {
m_pTrackTableView->restoreCurrentViewState();
bool DlgHidden::restoreCurrentViewState() {
return m_pTrackTableView->restoreCurrentViewState();
}

void DlgHidden::setFocus() {
Expand Down
2 changes: 1 addition & 1 deletion src/library/dlghidden.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class DlgHidden : public QWidget, public Ui::DlgHidden, public LibraryView {
void onSearch(const QString& text) override;
QString currentSearch();
void saveCurrentViewState() override;
void restoreCurrentViewState() override;
bool restoreCurrentViewState() override;

public slots:
void clicked();
Expand Down
5 changes: 3 additions & 2 deletions src/library/dlgmissing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@ bool DlgMissing::hasFocus() const {
void DlgMissing::saveCurrentViewState() {
m_pTrackTableView->saveCurrentViewState();
};
void DlgMissing::restoreCurrentViewState() {
m_pTrackTableView->restoreCurrentViewState();

bool DlgMissing::restoreCurrentViewState() {
return m_pTrackTableView->restoreCurrentViewState();
};

void DlgMissing::setFocus() {
Expand Down
2 changes: 1 addition & 1 deletion src/library/dlgmissing.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class DlgMissing : public QWidget, public Ui::DlgMissing, public LibraryView {
void onSearch(const QString& text) override;
QString currentSearch();
void saveCurrentViewState() override;
void restoreCurrentViewState() override;
bool restoreCurrentViewState() override;

public slots:
void clicked();
Expand Down
7 changes: 5 additions & 2 deletions src/library/libraryview.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ class LibraryView {
}
virtual void saveCurrentViewState() {
}
virtual void restoreCurrentViewState() {
}
/// @brief restores current view state.
/// @return true if restore succeeded
virtual bool restoreCurrentViewState() {
ronso0 marked this conversation as resolved.
Show resolved Hide resolved
return false;
};

/// If applicable, requests that the LibraryView load the selected track to
/// the specified group. Does nothing otherwise.
Expand Down
4 changes: 2 additions & 2 deletions src/library/recording/dlgrecording.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,6 @@ void DlgRecording::saveCurrentViewState() {
m_pTrackTableView->saveCurrentViewState();
}

void DlgRecording::restoreCurrentViewState() {
m_pTrackTableView->restoreCurrentViewState();
bool DlgRecording::restoreCurrentViewState() {
return m_pTrackTableView->restoreCurrentViewState();
}
2 changes: 1 addition & 1 deletion src/library/recording/dlgrecording.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class DlgRecording : public QWidget, public Ui::DlgRecording, public virtual Lib
void moveSelection(int delta) override;
inline const QString currentSearch() { return m_proxyModel.currentSearch(); }
void saveCurrentViewState() override;
void restoreCurrentViewState() override;
bool restoreCurrentViewState() override;

public slots:
void slotRecordingStateChanged(bool);
Expand Down
45 changes: 45 additions & 0 deletions src/library/searchqueryparser.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
#include "library/searchqueryparser.h"

#include <QRegularExpression>

#include "track/keyutils.h"

constexpr char kNegatePrefix[] = "-";
constexpr char kFuzzyPrefix[] = "~";
// see https://stackoverflow.com/questions/1310473/regex-matching-spaces-but-not-in-strings
const QRegularExpression kSplitIntoWordsRegexp = QRegularExpression(
QStringLiteral(" (?=[^\"]*(\"[^\"]*\"[^\"]*)*$)"));

SearchQueryParser::SearchQueryParser(TrackCollection* pTrackCollection)
: m_pTrackCollection(pTrackCollection) {
Expand Down Expand Up @@ -271,3 +276,43 @@ std::unique_ptr<QueryNode> SearchQueryParser::parseQuery(const QString& query,

return pQuery;
}

QStringList SearchQueryParser::splitQueryIntoWords(const QString& query) {
QStringList queryWordList = query.split(kSplitIntoWordsRegexp, QString::SkipEmptyParts);
return queryWordList;
}

bool SearchQueryParser::queryIsLessSpecific(const QString& original, const QString& changed) {
// separate search query into tokens
QStringList oldWordList = SearchQueryParser::splitQueryIntoWords(original);
QStringList newWordList = SearchQueryParser::splitQueryIntoWords(changed);

// we sort the lists for length so the comperator will pop the longest match first
std::sort(oldWordList.begin(), oldWordList.end(), [=](const QString& v1, const QString& v2) {
return v1.length() > v2.length();
});
std::sort(newWordList.begin(), newWordList.end(), [=](const QString& v1, const QString& v2) {
return v1.length() > v2.length();
});

for (int i = 0; i < oldWordList.length(); i++) {
const QString& oldWord = oldWordList.at(i);
for (int j = 0; j < newWordList.length(); j++) {
const QString& newWord = newWordList.at(j);
// Note(ronso0) Look for missing '~' in newWord (fuzzy matching)?
if ((oldWord.startsWith("-") && oldWord.startsWith(newWord)) ||
(!newWord.contains(":") && oldWord.contains(newWord)) ||
(newWord.contains(":") && oldWord.startsWith(newWord))) {
// we found a match and can remove the search term list
newWordList.removeAt(j);
break;
}
}
}
// if the new search query list contains no more terms, we have a reduced
// search term
if (newWordList.empty()) {
return true;
}
return false;
}
4 changes: 4 additions & 0 deletions src/library/searchqueryparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ class SearchQueryParser {
const QStringList& searchColumns,
const QString& extraFilter) const;

/// splits the query into a list of terms
static QStringList splitQueryIntoWords(const QString& query);
/// checks if the changed search query is less specific then the original term
static bool queryIsLessSpecific(const QString& original, const QString& changed);

private:
void parseTokens(QStringList tokens,
Expand Down
103 changes: 102 additions & 1 deletion src/test/searchqueryparsertest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -832,7 +832,6 @@ TEST_F(SearchQueryParserTest, ShortCrateFilter) {
EXPECT_TRUE(pQuery->match(pTrackC));
}


TEST_F(SearchQueryParserTest, CrateFilterEmpty) {
// Empty should match everything
auto pQuery(m_parser.parseQuery(QString("crate: "), QStringList(), ""));
Expand Down Expand Up @@ -996,3 +995,105 @@ TEST_F(SearchQueryParserTest, CrateFilterWithCrateFilterAndNegation){
") AND (NOT (" + m_crateFilterQuery.arg(searchTermB) + "))"),
qPrintable(pQueryB->toSql()));
}

TEST_F(SearchQueryParserTest, SplitQueryIntoWords) {
QStringList rv = SearchQueryParser::splitQueryIntoWords(QString("a test b"));
QStringList ex = QStringList() << "a"
<< "test"
<< "b";
qDebug() << rv << ex;
EXPECT_EQ(rv, ex);

QStringList rv2 = SearchQueryParser::splitQueryIntoWords(QString("a \"test ' b\" x"));
QStringList ex2 = QStringList() << "a"
<< "\"test ' b\""
<< "x";
qDebug() << rv2 << ex2;
EXPECT_EQ(rv2, ex2);

QStringList rv3 = SearchQueryParser::splitQueryIntoWords(QString("a x"));
QStringList ex3 = QStringList() << "a"
<< "x";
qDebug() << rv3 << ex3;
EXPECT_EQ(rv3, ex3);

QStringList rv4 = SearchQueryParser::splitQueryIntoWords(
QString("a crate:x title:\"S p A C e\" ~key:2m"));
QStringList ex4 = QStringList() << "a"
<< "crate:x"
<< "title:\"S p A C e\""
<< "~key:2m";
qDebug() << rv4 << ex4;
EXPECT_EQ(rv4, ex4);
}

TEST_F(SearchQueryParserTest, QueryIsLessSpecific) {
// Generate a file name for the temporary file
EXPECT_TRUE(SearchQueryParser::queryIsLessSpecific(
QStringLiteral("searchme"),
QStringLiteral("searchm")));

EXPECT_TRUE(SearchQueryParser::queryIsLessSpecific(
QStringLiteral("A B C"),
QStringLiteral("A C")));

EXPECT_FALSE(SearchQueryParser::queryIsLessSpecific(
QStringLiteral("A B C"),
QStringLiteral("A D C")));

EXPECT_TRUE(SearchQueryParser::queryIsLessSpecific(
QStringLiteral("A D C"),
QStringLiteral("A D C ")));

EXPECT_TRUE(SearchQueryParser::queryIsLessSpecific(
QStringLiteral("A D C "),
QStringLiteral("A D C")));

EXPECT_FALSE(SearchQueryParser::queryIsLessSpecific(
QStringLiteral("A B C"),
QStringLiteral("A D C")));

EXPECT_TRUE(SearchQueryParser::queryIsLessSpecific(
QStringLiteral("A D C"),
QStringLiteral("A D C ")));

EXPECT_TRUE(SearchQueryParser::queryIsLessSpecific(
QStringLiteral("A D C "),
QStringLiteral("A D C")));

EXPECT_FALSE(SearchQueryParser::queryIsLessSpecific(
QStringLiteral("A B C"),
QStringLiteral("A D C")));

EXPECT_TRUE(SearchQueryParser::queryIsLessSpecific(
QStringLiteral("Abba1 Abba2 Abb"),
QStringLiteral("Abba1 Abba Abb")));

EXPECT_FALSE(SearchQueryParser::queryIsLessSpecific(
QStringLiteral("Abba1 Abba2 Abb"),
QStringLiteral("Abba1 Aba Abb")));

EXPECT_TRUE(SearchQueryParser::queryIsLessSpecific(
QStringLiteral("Abba1"),
QLatin1String("")));

EXPECT_TRUE(SearchQueryParser::queryIsLessSpecific(
QStringLiteral("Abba1"),
QStringLiteral("bba")));

EXPECT_TRUE(SearchQueryParser::queryIsLessSpecific(
QStringLiteral("crate:abc"),
QStringLiteral("crate:ab")));

EXPECT_FALSE(SearchQueryParser::queryIsLessSpecific(
QStringLiteral("crate:\"a b c\""),
QStringLiteral("crate:\"a c\"")));

EXPECT_FALSE(SearchQueryParser::queryIsLessSpecific(
QStringLiteral("-crate:\"a b c\""),
QStringLiteral("crate:\"a b c\"")));

EXPECT_FALSE(SearchQueryParser::queryIsLessSpecific(
QStringLiteral("-crate:\"a b c\""),
QStringLiteral("crate:\"a b c\"")));
}
13 changes: 7 additions & 6 deletions src/widget/wlibrarytableview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,12 @@ void WLibraryTableView::saveTrackModelState(
m_modelStateCache.insert(key, state, 1);
}

void WLibraryTableView::restoreTrackModelState(
bool WLibraryTableView::restoreTrackModelState(
const QAbstractItemModel* model, const QString& key) {
//qDebug() << "restoreTrackModelState:" << model << key;
//qDebug() << m_modelStateCache.keys();
if (model == nullptr) {
return;
return false;
}

ModelState* state = m_modelStateCache.take(key);
Expand All @@ -148,7 +148,7 @@ void WLibraryTableView::restoreTrackModelState(
verticalScrollBar()->setValue(0);
horizontalScrollBar()->setValue(0);
setCurrentIndex(QModelIndex());
return;
return false;
}

verticalScrollBar()->setValue(state->verticalScrollPosition);
Expand All @@ -171,6 +171,7 @@ void WLibraryTableView::restoreTrackModelState(

// reinsert the state into the cache
m_modelStateCache.insert(key, state, 1);
return true;
}

void WLibraryTableView::setTrackTableFont(const QFont& font) {
Expand Down Expand Up @@ -212,13 +213,13 @@ void WLibraryTableView::saveCurrentViewState() {
saveTrackModelState(currentModel, key);
}

void WLibraryTableView::restoreCurrentViewState() {
bool WLibraryTableView::restoreCurrentViewState() {
const QAbstractItemModel* currentModel = model();
QString key = getModelStateKey();
if (!currentModel || key.isEmpty()) {
return;
return false;
}
restoreTrackModelState(currentModel, key);
return restoreTrackModelState(currentModel, key);
}

void WLibraryTableView::focusInEvent(QFocusEvent* event) {
Expand Down
7 changes: 5 additions & 2 deletions src/widget/wlibrarytableview.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,12 @@ class WLibraryTableView : public QTableView, public virtual LibraryView {
/// item selection and current index values associated with model by given
/// key and restores it
/// @param key unique for trackmodel
void restoreTrackModelState(const QAbstractItemModel* model, const QString& key);
/// @return true if restore succeeded
bool restoreTrackModelState(const QAbstractItemModel* model, const QString& key);
void saveCurrentViewState() override;
void restoreCurrentViewState() override;
/// @brief restores current view state.
/// @return true if restore succeeded
bool restoreCurrentViewState() override;

signals:
void loadTrack(TrackPointer pTrack);
Expand Down
Loading