Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bea6ec5b0 | ||
|
|
e8144487ee | ||
|
|
41d9d15dda | ||
|
|
124b97c024 | ||
|
|
98e0b45403 | ||
|
|
1f2b8d8bf6 | ||
|
|
8327751b91 | ||
|
|
6417f89596 | ||
|
|
2e53656f44 | ||
|
|
822cf0ad07 | ||
|
|
67f04a81b3 | ||
|
|
9232ad0125 | ||
|
|
0de87b3e1e | ||
|
|
74b8cd6156 | ||
|
|
ac959387fe | ||
|
|
ffd8ce9281 | ||
|
|
d8052b295f | ||
|
|
625929133c | ||
|
|
79c28e7e1d | ||
|
|
01f4a79f07 | ||
|
|
47c5a2215e | ||
|
|
bb6e38630f |
@@ -2,7 +2,7 @@ Strawberry Music Player
|
|||||||
=======================
|
=======================
|
||||||
ChangeLog
|
ChangeLog
|
||||||
|
|
||||||
Version 1.1.0-rc1:
|
Version 1.1.0 (2024.07.14):
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
* Fixed crash when pressing CTRL + C (#1359).
|
* Fixed crash when pressing CTRL + C (#1359).
|
||||||
@@ -28,14 +28,13 @@ Version 1.1.0-rc1:
|
|||||||
* Only use playbin3 with GStreamer 1.24 and higher, not with GStreamer 1.22 or lower.
|
* Only use playbin3 with GStreamer 1.24 and higher, not with GStreamer 1.22 or lower.
|
||||||
* (macOS/Windows) Fixed dash and hls streaming, plugins were missing.
|
* (macOS/Windows) Fixed dash and hls streaming, plugins were missing.
|
||||||
* (Windows) Fixed incorrect colors in smart playlist wizard with Fusion in dark mode (#1399).
|
* (Windows) Fixed incorrect colors in smart playlist wizard with Fusion in dark mode (#1399).
|
||||||
|
* (Windows) Fixed update window blocking sponsor window on startup.
|
||||||
|
|
||||||
Enhancements:
|
Enhancements:
|
||||||
* Improve error messages when connecting and copying to devices.
|
* Improve error messages when connecting and copying to devices.
|
||||||
* Allow enter to be used with multiselection to add songs to playlist (#1360)
|
* Allow enter to be used with multiselection to add songs to playlist (#1360)
|
||||||
* Add song progress to taskbar using D-Bus.
|
* Add song progress to taskbar using D-Bus.
|
||||||
* Use API to receive Radio Paradise channels.
|
* Use API to receive Radio Paradise channels.
|
||||||
* Added letras lyrics provider.
|
|
||||||
* Added Open Tidal API (openapi.tidal.com) cover provider.
|
|
||||||
* Added button for fetching lyrics to tag editor (#1391).
|
* Added button for fetching lyrics to tag editor (#1391).
|
||||||
* Added option not to skip "A", "An" and "The” when sorting artist names in collection (#1393).
|
* Added option not to skip "A", "An" and "The” when sorting artist names in collection (#1393).
|
||||||
* Improved album and title disc, remastered, etc matching and stripping (#1387).
|
* Improved album and title disc, remastered, etc matching and stripping (#1387).
|
||||||
@@ -54,6 +53,9 @@ Version 1.1.0-rc1:
|
|||||||
* (Windows MSVC) Add back WASAPI2.
|
* (Windows MSVC) Add back WASAPI2.
|
||||||
|
|
||||||
New features:
|
New features:
|
||||||
|
* Letras lyrics provider.
|
||||||
|
* Open Tidal API (openapi.tidal.com) cover provider.
|
||||||
|
* Turbine analyzer.
|
||||||
* WaveRubber analyzer.
|
* WaveRubber analyzer.
|
||||||
* Spotify streaming support.
|
* Spotify streaming support.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
set(STRAWBERRY_VERSION_MAJOR 1)
|
set(STRAWBERRY_VERSION_MAJOR 1)
|
||||||
set(STRAWBERRY_VERSION_MINOR 1)
|
set(STRAWBERRY_VERSION_MINOR 1)
|
||||||
set(STRAWBERRY_VERSION_PATCH 0)
|
set(STRAWBERRY_VERSION_PATCH 0)
|
||||||
set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
||||||
|
|
||||||
set(INCLUDE_GIT_REVISION OFF)
|
set(INCLUDE_GIT_REVISION OFF)
|
||||||
|
|
||||||
|
|||||||
@@ -94,9 +94,4 @@ CREATE INDEX idx_device_%deviceid_songs_album ON device_%deviceid_songs (album);
|
|||||||
|
|
||||||
CREATE INDEX idx_device_%deviceid_songs_comp_artist ON device_%deviceid_songs (compilation_effective, artist);
|
CREATE INDEX idx_device_%deviceid_songs_comp_artist ON device_%deviceid_songs (compilation_effective, artist);
|
||||||
|
|
||||||
CREATE VIRTUAL TABLE device_%deviceid_fts USING fts5(
|
|
||||||
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment,
|
|
||||||
tokenize = "unicode61 remove_diacritics 1"
|
|
||||||
);
|
|
||||||
|
|
||||||
UPDATE devices SET schema_version=5 WHERE ROWID=%deviceid;
|
UPDATE devices SET schema_version=5 WHERE ROWID=%deviceid;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS schema_version (
|
|||||||
|
|
||||||
DELETE FROM schema_version;
|
DELETE FROM schema_version;
|
||||||
|
|
||||||
INSERT INTO schema_version (version) VALUES (19);
|
INSERT INTO schema_version (version) VALUES (20);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS directories (
|
CREATE TABLE IF NOT EXISTS directories (
|
||||||
path TEXT NOT NULL,
|
path TEXT NOT NULL,
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
</screenshots>
|
</screenshots>
|
||||||
<update_contact>eclipseo@fedoraproject.org</update_contact>
|
<update_contact>eclipseo@fedoraproject.org</update_contact>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="1.1.0" date="2024-07-14"/>
|
||||||
<release version="1.0.23" date="2024-01-11"/>
|
<release version="1.0.23" date="2024-01-11"/>
|
||||||
<release version="1.0.22" date="2023-12-09"/>
|
<release version="1.0.22" date="2023-12-09"/>
|
||||||
<release version="1.0.21" date="2023-10-21"/>
|
<release version="1.0.21" date="2023-10-21"/>
|
||||||
|
|||||||
@@ -23,8 +23,6 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <boost/algorithm/string/trim.hpp>
|
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
@@ -64,7 +62,7 @@ class TagReaderTagLib : public TagReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline std::string TagLibStringToStdString(const TagLib::String &s) {
|
static inline std::string TagLibStringToStdString(const TagLib::String &s) {
|
||||||
return std::string(s.toCString(), s.length());
|
return std::string(s.toCString(true), s.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline TagLib::String QStringToTagLibString(const QString &s) {
|
static inline TagLib::String QStringToTagLibString(const QString &s) {
|
||||||
@@ -77,9 +75,9 @@ class TagReaderTagLib : public TagReaderBase {
|
|||||||
|
|
||||||
static inline void AssignTagLibStringToStdString(const TagLib::String &tstr, std::string *output) {
|
static inline void AssignTagLibStringToStdString(const TagLib::String &tstr, std::string *output) {
|
||||||
|
|
||||||
std::string stdstr = TagLibStringToStdString(tstr);
|
const QString qstr = TagLibStringToQString(tstr).trimmed();
|
||||||
boost::trim(stdstr);
|
const QByteArray data = qstr.toUtf8();
|
||||||
output->assign(stdstr);
|
output->assign(data.constData(), data.size());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,9 +72,10 @@ set(SOURCES
|
|||||||
analyzer/analyzercontainer.cpp
|
analyzer/analyzercontainer.cpp
|
||||||
analyzer/blockanalyzer.cpp
|
analyzer/blockanalyzer.cpp
|
||||||
analyzer/boomanalyzer.cpp
|
analyzer/boomanalyzer.cpp
|
||||||
|
analyzer/turbineanalyzer.cpp
|
||||||
|
analyzer/sonogramanalyzer.cpp
|
||||||
|
analyzer/waverubberanalyzer.cpp
|
||||||
analyzer/rainbowanalyzer.cpp
|
analyzer/rainbowanalyzer.cpp
|
||||||
analyzer/sonogram.cpp
|
|
||||||
analyzer/waverubber.cpp
|
|
||||||
|
|
||||||
equalizer/equalizer.cpp
|
equalizer/equalizer.cpp
|
||||||
equalizer/equalizerslider.cpp
|
equalizer/equalizerslider.cpp
|
||||||
@@ -331,9 +332,10 @@ set(HEADERS
|
|||||||
analyzer/analyzercontainer.h
|
analyzer/analyzercontainer.h
|
||||||
analyzer/blockanalyzer.h
|
analyzer/blockanalyzer.h
|
||||||
analyzer/boomanalyzer.h
|
analyzer/boomanalyzer.h
|
||||||
|
analyzer/turbineanalyzer.h
|
||||||
|
analyzer/sonogramanalyzer.h
|
||||||
|
analyzer/waverubberanalyzer.h
|
||||||
analyzer/rainbowanalyzer.h
|
analyzer/rainbowanalyzer.h
|
||||||
analyzer/sonogram.h
|
|
||||||
analyzer/waverubber.h
|
|
||||||
|
|
||||||
equalizer/equalizer.h
|
equalizer/equalizer.h
|
||||||
equalizer/equalizerslider.h
|
equalizer/equalizerslider.h
|
||||||
|
|||||||
@@ -39,9 +39,10 @@
|
|||||||
#include "analyzerbase.h"
|
#include "analyzerbase.h"
|
||||||
#include "blockanalyzer.h"
|
#include "blockanalyzer.h"
|
||||||
#include "boomanalyzer.h"
|
#include "boomanalyzer.h"
|
||||||
|
#include "turbineanalyzer.h"
|
||||||
|
#include "sonogramanalyzer.h"
|
||||||
|
#include "waverubberanalyzer.h"
|
||||||
#include "rainbowanalyzer.h"
|
#include "rainbowanalyzer.h"
|
||||||
#include "sonogram.h"
|
|
||||||
#include "waverubber.h"
|
|
||||||
|
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "core/shared_ptr.h"
|
#include "core/shared_ptr.h"
|
||||||
@@ -88,10 +89,11 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
|||||||
|
|
||||||
AddAnalyzerType<BlockAnalyzer>();
|
AddAnalyzerType<BlockAnalyzer>();
|
||||||
AddAnalyzerType<BoomAnalyzer>();
|
AddAnalyzerType<BoomAnalyzer>();
|
||||||
AddAnalyzerType<NyanCatAnalyzer>();
|
AddAnalyzerType<TurbineAnalyzer>();
|
||||||
|
AddAnalyzerType<SonogramAnalyzer>();
|
||||||
|
AddAnalyzerType<WaveRubberAnalyzer>();
|
||||||
AddAnalyzerType<RainbowDashAnalyzer>();
|
AddAnalyzerType<RainbowDashAnalyzer>();
|
||||||
AddAnalyzerType<Sonogram>();
|
AddAnalyzerType<NyanCatAnalyzer>();
|
||||||
AddAnalyzerType<WaveRubber>();
|
|
||||||
|
|
||||||
disable_action_ = context_menu_->addAction(tr("No analyzer"), this, &AnalyzerContainer::DisableAnalyzer);
|
disable_action_ = context_menu_->addAction(tr("No analyzer"), this, &AnalyzerContainer::DisableAnalyzer);
|
||||||
disable_action_->setCheckable(true);
|
disable_action_->setCheckable(true);
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ void BlockAnalyzer::transform(Scope &s) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockAnalyzer::analyze(QPainter &p, const Scope &s, bool new_frame) {
|
void BlockAnalyzer::analyze(QPainter &p, const Scope &s, const bool new_frame) {
|
||||||
|
|
||||||
// y = 2 3 2 1 0 2
|
// y = 2 3 2 1 0 2
|
||||||
// . . . . # .
|
// . . . . # .
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class BlockAnalyzer : public AnalyzerBase {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void transform(Scope&) override;
|
void transform(Scope&) override;
|
||||||
void analyze(QPainter &p, const Scope&, bool new_frame) override;
|
void analyze(QPainter &p, const Scope &s, const bool new_frame) override;
|
||||||
void resizeEvent(QResizeEvent*) override;
|
void resizeEvent(QResizeEvent*) override;
|
||||||
virtual void paletteChange(const QPalette&);
|
virtual void paletteChange(const QPalette&);
|
||||||
void framerateChanged() override;
|
void framerateChanged() override;
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class BoomAnalyzer : public AnalyzerBase {
|
|||||||
static const char *kName;
|
static const char *kName;
|
||||||
|
|
||||||
void transform(Scope &s) override;
|
void transform(Scope &s) override;
|
||||||
void analyze(QPainter &p, const Scope&, const bool new_frame) override;
|
void analyze(QPainter &p, const Scope &scope, const bool new_frame) override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void changeK_barHeight(int);
|
void changeK_barHeight(int);
|
||||||
|
|||||||
@@ -41,18 +41,21 @@
|
|||||||
#include "fht.h"
|
#include "fht.h"
|
||||||
#include "analyzerbase.h"
|
#include "analyzerbase.h"
|
||||||
|
|
||||||
|
const char *NyanCatAnalyzer::kName = "Nyanalyzer Cat";
|
||||||
|
const char *RainbowDashAnalyzer::kName = "Rainbow Dash";
|
||||||
|
|
||||||
|
RainbowAnalyzer::RainbowType RainbowAnalyzer::rainbowtype;
|
||||||
const int RainbowAnalyzer::kHeight[] = { 21, 33 };
|
const int RainbowAnalyzer::kHeight[] = { 21, 33 };
|
||||||
const int RainbowAnalyzer::kWidth[] = { 34, 53 };
|
const int RainbowAnalyzer::kWidth[] = { 34, 53 };
|
||||||
const int RainbowAnalyzer::kFrameCount[] = { 6, 16 };
|
const int RainbowAnalyzer::kFrameCount[] = { 6, 16 };
|
||||||
const int RainbowAnalyzer::kRainbowHeight[] = { 21, 16 };
|
|
||||||
const int RainbowAnalyzer::kRainbowOverlap[] = { 13, 15 };
|
|
||||||
const int RainbowAnalyzer::kSleepingHeight[] = { 24, 33 };
|
const int RainbowAnalyzer::kSleepingHeight[] = { 24, 33 };
|
||||||
|
|
||||||
const char *NyanCatAnalyzer::kName = "Nyanalyzer Cat";
|
namespace {
|
||||||
const char *RainbowDashAnalyzer::kName = "Rainbow Dash";
|
constexpr int kFrameIntervalMs = 150;
|
||||||
const float RainbowAnalyzer::kPixelScale = 0.02F;
|
constexpr int kRainbowHeight[] = { 21, 16 };
|
||||||
|
constexpr int kRainbowOverlap[] = { 13, 15 };
|
||||||
RainbowAnalyzer::RainbowType RainbowAnalyzer::rainbowtype;
|
constexpr float kPixelScale = 0.02F;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
RainbowAnalyzer::RainbowAnalyzer(const RainbowType rbtype, QWidget *parent)
|
RainbowAnalyzer::RainbowAnalyzer(const RainbowType rbtype, QWidget *parent)
|
||||||
: AnalyzerBase(parent, 9),
|
: AnalyzerBase(parent, 9),
|
||||||
@@ -106,7 +109,7 @@ void RainbowAnalyzer::resizeEvent(QResizeEvent *e) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RainbowAnalyzer::analyze(QPainter &p, const Scope &s, bool new_frame) {
|
void RainbowAnalyzer::analyze(QPainter &p, const Scope &s, const bool new_frame) {
|
||||||
|
|
||||||
// Discard the second half of the transform
|
// Discard the second half of the transform
|
||||||
const int scope_size = static_cast<int>(s.size() / 2);
|
const int scope_size = static_cast<int>(s.size() / 2);
|
||||||
|
|||||||
@@ -49,44 +49,37 @@ class RainbowAnalyzer : public AnalyzerBase {
|
|||||||
Dash = 1
|
Dash = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
RainbowAnalyzer(const RainbowType rbtype, QWidget *parent);
|
explicit RainbowAnalyzer(const RainbowType rbtype, QWidget *parent);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void transform(Scope&) override;
|
void transform(Scope &s) override;
|
||||||
void analyze(QPainter &p, const Scope&, bool new_frame) override;
|
void analyze(QPainter &p, const Scope &s, const bool new_frame) override;
|
||||||
|
|
||||||
void timerEvent(QTimerEvent *e) override;
|
void timerEvent(QTimerEvent *e) override;
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static const int kRainbowBands = 6;
|
||||||
|
static const int kHistorySize = 128;
|
||||||
|
static RainbowType rainbowtype;
|
||||||
static const int kHeight[];
|
static const int kHeight[];
|
||||||
static const int kWidth[];
|
static const int kWidth[];
|
||||||
static const int kFrameCount[];
|
static const int kFrameCount[];
|
||||||
static const int kRainbowHeight[];
|
|
||||||
static const int kRainbowOverlap[];
|
|
||||||
static const int kSleepingHeight[];
|
static const int kSleepingHeight[];
|
||||||
|
|
||||||
static const int kHistorySize = 128;
|
inline QRect SourceRect(const RainbowType _rainbowtype) const {
|
||||||
static const int kRainbowBands = 6;
|
|
||||||
static const float kPixelScale;
|
|
||||||
|
|
||||||
static const int kFrameIntervalMs = 150;
|
|
||||||
|
|
||||||
static RainbowType rainbowtype;
|
|
||||||
|
|
||||||
inline QRect SourceRect(RainbowType _rainbowtype) const {
|
|
||||||
return QRect(0, kHeight[_rainbowtype] * frame_, kWidth[_rainbowtype], kHeight[_rainbowtype]);
|
return QRect(0, kHeight[_rainbowtype] * frame_, kWidth[_rainbowtype], kHeight[_rainbowtype]);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QRect SleepingSourceRect(RainbowType _rainbowtype) const {
|
inline QRect SleepingSourceRect(const RainbowType _rainbowtype) const {
|
||||||
return QRect(0, kHeight[_rainbowtype] * kFrameCount[_rainbowtype], kWidth[_rainbowtype], kSleepingHeight[_rainbowtype]);
|
return QRect(0, kHeight[_rainbowtype] * kFrameCount[_rainbowtype], kWidth[_rainbowtype], kSleepingHeight[_rainbowtype]);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QRect DestRect(RainbowType _rainbowtype) const {
|
inline QRect DestRect(const RainbowType _rainbowtype) const {
|
||||||
return QRect(width() - kWidth[_rainbowtype], (height() - kHeight[_rainbowtype]) / 2, kWidth[_rainbowtype], kHeight[_rainbowtype]);
|
return QRect(width() - kWidth[_rainbowtype], (height() - kHeight[_rainbowtype]) / 2, kWidth[_rainbowtype], kHeight[_rainbowtype]);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QRect SleepingDestRect(RainbowType _rainbowtype) const {
|
inline QRect SleepingDestRect(const RainbowType _rainbowtype) const {
|
||||||
return QRect(width() - kWidth[_rainbowtype], (height() - kSleepingHeight[_rainbowtype]) / 2, kWidth[_rainbowtype], kSleepingHeight[_rainbowtype]);
|
return QRect(width() - kWidth[_rainbowtype], (height() - kSleepingHeight[_rainbowtype]) / 2, kWidth[_rainbowtype], kSleepingHeight[_rainbowtype]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,14 +26,14 @@
|
|||||||
|
|
||||||
#include "engine/enginebase.h"
|
#include "engine/enginebase.h"
|
||||||
|
|
||||||
#include "sonogram.h"
|
#include "sonogramanalyzer.h"
|
||||||
|
|
||||||
const char *Sonogram::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Sonogram");
|
const char *SonogramAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Sonogram");
|
||||||
|
|
||||||
Sonogram::Sonogram(QWidget *parent)
|
SonogramAnalyzer::SonogramAnalyzer(QWidget *parent)
|
||||||
: AnalyzerBase(parent, 9) {}
|
: AnalyzerBase(parent, 9) {}
|
||||||
|
|
||||||
void Sonogram::resizeEvent(QResizeEvent *e) {
|
void SonogramAnalyzer::resizeEvent(QResizeEvent *e) {
|
||||||
|
|
||||||
Q_UNUSED(e)
|
Q_UNUSED(e)
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ void Sonogram::resizeEvent(QResizeEvent *e) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sonogram::analyze(QPainter &p, const Scope &s, bool new_frame) {
|
void SonogramAnalyzer::analyze(QPainter &p, const Scope &s, const bool new_frame) {
|
||||||
|
|
||||||
if (!new_frame || engine_->state() == EngineBase::State::Paused) {
|
if (!new_frame || engine_->state() == EngineBase::State::Paused) {
|
||||||
p.drawPixmap(0, 0, canvas_);
|
p.drawPixmap(0, 0, canvas_);
|
||||||
@@ -81,7 +81,7 @@ void Sonogram::analyze(QPainter &p, const Scope &s, bool new_frame) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sonogram::transform(Scope &scope) {
|
void SonogramAnalyzer::transform(Scope &scope) {
|
||||||
|
|
||||||
fht_->power2(scope.data());
|
fht_->power2(scope.data());
|
||||||
fht_->scale(scope.data(), 1.0 / 256);
|
fht_->scale(scope.data(), 1.0 / 256);
|
||||||
@@ -89,6 +89,6 @@ void Sonogram::transform(Scope &scope) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sonogram::demo(QPainter &p) {
|
void SonogramAnalyzer::demo(QPainter &p) {
|
||||||
analyze(p, Scope(fht_->size(), 0), new_frame_);
|
analyze(p, Scope(fht_->size(), 0), new_frame_);
|
||||||
}
|
}
|
||||||
@@ -21,24 +21,25 @@
|
|||||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SONOGRAM_H
|
#ifndef SONOGRAMANALYZER_H
|
||||||
#define SONOGRAM_H
|
#define SONOGRAMANALYZER_H
|
||||||
|
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
|
||||||
#include "analyzerbase.h"
|
#include "analyzerbase.h"
|
||||||
|
|
||||||
class Sonogram : public AnalyzerBase {
|
class SonogramAnalyzer : public AnalyzerBase {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE explicit Sonogram(QWidget *parent);
|
Q_INVOKABLE explicit SonogramAnalyzer(QWidget *parent);
|
||||||
|
|
||||||
static const char *kName;
|
static const char *kName;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
void analyze(QPainter &p, const Scope &s, bool new_frame) override;
|
void analyze(QPainter &p, const Scope &s, const bool new_frame) override;
|
||||||
void transform(Scope &scope) override;
|
void transform(Scope &scope) override;
|
||||||
void demo(QPainter &p) override;
|
void demo(QPainter &p) override;
|
||||||
|
|
||||||
@@ -46,4 +47,4 @@ class Sonogram : public AnalyzerBase {
|
|||||||
QPixmap canvas_;
|
QPixmap canvas_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SONOGRAM_H
|
#endif // SONOGRAMANALYZER_H
|
||||||
100
src/analyzer/turbineanalyzer.cpp
Normal file
100
src/analyzer/turbineanalyzer.cpp
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
Strawberry Music Player
|
||||||
|
This file was part of Clementine.
|
||||||
|
Copyright 2003, Stanislav Karchebny <berkus@users.sf.net>
|
||||||
|
Copyright 2003, Max Howell <max.howell@methylblue.com>
|
||||||
|
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||||
|
Copyright 2014-2015, Mark Furneaux <mark@furneaux.ca>
|
||||||
|
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||||
|
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
||||||
|
|
||||||
|
Clementine is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Clementine is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
#include "turbineanalyzer.h"
|
||||||
|
#include "engine/enginebase.h"
|
||||||
|
|
||||||
|
const char *TurbineAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Turbine");
|
||||||
|
|
||||||
|
TurbineAnalyzer::TurbineAnalyzer(QWidget *parent) : BoomAnalyzer(parent) {}
|
||||||
|
|
||||||
|
void TurbineAnalyzer::analyze(QPainter &p, const Scope &scope, const bool new_frame) {
|
||||||
|
|
||||||
|
if (!new_frame || engine_->state() == EngineBase::State::Paused) {
|
||||||
|
p.drawPixmap(0, 0, canvas_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint hd2 = height() / 2;
|
||||||
|
const uint kMaxHeight = hd2 - 1;
|
||||||
|
|
||||||
|
QPainter canvas_painter(&canvas_);
|
||||||
|
canvas_.fill(palette().color(QPalette::Window));
|
||||||
|
|
||||||
|
AnalyzerBase::interpolate(scope, scope_);
|
||||||
|
|
||||||
|
for (uint i = 0, x = 0, y = 0; i < static_cast<uint>(bands_); ++i, x += kColumnWidth + 1) {
|
||||||
|
float h = static_cast<float>(std::min(log10(scope_[i] * 256.0) * F_ * 0.5, kMaxHeight * 1.0));
|
||||||
|
|
||||||
|
if (h > bar_height_[i]) {
|
||||||
|
bar_height_[i] = h;
|
||||||
|
if (h > peak_height_[i]) {
|
||||||
|
peak_height_[i] = h;
|
||||||
|
peak_speed_[i] = 0.01;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
goto peak_handling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (bar_height_[i] > 0.0) {
|
||||||
|
bar_height_[i] -= K_barHeight_; // 1.4
|
||||||
|
if (bar_height_[i] < 0.0) bar_height_[i] = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
peak_handling:
|
||||||
|
if (peak_height_[i] > 0.0) {
|
||||||
|
peak_height_[i] -= peak_speed_[i];
|
||||||
|
peak_speed_[i] *= F_peakSpeed_; // 1.12
|
||||||
|
peak_height_[i] = std::max(0.0, std::max(bar_height_[i], peak_height_[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
y = hd2 - static_cast<uint>(bar_height_[i]);
|
||||||
|
canvas_painter.drawPixmap(static_cast<int>(x + 1), static_cast<int>(y), barPixmap_, 0, static_cast<int>(y), -1, -1);
|
||||||
|
canvas_painter.drawPixmap(static_cast<int>(x + 1), static_cast<int>(hd2), barPixmap_, 0, static_cast<int>(bar_height_[i]), -1, -1);
|
||||||
|
|
||||||
|
canvas_painter.setPen(fg_);
|
||||||
|
if (bar_height_[i] > 0) {
|
||||||
|
canvas_painter.drawRect(static_cast<int>(x), static_cast<int>(y), kColumnWidth - 1, static_cast<int>(bar_height_[i]) * 2 - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint x2 = x + kColumnWidth - 1;
|
||||||
|
canvas_painter.setPen(palette().color(QPalette::Midlight));
|
||||||
|
y = hd2 - static_cast<uint>(peak_height_[i]);
|
||||||
|
canvas_painter.drawLine(static_cast<int>(x), static_cast<int>(y), static_cast<int>(x2), static_cast<int>(y));
|
||||||
|
y = hd2 + static_cast<uint>(peak_height_[i]);
|
||||||
|
canvas_painter.drawLine(static_cast<int>(x), static_cast<int>(y), static_cast<int>(x2), static_cast<int>(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
p.drawPixmap(0, 0, canvas_);
|
||||||
|
|
||||||
|
}
|
||||||
41
src/analyzer/turbineanalyzer.h
Normal file
41
src/analyzer/turbineanalyzer.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
Strawberry Music Player
|
||||||
|
This file was part of Clementine.
|
||||||
|
Copyright 2003, Stanislav Karchebny <berkus@users.sf.net>
|
||||||
|
Copyright 2009-2010, David Sansome <davidsansome@gmail.com>
|
||||||
|
Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
|
||||||
|
Copyright 2014, John Maguire <john.maguire@gmail.com>
|
||||||
|
|
||||||
|
Clementine is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Clementine is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TURBINEANALYZER_H
|
||||||
|
#define TURBINEANALYZER_H
|
||||||
|
|
||||||
|
#include "boomanalyzer.h"
|
||||||
|
|
||||||
|
class QPainter;
|
||||||
|
|
||||||
|
class TurbineAnalyzer : public BoomAnalyzer {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE explicit TurbineAnalyzer(QWidget *parent);
|
||||||
|
|
||||||
|
void analyze(QPainter &p, const Scope &scope, const bool new_frame);
|
||||||
|
|
||||||
|
static const char *kName;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TURBINEANALYZER_H
|
||||||
@@ -19,14 +19,14 @@
|
|||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QResizeEvent>
|
#include <QResizeEvent>
|
||||||
#include "engine/enginebase.h"
|
#include "engine/enginebase.h"
|
||||||
#include "waverubber.h"
|
#include "waverubberanalyzer.h"
|
||||||
|
|
||||||
const char *WaveRubber::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "WaveRubber");
|
const char *WaveRubberAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "WaveRubber");
|
||||||
|
|
||||||
WaveRubber::WaveRubber(QWidget *parent)
|
WaveRubberAnalyzer::WaveRubberAnalyzer(QWidget *parent)
|
||||||
: AnalyzerBase(parent, 9) {}
|
: AnalyzerBase(parent, 9) {}
|
||||||
|
|
||||||
void WaveRubber::resizeEvent(QResizeEvent *e) {
|
void WaveRubberAnalyzer::resizeEvent(QResizeEvent *e) {
|
||||||
|
|
||||||
Q_UNUSED(e)
|
Q_UNUSED(e)
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ void WaveRubber::resizeEvent(QResizeEvent *e) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaveRubber::analyze(QPainter &p, const Scope &s, const bool new_frame) {
|
void WaveRubberAnalyzer::analyze(QPainter &p, const Scope &s, const bool new_frame) {
|
||||||
|
|
||||||
if (!new_frame || engine_->state() == EngineBase::State::Paused) {
|
if (!new_frame || engine_->state() == EngineBase::State::Paused) {
|
||||||
p.drawPixmap(0, 0, canvas_);
|
p.drawPixmap(0, 0, canvas_);
|
||||||
@@ -82,11 +82,11 @@ void WaveRubber::analyze(QPainter &p, const Scope &s, const bool new_frame) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaveRubber::transform(Scope &s) {
|
void WaveRubberAnalyzer::transform(Scope &s) {
|
||||||
// No need transformation for waveform analyzer
|
// No need transformation for waveform analyzer
|
||||||
Q_UNUSED(s);
|
Q_UNUSED(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaveRubber::demo(QPainter &p) {
|
void WaveRubberAnalyzer::demo(QPainter &p) {
|
||||||
analyze(p, Scope(fht_->size(), 0), new_frame_);
|
analyze(p, Scope(fht_->size(), 0), new_frame_);
|
||||||
}
|
}
|
||||||
@@ -22,11 +22,11 @@
|
|||||||
|
|
||||||
#include "analyzerbase.h"
|
#include "analyzerbase.h"
|
||||||
|
|
||||||
class WaveRubber : public AnalyzerBase {
|
class WaveRubberAnalyzer : public AnalyzerBase {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE explicit WaveRubber(QWidget *parent);
|
Q_INVOKABLE explicit WaveRubberAnalyzer(QWidget *parent);
|
||||||
|
|
||||||
static const char *kName;
|
static const char *kName;
|
||||||
|
|
||||||
@@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
@@ -62,19 +64,17 @@ bool CollectionFilter::filterAcceptsRow(const int source_row, const QModelIndex
|
|||||||
|
|
||||||
if (filter_text.isEmpty()) return true;
|
if (filter_text.isEmpty()) return true;
|
||||||
|
|
||||||
filter_text = filter_text.replace(QRegularExpression(QStringLiteral("\\s*:\\s*")), QStringLiteral(":"))
|
for (const QString &foperator : Operators) {
|
||||||
.replace(QRegularExpression(QStringLiteral("\\s*=\\s*")), QStringLiteral("="))
|
if (filter_text.contains(foperator)) {
|
||||||
.replace(QRegularExpression(QStringLiteral("\\s*==\\s*")), QStringLiteral("=="))
|
QRegularExpression regex(QStringLiteral("\\s*") + foperator + QStringLiteral("\\s*"));
|
||||||
.replace(QRegularExpression(QStringLiteral("\\s*<>\\s*")), QStringLiteral("<>"))
|
filter_text = filter_text.replace(regex, foperator);
|
||||||
.replace(QRegularExpression(QStringLiteral("\\s*<\\s*")), QStringLiteral("<"))
|
}
|
||||||
.replace(QRegularExpression(QStringLiteral("\\s*>\\s*")), QStringLiteral(">"))
|
}
|
||||||
.replace(QRegularExpression(QStringLiteral("\\s*<=\\s*")), QStringLiteral("<="))
|
|
||||||
.replace(QRegularExpression(QStringLiteral("\\s*>=\\s*")), QStringLiteral(">="));
|
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||||
const QStringList tokens = filter_text.split(QRegularExpression(QStringLiteral("\\s+")), Qt::SkipEmptyParts);
|
const QStringList tokens = filter_text.split(QLatin1Char(' '), Qt::SkipEmptyParts);
|
||||||
#else
|
#else
|
||||||
const QStringList tokens = filter_text.split(QRegularExpression(QStringLiteral("\\s+")), QString::SkipEmptyParts);
|
const QStringList tokens = filter_text.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
filter_text.clear();
|
filter_text.clear();
|
||||||
@@ -326,7 +326,7 @@ bool CollectionFilter::FieldFloatValueMatchesData(const float value, const QStri
|
|||||||
|
|
||||||
bool CollectionFilter::ContainsOperators(const QString &token) {
|
bool CollectionFilter::ContainsOperators(const QString &token) {
|
||||||
|
|
||||||
for (const QString &foperator : Operators) {
|
for (const QString &foperator : std::as_const(Operators)) {
|
||||||
if (token.contains(foperator, Qt::CaseInsensitive)) return true;
|
if (token.contains(foperator, Qt::CaseInsensitive)) return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1071,17 +1071,25 @@ MainWindow::MainWindow(Application *app, SharedPtr<SystemTrayIcon> tray_icon, OS
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
{
|
{
|
||||||
|
bool asked_permission = true;
|
||||||
Settings s;
|
Settings s;
|
||||||
s.beginGroup(kSettingsGroup);
|
#ifdef HAVE_QTSPARKLE
|
||||||
constexpr char do_not_show_sponsor_message_key[] = "do_not_show_sponsor_message";
|
s.beginGroup("QtSparkle");
|
||||||
const bool do_not_show_sponsor_message = s.value(do_not_show_sponsor_message_key, false).toBool();
|
asked_permission = s.value("asked_permission", false).toBool();
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
if (!do_not_show_sponsor_message) {
|
#endif
|
||||||
MessageDialog *sponsor_message = new MessageDialog(this);
|
if (asked_permission) {
|
||||||
sponsor_message->set_settings_group(QLatin1String(kSettingsGroup));
|
s.beginGroup(kSettingsGroup);
|
||||||
sponsor_message->set_do_not_show_message_again(QLatin1String(do_not_show_sponsor_message_key));
|
constexpr char do_not_show_sponsor_message_key[] = "do_not_show_sponsor_message";
|
||||||
sponsor_message->setAttribute(Qt::WA_DeleteOnClose);
|
const bool do_not_show_sponsor_message = s.value(do_not_show_sponsor_message_key, false).toBool();
|
||||||
sponsor_message->ShowMessage(tr("Sponsoring Strawberry"), tr("Strawberry is free and open source software. If you like Strawberry, please consider sponsoring the project. For more information about sponsorship see our website %1").arg(QStringLiteral("<a href= \"https://www.strawberrymusicplayer.org/\">www.strawberrymusicplayer.org</a>")), IconLoader::Load(QStringLiteral("dialog-information")));
|
s.endGroup();
|
||||||
|
if (!do_not_show_sponsor_message) {
|
||||||
|
MessageDialog *sponsor_message = new MessageDialog(this);
|
||||||
|
sponsor_message->set_settings_group(QLatin1String(kSettingsGroup));
|
||||||
|
sponsor_message->set_do_not_show_message_again(QLatin1String(do_not_show_sponsor_message_key));
|
||||||
|
sponsor_message->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
sponsor_message->ShowMessage(tr("Sponsoring Strawberry"), tr("Strawberry is free and open source software. If you like Strawberry, please consider sponsoring the project. For more information about sponsorship see our website %1").arg(QStringLiteral("<a href= \"https://www.strawberrymusicplayer.org/\">www.strawberrymusicplayer.org</a>")), IconLoader::Load(QStringLiteral("dialog-information")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -710,6 +710,7 @@ void Playlist::set_current_row(const int i, const AutoScroll autoscroll, const b
|
|||||||
|
|
||||||
if (current_item_index_.isValid() && !is_stopping) {
|
if (current_item_index_.isValid() && !is_stopping) {
|
||||||
InformOfCurrentSongChange(false);
|
InformOfCurrentSongChange(false);
|
||||||
|
emit dataChanged(index(current_item_index_.row(), 0), index(current_item_index_.row(), ColumnCount - 1));
|
||||||
emit MaybeAutoscroll(autoscroll);
|
emit MaybeAutoscroll(autoscroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2012, David Sansome <me@davidsansome.com>
|
* Copyright 2012, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
PlaylistFilter::PlaylistFilter(QObject *parent)
|
PlaylistFilter::PlaylistFilter(QObject *parent)
|
||||||
: QSortFilterProxyModel(parent),
|
: QSortFilterProxyModel(parent),
|
||||||
filter_tree_(new NopFilter),
|
filter_tree_(new PlaylistNopFilter),
|
||||||
query_hash_(0) {
|
query_hash_(0) {
|
||||||
|
|
||||||
setDynamicSortFilter(true);
|
setDynamicSortFilter(true);
|
||||||
@@ -89,7 +89,7 @@ bool PlaylistFilter::filterAcceptsRow(const int row, const QModelIndex &parent)
|
|||||||
#endif
|
#endif
|
||||||
if (hash != query_hash_) {
|
if (hash != query_hash_) {
|
||||||
// Parse the query
|
// Parse the query
|
||||||
FilterParser p(filter_text_, column_names_, numerical_columns_);
|
PlaylistFilterParser p(filter_text_, column_names_, numerical_columns_);
|
||||||
filter_tree_.reset(p.parse());
|
filter_tree_.reset(p.parse());
|
||||||
|
|
||||||
query_hash_ = hash;
|
query_hash_ = hash;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2012, David Sansome <me@davidsansome.com>
|
* Copyright 2012, David Sansome <me@davidsansome.com>
|
||||||
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
class FilterTree;
|
class PlaylistFilterTree;
|
||||||
|
|
||||||
class PlaylistFilter : public QSortFilterProxyModel {
|
class PlaylistFilter : public QSortFilterProxyModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -55,7 +55,7 @@ class PlaylistFilter : public QSortFilterProxyModel {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// Mutable because they're modified from filterAcceptsRow() const
|
// Mutable because they're modified from filterAcceptsRow() const
|
||||||
mutable QScopedPointer<FilterTree> filter_tree_;
|
mutable QScopedPointer<PlaylistFilterTree> filter_tree_;
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
mutable size_t query_hash_;
|
mutable size_t query_hash_;
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2012, David Sansome <me@davidsansome.com>
|
* Copyright 2012, David Sansome <me@davidsansome.com>
|
||||||
|
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -36,31 +37,31 @@
|
|||||||
#include "playlistfilterparser.h"
|
#include "playlistfilterparser.h"
|
||||||
#include "utilities/searchparserutils.h"
|
#include "utilities/searchparserutils.h"
|
||||||
|
|
||||||
class SearchTermComparator {
|
class PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
SearchTermComparator() = default;
|
PlaylistSearchTermComparator() = default;
|
||||||
virtual ~SearchTermComparator() = default;
|
virtual ~PlaylistSearchTermComparator() = default;
|
||||||
virtual bool Matches(const QString &element) const = 0;
|
virtual bool Matches(const QString &element) const = 0;
|
||||||
private:
|
private:
|
||||||
Q_DISABLE_COPY(SearchTermComparator)
|
Q_DISABLE_COPY(PlaylistSearchTermComparator)
|
||||||
};
|
};
|
||||||
|
|
||||||
// "compares" by checking if the field contains the search term
|
// "compares" by checking if the field contains the search term
|
||||||
class DefaultComparator : public SearchTermComparator {
|
class PlaylistDefaultComparator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit DefaultComparator(const QString &value) : search_term_(value) {}
|
explicit PlaylistDefaultComparator(const QString &value) : search_term_(value) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return element.contains(search_term_);
|
return element.contains(search_term_);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
QString search_term_;
|
QString search_term_;
|
||||||
|
|
||||||
Q_DISABLE_COPY(DefaultComparator)
|
Q_DISABLE_COPY(PlaylistDefaultComparator)
|
||||||
};
|
};
|
||||||
|
|
||||||
class EqComparator : public SearchTermComparator {
|
class PlaylistEqComparator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit EqComparator(const QString &value) : search_term_(value) {}
|
explicit PlaylistEqComparator(const QString &value) : search_term_(value) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return search_term_ == element;
|
return search_term_ == element;
|
||||||
}
|
}
|
||||||
@@ -68,9 +69,9 @@ class EqComparator : public SearchTermComparator {
|
|||||||
QString search_term_;
|
QString search_term_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NeComparator : public SearchTermComparator {
|
class PlaylistNeComparator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit NeComparator(const QString &value) : search_term_(value) {}
|
explicit PlaylistNeComparator(const QString &value) : search_term_(value) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return search_term_ != element;
|
return search_term_ != element;
|
||||||
}
|
}
|
||||||
@@ -78,9 +79,9 @@ class NeComparator : public SearchTermComparator {
|
|||||||
QString search_term_;
|
QString search_term_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LexicalGtComparator : public SearchTermComparator {
|
class PlaylistLexicalGtComparator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit LexicalGtComparator(const QString &value) : search_term_(value) {}
|
explicit PlaylistLexicalGtComparator(const QString &value) : search_term_(value) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return element > search_term_;
|
return element > search_term_;
|
||||||
}
|
}
|
||||||
@@ -88,9 +89,9 @@ class LexicalGtComparator : public SearchTermComparator {
|
|||||||
QString search_term_;
|
QString search_term_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LexicalGeComparator : public SearchTermComparator {
|
class PlaylistLexicalGeComparator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit LexicalGeComparator(const QString &value) : search_term_(value) {}
|
explicit PlaylistLexicalGeComparator(const QString &value) : search_term_(value) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return element >= search_term_;
|
return element >= search_term_;
|
||||||
}
|
}
|
||||||
@@ -98,9 +99,9 @@ class LexicalGeComparator : public SearchTermComparator {
|
|||||||
QString search_term_;
|
QString search_term_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LexicalLtComparator : public SearchTermComparator {
|
class PlaylistLexicalLtComparator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit LexicalLtComparator(const QString &value) : search_term_(value) {}
|
explicit PlaylistLexicalLtComparator(const QString &value) : search_term_(value) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return element < search_term_;
|
return element < search_term_;
|
||||||
}
|
}
|
||||||
@@ -108,9 +109,9 @@ class LexicalLtComparator : public SearchTermComparator {
|
|||||||
QString search_term_;
|
QString search_term_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LexicalLeComparator : public SearchTermComparator {
|
class PlaylistLexicalLeComparator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit LexicalLeComparator(const QString &value) : search_term_(value) {}
|
explicit PlaylistLexicalLeComparator(const QString &value) : search_term_(value) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return element <= search_term_;
|
return element <= search_term_;
|
||||||
}
|
}
|
||||||
@@ -119,9 +120,9 @@ class LexicalLeComparator : public SearchTermComparator {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Float Comparators are for the rating
|
// Float Comparators are for the rating
|
||||||
class FloatEqComparator : public SearchTermComparator {
|
class PlaylistFloatEqComparator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit FloatEqComparator(const float value) : search_term_(value) {}
|
explicit PlaylistFloatEqComparator(const float value) : search_term_(value) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return search_term_ == element.toFloat();
|
return search_term_ == element.toFloat();
|
||||||
}
|
}
|
||||||
@@ -129,9 +130,9 @@ class FloatEqComparator : public SearchTermComparator {
|
|||||||
float search_term_;
|
float search_term_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FloatNeComparator : public SearchTermComparator {
|
class PlaylistFloatNeComparator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit FloatNeComparator(const float value) : search_term_(value) {}
|
explicit PlaylistFloatNeComparator(const float value) : search_term_(value) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return search_term_ != element.toFloat();
|
return search_term_ != element.toFloat();
|
||||||
}
|
}
|
||||||
@@ -139,9 +140,9 @@ class FloatNeComparator : public SearchTermComparator {
|
|||||||
float search_term_;
|
float search_term_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FloatGtComparator : public SearchTermComparator {
|
class PlaylistFloatGtComparator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit FloatGtComparator(const float value) : search_term_(value) {}
|
explicit PlaylistFloatGtComparator(const float value) : search_term_(value) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return element.toFloat() > search_term_;
|
return element.toFloat() > search_term_;
|
||||||
}
|
}
|
||||||
@@ -149,9 +150,9 @@ class FloatGtComparator : public SearchTermComparator {
|
|||||||
float search_term_;
|
float search_term_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FloatGeComparator : public SearchTermComparator {
|
class PlaylistFloatGeComparator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit FloatGeComparator(const float value) : search_term_(value) {}
|
explicit PlaylistFloatGeComparator(const float value) : search_term_(value) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return element.toFloat() >= search_term_;
|
return element.toFloat() >= search_term_;
|
||||||
}
|
}
|
||||||
@@ -159,9 +160,9 @@ class FloatGeComparator : public SearchTermComparator {
|
|||||||
float search_term_;
|
float search_term_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FloatLtComparator : public SearchTermComparator {
|
class PlaylistFloatLtComparator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit FloatLtComparator(const float value) : search_term_(value) {}
|
explicit PlaylistFloatLtComparator(const float value) : search_term_(value) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return element.toFloat() < search_term_;
|
return element.toFloat() < search_term_;
|
||||||
}
|
}
|
||||||
@@ -169,9 +170,9 @@ class FloatLtComparator : public SearchTermComparator {
|
|||||||
float search_term_;
|
float search_term_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FloatLeComparator : public SearchTermComparator {
|
class PlaylistFloatLeComparator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit FloatLeComparator(const float value) : search_term_(value) {}
|
explicit PlaylistFloatLeComparator(const float value) : search_term_(value) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return element.toFloat() <= search_term_;
|
return element.toFloat() <= search_term_;
|
||||||
}
|
}
|
||||||
@@ -179,9 +180,9 @@ class FloatLeComparator : public SearchTermComparator {
|
|||||||
float search_term_;
|
float search_term_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GtComparator : public SearchTermComparator {
|
class PlaylistGtComparator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit GtComparator(const int value) : search_term_(value) {}
|
explicit PlaylistGtComparator(const int value) : search_term_(value) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return element.toInt() > search_term_;
|
return element.toInt() > search_term_;
|
||||||
}
|
}
|
||||||
@@ -189,9 +190,9 @@ class GtComparator : public SearchTermComparator {
|
|||||||
int search_term_;
|
int search_term_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GeComparator : public SearchTermComparator {
|
class PlaylistGeComparator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit GeComparator(const int value) : search_term_(value) {}
|
explicit PlaylistGeComparator(const int value) : search_term_(value) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return element.toInt() >= search_term_;
|
return element.toInt() >= search_term_;
|
||||||
}
|
}
|
||||||
@@ -199,9 +200,9 @@ class GeComparator : public SearchTermComparator {
|
|||||||
int search_term_;
|
int search_term_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LtComparator : public SearchTermComparator {
|
class PlaylistLtComparator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit LtComparator(const int value) : search_term_(value) {}
|
explicit PlaylistLtComparator(const int value) : search_term_(value) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return element.toInt() < search_term_;
|
return element.toInt() < search_term_;
|
||||||
}
|
}
|
||||||
@@ -209,9 +210,9 @@ class LtComparator : public SearchTermComparator {
|
|||||||
int search_term_;
|
int search_term_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LeComparator : public SearchTermComparator {
|
class PlaylistLeComparator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit LeComparator(const int value) : search_term_(value) {}
|
explicit PlaylistLeComparator(const int value) : search_term_(value) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return element.toInt() <= search_term_;
|
return element.toInt() <= search_term_;
|
||||||
}
|
}
|
||||||
@@ -222,9 +223,9 @@ class LeComparator : public SearchTermComparator {
|
|||||||
// The length field of the playlist (entries) contains a song's running time in nanoseconds.
|
// The length field of the playlist (entries) contains a song's running time in nanoseconds.
|
||||||
// However, We don't really care about nanoseconds, just seconds.
|
// However, We don't really care about nanoseconds, just seconds.
|
||||||
// Thus, with this decorator we drop the last 9 digits, if that many are present.
|
// Thus, with this decorator we drop the last 9 digits, if that many are present.
|
||||||
class DropTailComparatorDecorator : public SearchTermComparator {
|
class PlaylistDropTailComparatorDecorator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit DropTailComparatorDecorator(SearchTermComparator *cmp) : cmp_(cmp) {}
|
explicit PlaylistDropTailComparatorDecorator(PlaylistSearchTermComparator *cmp) : cmp_(cmp) {}
|
||||||
|
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
if (element.length() > 9) {
|
if (element.length() > 9) {
|
||||||
@@ -235,97 +236,97 @@ class DropTailComparatorDecorator : public SearchTermComparator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
QScopedPointer<SearchTermComparator> cmp_;
|
QScopedPointer<PlaylistSearchTermComparator> cmp_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RatingComparatorDecorator : public SearchTermComparator {
|
class PlaylistRatingComparatorDecorator : public PlaylistSearchTermComparator {
|
||||||
public:
|
public:
|
||||||
explicit RatingComparatorDecorator(SearchTermComparator *cmp) : cmp_(cmp) {}
|
explicit PlaylistRatingComparatorDecorator(PlaylistSearchTermComparator *cmp) : cmp_(cmp) {}
|
||||||
bool Matches(const QString &element) const override {
|
bool Matches(const QString &element) const override {
|
||||||
return cmp_->Matches(QString::number(lround(element.toDouble() * 10.0)));
|
return cmp_->Matches(QString::number(lround(element.toDouble() * 10.0)));
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
QScopedPointer<SearchTermComparator> cmp_;
|
QScopedPointer<PlaylistSearchTermComparator> cmp_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// filter that applies a SearchTermComparator to all fields of a playlist entry
|
// Filter that applies a SearchTermComparator to all fields of a playlist entry
|
||||||
class FilterTerm : public FilterTree {
|
class PlaylistFilterTerm : public PlaylistFilterTree {
|
||||||
public:
|
public:
|
||||||
explicit FilterTerm(SearchTermComparator *comparator, const QList<int> &columns) : cmp_(comparator), columns_(columns) {}
|
explicit PlaylistFilterTerm(PlaylistSearchTermComparator *comparator, const QList<int> &columns) : cmp_(comparator), columns_(columns) {}
|
||||||
|
|
||||||
bool accept(int row, const QModelIndex &parent, const QAbstractItemModel *const model) const override {
|
bool accept(const int row, const QModelIndex &parent, const QAbstractItemModel *const model) const override {
|
||||||
for (int i : columns_) {
|
for (const int i : columns_) {
|
||||||
QModelIndex idx(model->index(row, i, parent));
|
const QModelIndex idx = model->index(row, i, parent);
|
||||||
if (cmp_->Matches(idx.data().toString().toLower())) return true;
|
if (cmp_->Matches(idx.data().toString().toLower())) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
FilterType type() override { return FilterType::Term; }
|
FilterType type() override { return FilterType::Term; }
|
||||||
private:
|
private:
|
||||||
QScopedPointer<SearchTermComparator> cmp_;
|
QScopedPointer<PlaylistSearchTermComparator> cmp_;
|
||||||
QList<int> columns_;
|
QList<int> columns_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// filter that applies a SearchTermComparator to one specific field of a playlist entry
|
// Filter that applies a SearchTermComparator to one specific field of a playlist entry
|
||||||
class FilterColumnTerm : public FilterTree {
|
class PlaylistFilterColumnTerm : public PlaylistFilterTree {
|
||||||
public:
|
public:
|
||||||
FilterColumnTerm(const int column, SearchTermComparator *comparator) : col(column), cmp_(comparator) {}
|
PlaylistFilterColumnTerm(const int column, PlaylistSearchTermComparator *comparator) : col(column), cmp_(comparator) {}
|
||||||
|
|
||||||
bool accept(int row, const QModelIndex &parent, const QAbstractItemModel *const model) const override {
|
bool accept(const int row, const QModelIndex &parent, const QAbstractItemModel *const model) const override {
|
||||||
QModelIndex idx(model->index(row, col, parent));
|
const QModelIndex idx = model->index(row, col, parent);
|
||||||
return cmp_->Matches(idx.data().toString().toLower());
|
return cmp_->Matches(idx.data().toString().toLower());
|
||||||
}
|
}
|
||||||
FilterType type() override { return FilterType::Column; }
|
FilterType type() override { return FilterType::Column; }
|
||||||
private:
|
private:
|
||||||
int col;
|
int col;
|
||||||
QScopedPointer<SearchTermComparator> cmp_;
|
QScopedPointer<PlaylistSearchTermComparator> cmp_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NotFilter : public FilterTree {
|
class PlaylistNotFilter : public PlaylistFilterTree {
|
||||||
public:
|
public:
|
||||||
explicit NotFilter(const FilterTree *inv) : child_(inv) {}
|
explicit PlaylistNotFilter(const PlaylistFilterTree *inv) : child_(inv) {}
|
||||||
|
|
||||||
bool accept(int row, const QModelIndex &parent, const QAbstractItemModel *const model) const override {
|
bool accept(const int row, const QModelIndex &parent, const QAbstractItemModel *const model) const override {
|
||||||
return !child_->accept(row, parent, model);
|
return !child_->accept(row, parent, model);
|
||||||
}
|
}
|
||||||
FilterType type() override { return FilterType::Not; }
|
FilterType type() override { return FilterType::Not; }
|
||||||
private:
|
private:
|
||||||
QScopedPointer<const FilterTree> child_;
|
QScopedPointer<const PlaylistFilterTree> child_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class OrFilter : public FilterTree {
|
class PlaylistOrFilter : public PlaylistFilterTree {
|
||||||
public:
|
public:
|
||||||
~OrFilter() override { qDeleteAll(children_); }
|
~PlaylistOrFilter() override { qDeleteAll(children_); }
|
||||||
virtual void add(FilterTree *child) { children_.append(child); }
|
virtual void add(PlaylistFilterTree *child) { children_.append(child); }
|
||||||
bool accept(int row, const QModelIndex &parent, const QAbstractItemModel *const model) const override {
|
bool accept(const int row, const QModelIndex &parent, const QAbstractItemModel *const model) const override {
|
||||||
return std::any_of(children_.begin(), children_.end(), [row, parent, model](FilterTree *child) { return child->accept(row, parent, model); });
|
return std::any_of(children_.begin(), children_.end(), [row, parent, model](PlaylistFilterTree *child) { return child->accept(row, parent, model); });
|
||||||
}
|
}
|
||||||
FilterType type() override { return FilterType::Or; }
|
FilterType type() override { return FilterType::Or; }
|
||||||
private:
|
private:
|
||||||
QList<FilterTree*> children_;
|
QList<PlaylistFilterTree*> children_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AndFilter : public FilterTree {
|
class PlaylistAndFilter : public PlaylistFilterTree {
|
||||||
public:
|
public:
|
||||||
~AndFilter() override { qDeleteAll(children_); }
|
~PlaylistAndFilter() override { qDeleteAll(children_); }
|
||||||
virtual void add(FilterTree *child) { children_.append(child); }
|
virtual void add(PlaylistFilterTree *child) { children_.append(child); }
|
||||||
bool accept(int row, const QModelIndex &parent, const QAbstractItemModel *const model) const override {
|
bool accept(const int row, const QModelIndex &parent, const QAbstractItemModel *const model) const override {
|
||||||
return !std::any_of(children_.begin(), children_.end(), [row, parent, model](FilterTree *child) { return !child->accept(row, parent, model); });
|
return !std::any_of(children_.begin(), children_.end(), [row, parent, model](PlaylistFilterTree *child) { return !child->accept(row, parent, model); });
|
||||||
}
|
}
|
||||||
FilterType type() override { return FilterType::And; }
|
FilterType type() override { return FilterType::And; }
|
||||||
private:
|
private:
|
||||||
QList<FilterTree*> children_;
|
QList<PlaylistFilterTree*> children_;
|
||||||
};
|
};
|
||||||
|
|
||||||
FilterParser::FilterParser(const QString &filter, const QMap<QString, int> &columns, const QSet<int> &numerical_cols) : iter_{}, end_{}, filterstring_(filter), columns_(columns), numerical_columns_(numerical_cols) {}
|
PlaylistFilterParser::PlaylistFilterParser(const QString &filter, const QMap<QString, int> &columns, const QSet<int> &numerical_cols) : iter_{}, end_{}, filterstring_(filter), columns_(columns), numerical_columns_(numerical_cols) {}
|
||||||
|
|
||||||
FilterTree *FilterParser::parse() {
|
PlaylistFilterTree *PlaylistFilterParser::parse() {
|
||||||
iter_ = filterstring_.constBegin();
|
iter_ = filterstring_.constBegin();
|
||||||
end_ = filterstring_.constEnd();
|
end_ = filterstring_.constEnd();
|
||||||
return parseOrGroup();
|
return parseOrGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilterParser::advance() {
|
void PlaylistFilterParser::advance() {
|
||||||
|
|
||||||
while (iter_ != end_ && iter_->isSpace()) {
|
while (iter_ != end_ && iter_->isSpace()) {
|
||||||
++iter_;
|
++iter_;
|
||||||
@@ -333,28 +334,29 @@ void FilterParser::advance() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FilterTree *FilterParser::parseOrGroup() {
|
PlaylistFilterTree *PlaylistFilterParser::parseOrGroup() {
|
||||||
|
|
||||||
advance();
|
advance();
|
||||||
if (iter_ == end_) return new NopFilter;
|
if (iter_ == end_) return new PlaylistNopFilter;
|
||||||
|
|
||||||
OrFilter *group = new OrFilter;
|
PlaylistOrFilter *group = new PlaylistOrFilter;
|
||||||
group->add(parseAndGroup());
|
group->add(parseAndGroup());
|
||||||
advance();
|
advance();
|
||||||
while (checkOr()) {
|
while (checkOr()) {
|
||||||
group->add(parseAndGroup());
|
group->add(parseAndGroup());
|
||||||
advance();
|
advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
return group;
|
return group;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FilterTree *FilterParser::parseAndGroup() {
|
PlaylistFilterTree *PlaylistFilterParser::parseAndGroup() {
|
||||||
|
|
||||||
advance();
|
advance();
|
||||||
if (iter_ == end_) return new NopFilter;
|
if (iter_ == end_) return new PlaylistNopFilter;
|
||||||
|
|
||||||
AndFilter *group = new AndFilter();
|
PlaylistAndFilter *group = new PlaylistAndFilter();
|
||||||
do {
|
do {
|
||||||
group->add(parseSearchExpression());
|
group->add(parseSearchExpression());
|
||||||
advance();
|
advance();
|
||||||
@@ -369,7 +371,7 @@ FilterTree *FilterParser::parseAndGroup() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FilterParser::checkAnd() {
|
bool PlaylistFilterParser::checkAnd() {
|
||||||
|
|
||||||
if (iter_ != end_) {
|
if (iter_ != end_) {
|
||||||
if (*iter_ == QLatin1Char('A')) {
|
if (*iter_ == QLatin1Char('A')) {
|
||||||
@@ -390,11 +392,12 @@ bool FilterParser::checkAnd() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FilterParser::checkOr(const bool step_over) {
|
bool PlaylistFilterParser::checkOr(const bool step_over) {
|
||||||
|
|
||||||
if (!buf_.isEmpty()) {
|
if (!buf_.isEmpty()) {
|
||||||
if (buf_ == QLatin1String("OR")) {
|
if (buf_ == QLatin1String("OR")) {
|
||||||
@@ -424,18 +427,19 @@ bool FilterParser::checkOr(const bool step_over) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FilterTree *FilterParser::parseSearchExpression() {
|
PlaylistFilterTree *PlaylistFilterParser::parseSearchExpression() {
|
||||||
|
|
||||||
advance();
|
advance();
|
||||||
if (iter_ == end_) return new NopFilter;
|
if (iter_ == end_) return new PlaylistNopFilter;
|
||||||
if (*iter_ == QLatin1Char('(')) {
|
if (*iter_ == QLatin1Char('(')) {
|
||||||
++iter_;
|
++iter_;
|
||||||
advance();
|
advance();
|
||||||
FilterTree *tree = parseOrGroup();
|
PlaylistFilterTree *tree = parseOrGroup();
|
||||||
advance();
|
advance();
|
||||||
if (iter_ != end_) {
|
if (iter_ != end_) {
|
||||||
if (*iter_ == QLatin1Char(')')) {
|
if (*iter_ == QLatin1Char(')')) {
|
||||||
@@ -446,8 +450,8 @@ FilterTree *FilterParser::parseSearchExpression() {
|
|||||||
}
|
}
|
||||||
else if (*iter_ == QLatin1Char('-')) {
|
else if (*iter_ == QLatin1Char('-')) {
|
||||||
++iter_;
|
++iter_;
|
||||||
FilterTree *tree = parseSearchExpression();
|
PlaylistFilterTree *tree = parseSearchExpression();
|
||||||
if (tree->type() != FilterTree::FilterType::Nop) return new NotFilter(tree);
|
if (tree->type() != PlaylistFilterTree::FilterType::Nop) return new PlaylistNotFilter(tree);
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -456,7 +460,7 @@ FilterTree *FilterParser::parseSearchExpression() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FilterTree *FilterParser::parseSearchTerm() {
|
PlaylistFilterTree *PlaylistFilterParser::parseSearchTerm() {
|
||||||
|
|
||||||
QString col;
|
QString col;
|
||||||
QString search;
|
QString search;
|
||||||
@@ -508,46 +512,45 @@ FilterTree *FilterParser::parseSearchTerm() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FilterTree *FilterParser::createSearchTermTreeNode(const QString &col, const QString &prefix, const QString &search) const {
|
PlaylistFilterTree *PlaylistFilterParser::createSearchTermTreeNode(const QString &col, const QString &prefix, const QString &search) const {
|
||||||
|
|
||||||
if (search.isEmpty() && prefix != QLatin1Char('=')) {
|
if (search.isEmpty() && prefix != QLatin1Char('=')) {
|
||||||
return new NopFilter;
|
return new PlaylistNopFilter;
|
||||||
}
|
}
|
||||||
// here comes a mess :/
|
|
||||||
// well, not that much of a mess, but so many options -_-
|
PlaylistSearchTermComparator *cmp = nullptr;
|
||||||
SearchTermComparator *cmp = nullptr;
|
|
||||||
|
|
||||||
// Handle the float based Rating Column
|
// Handle the float based Rating Column
|
||||||
if (columns_[col] == static_cast<int>(Playlist::Column::Rating)) {
|
if (columns_[col] == static_cast<int>(Playlist::Column::Rating)) {
|
||||||
float parsed_search = Utilities::ParseSearchRating(search);
|
float parsed_search = Utilities::ParseSearchRating(search);
|
||||||
|
|
||||||
if (prefix == QLatin1Char('=')) {
|
if (prefix == QLatin1Char('=')) {
|
||||||
cmp = new FloatEqComparator(parsed_search);
|
cmp = new PlaylistFloatEqComparator(parsed_search);
|
||||||
}
|
}
|
||||||
else if (prefix == QLatin1String("!=") || prefix == QLatin1String("<>")) {
|
else if (prefix == QLatin1String("!=") || prefix == QLatin1String("<>")) {
|
||||||
cmp = new FloatNeComparator(parsed_search);
|
cmp = new PlaylistFloatNeComparator(parsed_search);
|
||||||
}
|
}
|
||||||
else if (prefix == QLatin1Char('>')) {
|
else if (prefix == QLatin1Char('>')) {
|
||||||
cmp = new FloatGtComparator(parsed_search);
|
cmp = new PlaylistFloatGtComparator(parsed_search);
|
||||||
}
|
}
|
||||||
else if (prefix == QLatin1String(">=")) {
|
else if (prefix == QLatin1String(">=")) {
|
||||||
cmp = new FloatGeComparator(parsed_search);
|
cmp = new PlaylistFloatGeComparator(parsed_search);
|
||||||
}
|
}
|
||||||
else if (prefix == QLatin1Char('<')) {
|
else if (prefix == QLatin1Char('<')) {
|
||||||
cmp = new FloatLtComparator(parsed_search);
|
cmp = new PlaylistFloatLtComparator(parsed_search);
|
||||||
}
|
}
|
||||||
else if (prefix == QLatin1String("<=")) {
|
else if (prefix == QLatin1String("<=")) {
|
||||||
cmp = new FloatLeComparator(parsed_search);
|
cmp = new PlaylistFloatLeComparator(parsed_search);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cmp = new FloatEqComparator(parsed_search);
|
cmp = new PlaylistFloatEqComparator(parsed_search);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (prefix == QLatin1String("!=") || prefix == QLatin1String("<>")) {
|
else if (prefix == QLatin1String("!=") || prefix == QLatin1String("<>")) {
|
||||||
cmp = new NeComparator(search);
|
cmp = new PlaylistNeComparator(search);
|
||||||
}
|
}
|
||||||
else if (!col.isEmpty() && columns_.contains(col) && numerical_columns_.contains(columns_[col])) {
|
else if (!col.isEmpty() && columns_.contains(col) && numerical_columns_.contains(columns_[col])) {
|
||||||
// the length column contains the time in seconds (nanoseconds, actually - the "nano" part is handled by the DropTailComparatorDecorator, though).
|
// The length column contains the time in seconds (nanoseconds, actually - the "nano" part is handled by the DropTailComparatorDecorator, though).
|
||||||
int search_value = 0;
|
int search_value = 0;
|
||||||
if (columns_[col] == static_cast<int>(Playlist::Column::Length)) {
|
if (columns_[col] == static_cast<int>(Playlist::Column::Length)) {
|
||||||
search_value = Utilities::ParseSearchTime(search);
|
search_value = Utilities::ParseSearchTime(search);
|
||||||
@@ -555,53 +558,53 @@ FilterTree *FilterParser::createSearchTermTreeNode(const QString &col, const QSt
|
|||||||
else {
|
else {
|
||||||
search_value = search.toInt();
|
search_value = search.toInt();
|
||||||
}
|
}
|
||||||
// alright, back to deciding which comparator we'll use
|
// Alright, back to deciding which comparator we'll use
|
||||||
if (prefix == QLatin1Char('>')) {
|
if (prefix == QLatin1Char('>')) {
|
||||||
cmp = new GtComparator(search_value);
|
cmp = new PlaylistGtComparator(search_value);
|
||||||
}
|
}
|
||||||
else if (prefix == QLatin1String(">=")) {
|
else if (prefix == QLatin1String(">=")) {
|
||||||
cmp = new GeComparator(search_value);
|
cmp = new PlaylistGeComparator(search_value);
|
||||||
}
|
}
|
||||||
else if (prefix == QLatin1Char('<')) {
|
else if (prefix == QLatin1Char('<')) {
|
||||||
cmp = new LtComparator(search_value);
|
cmp = new PlaylistLtComparator(search_value);
|
||||||
}
|
}
|
||||||
else if (prefix == QLatin1String("<=")) {
|
else if (prefix == QLatin1String("<=")) {
|
||||||
cmp = new LeComparator(search_value);
|
cmp = new PlaylistLeComparator(search_value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// convert back because for time/rating
|
// Convert back because for time/rating
|
||||||
cmp = new EqComparator(QString::number(search_value));
|
cmp = new PlaylistEqComparator(QString::number(search_value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (prefix == QLatin1Char('=')) {
|
if (prefix == QLatin1Char('=')) {
|
||||||
cmp = new EqComparator(search);
|
cmp = new PlaylistEqComparator(search);
|
||||||
}
|
}
|
||||||
else if (prefix == QLatin1Char('>')) {
|
else if (prefix == QLatin1Char('>')) {
|
||||||
cmp = new LexicalGtComparator(search);
|
cmp = new PlaylistLexicalGtComparator(search);
|
||||||
}
|
}
|
||||||
else if (prefix == QLatin1String(">=")) {
|
else if (prefix == QLatin1String(">=")) {
|
||||||
cmp = new LexicalGeComparator(search);
|
cmp = new PlaylistLexicalGeComparator(search);
|
||||||
}
|
}
|
||||||
else if (prefix == QLatin1Char('<')) {
|
else if (prefix == QLatin1Char('<')) {
|
||||||
cmp = new LexicalLtComparator(search);
|
cmp = new PlaylistLexicalLtComparator(search);
|
||||||
}
|
}
|
||||||
else if (prefix == QLatin1String("<=")) {
|
else if (prefix == QLatin1String("<=")) {
|
||||||
cmp = new LexicalLeComparator(search);
|
cmp = new PlaylistLexicalLeComparator(search);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cmp = new DefaultComparator(search);
|
cmp = new PlaylistDefaultComparator(search);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (columns_.contains(col)) {
|
if (columns_.contains(col)) {
|
||||||
if (columns_[col] == static_cast<int>(Playlist::Column::Length)) {
|
if (columns_[col] == static_cast<int>(Playlist::Column::Length)) {
|
||||||
cmp = new DropTailComparatorDecorator(cmp);
|
cmp = new PlaylistDropTailComparatorDecorator(cmp);
|
||||||
}
|
}
|
||||||
return new FilterColumnTerm(columns_[col], cmp);
|
return new PlaylistFilterColumnTerm(columns_[col], cmp);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return new FilterTerm(cmp, columns_.values());
|
return new PlaylistFilterTerm(cmp, columns_.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Strawberry Music Player
|
* Strawberry Music Player
|
||||||
* This file was part of Clementine.
|
* This file was part of Clementine.
|
||||||
* Copyright 2012, David Sansome <me@davidsansome.com>
|
* Copyright 2012, David Sansome <me@davidsansome.com>
|
||||||
|
* Copyright 2018-2024, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
*
|
*
|
||||||
* Strawberry is free software: you can redistribute it and/or modify
|
* Strawberry is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -31,11 +32,11 @@ class QAbstractItemModel;
|
|||||||
class QModelIndex;
|
class QModelIndex;
|
||||||
|
|
||||||
// Structure for filter parse tree
|
// Structure for filter parse tree
|
||||||
class FilterTree {
|
class PlaylistFilterTree {
|
||||||
public:
|
public:
|
||||||
FilterTree() = default;
|
PlaylistFilterTree() = default;
|
||||||
virtual ~FilterTree() {}
|
virtual ~PlaylistFilterTree() {}
|
||||||
virtual bool accept(int row, const QModelIndex &parent, const QAbstractItemModel *const model) const = 0;
|
virtual bool accept(const int row, const QModelIndex &parent, const QAbstractItemModel *const model) const = 0;
|
||||||
enum class FilterType {
|
enum class FilterType {
|
||||||
Nop = 0,
|
Nop = 0,
|
||||||
Or,
|
Or,
|
||||||
@@ -46,13 +47,13 @@ class FilterTree {
|
|||||||
};
|
};
|
||||||
virtual FilterType type() = 0;
|
virtual FilterType type() = 0;
|
||||||
private:
|
private:
|
||||||
Q_DISABLE_COPY(FilterTree)
|
Q_DISABLE_COPY(PlaylistFilterTree)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Trivial filter that accepts *anything*
|
// Trivial filter that accepts *anything*
|
||||||
class NopFilter : public FilterTree {
|
class PlaylistNopFilter : public PlaylistFilterTree {
|
||||||
public:
|
public:
|
||||||
bool accept(int row, const QModelIndex &parent, const QAbstractItemModel *const model) const override { Q_UNUSED(row); Q_UNUSED(parent); Q_UNUSED(model); return true; }
|
bool accept(const int row, const QModelIndex &parent, const QAbstractItemModel *const model) const override { Q_UNUSED(row); Q_UNUSED(parent); Q_UNUSED(model); return true; }
|
||||||
FilterType type() override { return FilterType::Nop; }
|
FilterType type() override { return FilterType::Nop; }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -70,24 +71,24 @@ class NopFilter : public FilterTree {
|
|||||||
// string ::= [^:-()" ]+ | '"' [^"]+ '"'
|
// string ::= [^:-()" ]+ | '"' [^"]+ '"'
|
||||||
// prefix ::= '=' | '<' | '>' | '<=' | '>='
|
// prefix ::= '=' | '<' | '>' | '<=' | '>='
|
||||||
// col ::= "title" | "artist" | ...
|
// col ::= "title" | "artist" | ...
|
||||||
class FilterParser {
|
class PlaylistFilterParser {
|
||||||
public:
|
public:
|
||||||
explicit FilterParser(const QString &filter, const QMap<QString, int> &columns, const QSet<int> &numerical_cols);
|
explicit PlaylistFilterParser(const QString &filter, const QMap<QString, int> &columns, const QSet<int> &numerical_cols);
|
||||||
|
|
||||||
FilterTree *parse();
|
PlaylistFilterTree *parse();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void advance();
|
void advance();
|
||||||
FilterTree *parseOrGroup();
|
PlaylistFilterTree *parseOrGroup();
|
||||||
FilterTree *parseAndGroup();
|
PlaylistFilterTree *parseAndGroup();
|
||||||
// Check if iter is at the start of 'AND' if so, step over it and return true if not, return false and leave iter where it was
|
// Check if iter is at the start of 'AND' if so, step over it and return true if not, return false and leave iter where it was
|
||||||
bool checkAnd();
|
bool checkAnd();
|
||||||
// Check if iter is at the start of 'OR'
|
// Check if iter is at the start of 'OR'
|
||||||
bool checkOr(bool step_over = true);
|
bool checkOr(const bool step_over = true);
|
||||||
FilterTree *parseSearchExpression();
|
PlaylistFilterTree *parseSearchExpression();
|
||||||
FilterTree *parseSearchTerm();
|
PlaylistFilterTree *parseSearchTerm();
|
||||||
|
|
||||||
FilterTree *createSearchTermTreeNode(const QString &col, const QString &prefix, const QString &search) const;
|
PlaylistFilterTree *createSearchTermTreeNode(const QString &col, const QString &prefix, const QString &search) const;
|
||||||
|
|
||||||
QString::const_iterator iter_;
|
QString::const_iterator iter_;
|
||||||
QString::const_iterator end_;
|
QString::const_iterator end_;
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ constexpr int kMagicNumber = 0x502C9510;
|
|||||||
StretchHeaderView::StretchHeaderView(const Qt::Orientation orientation, QWidget *parent)
|
StretchHeaderView::StretchHeaderView(const Qt::Orientation orientation, QWidget *parent)
|
||||||
: QHeaderView(orientation, parent),
|
: QHeaderView(orientation, parent),
|
||||||
stretch_enabled_(false),
|
stretch_enabled_(false),
|
||||||
in_mouse_move_event_(false) {
|
in_mouse_move_event_(false),
|
||||||
|
forced_resize_logical_index_(-1) {
|
||||||
|
|
||||||
setDefaultSectionSize(100);
|
setDefaultSectionSize(100);
|
||||||
setMinimumSectionSize(30);
|
setMinimumSectionSize(30);
|
||||||
@@ -326,39 +327,48 @@ void StretchHeaderView::SectionResized(const int logical_index, const int old_si
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_mouse_move_event_) {
|
if (logical_index == forced_resize_logical_index_) {
|
||||||
bool resized = false;
|
forced_resize_logical_index_ = -1;
|
||||||
if (new_size >= minimumSectionSize()) {
|
return;
|
||||||
// Find the visible section to the right of the section that's being resized
|
}
|
||||||
const int visual_index = visualIndex(logical_index);
|
|
||||||
int right_section_logical_index = -1;
|
if (!in_mouse_move_event_) {
|
||||||
int right_section_visual_index = -1;
|
return;
|
||||||
for (int i = 0; i <= count(); ++i) {
|
}
|
||||||
if (!isSectionHidden(i) &&
|
|
||||||
visualIndex(i) > visual_index &&
|
bool resized = false;
|
||||||
(right_section_visual_index == -1 || visualIndex(i) < right_section_visual_index)) {
|
if (new_size >= minimumSectionSize()) {
|
||||||
right_section_logical_index = i;
|
// Find the visible section to the right of the section that's being resized
|
||||||
right_section_visual_index = visualIndex(i);
|
const int visual_index = visualIndex(logical_index);
|
||||||
}
|
int right_section_logical_index = -1;
|
||||||
}
|
int right_section_visual_index = -1;
|
||||||
if (right_section_logical_index != -1) {
|
for (int i = 0; i <= count(); ++i) {
|
||||||
const int right_section_size = sectionSize(right_section_logical_index) + (old_size - new_size);
|
if (!isSectionHidden(i) &&
|
||||||
if (right_section_size >= minimumSectionSize()) {
|
visualIndex(i) > visual_index &&
|
||||||
column_widths_[logical_index] = static_cast<ColumnWidthType>(new_size) / width();
|
(right_section_visual_index == -1 || visualIndex(i) < right_section_visual_index)) {
|
||||||
column_widths_[right_section_logical_index] = static_cast<ColumnWidthType>(right_section_size) / width();
|
right_section_logical_index = i;
|
||||||
in_mouse_move_event_ = false;
|
right_section_visual_index = visualIndex(i);
|
||||||
NormaliseWidths(QList<int>() << right_section_logical_index);
|
|
||||||
ResizeSections(QList<int>() << right_section_logical_index);
|
|
||||||
in_mouse_move_event_ = true;
|
|
||||||
resized = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!resized) {
|
if (right_section_logical_index != -1) {
|
||||||
in_mouse_move_event_ = true;
|
const int right_section_size = sectionSize(right_section_logical_index) + (old_size - new_size);
|
||||||
resizeSection(logical_index, old_size);
|
if (right_section_size >= minimumSectionSize()) {
|
||||||
in_mouse_move_event_ = false;
|
column_widths_[logical_index] = static_cast<ColumnWidthType>(new_size) / width();
|
||||||
|
column_widths_[right_section_logical_index] = static_cast<ColumnWidthType>(right_section_size) / width();
|
||||||
|
in_mouse_move_event_ = false;
|
||||||
|
NormaliseWidths(QList<int>() << right_section_logical_index);
|
||||||
|
ResizeSections(QList<int>() << right_section_logical_index);
|
||||||
|
in_mouse_move_event_ = true;
|
||||||
|
resized = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!resized) {
|
||||||
|
forced_resize_logical_index_ = logical_index;
|
||||||
|
in_mouse_move_event_ = false;
|
||||||
|
resizeSection(logical_index, old_size);
|
||||||
|
in_mouse_move_event_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ class StretchHeaderView : public QHeaderView {
|
|||||||
QVector<ColumnWidthType> column_widths_;
|
QVector<ColumnWidthType> column_widths_;
|
||||||
|
|
||||||
bool in_mouse_move_event_;
|
bool in_mouse_move_event_;
|
||||||
|
int forced_resize_logical_index_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // STRETCHHEADERVIEW_H
|
#endif // STRETCHHEADERVIEW_H
|
||||||
|
|||||||
@@ -3,14 +3,14 @@
|
|||||||
|
|
||||||
// clazy:excludeall=returning-void-expression
|
// clazy:excludeall=returning-void-expression
|
||||||
|
|
||||||
TEST(SqliteTest, FTS5SupportEnabled) {
|
TEST(SqliteTest, CreateTableTest) {
|
||||||
|
|
||||||
sqlite3* db = nullptr;
|
sqlite3 *db = nullptr;
|
||||||
int rc = sqlite3_open(":memory:", &db);
|
int rc = sqlite3_open(":memory:", &db);
|
||||||
ASSERT_EQ(0, rc);
|
ASSERT_EQ(0, rc);
|
||||||
|
|
||||||
char* errmsg = nullptr;
|
char *errmsg = nullptr;
|
||||||
rc = sqlite3_exec(db, "CREATE VIRTUAL TABLE foo USING fts5(content, TEXT, tokenize = 'unicode61 remove_diacritics 0')", nullptr, nullptr, &errmsg);
|
rc = sqlite3_exec(db, "CREATE TABLE foo (content TEXT)", nullptr, nullptr, &errmsg);
|
||||||
ASSERT_EQ(0, rc) << errmsg;
|
ASSERT_EQ(0, rc) << errmsg;
|
||||||
|
|
||||||
sqlite3_close(db);
|
sqlite3_close(db);
|
||||||
|
|||||||
Reference in New Issue
Block a user