Includes, comments and bugfixes

- Fix includes
- Use common regex (Song::kCoverRemoveDisc) for removing Disc/CD from album
- Remove Disc/CD from album when creating hash
- Make imobiledevice support compile
- Fix setting device on windows
This commit is contained in:
Jonas Kvinge 2018-05-01 00:41:33 +02:00
parent fccbd6790c
commit e337b7933b
518 changed files with 7003 additions and 4693 deletions

View File

@ -1,4 +1,4 @@
#include <QtGui/QApplication> #include <QApplication>
#include "gallery.h" #include "gallery.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])

View File

@ -3,6 +3,7 @@
#include <QWidget> #include <QWidget>
#include <QPointer> #include <QPointer>
#include <QString>
class QButtonPrivate; class QButtonPrivate;
class QButton : public QWidget class QButton : public QWidget

View File

@ -20,12 +20,19 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
*/ */
#include "qbutton.h" #include <QtGlobal>
#include <QObject>
#include <QWidget>
#include <QPointer>
#include <QString>
#include <QPixmap>
#include <QAbstractButton>
#include <QBoxLayout>
#include <QPushButton>
#include <QToolBar> #include <QToolBar>
#include <QToolButton> #include <QToolButton>
#include <QPushButton>
#include <QVBoxLayout> #include "qbutton.h"
class QButtonPrivate : public QObject class QButtonPrivate : public QObject
{ {

View File

@ -21,8 +21,9 @@ THE SOFTWARE.
*/ */
#include <Foundation/NSString.h> #include <Foundation/NSString.h>
#include <QByteArray>
#include <QString> #include <QString>
#include <QVBoxLayout> #include <QBoxLayout>
#include <QMacCocoaViewContainer> #include <QMacCocoaViewContainer>
static inline NSString* fromQString(const QString &string) static inline NSString* fromQString(const QString &string)

View File

@ -17,8 +17,7 @@ public:
Aqua = 12 Aqua = 12
}; };
explicit QProgressIndicatorSpinning(QWidget *parent, explicit QProgressIndicatorSpinning(QWidget *parent, Thickness thickness = Default);
Thickness thickness = Default);
public slots: public slots:
void animate(bool animate = true); void animate(bool animate = true);
private: private:

View File

@ -20,11 +20,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
*/ */
#include "qprogressindicatorspinning.h" #include <QtGlobal>
#include <QObject>
#include <QVBoxLayout> #include <QWidget>
#include <QMovie> #include <QBoxLayout>
#include <QLabel> #include <QLabel>
#include <QMovie>
#include <QPointer>
#include <QSize>
#include "qprogressindicatorspinning.h"
class QProgressIndicatorSpinningPrivate : public QObject class QProgressIndicatorSpinningPrivate : public QObject
{ {

View File

@ -2,7 +2,11 @@
#define QSEARCHFIELD_H #define QSEARCHFIELD_H
#include <QWidget> #include <QWidget>
#include <QObject>
#include <QPointer> #include <QPointer>
#include <QString>
#include <QEvent>
#include <QResizeEvent>
class QSearchFieldPrivate; class QSearchFieldPrivate;
class QSearchField : public QWidget class QSearchField : public QWidget

View File

@ -20,18 +20,22 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
*/ */
#include "qsearchfield.h" #include <QtGlobal>
#include "../../src/core/iconloader.h" #include <QObject>
#include <QWidget>
#include <QApplication> #include <QApplication>
#include <QEvent> #include <QPointer>
#include <QLineEdit> #include <QString>
#include <QVBoxLayout> #include <QIcon>
#include <QToolButton> #include <QSize>
#include <QStyle> #include <QStyle>
#include <QLineEdit>
#include <QBoxLayout>
#include <QToolButton>
#include <QtEvents>
#include <QDir> #include "../../src/core/iconloader.h"
#include <QDebug> #include "qsearchfield.h"
class QSearchFieldPrivate : public QObject class QSearchFieldPrivate : public QObject
{ {

View File

@ -41,24 +41,31 @@
#include "qsql_sqlite.h" #include "qsql_sqlite.h"
#include <qcoreapplication.h>
#include <qvariant.h>
#include <qsqlerror.h>
#include <qsqlfield.h>
#include <qsqlindex.h>
#include <qsqlquery.h>
#include <qstringlist.h>
#include <qvector.h>
#include <qdebug.h>
#if defined Q_OS_WIN #if defined Q_OS_WIN
# include <qt_windows.h> #include <qt_windows.h>
#else #else
# include <unistd.h> #include <unistd.h>
#endif #endif
#include <stddef.h>
#include <sqlite3.h> #include <sqlite3.h>
#include <QObject>
#include <QCoreApplication>
#include <QByteArray>
#include <QVariant>
#include <QString>
#include <QStringList>
#include <QChar>
#include <QList>
#include <QMetaType>
#include <QVector>
#include <QSqlError>
#include <QSqlField>
#include <QSqlQuery>
#include <QSqlResult>
#include <QSqlRecord>
Q_DECLARE_OPAQUE_POINTER(sqlite3*) Q_DECLARE_OPAQUE_POINTER(sqlite3*)
Q_DECLARE_METATYPE(sqlite3*) Q_DECLARE_METATYPE(sqlite3*)
@ -98,16 +105,11 @@ static QVariant::Type qGetColumnType(const QString &tpName)
return QVariant::String; return QVariant::String;
} }
static QSqlError qMakeError(sqlite3 *access, const QString &descr, QSqlError::ErrorType type, static QSqlError qMakeError(sqlite3 *access, const QString &descr, QSqlError::ErrorType type, int errorCode = -1) {
int errorCode = -1) return QSqlError(descr, QString(reinterpret_cast<const QChar *>(sqlite3_errmsg16(access))), type, errorCode);
{
return QSqlError(descr,
QString(reinterpret_cast<const QChar *>(sqlite3_errmsg16(access))),
type, errorCode);
} }
class QSQLiteDriverPrivate class QSQLiteDriverPrivate {
{
public: public:
inline QSQLiteDriverPrivate() : access(0) {} inline QSQLiteDriverPrivate() : access(0) {}
sqlite3 *access; sqlite3 *access;
@ -115,8 +117,7 @@ public:
}; };
class QSQLiteResultPrivate class QSQLiteResultPrivate {
{
public: public:
QSQLiteResultPrivate(QSQLiteResult *res); QSQLiteResultPrivate(QSQLiteResult *res);
void cleanup(); void cleanup();
@ -141,8 +142,7 @@ QSQLiteResultPrivate::QSQLiteResultPrivate(QSQLiteResult* res) : q(res), access(
{ {
} }
void QSQLiteResultPrivate::cleanup() void QSQLiteResultPrivate::cleanup() {
{
finalize(); finalize();
rInf.clear(); rInf.clear();
skippedStatus = false; skippedStatus = false;
@ -152,17 +152,14 @@ void QSQLiteResultPrivate::cleanup()
q->cleanup(); q->cleanup();
} }
void QSQLiteResultPrivate::finalize() void QSQLiteResultPrivate::finalize() {
{ if (!stmt) return;
if (!stmt)
return;
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
stmt = 0; stmt = 0;
} }
void QSQLiteResultPrivate::initColumns(bool emptyResultset) void QSQLiteResultPrivate::initColumns(bool emptyResultset) {
{
int nCols = sqlite3_column_count(stmt); int nCols = sqlite3_column_count(stmt);
if (nCols <= 0) if (nCols <= 0)
return; return;
@ -170,13 +167,10 @@ void QSQLiteResultPrivate::initColumns(bool emptyResultset)
q->init(nCols); q->init(nCols);
for (int i = 0; i < nCols; ++i) { for (int i = 0; i < nCols; ++i) {
QString colName = QString(reinterpret_cast<const QChar *>( QString colName = QString(reinterpret_cast<const QChar *>(sqlite3_column_name16(stmt, i))).remove(QLatin1Char('"'));
sqlite3_column_name16(stmt, i))
).remove(QLatin1Char('"'));
// must use typeName for resolving the type to match QSqliteDriver::record // must use typeName for resolving the type to match QSqliteDriver::record
QString typeName = QString(reinterpret_cast<const QChar *>( QString typeName = QString(reinterpret_cast<const QChar *>(sqlite3_column_decltype16(stmt, i)));
sqlite3_column_decltype16(stmt, i)));
// sqlite3_column_type is documented to have undefined behavior if the result set is empty // sqlite3_column_type is documented to have undefined behavior if the result set is empty
int stp = emptyResultset ? -1 : sqlite3_column_type(stmt, i); int stp = emptyResultset ? -1 : sqlite3_column_type(stmt, i);
@ -184,7 +178,8 @@ void QSQLiteResultPrivate::initColumns(bool emptyResultset)
if (!typeName.isEmpty()) { if (!typeName.isEmpty()) {
fieldType = qGetColumnType(typeName); fieldType = qGetColumnType(typeName);
} else { }
else {
// Get the proper type for the field based on stp value // Get the proper type for the field based on stp value
switch (stp) { switch (stp) {
case SQLITE_INTEGER: case SQLITE_INTEGER:
@ -213,8 +208,7 @@ void QSQLiteResultPrivate::initColumns(bool emptyResultset)
} }
} }
bool QSQLiteResultPrivate::fetchNext(ClementineSqlCachedResult::ValueCache &values, int idx, bool initialFetch) bool QSQLiteResultPrivate::fetchNext(ClementineSqlCachedResult::ValueCache &values, int idx, bool initialFetch) {
{
int res; int res;
int i; int i;
@ -234,8 +228,7 @@ bool QSQLiteResultPrivate::fetchNext(ClementineSqlCachedResult::ValueCache &valu
} }
if (!stmt) { if (!stmt) {
q->setLastError(QSqlError(QCoreApplication::translate("QSQLiteResult", "Unable to fetch row"), q->setLastError(QSqlError(QCoreApplication::translate("QSQLiteResult", "Unable to fetch row"), QCoreApplication::translate("QSQLiteResult", "No query"), QSqlError::ConnectionError));
QCoreApplication::translate("QSQLiteResult", "No query"), QSqlError::ConnectionError));
q->setAt(QSql::AfterLastRow); q->setAt(QSql::AfterLastRow);
return false; return false;
} }
@ -252,9 +245,7 @@ bool QSQLiteResultPrivate::fetchNext(ClementineSqlCachedResult::ValueCache &valu
for (i = 0; i < rInf.count(); ++i) { for (i = 0; i < rInf.count(); ++i) {
switch (sqlite3_column_type(stmt, i)) { switch (sqlite3_column_type(stmt, i)) {
case SQLITE_BLOB: case SQLITE_BLOB:
values[i + idx] = QByteArray(static_cast<const char *>( values[i + idx] = QByteArray(static_cast<const char *>(sqlite3_column_blob(stmt, i)), sqlite3_column_bytes(stmt, i));
sqlite3_column_blob(stmt, i)),
sqlite3_column_bytes(stmt, i));
break; break;
case SQLITE_INTEGER: case SQLITE_INTEGER:
values[i + idx] = sqlite3_column_int64(stmt, i); values[i + idx] = sqlite3_column_int64(stmt, i);
@ -278,9 +269,7 @@ bool QSQLiteResultPrivate::fetchNext(ClementineSqlCachedResult::ValueCache &valu
values[i + idx] = QVariant(QVariant::String); values[i + idx] = QVariant(QVariant::String);
break; break;
default: default:
values[i + idx] = QString(reinterpret_cast<const QChar *>( values[i + idx] = QString(reinterpret_cast<const QChar *>(sqlite3_column_text16(stmt, i)), sqlite3_column_bytes16(stmt, i) / sizeof(QChar));
sqlite3_column_text16(stmt, i)),
sqlite3_column_bytes16(stmt, i) / sizeof(QChar));
break; break;
} }
} }
@ -297,16 +286,14 @@ bool QSQLiteResultPrivate::fetchNext(ClementineSqlCachedResult::ValueCache &valu
// SQLITE_ERROR is a generic error code and we must call sqlite3_reset() // SQLITE_ERROR is a generic error code and we must call sqlite3_reset()
// to get the specific error message. // to get the specific error message.
res = sqlite3_reset(stmt); res = sqlite3_reset(stmt);
q->setLastError(qMakeError(access, QCoreApplication::translate("QSQLiteResult", q->setLastError(qMakeError(access, QCoreApplication::translate("QSQLiteResult", "Unable to fetch row"), QSqlError::ConnectionError, res));
"Unable to fetch row"), QSqlError::ConnectionError, res));
q->setAt(QSql::AfterLastRow); q->setAt(QSql::AfterLastRow);
return false; return false;
case SQLITE_MISUSE: case SQLITE_MISUSE:
case SQLITE_BUSY: case SQLITE_BUSY:
default: default:
// something wrong, don't get col info, but still return false // something wrong, don't get col info, but still return false
q->setLastError(qMakeError(access, QCoreApplication::translate("QSQLiteResult", q->setLastError(qMakeError(access, QCoreApplication::translate("QSQLiteResult", "Unable to fetch row"), QSqlError::ConnectionError, res));
"Unable to fetch row"), QSqlError::ConnectionError, res));
sqlite3_reset(stmt); sqlite3_reset(stmt);
q->setAt(QSql::AfterLastRow); q->setAt(QSql::AfterLastRow);
return false; return false;
@ -315,15 +302,13 @@ bool QSQLiteResultPrivate::fetchNext(ClementineSqlCachedResult::ValueCache &valu
} }
QSQLiteResult::QSQLiteResult(const QSQLiteDriver* db) QSQLiteResult::QSQLiteResult(const QSQLiteDriver* db)
: ClementineSqlCachedResult(db) : ClementineSqlCachedResult(db) {
{
d = new QSQLiteResultPrivate(this); d = new QSQLiteResultPrivate(this);
d->access = db->d->access; d->access = db->d->access;
db->d->results.append(this); db->d->results.append(this);
} }
QSQLiteResult::~QSQLiteResult() QSQLiteResult::~QSQLiteResult() {
{
const QSqlDriver *sqlDriver = driver(); const QSqlDriver *sqlDriver = driver();
if (sqlDriver) if (sqlDriver)
qobject_cast<const QSQLiteDriver *>(sqlDriver)->d->results.removeOne(this); qobject_cast<const QSQLiteDriver *>(sqlDriver)->d->results.removeOne(this);
@ -331,20 +316,17 @@ QSQLiteResult::~QSQLiteResult()
delete d; delete d;
} }
void QSQLiteResult::virtual_hook(int id, void *data) void QSQLiteResult::virtual_hook(int id, void *data) {
{
ClementineSqlCachedResult::virtual_hook(id, data); ClementineSqlCachedResult::virtual_hook(id, data);
} }
bool QSQLiteResult::reset(const QString &query) bool QSQLiteResult::reset(const QString &query) {
{
if (!prepare(query)) if (!prepare(query))
return false; return false;
return exec(); return exec();
} }
bool QSQLiteResult::prepare(const QString &query) bool QSQLiteResult::prepare(const QString &query) {
{
if (!driver() || !driver()->isOpen() || driver()->isOpenError()) if (!driver() || !driver()->isOpen() || driver()->isOpenError())
return false; return false;
@ -355,29 +337,25 @@ bool QSQLiteResult::prepare(const QString &query)
const void *pzTail = NULL; const void *pzTail = NULL;
#if (SQLITE_VERSION_NUMBER >= 3003011) #if (SQLITE_VERSION_NUMBER >= 3003011)
int res = sqlite3_prepare16_v2(d->access, query.constData(), (query.size() + 1) * sizeof(QChar), int res = sqlite3_prepare16_v2(d->access, query.constData(), (query.size() + 1) * sizeof(QChar), &d->stmt, &pzTail);
&d->stmt, &pzTail);
#else #else
int res = sqlite3_prepare16(d->access, query.constData(), (query.size() + 1) * sizeof(QChar), int res = sqlite3_prepare16(d->access, query.constData(), (query.size() + 1) * sizeof(QChar), &d->stmt, &pzTail);
&d->stmt, &pzTail);
#endif #endif
if (res != SQLITE_OK) { if (res != SQLITE_OK) {
setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult", setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult", "Unable to execute statement"), QSqlError::StatementError, res));
"Unable to execute statement"), QSqlError::StatementError, res));
d->finalize(); d->finalize();
return false; return false;
} else if (pzTail && !QString(reinterpret_cast<const QChar *>(pzTail)).trimmed().isEmpty()) { }
setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult", else if (pzTail && !QString(reinterpret_cast<const QChar *>(pzTail)).trimmed().isEmpty()) {
"Unable to execute multiple statements at a time"), QSqlError::StatementError, SQLITE_MISUSE)); setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult", "Unable to execute multiple statements at a time"), QSqlError::StatementError, SQLITE_MISUSE));
d->finalize(); d->finalize();
return false; return false;
} }
return true; return true;
} }
bool QSQLiteResult::exec() bool QSQLiteResult::exec() {
{
const QVector<QVariant> values = boundValues(); const QVector<QVariant> values = boundValues();
d->skippedStatus = false; d->skippedStatus = false;
@ -388,8 +366,7 @@ bool QSQLiteResult::exec()
int res = sqlite3_reset(d->stmt); int res = sqlite3_reset(d->stmt);
if (res != SQLITE_OK) { if (res != SQLITE_OK) {
setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult", setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult", "Unable to reset statement"), QSqlError::StatementError, res));
"Unable to reset statement"), QSqlError::StatementError, res));
d->finalize(); d->finalize();
return false; return false;
} }
@ -401,7 +378,8 @@ bool QSQLiteResult::exec()
if (value.isNull()) { if (value.isNull()) {
res = sqlite3_bind_null(d->stmt, i + 1); res = sqlite3_bind_null(d->stmt, i + 1);
} else { }
else {
switch (value.type()) { switch (value.type()) {
case QVariant::ByteArray: { case QVariant::ByteArray: {
const QByteArray *ba = static_cast<const QByteArray*>(value.constData()); const QByteArray *ba = static_cast<const QByteArray*>(value.constData());
@ -434,13 +412,13 @@ bool QSQLiteResult::exec()
} }
} }
if (res != SQLITE_OK) { if (res != SQLITE_OK) {
setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult", setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult", "Unable to bind parameters"), QSqlError::StatementError, res));
"Unable to bind parameters"), QSqlError::StatementError, res));
d->finalize(); d->finalize();
return false; return false;
} }
} }
} else { }
else {
setLastError(QSqlError(QCoreApplication::translate("QSQLiteResult", setLastError(QSqlError(QCoreApplication::translate("QSQLiteResult",
"Parameter count mismatch") + QString::number(paramCount, 10) + "/" + QString::number(values.count(), 10), QString(), QSqlError::StatementError)); "Parameter count mismatch") + QString::number(paramCount, 10) + "/" + QString::number(values.count(), 10), QString(), QSqlError::StatementError));
return false; return false;
@ -456,23 +434,19 @@ bool QSQLiteResult::exec()
return true; return true;
} }
bool QSQLiteResult::gotoNext(ClementineSqlCachedResult::ValueCache& row, int idx) bool QSQLiteResult::gotoNext(ClementineSqlCachedResult::ValueCache& row, int idx) {
{
return d->fetchNext(row, idx, false); return d->fetchNext(row, idx, false);
} }
int QSQLiteResult::size() int QSQLiteResult::size() {
{
return -1; return -1;
} }
int QSQLiteResult::numRowsAffected() int QSQLiteResult::numRowsAffected() {
{
return sqlite3_changes(d->access); return sqlite3_changes(d->access);
} }
QVariant QSQLiteResult::lastInsertId() const QVariant QSQLiteResult::lastInsertId() const {
{
if (isActive()) { if (isActive()) {
qint64 id = sqlite3_last_insert_rowid(d->access); qint64 id = sqlite3_last_insert_rowid(d->access);
if (id) if (id)
@ -481,35 +455,30 @@ QVariant QSQLiteResult::lastInsertId() const
return QVariant(); return QVariant();
} }
QSqlRecord QSQLiteResult::record() const QSqlRecord QSQLiteResult::record() const {
{
if (!isActive() || !isSelect()) if (!isActive() || !isSelect())
return QSqlRecord(); return QSqlRecord();
return d->rInf; return d->rInf;
} }
void QSQLiteResult::detachFromResultSet() void QSQLiteResult::detachFromResultSet() {
{
if (d->stmt) if (d->stmt)
sqlite3_reset(d->stmt); sqlite3_reset(d->stmt);
} }
QVariant QSQLiteResult::handle() const QVariant QSQLiteResult::handle() const {
{
return QVariant::fromValue(d->stmt); return QVariant::fromValue(d->stmt);
} }
///////////////////////////////////////////////////////// /////////////////////////////////////////////////////////
QSQLiteDriver::QSQLiteDriver(QObject * parent) QSQLiteDriver::QSQLiteDriver(QObject * parent)
: QSqlDriver(parent) : QSqlDriver(parent) {
{
d = new QSQLiteDriverPrivate(); d = new QSQLiteDriverPrivate();
} }
QSQLiteDriver::QSQLiteDriver(sqlite3 *connection, QObject *parent) QSQLiteDriver::QSQLiteDriver(sqlite3 *connection, QObject *parent)
: QSqlDriver(parent) : QSqlDriver(parent) {
{
d = new QSQLiteDriverPrivate(); d = new QSQLiteDriverPrivate();
d->access = connection; d->access = connection;
setOpen(true); setOpen(true);
@ -579,87 +548,77 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c
setOpen(true); setOpen(true);
setOpenError(false); setOpenError(false);
return true; return true;
} else { }
else {
if (d->access) { if (d->access) {
sqlite3_close(d->access); sqlite3_close(d->access);
d->access = 0; d->access = 0;
} }
setLastError(qMakeError(d->access, tr("Error opening database"), setLastError(qMakeError(d->access, tr("Error opening database"), QSqlError::ConnectionError));
QSqlError::ConnectionError));
setOpenError(true); setOpenError(true);
return false; return false;
} }
} }
void QSQLiteDriver::close() void QSQLiteDriver::close() {
{
if (isOpen()) { if (isOpen()) {
foreach (QSQLiteResult *result, d->results) { foreach (QSQLiteResult *result, d->results) {
result->d->finalize(); result->d->finalize();
} }
if (sqlite3_close(d->access) != SQLITE_OK) if (sqlite3_close(d->access) != SQLITE_OK)
setLastError(qMakeError(d->access, tr("Error closing database"), setLastError(qMakeError(d->access, tr("Error closing database"), QSqlError::ConnectionError));
QSqlError::ConnectionError));
d->access = 0; d->access = 0;
setOpen(false); setOpen(false);
setOpenError(false); setOpenError(false);
} }
} }
QSqlResult *QSQLiteDriver::createResult() const QSqlResult *QSQLiteDriver::createResult() const {
{
return new QSQLiteResult(this); return new QSQLiteResult(this);
} }
bool QSQLiteDriver::beginTransaction() bool QSQLiteDriver::beginTransaction() {
{
if (!isOpen() || isOpenError()) if (!isOpen() || isOpenError())
return false; return false;
QSqlQuery q(createResult()); QSqlQuery q(createResult());
if (!q.exec(QLatin1String("BEGIN"))) { if (!q.exec(QLatin1String("BEGIN"))) {
setLastError(QSqlError(tr("Unable to begin transaction"), setLastError(QSqlError(tr("Unable to begin transaction"), q.lastError().databaseText(), QSqlError::TransactionError));
q.lastError().databaseText(), QSqlError::TransactionError));
return false; return false;
} }
return true; return true;
} }
bool QSQLiteDriver::commitTransaction() bool QSQLiteDriver::commitTransaction() {
{
if (!isOpen() || isOpenError()) if (!isOpen() || isOpenError())
return false; return false;
QSqlQuery q(createResult()); QSqlQuery q(createResult());
if (!q.exec(QLatin1String("COMMIT"))) { if (!q.exec(QLatin1String("COMMIT"))) {
setLastError(QSqlError(tr("Unable to commit transaction"), setLastError(QSqlError(tr("Unable to commit transaction"), q.lastError().databaseText(), QSqlError::TransactionError));
q.lastError().databaseText(), QSqlError::TransactionError));
return false; return false;
} }
return true; return true;
} }
bool QSQLiteDriver::rollbackTransaction() bool QSQLiteDriver::rollbackTransaction() {
{
if (!isOpen() || isOpenError()) if (!isOpen() || isOpenError())
return false; return false;
QSqlQuery q(createResult()); QSqlQuery q(createResult());
if (!q.exec(QLatin1String("ROLLBACK"))) { if (!q.exec(QLatin1String("ROLLBACK"))) {
setLastError(QSqlError(tr("Unable to rollback transaction"), setLastError(QSqlError(tr("Unable to rollback transaction"), q.lastError().databaseText(), QSqlError::TransactionError));
q.lastError().databaseText(), QSqlError::TransactionError));
return false; return false;
} }
return true; return true;
} }
QStringList QSQLiteDriver::tables(QSql::TableType type) const QStringList QSQLiteDriver::tables(QSql::TableType type) const {
{
QStringList res; QStringList res;
if (!isOpen()) if (!isOpen())
return res; return res;
@ -691,8 +650,7 @@ QStringList QSQLiteDriver::tables(QSql::TableType type) const
return res; return res;
} }
static QSqlIndex qGetTableInfo(QSqlQuery &q, const QString &tableName, bool onlyPIndex = false) static QSqlIndex qGetTableInfo(QSqlQuery &q, const QString &tableName, bool onlyPIndex = false) {
{
QString schema; QString schema;
QString table(tableName); QString table(tableName);
int indexOfSeparator = tableName.indexOf(QLatin1Char('.')); int indexOfSeparator = tableName.indexOf(QLatin1Char('.'));
@ -720,8 +678,7 @@ static QSqlIndex qGetTableInfo(QSqlQuery &q, const QString &tableName, bool only
return ind; return ind;
} }
QSqlIndex QSQLiteDriver::primaryIndex(const QString &tblname) const QSqlIndex QSQLiteDriver::primaryIndex(const QString &tblname) const {
{
if (!isOpen()) if (!isOpen())
return QSqlIndex(); return QSqlIndex();
@ -734,8 +691,7 @@ QSqlIndex QSQLiteDriver::primaryIndex(const QString &tblname) const
return qGetTableInfo(q, table, true); return qGetTableInfo(q, table, true);
} }
QSqlRecord QSQLiteDriver::record(const QString &tbl) const QSqlRecord QSQLiteDriver::record(const QString &tbl) const {
{
if (!isOpen()) if (!isOpen())
return QSqlRecord(); return QSqlRecord();
@ -748,13 +704,11 @@ QSqlRecord QSQLiteDriver::record(const QString &tbl) const
return qGetTableInfo(q, table); return qGetTableInfo(q, table);
} }
QVariant QSQLiteDriver::handle() const QVariant QSQLiteDriver::handle() const {
{
return QVariant::fromValue(d->access); return QVariant::fromValue(d->access);
} }
QString QSQLiteDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const QString QSQLiteDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const {
{
Q_UNUSED(type); Q_UNUSED(type);
return _q_escapeIdentifier(identifier); return _q_escapeIdentifier(identifier);
} }

View File

@ -42,8 +42,17 @@
#ifndef QSQL_SQLITE_H #ifndef QSQL_SQLITE_H
#define QSQL_SQLITE_H #define QSQL_SQLITE_H
#include <QtSql/qsqldriver.h> #include <QtGlobal>
#include <QtSql/qsqlresult.h> #include <QObject>
#include <QVariant>
#include <QString>
#include <QStringList>
#include <QSql>
#include <QSqlDriver>
#include <QSqlIndex>
#include <QSqlRecord>
#include <QSqlResult>
#include "sqlcachedresult.h" #include "sqlcachedresult.h"
struct sqlite3; struct sqlite3;
@ -57,16 +66,16 @@ struct sqlite3;
QT_BEGIN_HEADER QT_BEGIN_HEADER
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QSQLiteDriver;
class QSQLiteDriverPrivate; class QSQLiteDriverPrivate;
class QSQLiteResultPrivate; class QSQLiteResultPrivate;
class QSQLiteDriver;
class QSQLiteResult : public ClementineSqlCachedResult class QSQLiteResult : public ClementineSqlCachedResult
{ {
friend class QSQLiteDriver; friend class QSQLiteDriver;
friend class QSQLiteResultPrivate; friend class QSQLiteResultPrivate;
public: public:
explicit QSQLiteResult(const QSQLiteDriver* db); explicit QSQLiteResult(const QSQLiteDriver *db);
~QSQLiteResult(); ~QSQLiteResult();
QVariant handle() const; QVariant handle() const;
@ -83,7 +92,7 @@ protected:
void virtual_hook(int id, void *data); void virtual_hook(int id, void *data);
private: private:
QSQLiteResultPrivate* d; QSQLiteResultPrivate *d;
}; };
class Q_EXPORT_SQLDRIVER_SQLITE QSQLiteDriver : public QSqlDriver class Q_EXPORT_SQLDRIVER_SQLITE QSQLiteDriver : public QSqlDriver
@ -95,12 +104,7 @@ public:
explicit QSQLiteDriver(sqlite3 *connection, QObject *parent = 0); explicit QSQLiteDriver(sqlite3 *connection, QObject *parent = 0);
~QSQLiteDriver(); ~QSQLiteDriver();
bool hasFeature(DriverFeature f) const; bool hasFeature(DriverFeature f) const;
bool open(const QString & db, bool open(const QString & db, const QString & user, const QString & password, const QString & host, int port, const QString & connOpts);
const QString & user,
const QString & password,
const QString & host,
int port,
const QString & connOpts);
void close(); void close();
QSqlResult *createResult() const; QSqlResult *createResult() const;
bool beginTransaction(); bool beginTransaction();
@ -114,7 +118,7 @@ public:
QString escapeIdentifier(const QString &identifier, IdentifierType) const; QString escapeIdentifier(const QString &identifier, IdentifierType) const;
private: private:
QSQLiteDriverPrivate* d; QSQLiteDriverPrivate *d;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -31,8 +31,13 @@
** **
****************************************************************************/ ****************************************************************************/
#include <QString>
#include <QSqlDriver>
#include "smain.h" #include "smain.h"
#include "qsql_sqlite.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
QSQLiteDriverPlugin::QSQLiteDriverPlugin() QSQLiteDriverPlugin::QSQLiteDriverPlugin()
@ -40,8 +45,7 @@ QSQLiteDriverPlugin::QSQLiteDriverPlugin()
{ {
} }
QSqlDriver* QSQLiteDriverPlugin::create(const QString &name) QSqlDriver *QSQLiteDriverPlugin::create(const QString &name) {
{
if (name == QLatin1String("QSQLITE")) { if (name == QLatin1String("QSQLITE")) {
QSQLiteDriver* driver = new QSQLiteDriver(); QSQLiteDriver* driver = new QSQLiteDriver();
return driver; return driver;

View File

@ -31,9 +31,11 @@
** **
****************************************************************************/ ****************************************************************************/
#include <qsqldriverplugin.h> #include <QtGlobal>
#include <qstringlist.h> #include <QObject>
#include "qsql_sqlite.h" #include <QString>
#include <QSqlDriver>
#include <QSqlDriverPlugin>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -45,7 +47,7 @@ class QSQLiteDriverPlugin : public QSqlDriverPlugin
public: public:
QSQLiteDriverPlugin(); QSQLiteDriverPlugin();
QSqlDriver* create(const QString &); QSqlDriver *create(const QString &);
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -39,9 +39,9 @@
** **
****************************************************************************/ ****************************************************************************/
#include <qvariant.h> #include <QVariant>
#include <qdatetime.h> #include <QSqlResult>
#include <qvector.h> #include <QSqlDriver>
#include "sqlcachedresult.h" #include "sqlcachedresult.h"
@ -61,8 +61,7 @@ QT_BEGIN_NAMESPACE
static const uint initial_cache_size = 128; static const uint initial_cache_size = 128;
class ClementineSqlCachedResultPrivate class ClementineSqlCachedResultPrivate {
{
public: public:
ClementineSqlCachedResultPrivate(); ClementineSqlCachedResultPrivate();
bool canSeek(int i) const; bool canSeek(int i) const;
@ -84,8 +83,7 @@ ClementineSqlCachedResultPrivate::ClementineSqlCachedResultPrivate():
{ {
} }
void ClementineSqlCachedResultPrivate::cleanup() void ClementineSqlCachedResultPrivate::cleanup() {
{
cache.clear(); cache.clear();
forwardOnly = false; forwardOnly = false;
atEnd = false; atEnd = false;
@ -93,8 +91,7 @@ void ClementineSqlCachedResultPrivate::cleanup()
rowCacheEnd = 0; rowCacheEnd = 0;
} }
void ClementineSqlCachedResultPrivate::init(int count, bool fo) void ClementineSqlCachedResultPrivate::init(int count, bool fo) {
{
Q_ASSERT(count); Q_ASSERT(count);
cleanup(); cleanup();
forwardOnly = fo; forwardOnly = fo;
@ -102,13 +99,13 @@ void ClementineSqlCachedResultPrivate::init(int count, bool fo)
if (fo) { if (fo) {
cache.resize(count); cache.resize(count);
rowCacheEnd = count; rowCacheEnd = count;
} else { }
else {
cache.resize(initial_cache_size * count); cache.resize(initial_cache_size * count);
} }
} }
int ClementineSqlCachedResultPrivate::nextIndex() int ClementineSqlCachedResultPrivate::nextIndex() {
{
if (forwardOnly) if (forwardOnly)
return 0; return 0;
int newIdx = rowCacheEnd; int newIdx = rowCacheEnd;
@ -119,15 +116,13 @@ int ClementineSqlCachedResultPrivate::nextIndex()
return newIdx; return newIdx;
} }
bool ClementineSqlCachedResultPrivate::canSeek(int i) const bool ClementineSqlCachedResultPrivate::canSeek(int i) const {
{
if (forwardOnly || i < 0) if (forwardOnly || i < 0)
return false; return false;
return rowCacheEnd >= (i + 1) * colCount; return rowCacheEnd >= (i + 1) * colCount;
} }
void ClementineSqlCachedResultPrivate::revertLast() void ClementineSqlCachedResultPrivate::revertLast() {
{
if (forwardOnly) if (forwardOnly)
return; return;
rowCacheEnd -= colCount; rowCacheEnd -= colCount;
@ -142,23 +137,19 @@ inline int ClementineSqlCachedResultPrivate::cacheCount() const
////////////// //////////////
ClementineSqlCachedResult::ClementineSqlCachedResult(const QSqlDriver * db): QSqlResult (db) ClementineSqlCachedResult::ClementineSqlCachedResult(const QSqlDriver * db): QSqlResult (db) {
{
d = new ClementineSqlCachedResultPrivate(); d = new ClementineSqlCachedResultPrivate();
} }
ClementineSqlCachedResult::~ClementineSqlCachedResult() ClementineSqlCachedResult::~ClementineSqlCachedResult() {
{
delete d; delete d;
} }
void ClementineSqlCachedResult::init(int colCount) void ClementineSqlCachedResult::init(int colCount) {
{
d->init(colCount, isForwardOnly()); d->init(colCount, isForwardOnly());
} }
bool ClementineSqlCachedResult::fetch(int i) bool ClementineSqlCachedResult::fetch(int i) {
{
if ((!isActive()) || (i < 0)) if ((!isActive()) || (i < 0))
return false; return false;
if (at() == i) if (at() == i)
@ -195,8 +186,7 @@ bool ClementineSqlCachedResult::fetch(int i)
return true; return true;
} }
bool ClementineSqlCachedResult::fetchNext() bool ClementineSqlCachedResult::fetchNext() {
{
if (d->canSeek(at() + 1)) { if (d->canSeek(at() + 1)) {
setAt(at() + 1); setAt(at() + 1);
return true; return true;
@ -204,13 +194,11 @@ bool ClementineSqlCachedResult::fetchNext()
return cacheNext(); return cacheNext();
} }
bool ClementineSqlCachedResult::fetchPrevious() bool ClementineSqlCachedResult::fetchPrevious() {
{
return fetch(at() - 1); return fetch(at() - 1);
} }
bool ClementineSqlCachedResult::fetchFirst() bool ClementineSqlCachedResult::fetchFirst() {
{
if (d->forwardOnly && at() != QSql::BeforeFirstRow) { if (d->forwardOnly && at() != QSql::BeforeFirstRow) {
return false; return false;
} }
@ -221,8 +209,7 @@ bool ClementineSqlCachedResult::fetchFirst()
return cacheNext(); return cacheNext();
} }
bool ClementineSqlCachedResult::fetchLast() bool ClementineSqlCachedResult::fetchLast() {
{
if (d->atEnd) { if (d->atEnd) {
if (d->forwardOnly) if (d->forwardOnly)
return false; return false;
@ -236,13 +223,13 @@ bool ClementineSqlCachedResult::fetchLast()
if (d->forwardOnly && at() == QSql::AfterLastRow) { if (d->forwardOnly && at() == QSql::AfterLastRow) {
setAt(i); setAt(i);
return true; return true;
} else { }
else {
return fetch(i); return fetch(i);
} }
} }
QVariant ClementineSqlCachedResult::data(int i) QVariant ClementineSqlCachedResult::data(int i) {
{
int idx = d->forwardOnly ? i : at() * d->colCount + i; int idx = d->forwardOnly ? i : at() * d->colCount + i;
if (i >= d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd) if (i >= d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
return QVariant(); return QVariant();
@ -250,8 +237,7 @@ QVariant ClementineSqlCachedResult::data(int i)
return d->cache.at(idx); return d->cache.at(idx);
} }
bool ClementineSqlCachedResult::isNull(int i) bool ClementineSqlCachedResult::isNull(int i) {
{
int idx = d->forwardOnly ? i : at() * d->colCount + i; int idx = d->forwardOnly ? i : at() * d->colCount + i;
if (i > d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd) if (i > d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
return true; return true;
@ -259,22 +245,20 @@ bool ClementineSqlCachedResult::isNull(int i)
return d->cache.at(idx).isNull(); return d->cache.at(idx).isNull();
} }
void ClementineSqlCachedResult::cleanup() void ClementineSqlCachedResult::cleanup() {
{
setAt(QSql::BeforeFirstRow); setAt(QSql::BeforeFirstRow);
setActive(false); setActive(false);
d->cleanup(); d->cleanup();
} }
void ClementineSqlCachedResult::clearValues() void ClementineSqlCachedResult::clearValues() {
{
setAt(QSql::BeforeFirstRow); setAt(QSql::BeforeFirstRow);
d->rowCacheEnd = 0; d->rowCacheEnd = 0;
d->atEnd = false; d->atEnd = false;
} }
bool ClementineSqlCachedResult::cacheNext() bool ClementineSqlCachedResult::cacheNext() {
{
if (d->atEnd) if (d->atEnd)
return false; return false;
@ -292,28 +276,23 @@ bool ClementineSqlCachedResult::cacheNext()
return true; return true;
} }
int ClementineSqlCachedResult::colCount() const int ClementineSqlCachedResult::colCount() const {
{
return d->colCount; return d->colCount;
} }
ClementineSqlCachedResult::ValueCache &ClementineSqlCachedResult::cache() ClementineSqlCachedResult::ValueCache &ClementineSqlCachedResult::cache() {
{
return d->cache; return d->cache;
} }
void ClementineSqlCachedResult::virtual_hook(int id, void *data) void ClementineSqlCachedResult::virtual_hook(int id, void *data) {
{
QSqlResult::virtual_hook(id, data); QSqlResult::virtual_hook(id, data);
} }
void ClementineSqlCachedResult::detachFromResultSet() void ClementineSqlCachedResult::detachFromResultSet() {
{
cleanup(); cleanup();
} }
void ClementineSqlCachedResult::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy policy) void ClementineSqlCachedResult::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy policy) {
{
QSqlResult::setNumericalPrecisionPolicy(policy); QSqlResult::setNumericalPrecisionPolicy(policy);
cleanup(); cleanup();
} }

View File

@ -53,13 +53,15 @@
// We mean it. // We mean it.
// //
#include <QtSql/qsqlresult.h> #include <QtGlobal>
#include <QVariant>
#include <QVector>
#include <QSql>
#include <QSqlDriver>
#include <QSqlResult>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QVariant;
template <typename T> class QVector;
class ClementineSqlCachedResultPrivate; class ClementineSqlCachedResultPrivate;
class ClementineSqlCachedResult: public QSqlResult class ClementineSqlCachedResult: public QSqlResult

View File

@ -39,14 +39,27 @@
#include "qtlocalpeer.h" #include "qtlocalpeer.h"
#include <QCoreApplication>
#include <QTime>
#include <QDataStream>
#include <unistd.h> #include <unistd.h>
#include <QtGlobal>
#include <QCoreApplication>
#include <QObject>
#include <QAbstractSocket>
#include <QByteArray>
#include <QChar>
#include <QDataStream>
#include <QDir>
#include <QFile>
#include <QIODevice>
#include <QLocalServer>
#include <QLocalSocket>
#include <QRegExp>
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
#include <QLibrary>
#include <qt_windows.h> #include <qt_windows.h>
#include <QLibrary>
typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*);
static PProcessIdToSessionId pProcessIdToSessionId = 0; static PProcessIdToSessionId pProcessIdToSessionId = 0;
#endif #endif

View File

@ -40,9 +40,9 @@
#ifndef QTLOCALPEER_H #ifndef QTLOCALPEER_H
#define QTLOCALPEER_H #define QTLOCALPEER_H
#include <QObject>
#include <QString>
#include <QLocalServer> #include <QLocalServer>
#include <QLocalSocket>
#include <QDir>
#include "qtlockedfile.h" #include "qtlockedfile.h"

View File

@ -37,6 +37,9 @@
** **
****************************************************************************/ ****************************************************************************/
#include <QFile>
#include <QString>
#include "qtlockedfile.h" #include "qtlockedfile.h"
/*! /*!

View File

@ -42,7 +42,7 @@
#include <QFile> #include <QFile>
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include <QtCore/QVector> #include <QVector>
#endif #endif
#if defined(Q_WS_WIN) || defined(Q_OS_WIN) #if defined(Q_WS_WIN) || defined(Q_OS_WIN)

View File

@ -37,9 +37,10 @@
** **
****************************************************************************/ ****************************************************************************/
#include <QFileInfo>
#include "qtlockedfile.h" #include "qtlockedfile.h"
#include <qt_windows.h> #include <qt_windows.h>
#include <QFileInfo>
#define MUTEX_PREFIX "QtLockedFile mutex " #define MUTEX_PREFIX "QtLockedFile mutex "
// Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS // Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS

View File

@ -39,8 +39,12 @@
#include "qtsingleapplication.h" #include "qtsingleapplication.h"
#include "qtlocalpeer.h"
#include <QApplication>
#include <QWidget> #include <QWidget>
#include <QString>
#include "qtlocalpeer.h"
/*! /*!
\class QtSingleApplication qtsingleapplication.h \class QtSingleApplication qtsingleapplication.h

View File

@ -40,8 +40,11 @@
#ifndef QTSINGLEAPPLICATION_H #ifndef QTSINGLEAPPLICATION_H
#define QTSINGLEAPPLICATION_H #define QTSINGLEAPPLICATION_H
#include <QtGlobal>
#include <QObject>
#include <QWidget>
#include <QApplication> #include <QApplication>
#include <QString>
class QtLocalPeer; class QtLocalPeer;
#if defined(Q_WS_WIN) || defined(Q_OS_WIN32) #if defined(Q_WS_WIN) || defined(Q_OS_WIN32)

View File

@ -37,6 +37,9 @@
** **
****************************************************************************/ ****************************************************************************/
#include <QCoreApplication>
#include <QString>
#include <QFlags>
#include "qtsinglecoreapplication.h" #include "qtsinglecoreapplication.h"
#include "qtlocalpeer.h" #include "qtlocalpeer.h"
@ -70,8 +73,7 @@
*/ */
QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv) QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv)
: QCoreApplication(argc, argv) : QCoreApplication(argc, argv) {
{
peer = new QtLocalPeer(this); peer = new QtLocalPeer(this);
connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
} }
@ -83,8 +85,7 @@ QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv)
QCoreAppliation constructor. QCoreAppliation constructor.
*/ */
QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv) QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv)
: QCoreApplication(argc, argv) : QCoreApplication(argc, argv) {
{
peer = new QtLocalPeer(this, appId); peer = new QtLocalPeer(this, appId);
connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
} }
@ -101,8 +102,7 @@ QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc
\sa sendMessage() \sa sendMessage()
*/ */
bool QtSingleCoreApplication::isRunning() bool QtSingleCoreApplication::isRunning() {
{
return peer->isClient(); return peer->isClient();
} }
@ -121,8 +121,7 @@ bool QtSingleCoreApplication::isRunning()
\sa isRunning(), messageReceived() \sa isRunning(), messageReceived()
*/ */
bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout) bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout) {
{
return peer->sendMessage(message, timeout); return peer->sendMessage(message, timeout);
} }
@ -132,8 +131,7 @@ bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout)
identifier will be regarded as instances of the same application. identifier will be regarded as instances of the same application.
*/ */
QString QtSingleCoreApplication::id() const QString QtSingleCoreApplication::id() const {
{
return peer->applicationId(); return peer->applicationId();
} }

View File

@ -41,6 +41,8 @@
#define QTSINGLECOREAPPLICATION_H #define QTSINGLECOREAPPLICATION_H
#include <QCoreApplication> #include <QCoreApplication>
#include <QObject>
#include <QString>
class QtLocalPeer; class QtLocalPeer;

View File

@ -29,10 +29,14 @@
** <http://libqxt.org> <foundation@libqxt.org> ** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/ *****************************************************************************/
#include "qxtglobalshortcut_p.h" #include <QtGlobal>
#include <QAbstractEventDispatcher> #include <QAbstractEventDispatcher>
#include <QHash>
#include <QPair>
#include <QtDebug> #include <QtDebug>
#include "qxtglobalshortcut_p.h"
bool QxtGlobalShortcutPrivate::error = false; bool QxtGlobalShortcutPrivate::error = false;
#ifndef Q_WS_MAC #ifndef Q_WS_MAC
int QxtGlobalShortcutPrivate::ref = 0; int QxtGlobalShortcutPrivate::ref = 0;

View File

@ -31,9 +31,12 @@
#define QXTGLOBALSHORTCUT_H #define QXTGLOBALSHORTCUT_H
#include "qxtglobal.h"
#include <QObject> #include <QObject>
#include <QKeySequence> #include <QKeySequence>
#include <QString>
#include "qxtglobal.h"
class QxtGlobalShortcutPrivate; class QxtGlobalShortcutPrivate;
class QXT_GUI_EXPORT QxtGlobalShortcut : public QObject class QXT_GUI_EXPORT QxtGlobalShortcut : public QObject

View File

@ -32,12 +32,21 @@
#if QT_VERSION < QT_VERSION_CHECK(5,0,0) #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
# include <QX11Info> # include <QX11Info>
#else #else
# include <QApplication>
# include <qpa/qplatformnativeinterface.h> # include <qpa/qplatformnativeinterface.h>
# include <xcb/xcb.h> # include <xcb/xcb.h>
# include <QApplication>
#endif #endif
#include <QVector> #include <X11/X.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <xcb/xproto.h>
#include <QtGlobal>
#include <QByteArray>
#include <QGuiApplication>
#include <QKeySequence>
#include <QString>
#include <QVector>
#include "keymapper_x11.h" #include "keymapper_x11.h"
namespace { namespace {

View File

@ -31,8 +31,9 @@
* *
*/ */
#include <string.h> /* memcpy()/memset() or bcopy()/bzero() */ #include <string.h>
#include <assert.h> /* assert() */ #include <assert.h>
#include "sha2.h" #include "sha2.h"
/* /*

View File

@ -35,6 +35,8 @@
#ifndef __STRAWBERRY_SHA2_H__ #ifndef __STRAWBERRY_SHA2_H__
#define __STRAWBERRY_SHA2_H__ #define __STRAWBERRY_SHA2_H__
#include <stddef.h>
/* /*
* Import u_intXX_t size_t type definitions from system headers. You * Import u_intXX_t size_t type definitions from system headers. You
* may need to change this, or define these things yourself in this * may need to change this, or define these things yourself in this

View File

@ -96,21 +96,19 @@ pkg_check_modules(GSTREAMER_APP gstreamer-app-1.0)
pkg_check_modules(GSTREAMER_AUDIO gstreamer-audio-1.0) pkg_check_modules(GSTREAMER_AUDIO gstreamer-audio-1.0)
pkg_check_modules(GSTREAMER_TAG gstreamer-tag-1.0) pkg_check_modules(GSTREAMER_TAG gstreamer-tag-1.0)
pkg_check_modules(GSTREAMER_PBUTILS gstreamer-pbutils-1.0) pkg_check_modules(GSTREAMER_PBUTILS gstreamer-pbutils-1.0)
#pkg_check_modules(GSTREAMER_QTGLIB Qt5GLib-2.0)
#pkg_check_modules(GSTREAMER_QTGST Qt5GStreamer-1.0)
#pkg_check_modules(GSTREAMER_QTGSTUI Qt5GStreamerUi-1.0)
#pkg_check_modules(GSTREAMER_QTGSTUTILS Qt5GStreamerUtils-1.0)
pkg_check_modules(LIBXINE libxine) pkg_check_modules(LIBXINE libxine)
pkg_check_modules(LIBVLC libvlc) pkg_check_modules(LIBVLC libvlc)
pkg_check_modules(PHONON phonon4qt5) pkg_check_modules(PHONON phonon4qt5)
pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92) pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92)
pkg_check_modules(LIBMTP libmtp>=1.0) pkg_check_modules(LIBMTP libmtp>=1.0)
pkg_check_modules(LIBPULSE libpulse) pkg_check_modules(LIBPULSE libpulse)
pkg_check_modules(LIBXML libxml-2.0) pkg_check_modules(LIBXML libxml-2.0)
pkg_check_modules(TAGLIB REQUIRED taglib>=1.8) pkg_check_modules(TAGLIB REQUIRED taglib>=1.8)
pkg_check_modules(SQLITE REQUIRED sqlite3>=3.7) pkg_check_modules(SQLITE REQUIRED sqlite3>=3.7)
pkg_check_modules(LIBGLU REQUIRED glu) pkg_check_modules(LIBGLU REQUIRED glu)
pkg_check_modules(IMOBILEDEVICE libimobiledevice-1.0)
pkg_check_modules(USBMUXD libusbmuxd)
pkg_check_modules(PLIST libplist)
find_library(PROTOBUF_STATIC_LIBRARY libprotobuf.a libprotobuf) find_library(PROTOBUF_STATIC_LIBRARY libprotobuf.a libprotobuf)
@ -178,10 +176,6 @@ include_directories(${GSTREAMER_AUDIO_INCLUDE_DIRS})
include_directories(${GSTREAMER_BASE_INCLUDE_DIRS}) include_directories(${GSTREAMER_BASE_INCLUDE_DIRS})
include_directories(${GSTREAMER_TAG_INCLUDE_DIRS}) include_directories(${GSTREAMER_TAG_INCLUDE_DIRS})
include_directories(${GSTREAMER_PBUTILS_INCLUDE_DIRS}) include_directories(${GSTREAMER_PBUTILS_INCLUDE_DIRS})
#include_directories(${GSTREAMER_QTGLIB})
#include_directories(${GSTREAMER_QTGST})
#include_directories(${GSTREAMER_QTGSTUI})
#include_directories(${GSTREAMER_QTGSTUTILS})
if (WIN32) if (WIN32)
# RC compiler # RC compiler
@ -194,9 +188,30 @@ add_definitions(-DQT_NO_CAST_TO_ASCII -DQT_STRICT_ITERATORS)
# Optional bits # Optional bits
if(WIN32) if(WIN32)
option(ENABLE_WIN32_CONSOLE "Show the windows console even outside Debug mode" OFF) option(ENABLE_WIN32_CONSOLE "Show the windows console even outside Debug mode" ON)
endif(WIN32) endif(WIN32)
optional_component(GSTREAMER ON "Engine: GStreamer backend"
DEPENDS "gstreamer-1.0" GSTREAMER_FOUND
DEPENDS "gstreamer-base-1.0" GSTREAMER_BASE_FOUND
DEPENDS "gstreamer-app-1.0" GSTREAMER_APP_FOUND
DEPENDS "gstreamer-audio-1.0" GSTREAMER_AUDIO_FOUND
DEPENDS "gstreamer-tag-1.0" GSTREAMER_TAG_FOUND
DEPENDS "gstreamer-pbutils-1.0" GSTREAMER_PBUTILS_FOUND
)
optional_component(XINE ON "Engine: Xine backend"
DEPENDS "libxine" LIBXINE_FOUND
)
optional_component(VLC ON "Engine: VLC backend"
DEPENDS "libvlc" LIBVLC_FOUND
)
optional_component(PHONON OFF "Engine: Phonon backend"
DEPENDS "phonon4qt5" PHONON4QT5_FOUND
)
optional_component(AUDIOCD ON "Devices: Audio CD support" optional_component(AUDIOCD ON "Devices: Audio CD support"
DEPENDS "libcdio" CDIO_FOUND DEPENDS "libcdio" CDIO_FOUND
) )
@ -262,7 +277,7 @@ else(WIN32)
endif(WIN32) endif(WIN32)
# Remove GLU and GL from the link line - they're not really required and don't exist on my mingw toolchain # Remove GLU and GL from the link line - they're not really required and don't exist on my mingw toolchain
#list(REMOVE_ITEM QT_LIBRARIES "-lGLU -lGL") list(REMOVE_ITEM QT_LIBRARIES "-lGLU -lGL")
# QSqlLiteDriver: # QSqlLiteDriver:
# We do this because we can't guarantee that the driver shipped with Qt exposes the raw sqlite3 functions required for FTS support. # We do this because we can't guarantee that the driver shipped with Qt exposes the raw sqlite3 functions required for FTS support.
@ -323,6 +338,10 @@ endif()
# Qocoa # Qocoa
add_subdirectory(3rdparty/qocoa) add_subdirectory(3rdparty/qocoa)
#if(IMOBILEDEVICE_FOUND AND PLIST_FOUND)
#add_subdirectory(ext/gstafc)
#endif(IMOBILEDEVICE_FOUND AND PLIST_FOUND)
# Subdirectories # Subdirectories
add_subdirectory(src) add_subdirectory(src)
if (WIN32) if (WIN32)

5
TODO
View File

@ -1,5 +0,0 @@
Strawberry Music Player
=======================
TODO
- Finalize / Improve status/context

View File

@ -62,8 +62,7 @@ function(optional_component name default description)
elseif(${testing_deps}) elseif(${testing_deps})
string(REPLACE " " ";" arglist "${arg}") string(REPLACE " " ";" arglist "${arg}")
if(${arglist}) if(${arglist})
# We have to do this instead of if(NOT ${arg}) so that tests may contain # We have to do this instead of if(NOT ${arg}) so that tests may contain "NOT" themselves.
# "NOT" themselves.
else() else()
list(APPEND missing_deps "${current_dep_name}") list(APPEND missing_deps "${current_dep_name}")
set(testing_deps FALSE) set(testing_deps FALSE)

View File

@ -15,6 +15,11 @@
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <QObject>
#include <QList>
#include <QTimer>
#include <QGenericArgument>
#include "closure.h" #include "closure.h"
#include "core/timeconstants.h" #include "core/timeconstants.h"

View File

@ -18,15 +18,20 @@
#ifndef CLOSURE_H #ifndef CLOSURE_H
#define CLOSURE_H #define CLOSURE_H
#include <chrono>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <chrono>
#include <QtGlobal>
#include <QObject>
#include <QFuture> #include <QFuture>
#include <QFutureWatcher> #include <QFutureWatcher>
#include <QMetaObject>
#include <QMetaMethod> #include <QMetaMethod>
#include <QObject>
#include <QSharedPointer> #include <QSharedPointer>
#include <QByteArray>
#include <QString>
#include <QList>
#include <QTimer> #include <QTimer>
namespace _detail { namespace _detail {
@ -40,11 +45,11 @@ class ClosureBase {
virtual void Invoke() = 0; virtual void Invoke() = 0;
// Tests only. // Tests only.
ObjectHelper* helper() const; ObjectHelper *helper() const;
protected: protected:
explicit ClosureBase(ObjectHelper*); explicit ClosureBase(ObjectHelper*);
ObjectHelper* helper_; ObjectHelper *helper_;
private: private:
Q_DISABLE_COPY(ClosureBase); Q_DISABLE_COPY(ClosureBase);
@ -56,7 +61,7 @@ class ClosureBase {
class ObjectHelper : public QObject { class ObjectHelper : public QObject {
Q_OBJECT Q_OBJECT
public: public:
ObjectHelper(QObject* parent, const char* signal, ClosureBase* closure); ObjectHelper(QObject *parent, const char *signal, ClosureBase *closure);
private slots: private slots:
void Invoked(); void Invoked();
@ -72,12 +77,12 @@ class ObjectHelper : public QObject {
void Unpack(QList<QGenericArgument>*); void Unpack(QList<QGenericArgument>*);
template <typename Arg> template <typename Arg>
void Unpack(QList<QGenericArgument>* list, const Arg& arg) { void Unpack(QList<QGenericArgument> *list, const Arg &arg) {
list->append(Q_ARG(Arg, arg)); list->append(Q_ARG(Arg, arg));
} }
template <typename Head, typename... Tail> template <typename Head, typename... Tail>
void Unpack(QList<QGenericArgument>* list, const Head& head, const Tail&... tail) { void Unpack(QList<QGenericArgument> *list, const Head &head, const Tail&... tail) {
Unpack(list, head); Unpack(list, head);
Unpack(list, tail...); Unpack(list, tail...);
} }
@ -86,16 +91,16 @@ template <typename... Args>
class Closure : public ClosureBase { class Closure : public ClosureBase {
public: public:
Closure( Closure(
QObject* sender, QObject *sender,
const char* signal, const char *signal,
QObject* receiver, QObject *receiver,
const char* slot, const char *slot,
const Args&... args) const Args&... args)
: ClosureBase(new ObjectHelper(sender, signal, this)), : ClosureBase(new ObjectHelper(sender, signal, this)),
// std::bind is the easiest way to store an argument list. // std::bind is the easiest way to store an argument list.
function_(std::bind(&Closure<Args...>::Call, this, args...)), function_(std::bind(&Closure<Args...>::Call, this, args...)),
receiver_(receiver) { receiver_(receiver) {
const QMetaObject* meta_receiver = receiver->metaObject(); const QMetaObject *meta_receiver = receiver->metaObject();
QByteArray normalised_slot = QMetaObject::normalizedSignature(slot + 1); QByteArray normalised_slot = QMetaObject::normalizedSignature(slot + 1);
const int index = meta_receiver->indexOfSlot(normalised_slot.constData()); const int index = meta_receiver->indexOfSlot(normalised_slot.constData());
Q_ASSERT(index != -1); Q_ASSERT(index != -1);
@ -127,7 +132,7 @@ class Closure : public ClosureBase {
} }
std::function<void()> function_; std::function<void()> function_;
QObject* receiver_; QObject *receiver_;
QMetaMethod slot_; QMetaMethod slot_;
}; };
@ -136,9 +141,9 @@ class SharedClosure : public Closure<Args...> {
public: public:
SharedClosure( SharedClosure(
QSharedPointer<T> sender, QSharedPointer<T> sender,
const char* signal, const char *signal,
QObject* receiver, QObject *receiver,
const char* slot, const char *slot,
const Args&... args) const Args&... args)
: Closure<Args...>( : Closure<Args...>(
sender.data(), sender.data(),
@ -155,8 +160,7 @@ class SharedClosure : public Closure<Args...> {
class CallbackClosure : public ClosureBase { class CallbackClosure : public ClosureBase {
public: public:
CallbackClosure(QObject* sender, const char* signal, CallbackClosure(QObject *sender, const char *signal, std::function<void()> callback);
std::function<void()> callback);
virtual void Invoke(); virtual void Invoke();
@ -167,11 +171,11 @@ class CallbackClosure : public ClosureBase {
} // namespace _detail } // namespace _detail
template <typename... Args> template <typename... Args>
_detail::ClosureBase* NewClosure( _detail::ClosureBase *NewClosure(
QObject* sender, QObject *sender,
const char* signal, const char *signal,
QObject* receiver, QObject *receiver,
const char* slot, const char *slot,
const Args&... args) { const Args&... args) {
return new _detail::Closure<Args...>( return new _detail::Closure<Args...>(
sender, signal, receiver, slot, args...); sender, signal, receiver, slot, args...);
@ -179,64 +183,64 @@ _detail::ClosureBase* NewClosure(
// QSharedPointer variant // QSharedPointer variant
template <typename T, typename... Args> template <typename T, typename... Args>
_detail::ClosureBase* NewClosure( _detail::ClosureBase *NewClosure(
QSharedPointer<T> sender, QSharedPointer<T> sender,
const char* signal, const char *signal,
QObject* receiver, QObject *receiver,
const char* slot, const char *slot,
const Args&... args) { const Args&... args) {
return new _detail::SharedClosure<T, Args...>( return new _detail::SharedClosure<T, Args...>(
sender, signal, receiver, slot, args...); sender, signal, receiver, slot, args...);
} }
_detail::ClosureBase* NewClosure(QObject* sender, const char* signal, std::function<void()> callback); _detail::ClosureBase *NewClosure(QObject *sender, const char *signal, std::function<void()> callback);
template <typename... Args> template <typename... Args>
_detail::ClosureBase* NewClosure(QObject* sender, const char* signal, std::function<void(Args...)> callback, const Args&... args) { _detail::ClosureBase *NewClosure(QObject *sender, const char *signal, std::function<void(Args...)> callback, const Args&... args) {
return NewClosure(sender, signal, std::bind(callback, args...)); return NewClosure(sender, signal, std::bind(callback, args...));
} }
template <typename... Args> template <typename... Args>
_detail::ClosureBase* NewClosure( _detail::ClosureBase *NewClosure(
QObject* sender, QObject *sender,
const char* signal, const char *signal,
void (*callback)(Args...), void (*callback)(Args...),
const Args&... args) { const Args&... args) {
return NewClosure(sender, signal, std::bind(callback, args...)); return NewClosure(sender, signal, std::bind(callback, args...));
} }
template <typename T, typename Unused, typename... Args> template <typename T, typename Unused, typename... Args>
_detail::ClosureBase* NewClosure( _detail::ClosureBase *NewClosure(
QObject* sender, QObject *sender,
const char* signal, const char *signal,
T* receiver, Unused (T::*callback)(Args...), T *receiver, Unused (T::*callback)(Args...),
const Args&... args) { const Args&... args) {
return NewClosure(sender, signal, std::bind(callback, receiver, args...)); return NewClosure(sender, signal, std::bind(callback, receiver, args...));
} }
template <typename T, typename... Args> template <typename T, typename... Args>
_detail::ClosureBase* NewClosure(QFuture<T> future, QObject* receiver, const char* slot, const Args&... args) { _detail::ClosureBase *NewClosure(QFuture<T> future, QObject *receiver, const char *slot, const Args&... args) {
QFutureWatcher<T>* watcher = new QFutureWatcher<T>; QFutureWatcher<T> *watcher = new QFutureWatcher<T>;
watcher->setFuture(future); watcher->setFuture(future);
QObject::connect(watcher, SIGNAL(finished()), watcher, SLOT(deleteLater())); QObject::connect(watcher, SIGNAL(finished()), watcher, SLOT(deleteLater()));
return NewClosure(watcher, SIGNAL(finished()), receiver, slot, args...); return NewClosure(watcher, SIGNAL(finished()), receiver, slot, args...);
} }
template <typename T, typename F, typename... Args> template <typename T, typename F, typename... Args>
_detail::ClosureBase* NewClosure(QFuture<T> future, const F& callback, const Args&... args) { _detail::ClosureBase *NewClosure(QFuture<T> future, const F &callback, const Args&... args) {
QFutureWatcher<T>* watcher = new QFutureWatcher<T>; QFutureWatcher<T> *watcher = new QFutureWatcher<T>;
watcher->setFuture(future); watcher->setFuture(future);
QObject::connect(watcher, SIGNAL(finished()), watcher, SLOT(deleteLater())); QObject::connect(watcher, SIGNAL(finished()), watcher, SLOT(deleteLater()));
return NewClosure(watcher, SIGNAL(finished()), callback, args...); return NewClosure(watcher, SIGNAL(finished()), callback, args...);
} }
void DoAfter(QObject* receiver, const char* slot, int msec); void DoAfter(QObject *receiver, const char *slot, int msec);
void DoAfter(std::function<void()> callback, std::chrono::milliseconds msec); void DoAfter(std::function<void()> callback, std::chrono::milliseconds msec);
void DoInAMinuteOrSo(QObject* receiver, const char* slot); void DoInAMinuteOrSo(QObject *receiver, const char *slot);
template <typename R, typename P> template <typename R, typename P>
void DoAfter(std::function<void()> callback, std::chrono::duration<R, P> duration) { void DoAfter(std::function<void()> callback, std::chrono::duration<R, P> duration) {
QTimer* timer = new QTimer; QTimer *timer = new QTimer;
timer->setSingleShot(true); timer->setSingleShot(true);
NewClosure(timer, SIGNAL(timeout()), callback); NewClosure(timer, SIGNAL(timeout()), callback);
QObject::connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater())); QObject::connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater()));

View File

@ -55,9 +55,7 @@ class ThreadFunctorBase : public QFutureInterface<ReturnType>, public QRunnable
this->reportStarted(); this->reportStarted();
Q_ASSERT(thread_pool); Q_ASSERT(thread_pool);
QFuture<ReturnType> future = this->future(); QFuture<ReturnType> future = this->future();
thread_pool->start(this, 0 /* priority: currently we do not support thread_pool->start(this, 0 /* priority: currently we do not support changing the priority. Might be added later if needed */);
changing the priority. Might be added later
if needed */);
return future; return future;
} }
@ -67,8 +65,7 @@ class ThreadFunctorBase : public QFutureInterface<ReturnType>, public QRunnable
template <typename ReturnType, typename... Args> template <typename ReturnType, typename... Args>
class ThreadFunctor : public ThreadFunctorBase<ReturnType> { class ThreadFunctor : public ThreadFunctorBase<ReturnType> {
public: public:
ThreadFunctor(std::function<ReturnType (Args...)> function, ThreadFunctor(std::function<ReturnType (Args...)> function, Args... args)
Args... args)
: function_(std::bind(function, args...)) { : function_(std::bind(function, args...)) {
} }
@ -85,8 +82,7 @@ class ThreadFunctor : public ThreadFunctorBase<ReturnType> {
template <typename... Args> template <typename... Args>
class ThreadFunctor <void, Args...> : public ThreadFunctorBase<void> { class ThreadFunctor <void, Args...> : public ThreadFunctorBase<void> {
public: public:
ThreadFunctor(std::function<void (Args...)> function, ThreadFunctor(std::function<void (Args...)> function, Args... args)
Args... args)
: function_(std::bind(function, args...)) { : function_(std::bind(function, args...)) {
} }
@ -107,30 +103,20 @@ namespace ConcurrentRun {
// Empty argument form. // Empty argument form.
template <typename ReturnType> template <typename ReturnType>
QFuture<ReturnType> Run( QFuture<ReturnType> Run(QThreadPool* threadpool, std::function<ReturnType ()> function) {
QThreadPool* threadpool,
std::function<ReturnType ()> function) {
return (new ThreadFunctor<ReturnType>(function))->Start(threadpool); return (new ThreadFunctor<ReturnType>(function))->Start(threadpool);
} }
// Function object with arguments form. // Function object with arguments form.
template <typename ReturnType, typename... Args> template <typename ReturnType, typename... Args>
QFuture<ReturnType> Run( QFuture<ReturnType> Run(QThreadPool* threadpool, std::function<ReturnType (Args...)> function, const Args&... args) {
QThreadPool* threadpool, return (new ThreadFunctor<ReturnType, Args...>(function, args...))->Start(threadpool);
std::function<ReturnType (Args...)> function,
const Args&... args) {
return (new ThreadFunctor<ReturnType, Args...>(
function, args...))->Start(threadpool);
} }
// Support passing C function pointers instead of function objects. // Support passing C function pointers instead of function objects.
template <typename ReturnType, typename... Args> template <typename ReturnType, typename... Args>
QFuture<ReturnType> Run( QFuture<ReturnType> Run(QThreadPool* threadpool, ReturnType (*function) (Args...), const Args&... args) {
QThreadPool* threadpool, return Run(threadpool, std::function<ReturnType (Args...)>(function), args...);
ReturnType (*function) (Args...),
const Args&... args) {
return Run(
threadpool, std::function<ReturnType (Args...)>(function), args...);
} }
} }

View File

@ -14,21 +14,28 @@
limitations under the License. limitations under the License.
*/ */
#include <glib.h>
#include <iostream>
#include <QtGlobal> #include <QtGlobal>
#include <QByteArray>
#include <QList>
#include <QMap>
#include <QString>
#include <QStringList>
#include <QRegExp>
#include <QDateTime>
#include <QIODevice>
#include <QtMessageHandler>
#include <QMessageLogContext>
#include <cxxabi.h> #include <cxxabi.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#ifdef Q_OS_UNIX #ifdef Q_OS_UNIX
#include <execinfo.h> #include <execinfo.h>
#endif #endif
#include <QCoreApplication>
#include <QDateTime>
#include <QStringList>
#include <QtMessageHandler>
#include "logging.h" #include "logging.h"
namespace logging { namespace logging {
@ -75,7 +82,7 @@ static void MessageHandler(QtMsgType type, const QMessageLogContext &context, co
default: level = Level_Debug; break; default: level = Level_Debug; break;
} }
for (const QString& line : message.split('\n')) { for (const QString &line : message.split('\n')) {
CreateLogger(level, "unknown", -1) << line.toLocal8Bit().constData(); CreateLogger(level, "unknown", -1) << line.toLocal8Bit().constData();
} }

View File

@ -18,8 +18,10 @@
#define LOGGING_H #define LOGGING_H
#include <chrono> #include <chrono>
#include <string>
#include <QtGlobal>
#include <QIODevice>
#include <QString>
#include <QDebug> #include <QDebug>
#ifdef QT_NO_DEBUG_STREAM #ifdef QT_NO_DEBUG_STREAM
@ -61,30 +63,30 @@ enum Level {
void DumpStackTrace(); void DumpStackTrace();
QString ParsePrettyFunction(const char* pretty_function); QString ParsePrettyFunction(const char *pretty_function);
QDebug CreateLogger(Level level, const QString &class_name, int line); QDebug CreateLogger(Level level, const QString &class_name, int line);
QDebug CreateLoggerFatal(int line, const char* class_name); QDebug CreateLoggerFatal(int line, const char *class_name);
QDebug CreateLoggerError(int line, const char* class_name); QDebug CreateLoggerError(int line, const char *class_name);
#ifdef QT_NO_WARNING_OUTPUT #ifdef QT_NO_WARNING_OUTPUT
QNoDebug CreateLoggerWarning(int, const char*); QNoDebug CreateLoggerWarning(int, const char*);
#else #else
QDebug CreateLoggerWarning(int line, const char* class_name); QDebug CreateLoggerWarning(int line, const char *class_name);
#endif // QT_NO_WARNING_OUTPUT #endif // QT_NO_WARNING_OUTPUT
#ifdef QT_NO_DEBUG_OUTPUT #ifdef QT_NO_DEBUG_OUTPUT
QNoDebug CreateLoggerInfo(int, const char*); QNoDebug CreateLoggerInfo(int, const char*);
QNoDebug CreateLoggerDebug(int, const char*); QNoDebug CreateLoggerDebug(int, const char*);
#else #else
QDebug CreateLoggerInfo(int line, const char* class_name); QDebug CreateLoggerInfo(int line, const char *class_name);
QDebug CreateLoggerDebug(int line, const char* class_name); QDebug CreateLoggerDebug(int line, const char *class_name);
#endif // QT_NO_DEBUG_OUTPUT #endif // QT_NO_DEBUG_OUTPUT
void GLog(const char* domain, int level, const char* message, void* user_data); void GLog(const char *domain, int level, const char *message, void *user_data);
extern const char* kDefaultLogLevels; extern const char *kDefaultLogLevels;
} }
QDebug operator<<(QDebug debug, std::chrono::seconds secs); QDebug operator<<(QDebug debug, std::chrono::seconds secs);

View File

@ -15,11 +15,15 @@
*/ */
#include "messagehandler.h" #include "messagehandler.h"
#include "core/logging.h"
#include <QObject>
#include <QAbstractSocket> #include <QAbstractSocket>
#include <QLocalSocket>
#include <QDataStream> #include <QDataStream>
#include <QIODevice>
#include <QLocalSocket>
#include <QByteArray>
#include "core/logging.h"
_MessageHandlerBase::_MessageHandlerBase(QIODevice *device, QObject *parent) _MessageHandlerBase::_MessageHandlerBase(QIODevice *device, QObject *parent)
: QObject(parent), : QObject(parent),

View File

@ -18,21 +18,21 @@
#ifndef MESSAGEHANDLER_H #ifndef MESSAGEHANDLER_H
#define MESSAGEHANDLER_H #define MESSAGEHANDLER_H
#include <QBuffer> #include <string>
#include <QMap>
#include <QMutex> #include <QtGlobal>
#include <QMutexLocker>
#include <QObject> #include <QObject>
#include <QSemaphore>
#include <QThread> #include <QThread>
#include <QIODevice>
#include <QBuffer>
#include <QByteArray>
#include <QMap>
#include <QString>
#include <QLocalSocket>
#include <QAbstractSocket>
#include "core/logging.h"
#include "core/messagereply.h" #include "core/messagereply.h"
class QAbstractSocket;
class QIODevice;
class QLocalSocket;
#define QStringFromStdString(x) QString::fromUtf8(x.data(), x.size()) #define QStringFromStdString(x) QString::fromUtf8(x.data(), x.size())
#define DataCommaSizeFromQString(x) x.toUtf8().constData(), x.toUtf8().length() #define DataCommaSizeFromQString(x) x.toUtf8().constData(), x.toUtf8().length()
@ -45,27 +45,27 @@ class _MessageHandlerBase : public QObject {
public: public:
// device can be NULL, in which case you must call SetDevice before writing // device can be NULL, in which case you must call SetDevice before writing
// any messages. // any messages.
_MessageHandlerBase(QIODevice* device, QObject* parent); _MessageHandlerBase(QIODevice *device, QObject *parent);
void SetDevice(QIODevice* device); void SetDevice(QIODevice *device);
// After this is true, messages cannot be sent to the handler any more. // After this is true, messages cannot be sent to the handler any more.
bool is_device_closed() const { return is_device_closed_; } bool is_device_closed() const { return is_device_closed_; }
protected slots: protected slots:
void WriteMessage(const QByteArray& data); void WriteMessage(const QByteArray &data);
void DeviceReadyRead(); void DeviceReadyRead();
virtual void DeviceClosed(); virtual void DeviceClosed();
protected: protected:
virtual bool RawMessageArrived(const QByteArray& data) = 0; virtual bool RawMessageArrived(const QByteArray &data) = 0;
virtual void AbortAll() = 0; virtual void AbortAll() = 0;
protected: protected:
typedef bool (QAbstractSocket::*FlushAbstractSocket)(); typedef bool (QAbstractSocket::*FlushAbstractSocket)();
typedef bool (QLocalSocket::*FlushLocalSocket)(); typedef bool (QLocalSocket::*FlushLocalSocket)();
QIODevice* device_; QIODevice *device_;
FlushAbstractSocket flush_abstract_socket_; FlushAbstractSocket flush_abstract_socket_;
FlushLocalSocket flush_local_socket_; FlushLocalSocket flush_local_socket_;
@ -82,7 +82,7 @@ protected:
template <typename MT> template <typename MT>
class AbstractMessageHandler : public _MessageHandlerBase { class AbstractMessageHandler : public _MessageHandlerBase {
public: public:
AbstractMessageHandler(QIODevice* device, QObject* parent); AbstractMessageHandler(QIODevice *device, QObject *parent);
~AbstractMessageHandler() { AbortAll(); } ~AbstractMessageHandler() { AbortAll(); }
typedef MT MessageType; typedef MT MessageType;
@ -90,27 +90,27 @@ public:
// Serialises the message and writes it to the socket. This version MUST be // Serialises the message and writes it to the socket. This version MUST be
// called from the thread in which the AbstractMessageHandler was created. // called from the thread in which the AbstractMessageHandler was created.
void SendMessage(const MessageType& message); void SendMessage(const MessageType &message);
// Serialises the message and writes it to the socket. This version may be // Serialises the message and writes it to the socket. This version may be
// called from any thread. // called from any thread.
void SendMessageAsync(const MessageType& message); void SendMessageAsync(const MessageType &message);
// Sends the request message inside and takes ownership of the MessageReply. // Sends the request message inside and takes ownership of the MessageReply.
// The MessageReply's Finished() signal will be emitted when a reply arrives // The MessageReply's Finished() signal will be emitted when a reply arrives
// with the same ID. Must be called from my thread. // with the same ID. Must be called from my thread.
void SendRequest(ReplyType* reply); void SendRequest(ReplyType *reply);
// Sets the "id" field of reply to the same as the request, and sends the // Sets the "id" field of reply to the same as the request, and sends the
// reply on the socket. Used on the worker side. // reply on the socket. Used on the worker side.
void SendReply(const MessageType& request, MessageType* reply); void SendReply(const MessageType &request, MessageType *reply);
protected: protected:
// Called when a message is received from the socket. // Called when a message is received from the socket.
virtual void MessageArrived(const MessageType& message) {} virtual void MessageArrived(const MessageType &message) {}
// _MessageHandlerBase // _MessageHandlerBase
bool RawMessageArrived(const QByteArray& data); bool RawMessageArrived(const QByteArray &data);
void AbortAll(); void AbortAll();
private: private:
@ -118,12 +118,11 @@ private:
}; };
template <typename MT> template <typename MT>
AbstractMessageHandler<MT>::AbstractMessageHandler(QIODevice* device, AbstractMessageHandler<MT>::AbstractMessageHandler(QIODevice *device, QObject *parent)
QObject* parent)
: _MessageHandlerBase(device, parent) {} : _MessageHandlerBase(device, parent) {}
template <typename MT> template <typename MT>
void AbstractMessageHandler<MT>::SendMessage(const MessageType& message) { void AbstractMessageHandler<MT>::SendMessage(const MessageType &message) {
Q_ASSERT(QThread::currentThread() == thread()); Q_ASSERT(QThread::currentThread() == thread());
std::string data = message.SerializeAsString(); std::string data = message.SerializeAsString();
@ -131,33 +130,32 @@ void AbstractMessageHandler<MT>::SendMessage(const MessageType& message) {
} }
template <typename MT> template <typename MT>
void AbstractMessageHandler<MT>::SendMessageAsync(const MessageType& message) { void AbstractMessageHandler<MT>::SendMessageAsync(const MessageType &message) {
std::string data = message.SerializeAsString(); std::string data = message.SerializeAsString();
metaObject()->invokeMethod(this, "WriteMessage", Qt::QueuedConnection, metaObject()->invokeMethod(this, "WriteMessage", Qt::QueuedConnection,
Q_ARG(QByteArray, QByteArray(data.data(), data.size()))); Q_ARG(QByteArray, QByteArray(data.data(), data.size())));
} }
template<typename MT> template<typename MT>
void AbstractMessageHandler<MT>::SendRequest(ReplyType* reply) { void AbstractMessageHandler<MT>::SendRequest(ReplyType *reply) {
pending_replies_[reply->id()] = reply; pending_replies_[reply->id()] = reply;
SendMessage(reply->request_message()); SendMessage(reply->request_message());
} }
template<typename MT> template<typename MT>
void AbstractMessageHandler<MT>::SendReply(const MessageType& request, void AbstractMessageHandler<MT>::SendReply(const MessageType &request, MessageType *reply) {
MessageType* reply) {
reply->set_id(request.id()); reply->set_id(request.id());
SendMessage(*reply); SendMessage(*reply);
} }
template<typename MT> template<typename MT>
bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray& data) { bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray &data) {
MessageType message; MessageType message;
if (!message.ParseFromArray(data.constData(), data.size())) { if (!message.ParseFromArray(data.constData(), data.size())) {
return false; return false;
} }
ReplyType* reply = pending_replies_.take(message.id()); ReplyType *reply = pending_replies_.take(message.id());
if (reply) { if (reply) {
// This is a reply to a message that we created earlier. // This is a reply to a message that we created earlier.
@ -171,7 +169,7 @@ bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray& data) {
template<typename MT> template<typename MT>
void AbstractMessageHandler<MT>::AbortAll() { void AbstractMessageHandler<MT>::AbortAll() {
for (ReplyType* reply : pending_replies_) { for (ReplyType *reply : pending_replies_) {
reply->Abort(); reply->Abort();
} }
pending_replies_.clear(); pending_replies_.clear();

View File

@ -17,6 +17,11 @@
#include "messagereply.h" #include "messagereply.h"
#include <QObject>
#include <QtDebug>
#include "core/logging.h"
_MessageReplyBase::_MessageReplyBase(QObject *parent) _MessageReplyBase::_MessageReplyBase(QObject *parent)
: QObject(parent), finished_(false), success_(false) {} : QObject(parent), finished_(false), success_(false) {}

View File

@ -18,26 +18,26 @@
#ifndef MESSAGEREPLY_H #ifndef MESSAGEREPLY_H
#define MESSAGEREPLY_H #define MESSAGEREPLY_H
#include <QtGlobal>
#include <QObject> #include <QObject>
#include <QSemaphore> #include <QSemaphore>
#include <QString>
#include "core/logging.h" #include "core/logging.h"
// Base QObject for a reply future class that is returned immediately for // Base QObject for a reply future class that is returned immediately for requests that will occur in the background.
// requests that will occur in the background. Similar to QNetworkReply. // Similar to QNetworkReply. Use MessageReply instead.
// Use MessageReply instead.
class _MessageReplyBase : public QObject { class _MessageReplyBase : public QObject {
Q_OBJECT Q_OBJECT
public: public:
_MessageReplyBase(QObject* parent = nullptr); _MessageReplyBase(QObject *parent = nullptr);
virtual int id() const = 0; virtual int id() const = 0;
bool is_finished() const { return finished_; } bool is_finished() const { return finished_; }
bool is_successful() const { return success_; } bool is_successful() const { return success_; }
// Waits for the reply to finish by waiting on a semaphore. Never call this // Waits for the reply to finish by waiting on a semaphore. Never call this from the MessageHandler's thread or it will block forever.
// from the MessageHandler's thread or it will block forever.
// Returns true if the call was successful. // Returns true if the call was successful.
bool WaitForFinished(); bool WaitForFinished();
@ -58,7 +58,7 @@ protected:
template <typename MessageType> template <typename MessageType>
class MessageReply : public _MessageReplyBase { class MessageReply : public _MessageReplyBase {
public: public:
MessageReply(const MessageType& request_message, QObject* parent = nullptr); MessageReply(const MessageType& request_message, QObject *parent = nullptr);
int id() const { return request_message_.id(); } int id() const { return request_message_.id(); }
const MessageType& request_message() const { return request_message_; } const MessageType& request_message() const { return request_message_; }
@ -73,8 +73,7 @@ private:
template<typename MessageType> template<typename MessageType>
MessageReply<MessageType>::MessageReply(const MessageType& request_message, MessageReply<MessageType>::MessageReply(const MessageType& request_message, QObject *parent)
QObject* parent)
: _MessageReplyBase(parent) : _MessageReplyBase(parent)
{ {
request_message_.MergeFrom(request_message); request_message_.MergeFrom(request_message);
@ -94,4 +93,3 @@ void MessageReply<MessageType>::SetReply(const MessageType& message) {
} }
#endif // MESSAGEREPLY_H #endif // MESSAGEREPLY_H

View File

@ -17,6 +17,7 @@
#include "waitforsignal.h" #include "waitforsignal.h"
#include <QObject>
#include <QEventLoop> #include <QEventLoop>
void WaitForSignal(QObject *sender, const char *signal) { void WaitForSignal(QObject *sender, const char *signal) {

View File

@ -15,6 +15,8 @@
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <QObject>
#include "workerpool.h" #include "workerpool.h"
_WorkerPoolBase::_WorkerPoolBase(QObject *parent) : QObject(parent) {} _WorkerPoolBase::_WorkerPoolBase(QObject *parent) : QObject(parent) {}

View File

@ -18,32 +18,37 @@
#ifndef WORKERPOOL_H #ifndef WORKERPOOL_H
#define WORKERPOOL_H #define WORKERPOOL_H
#include <QAtomicInt> #include <stddef.h>
#include <QCoreApplication>
#include <QFile> #include <QtGlobal>
#include <QLocalServer>
#include <QLocalSocket>
#include <QMutex>
#include <QObject> #include <QObject>
#include <QCoreApplication>
#include <QThread>
#include <QMutex>
#include <QProcess> #include <QProcess>
#include <QQueue> #include <QQueue>
#include <QThread> #include <QFile>
#include <QList>
#include <QLocalServer>
#include <QLocalSocket>
#include <QString>
#include <QStringList>
#include <QAtomicInt>
#include "core/closure.h"
#include "core/logging.h" #include "core/logging.h"
class QLocalSocket;
// Base class containing signals and slots - required because moc doesn't do
// templated objects. // Base class containing signals and slots - required because moc doesn't do templated objects.
class _WorkerPoolBase : public QObject { class _WorkerPoolBase : public QObject {
Q_OBJECT Q_OBJECT
public: public:
_WorkerPoolBase(QObject* parent = nullptr); _WorkerPoolBase(QObject *parent = nullptr);
signals: signals:
// Emitted when a worker failed to start. This usually happens when the // Emitted when a worker failed to start. This usually happens when the worker wasn't found, or couldn't be executed.
// worker wasn't found, or couldn't be executed.
void WorkerFailedToStart(); void WorkerFailedToStart();
protected slots: protected slots:
@ -54,45 +59,41 @@ protected slots:
}; };
// Manages a pool of one or more external processes. A local socket server is // Manages a pool of one or more external processes.
// started for each process, and the address is passed to the process as // A local socket server is started for each process, and the address is passed to the process as argv[1].
// argv[1]. The process is expected to connect back to the socket server, and // The process is expected to connect back to the socket server, and when it does a HandlerType is created for it.
// when it does a HandlerType is created for it.
// Instances of HandlerType are created in the WorkerPool's thread. // Instances of HandlerType are created in the WorkerPool's thread.
template <typename HandlerType> template <typename HandlerType>
class WorkerPool : public _WorkerPoolBase { class WorkerPool : public _WorkerPoolBase {
public: public:
WorkerPool(QObject* parent = nullptr); WorkerPool(QObject *parent = nullptr);
~WorkerPool(); ~WorkerPool();
typedef typename HandlerType::MessageType MessageType; typedef typename HandlerType::MessageType MessageType;
typedef typename HandlerType::ReplyType ReplyType; typedef typename HandlerType::ReplyType ReplyType;
// Sets the name of the worker executable. This is looked for first in the // Sets the name of the worker executable. This is looked for first in the current directory, and then in $PATH.
// current directory, and then in $PATH. You must call this before calling // You must call this before calling Start().
// Start(). void SetExecutableName(const QString &executable_name);
void SetExecutableName(const QString& executable_name);
// Sets the number of worker process to use. Defaults to // Sets the number of worker process to use. Defaults to 1 <= (processors / 2) <= 2.
// 1 <= (processors / 2) <= 2.
void SetWorkerCount(int count); void SetWorkerCount(int count);
// Sets the prefix to use for the local server (on unix this is a named pipe // Sets the prefix to use for the local server (on unix this is a named pipe in /tmp).
// in /tmp). Defaults to QApplication::applicationName(). A random number // Defaults to QApplication::applicationName().
// is appended to this name when creating each server. // A random number is appended to this name when creating each server.
void SetLocalServerName(const QString& local_server_name); void SetLocalServerName(const QString &local_server_name);
// Starts all workers. // Starts all workers.
void Start(); void Start();
// Fills in the message's "id" field and creates a reply future. The message // Fills in the message's "id" field and creates a reply future.
// is queued and the WorkerPool's thread will send it to the next available // The message is queued and the WorkerPool's thread will send it to the next available worker.
// worker. Can be called from any thread. // Can be called from any thread.
ReplyType* SendMessageWithReply(MessageType* message); ReplyType *SendMessageWithReply(MessageType *message);
protected: protected:
// These are all reimplemented slots, they are called on the WorkerPool's // These are all reimplemented slots, they are called on the WorkerPool's thread.
// thread.
void DoStart(); void DoStart();
void NewConnection(); void NewConnection();
void ProcessError(QProcess::ProcessError error); void ProcessError(QProcess::ProcessError error);
@ -105,14 +106,14 @@ private:
QLocalServer *local_server_; QLocalServer *local_server_;
QLocalSocket *local_socket_; QLocalSocket *local_socket_;
QProcess *process_; QProcess *process_;
HandlerType* handler_; HandlerType *handler_;
}; };
// Must only ever be called on my thread. // Must only ever be called on my thread.
void StartOneWorker(Worker* worker); void StartOneWorker(Worker *worker);
template <typename T> template <typename T>
Worker* FindWorker(T Worker::*member, T value) { Worker *FindWorker(T Worker::*member, T value) {
for (typename QList<Worker>::iterator it = workers_.begin() ; for (typename QList<Worker>::iterator it = workers_.begin() ;
it != workers_.end() ; ++it) { it != workers_.end() ; ++it) {
if ((*it).*member == value) { if ((*it).*member == value) {
@ -123,7 +124,7 @@ private:
} }
template <typename T> template <typename T>
void DeleteQObjectPointerLater(T** p) { void DeleteQObjectPointerLater(T **p) {
if (*p) { if (*p) {
(*p)->deleteLater(); (*p)->deleteLater();
*p = NULL; *p = NULL;
@ -131,13 +132,11 @@ private:
} }
// Creates a new reply future for the request with the next sequential ID, // Creates a new reply future for the request with the next sequential ID,
// and sets the request's ID to the ID of the reply. Can be called from any // and sets the request's ID to the ID of the reply. Can be called from any thread
// thread ReplyType *NewReply(MessageType *message);
ReplyType* NewReply(MessageType* message);
// Returns the next handler, or NULL if there isn't one. Must be called from // Returns the next handler, or NULL if there isn't one. Must be called from my thread.
// my thread. HandlerType *NextHandler() const;
HandlerType* NextHandler() const;
private: private:
QString local_server_name_; QString local_server_name_;
@ -156,7 +155,7 @@ private:
template <typename HandlerType> template <typename HandlerType>
WorkerPool<HandlerType>::WorkerPool(QObject* parent) WorkerPool<HandlerType>::WorkerPool(QObject *parent)
: _WorkerPoolBase(parent), : _WorkerPoolBase(parent),
next_worker_(0), next_worker_(0),
next_id_(0) next_id_(0)
@ -170,7 +169,7 @@ WorkerPool<HandlerType>::WorkerPool(QObject* parent)
template <typename HandlerType> template <typename HandlerType>
WorkerPool<HandlerType>::~WorkerPool() { WorkerPool<HandlerType>::~WorkerPool() {
for (const Worker& worker : workers_) { for (const Worker &worker : workers_) {
if (worker.local_socket_ && worker.process_) { if (worker.local_socket_ && worker.process_) {
disconnect(worker.process_, SIGNAL(error(QProcess::ProcessError)), this, SLOT(ProcessError(QProcess::ProcessError))); disconnect(worker.process_, SIGNAL(error(QProcess::ProcessError)), this, SLOT(ProcessError(QProcess::ProcessError)));
@ -190,7 +189,7 @@ WorkerPool<HandlerType>::~WorkerPool() {
} }
} }
for (ReplyType* reply : message_queue_) { for (ReplyType *reply : message_queue_) {
reply->Abort(); reply->Abort();
} }
} }
@ -202,13 +201,13 @@ void WorkerPool<HandlerType>::SetWorkerCount(int count) {
} }
template <typename HandlerType> template <typename HandlerType>
void WorkerPool<HandlerType>::SetLocalServerName(const QString& local_server_name) { void WorkerPool<HandlerType>::SetLocalServerName(const QString &local_server_name) {
Q_ASSERT(workers_.isEmpty()); Q_ASSERT(workers_.isEmpty());
local_server_name_ = local_server_name; local_server_name_ = local_server_name;
} }
template <typename HandlerType> template <typename HandlerType>
void WorkerPool<HandlerType>::SetExecutableName(const QString& executable_name) { void WorkerPool<HandlerType>::SetExecutableName(const QString &executable_name) {
Q_ASSERT(workers_.isEmpty()); Q_ASSERT(workers_.isEmpty());
executable_name_ = executable_name; executable_name_ = executable_name;
} }
@ -233,7 +232,7 @@ void WorkerPool<HandlerType>::DoStart() {
search_path << qApp->applicationDirPath() + "/../PlugIns"; search_path << qApp->applicationDirPath() + "/../PlugIns";
#endif #endif
for (const QString& path_prefix : search_path) { for (const QString &path_prefix : search_path) {
const QString executable_path = path_prefix + "/" + executable_name_; const QString executable_path = path_prefix + "/" + executable_name_;
if (QFile::exists(executable_path)) { if (QFile::exists(executable_path)) {
executable_path_ = executable_path; executable_path_ = executable_path;
@ -290,7 +289,7 @@ void WorkerPool<HandlerType>::NewConnection() {
QLocalServer *server = qobject_cast<QLocalServer*>(sender()); QLocalServer *server = qobject_cast<QLocalServer*>(sender());
// Find the worker with this server. // Find the worker with this server.
Worker* worker = FindWorker(&Worker::local_server_, server); Worker *worker = FindWorker(&Worker::local_server_, server);
if (!worker) return; if (!worker) return;
qLog(Debug) << "Worker" << worker << "connected to" << server->fullServerName(); qLog(Debug) << "Worker" << worker << "connected to" << server->fullServerName();
@ -322,9 +321,8 @@ void WorkerPool<HandlerType>::ProcessError(QProcess::ProcessError error) {
switch (error) { switch (error) {
case QProcess::FailedToStart: case QProcess::FailedToStart:
// Failed to start errors are bad - it usually means the worker isn't // Failed to start errors are bad - it usually means the worker isn't installed.
// installed. Don't restart the process, but tell our owner, who will // Don't restart the process, but tell our owner, who will probably want to do something fatal.
// probably want to do something fatal.
qLog(Error) << "Worker failed to start"; qLog(Error) << "Worker failed to start";
emit WorkerFailedToStart(); emit WorkerFailedToStart();
break; break;
@ -339,7 +337,7 @@ void WorkerPool<HandlerType>::ProcessError(QProcess::ProcessError error) {
template <typename HandlerType> template <typename HandlerType>
typename WorkerPool<HandlerType>::ReplyType* typename WorkerPool<HandlerType>::ReplyType*
WorkerPool<HandlerType>::NewReply(MessageType* message) { WorkerPool<HandlerType>::NewReply(MessageType *message) {
const int id = next_id_.fetchAndAddOrdered(1); const int id = next_id_.fetchAndAddOrdered(1);
message->set_id(id); message->set_id(id);
@ -348,8 +346,8 @@ WorkerPool<HandlerType>::NewReply(MessageType* message) {
template <typename HandlerType> template <typename HandlerType>
typename WorkerPool<HandlerType>::ReplyType* typename WorkerPool<HandlerType>::ReplyType*
WorkerPool<HandlerType>::SendMessageWithReply(MessageType* message) { WorkerPool<HandlerType>::SendMessageWithReply(MessageType *message) {
ReplyType* reply = NewReply(message); ReplyType *reply = NewReply(message);
// Add the pending reply to the queue // Add the pending reply to the queue
{ {
@ -371,7 +369,7 @@ void WorkerPool<HandlerType>::SendQueuedMessages() {
ReplyType *reply = message_queue_.dequeue(); ReplyType *reply = message_queue_.dequeue();
// Find a worker for this message // Find a worker for this message
HandlerType* handler = NextHandler(); HandlerType *handler = NextHandler();
if (!handler) { if (!handler) {
// No available handlers - put the message on the front of the queue. // No available handlers - put the message on the front of the queue.
message_queue_.prepend(reply); message_queue_.prepend(reply);

View File

@ -15,12 +15,15 @@
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "fmpsparser.h" #include "config.h"
#include <functional> #include <functional>
#include <QStringList> #include <QList>
#include <QtDebug> #include <QVariant>
#include <QString>
#include "fmpsparser.h"
using std::placeholders::_1; using std::placeholders::_1;
using std::placeholders::_2; using std::placeholders::_2;
@ -114,8 +117,7 @@ int FMPSParser::ParseListListRef(const QStringRef &data, Result *ret) const {
return ParseContainer<';'>(data, std::bind(&FMPSParser::ParseListRef, this, _1, _2), ret); return ParseContainer<';'>(data, std::bind(&FMPSParser::ParseListRef, this, _1, _2), ret);
} }
// Convenience functions that take QStrings instead of QStringRefs. Use the // Convenience functions that take QStrings instead of QStringRefs. Use the QStringRef versions if possible, they're faster.
// QStringRef versions if possible, they're faster.
int FMPSParser::ParseValue(const QString &data, QVariant *ret) const { int FMPSParser::ParseValue(const QString &data, QVariant *ret) const {
return ParseValueRef(QStringRef(&data), ret); return ParseValueRef(QStringRef(&data), ret);
} }

View File

@ -18,8 +18,13 @@
#ifndef FMPSPARSER_H #ifndef FMPSPARSER_H
#define FMPSPARSER_H #define FMPSPARSER_H
#include "config.h"
#include <QList>
#include <QMetaType>
#include <QVariant>
#include <QString>
#include <QRegExp> #include <QRegExp>
#include <QVariantList>
class FMPSParser { class FMPSParser {
public: public:
@ -30,7 +35,7 @@ public:
typedef QList<QVariantList> Result; typedef QList<QVariantList> Result;
// Parses a FMPS value and returns true on success. // Parses a FMPS value and returns true on success.
bool Parse(const QString& data); bool Parse(const QString &data);
// Gets the result of the last successful Parse. // Gets the result of the last successful Parse.
Result result() const { return result_; } Result result() const { return result_; }

View File

@ -15,50 +15,75 @@
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "config.h"
#include "tagreader.h" #include "tagreader.h"
#include <memory> #include <memory>
#include <list>
#include <QCoreApplication> #include <map>
#include <QDateTime> #include <utility>
#include <QFileInfo>
#include <QNetworkAccessManager>
#include <QTextCodec>
#include <QUrl>
#include <QVector>
#include <aifffile.h>
#include <asffile.h>
#include <attachedpictureframe.h>
#include <commentsframe.h>
#include <fileref.h>
#include <audioproperties.h>
#include <flacfile.h>
#include <flacproperties.h>
#include <id3v2tag.h>
#include <mp4file.h>
#include <mp4tag.h>
#include <mpcfile.h>
#include <mpegfile.h>
#include <oggfile.h>
#ifdef TAGLIB_HAS_OPUS
#include <opusfile.h>
#endif
#include <oggflacfile.h>
#include <popularimeterframe.h>
#include <speexfile.h>
#include <tag.h>
#include <textidentificationframe.h>
#include <trueaudiofile.h>
#include <tstring.h>
#include <vorbisfile.h>
#include <wavfile.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "fmpsparser.h" #include <taglib/taglib.h>
#include <taglib/taglib_config.h>
#include <taglib/fileref.h>
#include <taglib/tbytevector.h>
#include <taglib/tfile.h>
#include <taglib/tlist.h>
#include <taglib/tstring.h>
#include <taglib/tstringlist.h>
#include <taglib/audioproperties.h>
#include <taglib/trueaudiofile.h>
#include <taglib/attachedpictureframe.h>
#include <taglib/textidentificationframe.h>
#include <taglib/xiphcomment.h>
#include <taglib/commentsframe.h>
#include <taglib/tag.h>
#include <taglib/id3v2tag.h>
#include "taglib/id3v2frame.h"
#include <taglib/flacfile.h>
#include <taglib/oggflacfile.h>
#include <taglib/flacproperties.h>
#include <taglib/flacpicture.h>
#include <taglib/vorbisfile.h>
#include <taglib/speexfile.h>
#include <taglib/wavfile.h>
#include <taglib/aifffile.h>
#include <taglib/asffile.h>
#include "taglib/asftag.h"
#include "taglib/asfattribute.h"
#include "taglib/asfproperties.h"
#include <taglib/mp4file.h>
#include <taglib/mp4tag.h>
#include "taglib/mp4item.h"
#include <taglib/mp4coverart.h>
#include <taglib/mp4properties.h>
#include <taglib/mpcfile.h>
#include <taglib/mpegfile.h>
#ifdef TAGLIB_HAS_OPUS
#include <taglib/opusfile.h>
#endif
#include <QtGlobal>
#include <QFile>
#include <QFileInfo>
#include <QList>
#include <QByteArray>
#include <QDateTime>
#include <QVariant>
#include <QString>
#include <QUrl>
#include <QTextCodec>
#include <QVector>
#include <QNetworkAccessManager>
#include <QtDebug>
#include "core/logging.h" #include "core/logging.h"
#include "core/messagehandler.h" #include "core/messagehandler.h"
#include "fmpsparser.h"
#include "core/timeconstants.h" #include "core/timeconstants.h"
// Taglib added support for FLAC pictures in 1.7.0 // Taglib added support for FLAC pictures in 1.7.0
@ -71,12 +96,12 @@
class FileRefFactory { class FileRefFactory {
public: public:
virtual ~FileRefFactory() {} virtual ~FileRefFactory() {}
virtual TagLib::FileRef *GetFileRef(const QString& filename) = 0; virtual TagLib::FileRef *GetFileRef(const QString &filename) = 0;
}; };
class TagLibFileRefFactory : public FileRefFactory { class TagLibFileRefFactory : public FileRefFactory {
public: public:
virtual TagLib::FileRef *GetFileRef(const QString& filename) { virtual TagLib::FileRef *GetFileRef(const QString &filename) {
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
return new TagLib::FileRef(filename.toStdWString().c_str()); return new TagLib::FileRef(filename.toStdWString().c_str());
#else #else
@ -87,11 +112,11 @@ class TagLibFileRefFactory : public FileRefFactory {
namespace { namespace {
TagLib::String StdStringToTaglibString(const std::string& s) { TagLib::String StdStringToTaglibString(const std::string &s) {
return TagLib::String(s.c_str(), TagLib::String::UTF8); return TagLib::String(s.c_str(), TagLib::String::UTF8);
} }
TagLib::String QStringToTaglibString(const QString& s) { TagLib::String QStringToTaglibString(const QString &s) {
return TagLib::String(s.toUtf8().constData(), TagLib::String::UTF8); return TagLib::String(s.toUtf8().constData(), TagLib::String::UTF8);
} }
@ -508,8 +533,7 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
} }
// Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the same way; // Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the same way;
// apart, so we keep specific behavior for some formats by adding another // apart, so we keep specific behavior for some formats by adding another "else if" block above.
// "else if" block above.
if (TagLib::Ogg::XiphComment *tag = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) { if (TagLib::Ogg::XiphComment *tag = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
SetVorbisComments(tag, song); SetVorbisComments(tag, song);
} }
@ -625,9 +649,7 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
TagLib::Ogg::FieldListMap map = xiph_comment->fieldListMap(); TagLib::Ogg::FieldListMap map = xiph_comment->fieldListMap();
#if TAGLIB_MAJOR_VERSION <= 1 && TAGLIB_MINOR_VERSION < 11 #if TAGLIB_MAJOR_VERSION <= 1 && TAGLIB_MINOR_VERSION < 11
// Other than the below mentioned non-standard COVERART, // Other than the below mentioned non-standard COVERART, METADATA_BLOCK_PICTURE is the proposed tag for cover pictures.
// METADATA_BLOCK_PICTURE
// is the proposed tag for cover pictures.
// (see http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE) // (see http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE)
if (map.contains("METADATA_BLOCK_PICTURE")) { if (map.contains("METADATA_BLOCK_PICTURE")) {
TagLib::StringList pict_list = map["METADATA_BLOCK_PICTURE"]; TagLib::StringList pict_list = map["METADATA_BLOCK_PICTURE"];
@ -687,10 +709,10 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
TagLib::MP4::File *aac_file = dynamic_cast<TagLib::MP4::File*>(ref.file()); TagLib::MP4::File *aac_file = dynamic_cast<TagLib::MP4::File*>(ref.file());
if (aac_file) { if (aac_file) {
TagLib::MP4::Tag *tag = aac_file->tag(); TagLib::MP4::Tag *tag = aac_file->tag();
const TagLib::MP4::ItemListMap& items = tag->itemListMap(); const TagLib::MP4::ItemListMap &items = tag->itemListMap();
TagLib::MP4::ItemListMap::ConstIterator it = items.find("covr"); TagLib::MP4::ItemListMap::ConstIterator it = items.find("covr");
if (it != items.end()) { if (it != items.end()) {
const TagLib::MP4::CoverArtList& art_list = it->second.toCoverArtList(); const TagLib::MP4::CoverArtList &art_list = it->second.toCoverArtList();
if (!art_list.isEmpty()) { if (!art_list.isEmpty()) {
// Just take the first one for now // Just take the first one for now

View File

@ -18,18 +18,19 @@
#ifndef TAGREADER_H #ifndef TAGREADER_H
#define TAGREADER_H #define TAGREADER_H
#include "config.h"
#include <string>
#include <QByteArray> #include <QByteArray>
#include <QString>
#include <QNetworkAccessManager>
#include <QTextCodec>
#include <taglib/xiphcomment.h> #include <taglib/xiphcomment.h>
#include "config.h"
#include "tagreadermessages.pb.h" #include "tagreadermessages.pb.h"
class QNetworkAccessManager;
class QString;
class QTextCodec;
class QUrl;
namespace TagLib { namespace TagLib {
class FileRef; class FileRef;
class String; class String;
@ -44,15 +45,14 @@ class FileRefFactory;
/** /**
* This class holds all useful methods to read and write tags from/to files. * This class holds all useful methods to read and write tags from/to files.
* You should not use it directly in the main process but rather use a * You should not use it directly in the main process but rather use a TagReaderWorker process (using TagReaderClient)
* TagReaderWorker process (using TagReaderClient)
*/ */
class TagReader { class TagReader {
public: public:
TagReader(); TagReader();
void ReadFile(const QString& filename, pb::tagreader::SongMetadata *song) const; void ReadFile(const QString &filename, pb::tagreader::SongMetadata *song) const;
bool SaveFile(const QString& filename, const pb::tagreader::SongMetadata &song) const; bool SaveFile(const QString &filename, const pb::tagreader::SongMetadata &song) const;
bool IsMediaFile(const QString &filename) const; bool IsMediaFile(const QString &filename) const;
QByteArray LoadEmbeddedArt(const QString &filename) const; QByteArray LoadEmbeddedArt(const QString &filename) const;

View File

@ -15,16 +15,23 @@
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "tagreaderworker.h" #include "config.h"
#include "core/logging.h"
#include <sys/time.h>
#include <iostream>
#include <QtGlobal>
#include <QCoreApplication> #include <QCoreApplication>
#include <QList>
#include <QLocalSocket> #include <QLocalSocket>
#include <QSsl>
#include <QSslCertificate>
#include <QSslSocket> #include <QSslSocket>
#include <QStringList> #include <QStringList>
#include <QtDebug>
#include <iostream> #include "core/logging.h"
#include <sys/time.h> #include "tagreaderworker.h"
int main(int argc, char **argv) { int main(int argc, char **argv) {

View File

@ -15,15 +15,16 @@
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "tagreaderworker.h" #include "config.h"
#include <string>
#include <QCoreApplication> #include <QCoreApplication>
#include <QDateTime> #include <QObject>
#include <QFileInfo> #include <QIODevice>
#include <QNetworkAccessManager> #include <QByteArray>
#include <QTextCodec>
#include <QUrl>
#include "tagreaderworker.h"
TagReaderWorker::TagReaderWorker(QIODevice *socket, QObject *parent) TagReaderWorker::TagReaderWorker(QIODevice *socket, QObject *parent)
: AbstractMessageHandler<pb::tagreader::Message>(socket, parent) : AbstractMessageHandler<pb::tagreader::Message>(socket, parent)

View File

@ -19,9 +19,15 @@
#define TAGREADERWORKER_H #define TAGREADERWORKER_H
#include "config.h" #include "config.h"
#include <stddef.h>
#include <QObject>
#include <QIODevice>
#include "core/messagehandler.h"
#include "tagreader.h" #include "tagreader.h"
#include "tagreadermessages.pb.h" #include "tagreadermessages.pb.h"
#include "core/messagehandler.h"
class TagReaderWorker : public AbstractMessageHandler<pb::tagreader::Message> { class TagReaderWorker : public AbstractMessageHandler<pb::tagreader::Message> {
public: public:

View File

@ -474,11 +474,7 @@ set(UI
) )
set(RESOURCES set(RESOURCES ../data/data.qrc)
../data/data.qrc
#../data/icons.qrc
)
set(OTHER_SOURCES) set(OTHER_SOURCES)
option(USE_INSTALL_PREFIX "Look for data in CMAKE_INSTALL_PREFIX" ON) option(USE_INSTALL_PREFIX "Look for data in CMAKE_INSTALL_PREFIX" ON)
@ -487,7 +483,6 @@ option(USE_INSTALL_PREFIX "Look for data in CMAKE_INSTALL_PREFIX" ON)
set(GST_ENGINE_SRC engine/gstengine.cpp engine/gstenginepipeline.cpp engine/gstelementdeleter.cpp) set(GST_ENGINE_SRC engine/gstengine.cpp engine/gstenginepipeline.cpp engine/gstelementdeleter.cpp)
set(GST_ENGINE_MOC engine/gstengine.h engine/gstenginepipeline.h engine/gstelementdeleter.h) set(GST_ENGINE_MOC engine/gstengine.h engine/gstenginepipeline.h engine/gstelementdeleter.h)
#set(GST_ENGINE_LIB GSTREAMER GSTREAMER_BASE GSTREAMER_APP GSTREAMER_AUDIO GSTREAMER_TAG GSTREAMER_PBUTILS GSTREAMER_QTGLIB GSTREAMER_QTGST GSTREAMER_QTGSTUI GSTREAMER_QTGSTUTILS)
set(GST_ENGINE_LIB GSTREAMER GSTREAMER_BASE GSTREAMER_APP GSTREAMER_AUDIO GSTREAMER_TAG GSTREAMER_PBUTILS) set(GST_ENGINE_LIB GSTREAMER GSTREAMER_BASE GSTREAMER_APP GSTREAMER_AUDIO GSTREAMER_TAG GSTREAMER_PBUTILS)
#set(GST_ENGINE_LIB gstreamer-1.0 gstreamer-base-1.0 gstreamer-app-1.0 streamer-audio-1.0 gstreamer-tag-1.0 gstreamer-pbutils-1.0) #set(GST_ENGINE_LIB gstreamer-1.0 gstreamer-base-1.0 gstreamer-app-1.0 streamer-audio-1.0 gstreamer-tag-1.0 gstreamer-pbutils-1.0)
#set(GST_ENGINE_LIB ${GSTREAMER_BASE_LIBRARIES} ${GSTREAMER_LIBRARIES} ${GSTREAMER_APP_LIBRARIES} ${GSTREAMER_TAG_LIBRARIES} ${GSTREAMER_PBUTILS_LIBRARIES}) #set(GST_ENGINE_LIB ${GSTREAMER_BASE_LIBRARIES} ${GSTREAMER_LIBRARIES} ${GSTREAMER_APP_LIBRARIES} ${GSTREAMER_TAG_LIBRARIES} ${GSTREAMER_PBUTILS_LIBRARIES})
@ -846,6 +841,7 @@ add_library(strawberry_lib STATIC
target_link_libraries(strawberry_lib target_link_libraries(strawberry_lib
libstrawberry-common libstrawberry-common
libstrawberry-tagreader libstrawberry-tagreader
#gstafc
${GLIB_LIBRARIES} ${GLIB_LIBRARIES}
${GIO_LIBRARIES} ${GIO_LIBRARIES}
${SHA2_LIBRARIES} ${SHA2_LIBRARIES}
@ -886,7 +882,6 @@ if(HAVE_IMOBILEDEVICE)
${IMOBILEDEVICE_LIBRARIES} ${IMOBILEDEVICE_LIBRARIES}
${PLIST_LIBRARIES} ${PLIST_LIBRARIES}
${USBMUXD_LIBRARIES} ${USBMUXD_LIBRARIES}
gstafcsrc
) )
link_directories(${IMOBILEDEVICE_LIBRARY_DIRS}) link_directories(${IMOBILEDEVICE_LIBRARY_DIRS})
link_directories(${USBMUXD_LIBRARY_DIRS}) link_directories(${USBMUXD_LIBRARY_DIRS})

View File

@ -1,11 +1,17 @@
#include "config.h"
#include <QWidget>
#include <QGLWidget>
#include <QVector>
#include "analyzer.h" #include "analyzer.h"
#include "engines/enginebase.h" #include "engine/enginebase.h"
AnalyzerBase::AnalyzerBase(QWidget* parent) AnalyzerBase::AnalyzerBase(QWidget *parent)
: QGLWidget(parent), engine_(nullptr) {} : QGLWidget(parent), engine_(nullptr) {}
void AnalyzerBase::set_engine(Engine::Base* engine) { void AnalyzerBase::set_engine(Engine::Base *engine) {
disconnect(engine_); disconnect(engine_);
engine_ = engine; engine_ = engine;
if (engine_) { if (engine_) {

View File

@ -15,14 +15,17 @@
* * * *
***************************************************************************/ ***************************************************************************/
#include "analyzerbase.h" #include "config.h"
#include <cmath> //interpolate() #include <cmath>
#include <QEvent> //event() #include <QWidget>
#include <QPainter> #include <QPainter>
#include <QPaintEvent> #include <QPalette>
#include <QtDebug> #include <QTimerEvent>
#include <QtEvents>
#include "analyzerbase.h"
#include "engine/enginebase.h" #include "engine/enginebase.h"
@ -45,7 +48,7 @@
template class Analyzer::Base<QWidget>; template class Analyzer::Base<QWidget>;
#endif #endif
Analyzer::Base::Base(QWidget* parent, uint scopeSize) Analyzer::Base::Base(QWidget *parent, uint scopeSize)
: QWidget(parent), : QWidget(parent),
m_timeout(40) // msec m_timeout(40) // msec
, ,
@ -70,9 +73,9 @@ void Analyzer::Base::transform(Scope& scope) // virtual
// values // values
// scope.resize( m_fht->size() ); // scope.resize( m_fht->size() );
float* front = static_cast<float*>(&scope.front()); float *front = static_cast<float*>(&scope.front());
float* f = new float[m_fht->size()]; float *f = new float[m_fht->size()];
m_fht->copy(&f[0], front); m_fht->copy(&f[0], front);
m_fht->logSpectrum(front, &f[0]); m_fht->logSpectrum(front, &f[0]);
m_fht->scale(front, 1.0 / 20); m_fht->scale(front, 1.0 / 20);
@ -82,7 +85,7 @@ void Analyzer::Base::transform(Scope& scope) // virtual
} }
void Analyzer::Base::paintEvent(QPaintEvent* e) { void Analyzer::Base::paintEvent(QPaintEvent *e) {
QPainter p(this); QPainter p(this);
p.fillRect(e->rect(), palette().color(QPalette::Window)); p.fillRect(e->rect(), palette().color(QPalette::Window));
@ -92,8 +95,7 @@ void Analyzer::Base::paintEvent(QPaintEvent* e) {
const Engine::Scope& thescope = m_engine->scope(m_timeout); const Engine::Scope& thescope = m_engine->scope(m_timeout);
int i = 0; int i = 0;
// convert to mono here - our built in analyzers need mono, but the // convert to mono here - our built in analyzers need mono, but the engines provide interleaved pcm
// engines provide interleaved pcm
for (uint x = 0; (int)x < m_fht->size(); ++x) { for (uint x = 0; (int)x < m_fht->size(); ++x) {
m_lastScope[x] = double(thescope[i] + thescope[i + 1]) / (2 * (1 << 15)); m_lastScope[x] = double(thescope[i] + thescope[i + 1]) / (2 * (1 << 15));
i += 2; i += 2;
@ -180,8 +182,7 @@ void Analyzer::Base::polishEvent() {
init(); // virtual init(); // virtual
} }
void Analyzer::interpolate(const Scope& inVec, Scope& outVec) // static void Analyzer::interpolate(const Scope& inVec, Scope& outVec) {
{
double pos = 0.0; double pos = 0.0;
const double step = (double)inVec.size() / outVec.size(); const double step = (double)inVec.size() / outVec.size();
@ -203,8 +204,7 @@ void Analyzer::interpolate(const Scope& inVec, Scope& outVec) // static
} }
void Analyzer::initSin(Scope& v, const uint size) // static void Analyzer::initSin(Scope& v, const uint size) {
{
double step = (M_PI * 2) / size; double step = (M_PI * 2) / size;
double radian = 0; double radian = 0;
@ -214,7 +214,7 @@ void Analyzer::initSin(Scope& v, const uint size) // static
} }
} }
void Analyzer::Base::timerEvent(QTimerEvent* e) { void Analyzer::Base::timerEvent(QTimerEvent *e) {
QWidget::timerEvent(e); QWidget::timerEvent(e);
if (e->timerId() != m_timer.timerId()) return; if (e->timerId() != m_timer.timerId()) return;

View File

@ -4,29 +4,38 @@
#ifndef ANALYZERBASE_H #ifndef ANALYZERBASE_H
#define ANALYZERBASE_H #define ANALYZERBASE_H
#include "config.h"
#ifdef __FreeBSD__ #ifdef __FreeBSD__
#include <sys/types.h> #include <sys/types.h>
#endif #endif
#include "analyzer/fht.h" //stack allocated and convenience #include <stdbool.h>
#include "engine/engine_fwd.h" #include <vector>
#include <QPixmap> //stack allocated and convenience
#include <QBasicTimer> //stack allocated
#include <QWidget> //baseclass
#include <vector> //included for convenience
#include <QGLWidget> //baseclass
#ifdef Q_WS_MACX #ifdef Q_WS_MACX
#include <OpenGL/gl.h> //included for convenience #include <OpenGL/gl.h> //included for convenience
#include <OpenGL/glu.h> //included for convenience #include <OpenGL/glu.h> //included for convenience
#else #else
#include <GL/gl.h> //included for convenience #include <GL/gl.h> //included for convenience
#include <GL/glu.h> //included for convenience #include <GL/glu.h> //included for convenience
#endif #endif
class QEvent; #include <QtGlobal>
#include <QObject>
#include <QWidget>
#include <QBasicTimer>
#include <QString>
#include <QPainter>
#include <QtEvents>
#include "analyzer/fht.h"
#include "engine/engine_fwd.h"
class QHideEvent;
class QShowEvent;
class QTimerEvent;
class QPaintEvent; class QPaintEvent;
class QResizeEvent;
namespace Analyzer { namespace Analyzer {

View File

@ -17,19 +17,32 @@
along with Strawberry. If not, see <http://www.gnu.org/licenses/>. along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "config.h"
#include <QObject>
#include <QWidget>
#include <QVariant>
#include <QString>
#include <QMenu>
#include <QAction>
#include <QActionGroup>
#include <QTimer>
#include <QBoxLayout>
#include <QLayout>
#include <QSignalMapper>
#include <QSettings>
#include <QtEvents>
#include <QtDebug>
#include "analyzercontainer.h" #include "analyzercontainer.h"
#include "analyzerbase.h"
#include "blockanalyzer.h" #include "blockanalyzer.h"
#include "core/logging.h" #include "core/logging.h"
#include <QMouseEvent> const char *AnalyzerContainer::kSettingsGroup = "Analyzer";
#include <QHBoxLayout> const char *AnalyzerContainer::kSettingsFramerate = "framerate";
#include <QSettings>
#include <QTimer>
#include <QtDebug>
const char* AnalyzerContainer::kSettingsGroup = "Analyzer";
const char* AnalyzerContainer::kSettingsFramerate = "framerate";
// Framerates // Framerates
const int AnalyzerContainer::kLowFramerate = 20; const int AnalyzerContainer::kLowFramerate = 20;
@ -37,7 +50,7 @@ const int AnalyzerContainer::kMediumFramerate = 25;
const int AnalyzerContainer::kHighFramerate = 30; const int AnalyzerContainer::kHighFramerate = 30;
const int AnalyzerContainer::kSuperHighFramerate = 60; const int AnalyzerContainer::kSuperHighFramerate = 60;
AnalyzerContainer::AnalyzerContainer(QWidget* parent) AnalyzerContainer::AnalyzerContainer(QWidget *parent)
: QWidget(parent), : QWidget(parent),
current_framerate_(kMediumFramerate), current_framerate_(kMediumFramerate),
context_menu_(new QMenu(this)), context_menu_(new QMenu(this)),
@ -51,7 +64,7 @@ AnalyzerContainer::AnalyzerContainer(QWidget* parent)
ignore_next_click_(false), ignore_next_click_(false),
current_analyzer_(nullptr), current_analyzer_(nullptr),
engine_(nullptr) { engine_(nullptr) {
QHBoxLayout* layout = new QHBoxLayout(this); QHBoxLayout *layout = new QHBoxLayout(this);
setLayout(layout); setLayout(layout);
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
@ -82,19 +95,18 @@ AnalyzerContainer::AnalyzerContainer(QWidget* parent)
Load(); Load();
} }
void AnalyzerContainer::SetActions(QAction* visualisation) { void AnalyzerContainer::SetActions(QAction *visualisation) {
visualisation_action_ = visualisation; visualisation_action_ = visualisation;
context_menu_->addAction(visualisation_action_); context_menu_->addAction(visualisation_action_);
} }
void AnalyzerContainer::mouseReleaseEvent(QMouseEvent* e) { void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
if (e->button() == Qt::LeftButton) { if (e->button() == Qt::LeftButton) {
if (ignore_next_click_) { if (ignore_next_click_) {
ignore_next_click_ = false; ignore_next_click_ = false;
} }
else { else {
// Might be the first click in a double click, so wait a while before // Might be the first click in a double click, so wait a while before actually doing anything
// actually doing anything
double_click_timer_->start(); double_click_timer_->start();
last_click_pos_ = e->globalPos(); last_click_pos_ = e->globalPos();
} }
@ -115,11 +127,11 @@ void AnalyzerContainer::mouseDoubleClickEvent(QMouseEvent*) {
if (visualisation_action_) visualisation_action_->trigger(); if (visualisation_action_) visualisation_action_->trigger();
} }
void AnalyzerContainer::wheelEvent(QWheelEvent* e) { void AnalyzerContainer::wheelEvent(QWheelEvent *e) {
emit WheelEvent(e->delta()); emit WheelEvent(e->delta());
} }
void AnalyzerContainer::SetEngine(EngineBase* engine) { void AnalyzerContainer::SetEngine(EngineBase *engine) {
if (current_analyzer_) current_analyzer_->set_engine(engine); if (current_analyzer_) current_analyzer_->set_engine(engine);
engine_ = engine; engine_ = engine;
} }
@ -132,11 +144,10 @@ void AnalyzerContainer::DisableAnalyzer() {
} }
void AnalyzerContainer::ChangeAnalyzer(int id) { void AnalyzerContainer::ChangeAnalyzer(int id) {
QObject* instance = analyzer_types_[id]->newInstance(Q_ARG(QWidget*, this)); QObject *instance = analyzer_types_[id]->newInstance(Q_ARG(QWidget*, this));
if (!instance) { if (!instance) {
qLog(Warning) << "Couldn't intialise a new" qLog(Warning) << "Couldn't intialise a new" << analyzer_types_[id]->className();
<< analyzer_types_[id]->className();
return; return;
} }
@ -196,8 +207,7 @@ void AnalyzerContainer::Load() {
} }
void AnalyzerContainer::SaveFramerate(int framerate) { void AnalyzerContainer::SaveFramerate(int framerate) {
// For now, framerate is common for all analyzers. Maybe each analyzer should // For now, framerate is common for all analyzers. Maybe each analyzer should have its own framerate?
// have its own framerate?
current_framerate_ = framerate; current_framerate_ = framerate;
QSettings s; QSettings s;
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
@ -212,7 +222,7 @@ void AnalyzerContainer::Save() {
} }
void AnalyzerContainer::AddFramerate(const QString& name, int framerate) { void AnalyzerContainer::AddFramerate(const QString& name, int framerate) {
QAction* action = context_menu_framerate_->addAction(name, mapper_framerate_, SLOT(map())); QAction *action = context_menu_framerate_->addAction(name, mapper_framerate_, SLOT(map()));
mapper_framerate_->setMapping(action, framerate); mapper_framerate_->setMapping(action, framerate);
group_framerate_->addAction(action); group_framerate_->addAction(action);
framerate_list_ << framerate; framerate_list_ << framerate;

View File

@ -20,13 +20,31 @@
#ifndef ANALYZERCONTAINER_H #ifndef ANALYZERCONTAINER_H
#define ANALYZERCONTAINER_H #define ANALYZERCONTAINER_H
#include <QWidget> #include "config.h"
#include <QMenu>
#include <QSignalMapper> #include <stdbool.h>
#include <QObject>
#include <QWidget>
#include <QPoint>
#include <QMenu>
#include <QAction>
#include <QActionGroup>
#include <QList>
#include <QString>
#include <QSignalMapper>
#include <QTimer>
#include <QtEvents>
#include "analyzerbase.h"
#include "engine/engine_fwd.h" #include "engine/engine_fwd.h"
class QMouseEvent;
class QWheelEvent;
namespace Analyzer {
class Base;
} // namespace Analyzer
class AnalyzerContainer : public QWidget { class AnalyzerContainer : public QWidget {
Q_OBJECT Q_OBJECT

View File

@ -5,12 +5,19 @@
#include "blockanalyzer.h" #include "blockanalyzer.h"
#include <cmath>
#include <QMouseEvent>
#include <QResizeEvent>
#include <cstdlib> #include <cstdlib>
#include <cmath>
#include <scoped_allocator>
#include <QWidget>
#include <QPixmap>
#include <QPainter> #include <QPainter>
#include <QPalette>
#include <QColor>
#include <QtEvents>
#include "analyzerbase.h"
#include "fht.h"
const uint BlockAnalyzer::HEIGHT = 2; const uint BlockAnalyzer::HEIGHT = 2;
const uint BlockAnalyzer::WIDTH = 4; const uint BlockAnalyzer::WIDTH = 4;
@ -19,10 +26,10 @@ const uint BlockAnalyzer::MIN_COLUMNS = 32; // arbituary
const uint BlockAnalyzer::MAX_COLUMNS = 256; // must be 2**n const uint BlockAnalyzer::MAX_COLUMNS = 256; // must be 2**n
const uint BlockAnalyzer::FADE_SIZE = 90; const uint BlockAnalyzer::FADE_SIZE = 90;
const char* BlockAnalyzer::kName = const char *BlockAnalyzer::kName =
QT_TRANSLATE_NOOP("AnalyzerContainer", "Block analyzer"); QT_TRANSLATE_NOOP("AnalyzerContainer", "Block analyzer");
BlockAnalyzer::BlockAnalyzer(QWidget* parent) BlockAnalyzer::BlockAnalyzer(QWidget *parent)
: Analyzer::Base(parent, 9), : Analyzer::Base(parent, 9),
m_columns(0) // uint m_columns(0) // uint
, ,
@ -43,9 +50,7 @@ BlockAnalyzer::BlockAnalyzer(QWidget* parent)
, ,
m_fade_intensity(1 << 8, 32) // vector<uint> m_fade_intensity(1 << 8, 32) // vector<uint>
{ {
setMinimumSize(MIN_COLUMNS * (WIDTH + 1) - 1, setMinimumSize(MIN_COLUMNS * (WIDTH + 1) - 1, MIN_ROWS * (HEIGHT + 1) - 1); //-1 is padding, no drawing takes place there
MIN_ROWS * (HEIGHT + 1) -
1); //-1 is padding, no drawing takes place there
setMaximumWidth(MAX_COLUMNS * (WIDTH + 1) - 1); setMaximumWidth(MAX_COLUMNS * (WIDTH + 1) - 1);
// mxcl says null pixmaps cause crashes, so let's play it safe // mxcl says null pixmaps cause crashes, so let's play it safe
@ -54,7 +59,7 @@ BlockAnalyzer::BlockAnalyzer(QWidget* parent)
BlockAnalyzer::~BlockAnalyzer() {} BlockAnalyzer::~BlockAnalyzer() {}
void BlockAnalyzer::resizeEvent(QResizeEvent* e) { void BlockAnalyzer::resizeEvent(QResizeEvent *e) {
QWidget::resizeEvent(e); QWidget::resizeEvent(e);
m_background = QPixmap(size()); m_background = QPixmap(size());
@ -96,8 +101,7 @@ void BlockAnalyzer::resizeEvent(QResizeEvent* e) {
} }
void BlockAnalyzer::determineStep() { void BlockAnalyzer::determineStep() {
// falltime is dependent on rowcount due to our digital resolution (ie we have // falltime is dependent on rowcount due to our digital resolution (ie we have boxes/blocks of pixels)
// boxes/blocks of pixels)
// I calculated the value 30 based on some trial and error // I calculated the value 30 based on some trial and error
// the fall time of 30 is too slow on framerates above 50fps // the fall time of 30 is too slow on framerates above 50fps
@ -119,10 +123,7 @@ void BlockAnalyzer::transform(Analyzer::Scope& s) // pure virtual
m_fht->spectrum(front); m_fht->spectrum(front);
m_fht->scale(front, 1.0 / 20); m_fht->scale(front, 1.0 / 20);
// the second half is pretty dull, so only show it if the user has a large // the second half is pretty dull, so only show it if the user has a large analyzer by setting to m_scope.size() if large we prevent interpolation of large analyzers, this is good!
// analyzer
// by setting to m_scope.size() if large we prevent interpolation of large
// analyzers, this is good!
s.resize(m_scope.size() <= MAX_COLUMNS / 2 ? MAX_COLUMNS / 2 : m_scope.size()); s.resize(m_scope.size() <= MAX_COLUMNS / 2 ? MAX_COLUMNS / 2 : m_scope.size());
} }
@ -158,15 +159,13 @@ void BlockAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
for (y = 0; m_scope[x] < m_yscale[y]; ++y) for (y = 0; m_scope[x] < m_yscale[y]; ++y)
; ;
// this is opposite to what you'd think, higher than y // this is opposite to what you'd think, higher than y means the bar is lower than y (physically)
// means the bar is lower than y (physically)
if ((float)y > m_store[x]) if ((float)y > m_store[x])
y = int(m_store[x] += m_step); y = int(m_store[x] += m_step);
else else
m_store[x] = y; m_store[x] = y;
// if y is lower than m_fade_pos, then the bar has exceeded the height of // if y is lower than m_fade_pos, then the bar has exceeded the height of the fadeout
// the fadeout
// if the fadeout is quite faded now, then display the new one // if the fadeout is quite faded now, then display the new one
if (y <= m_fade_pos[x] /*|| m_fade_intensity[x] < FADE_SIZE / 3*/) { if (y <= m_fade_pos[x] /*|| m_fade_intensity[x] < FADE_SIZE / 3*/) {
m_fade_pos[x] = y; m_fade_pos[x] = y;
@ -181,8 +180,7 @@ void BlockAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
if (m_fade_intensity[x] == 0) m_fade_pos[x] = m_rows; if (m_fade_intensity[x] == 0) m_fade_pos[x] = m_rows;
// REMEMBER: y is a number from 0 to m_rows, 0 means all blocks are glowing, // REMEMBER: y is a number from 0 to m_rows, 0 means all blocks are glowing, m_rows means none are
// m_rows means none are
canvas_painter.drawPixmap(x * (WIDTH + 1), y * (HEIGHT + 1) + m_y, *bar(), canvas_painter.drawPixmap(x * (WIDTH + 1), y * (HEIGHT + 1) + m_y, *bar(),
0, y * (HEIGHT + 1), bar()->width(), 0, y * (HEIGHT + 1), bar()->width(),
bar()->height()); bar()->height());
@ -195,8 +193,7 @@ void BlockAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
} }
static inline void adjustToLimits(int& b, int& f, uint& amount) { static inline void adjustToLimits(int& b, int& f, uint& amount) {
// with a range of 0-255 and maximum adjustment of amount, // with a range of 0-255 and maximum adjustment of amount, maximise the difference between f and b
// maximise the difference between f and b
if (b < f) { if (b < f) {
if (b > 255 - f) { if (b > 255 - f) {

View File

@ -5,12 +5,21 @@
#ifndef BLOCKANALYZER_H #ifndef BLOCKANALYZER_H
#define BLOCKANALYZER_H #define BLOCKANALYZER_H
#include <stdbool.h>
#include <vector>
#include <QtGlobal>
#include <QObject>
#include <QWidget>
#include <QString>
#include <QPixmap>
#include <QPainter>
#include <QPalette>
#include <QtEvents>
#include "analyzerbase.h" #include "analyzerbase.h"
#include <qcolor.h>
class QResizeEvent; class QResizeEvent;
class QMouseEvent;
class QPalette;
/** /**
* @author Max Howell * @author Max Howell

View File

@ -20,6 +20,7 @@
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#include "fht.h" #include "fht.h"
FHT::FHT(int n) : m_buf(0), m_tab(0), m_log(0) { FHT::FHT(int n) : m_buf(0), m_tab(0), m_log(0) {

View File

@ -20,19 +20,23 @@
#include "config.h" #include "config.h"
#include <stdbool.h>
#include <QObject>
#include <QThread> #include <QThread>
#include <QList>
#include "collection.h"
#include "collectionmodel.h"
#include "collectionbackend.h"
#include "core/application.h" #include "core/application.h"
#include "core/database.h" #include "core/database.h"
#include "core/player.h" #include "core/player.h"
#include "core/tagreaderclient.h" #include "core/tagreaderclient.h"
#include "core/taskmanager.h"
#include "core/thread.h" #include "core/thread.h"
#include "core/logging.h" #include "core/utilities.h"
#include "collection.h"
#include "collectionwatcher.h"
#include "collectionbackend.h"
#include "collectionmodel.h"
#include "playlist/playlistmanager.h"
const char *Collection::kSongsTable = "songs"; const char *Collection::kSongsTable = "songs";
const char *Collection::kDirsTable = "directories"; const char *Collection::kDirsTable = "directories";
@ -47,8 +51,6 @@ Collection::Collection(Application *app, QObject *parent)
watcher_(nullptr), watcher_(nullptr),
watcher_thread_(nullptr) { watcher_thread_(nullptr) {
//qLog(Debug) << __PRETTY_FUNCTION__;
backend_ = new CollectionBackend; backend_ = new CollectionBackend;
backend()->moveToThread(app->database()->thread()); backend()->moveToThread(app->database()->thread());
@ -62,8 +64,6 @@ Collection::Collection(Application *app, QObject *parent)
} }
Collection::~Collection() { Collection::~Collection() {
//qLog(Debug) << __PRETTY_FUNCTION__;
watcher_->deleteLater(); watcher_->deleteLater();
watcher_thread_->exit(); watcher_thread_->exit();
@ -71,8 +71,6 @@ Collection::~Collection() {
} }
void Collection::Init() { void Collection::Init() {
//qLog(Debug) << __PRETTY_FUNCTION__;
watcher_ = new CollectionWatcher; watcher_ = new CollectionWatcher;
watcher_thread_ = new Thread(this); watcher_thread_ = new Thread(this);
@ -109,8 +107,6 @@ void Collection::PauseWatcher() { watcher_->SetRescanPausedAsync(true); }
void Collection::ResumeWatcher() { watcher_->SetRescanPausedAsync(false); } void Collection::ResumeWatcher() { watcher_->SetRescanPausedAsync(false); }
void Collection::ReloadSettings() { void Collection::ReloadSettings() {
//qLog(Debug) << __PRETTY_FUNCTION__;
watcher_->ReloadSettingsAsync(); watcher_->ReloadSettingsAsync();
@ -118,14 +114,10 @@ void Collection::ReloadSettings() {
void Collection::Stopped() { void Collection::Stopped() {
//qLog(Debug) << __PRETTY_FUNCTION__;
CurrentSongChanged(Song()); CurrentSongChanged(Song());
} }
void Collection::CurrentSongChanged(const Song &song) { void Collection::CurrentSongChanged(const Song &song) {
//qLog(Debug) << __PRETTY_FUNCTION__;
TagReaderReply *reply = nullptr; TagReaderReply *reply = nullptr;
@ -140,8 +132,6 @@ void Collection::CurrentSongChanged(const Song &song) {
SongList Collection::FilterCurrentWMASong(SongList songs, Song* queued) { SongList Collection::FilterCurrentWMASong(SongList songs, Song* queued) {
//qLog(Debug) << __PRETTY_FUNCTION__;
for (SongList::iterator it = songs.begin(); it != songs.end(); ) { for (SongList::iterator it = songs.begin(); it != songs.end(); ) {
if (it->url() == current_wma_song_url_) { if (it->url() == current_wma_song_url_) {
*queued = *it; *queued = *it;

View File

@ -23,19 +23,18 @@
#include "config.h" #include "config.h"
#include <QHash>
#include <QObject> #include <QObject>
#include <QHash>
#include <QString>
#include <QUrl> #include <QUrl>
#include "core/song.h" #include "core/song.h"
class Application; class Application;
class Database; class Thread;
class CollectionBackend; class CollectionBackend;
class CollectionModel; class CollectionModel;
class CollectionWatcher; class CollectionWatcher;
class TaskManager;
class Thread;
class Collection : public QObject { class Collection : public QObject {
Q_OBJECT Q_OBJECT
@ -85,13 +84,11 @@ class Collection : public QObject {
CollectionWatcher *watcher_; CollectionWatcher *watcher_;
Thread *watcher_thread_; Thread *watcher_thread_;
// Hack: Gstreamer doesn't cope well with WMA files being rewritten while // Hack: Gstreamer doesn't cope well with WMA files being rewritten while being played,
// being played, so we delay statistics and rating changes until the current // so we delay statistics and rating changes until the current song has finished playing.
// song has finished playing.
QUrl current_wma_song_url_; QUrl current_wma_song_url_;
// DB schema versions which should trigger a full collection rescan (each of // DB schema versions which should trigger a full collection rescan (each of those with a short reason why).
// those with a short reason why).
QHash<int, QString> full_rescan_revisions_; QHash<int, QString> full_rescan_revisions_;
}; };

View File

@ -20,22 +20,32 @@
#include "config.h" #include "config.h"
#include <QCoreApplication> #include <QtGlobal>
#include <QDateTime> #include <QObject>
#include <QDir> #include <QMutex>
#include <QSet>
#include <QMap>
#include <QByteArray>
#include <QFileInfo> #include <QFileInfo>
#include <QSettings> #include <QDateTime>
#include <QVariant> #include <QVariant>
#include <QString>
#include <QStringList>
#include <QUrl>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QtDebug> #include <QtDebug>
#include "core/application.h"
#include "core/database.h"
#include "core/logging.h"
#include "core/scopedtransaction.h"
#include "core/utilities.h"
#include "directory.h"
#include "collectionbackend.h" #include "collectionbackend.h"
#include "collectionquery.h" #include "collectionquery.h"
#include "sqlrow.h" #include "sqlrow.h"
#include "core/application.h"
#include "core/database.h"
#include "core/scopedtransaction.h"
#include "core/tagreaderclient.h"
#include "core/utilities.h"
const char *CollectionBackend::kSettingsGroup = "Collection"; const char *CollectionBackend::kSettingsGroup = "Collection";
@ -258,6 +268,7 @@ void CollectionBackend::AddDirectory(const QString &path) {
dir.id = q.lastInsertId().toInt(); dir.id = q.lastInsertId().toInt();
emit DirectoryDiscovered(dir, SubdirectoryList()); emit DirectoryDiscovered(dir, SubdirectoryList());
} }
void CollectionBackend::RemoveDirectory(const Directory &dir) { void CollectionBackend::RemoveDirectory(const Directory &dir) {
@ -287,6 +298,7 @@ void CollectionBackend::RemoveDirectory(const Directory &dir) {
emit DirectoryDeleted(dir); emit DirectoryDeleted(dir);
transaction.Commit(); transaction.Commit();
} }
SongList CollectionBackend::FindSongsInDirectory(int id) { SongList CollectionBackend::FindSongsInDirectory(int id) {
@ -307,6 +319,7 @@ SongList CollectionBackend::FindSongsInDirectory(int id) {
ret << song; ret << song;
} }
return ret; return ret;
} }
void CollectionBackend::AddOrUpdateSubdirs(const SubdirectoryList &subdirs) { void CollectionBackend::AddOrUpdateSubdirs(const SubdirectoryList &subdirs) {
@ -381,8 +394,7 @@ void CollectionBackend::AddOrUpdateSongs(const SongList &songs) {
for (const Song &song : songs) { for (const Song &song : songs) {
// Do a sanity check first - make sure the song's directory still exists // Do a sanity check first - make sure the song's directory still exists
// This is to fix a possible race condition when a directory is removed // This is to fix a possible race condition when a directory is removed while CollectionWatcher is scanning it.
// while CollectionWatcher is scanning it.
if (!dirs_table_.isEmpty()) { if (!dirs_table_.isEmpty()) {
check_dir.bindValue(":id", song.directory_id()); check_dir.bindValue(":id", song.directory_id());
check_dir.exec(); check_dir.exec();
@ -752,8 +764,7 @@ void CollectionBackend::UpdateCompilations() {
QMutexLocker l(db_->Mutex()); QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect()); QSqlDatabase db(db_->Connect());
// Look for albums that have songs by more than one 'effective album artist' in the same // Look for albums that have songs by more than one 'effective album artist' in the same directory
// directory
QSqlQuery q(db); QSqlQuery q(db);
q.prepare(QString("SELECT effective_albumartist, album, filename, compilation_detected FROM %1 WHERE unavailable = 0 ORDER BY album").arg(songs_table_)); q.prepare(QString("SELECT effective_albumartist, album, filename, compilation_detected FROM %1 WHERE unavailable = 0 ORDER BY album").arg(songs_table_));
@ -819,8 +830,7 @@ void CollectionBackend::UpdateCompilations() {
void CollectionBackend::UpdateCompilations(QSqlQuery &find_songs, QSqlQuery &update, SongList &deleted_songs, SongList &added_songs, const QString &album, int compilation_detected) { void CollectionBackend::UpdateCompilations(QSqlQuery &find_songs, QSqlQuery &update, SongList &deleted_songs, SongList &added_songs, const QString &album, int compilation_detected) {
// Get songs that were already in that album, so we can tell the model // Get songs that were already in that album, so we can tell the model they've been updated
// they've been updated
find_songs.bindValue(":album", album); find_songs.bindValue(":album", album);
find_songs.bindValue(":compilation_detected", int(!compilation_detected)); find_songs.bindValue(":compilation_detected", int(!compilation_detected));
find_songs.exec(); find_songs.exec();
@ -1104,6 +1114,7 @@ void CollectionBackend::ResetStatistics(int id) {
} }
void CollectionBackend::DeleteAll() { void CollectionBackend::DeleteAll() {
{ {
QMutexLocker l(db_->Mutex()); QMutexLocker l(db_->Mutex());
QSqlDatabase db(db_->Connect()); QSqlDatabase db(db_->Connect());

View File

@ -23,13 +23,22 @@
#include "config.h" #include "config.h"
#include <QObject> #include <stdbool.h>
#include <QSet>
#include <QUrl> #include <QtGlobal>
#include <QObject>
#include <QList>
#include <QVector>
#include <QSet>
#include <QString>
#include <QStringList>
#include <QUrl>
#include <QSqlDatabase>
#include <QSqlQuery>
#include "directory.h"
#include "collectionquery.h"
#include "core/song.h" #include "core/song.h"
#include "collectionquery.h"
#include "directory.h"
class Database; class Database;
@ -94,11 +103,9 @@ class CollectionBackendInterface : public QObject {
virtual Song GetSongById(int id) = 0; virtual Song GetSongById(int id) = 0;
// Returns all sections of a song with the given filename. If there's just one section // Returns all sections of a song with the given filename. If there's just one section the resulting list will have it's size equal to 1.
// the resulting list will have it's size equal to 1.
virtual SongList GetSongsByUrl(const QUrl &url) = 0; virtual SongList GetSongsByUrl(const QUrl &url) = 0;
// Returns a section of a song with the given filename and beginning. If the section // Returns a section of a song with the given filename and beginning. If the section is not present in collection, returns invalid song.
// is not present in collection, returns invalid song.
// Using default beginning value is suitable when searching for single-section songs. // Using default beginning value is suitable when searching for single-section songs.
virtual Song GetSongByUrl(const QUrl &url, qint64 beginning = 0) = 0; virtual Song GetSongByUrl(const QUrl &url, qint64 beginning = 0) = 0;

View File

@ -20,15 +20,23 @@
#include "config.h" #include "config.h"
#include "collectiondirectorymodel.h" #include <QObject>
#include "collectionbackend.h" #include <QStandardItemModel>
#include <QAbstractItemModel>
#include <QVariant>
#include <QString>
#include <QUrl>
#include "core/application.h" #include "core/application.h"
#include "core/filesystemmusicstorage.h" #include "core/filesystemmusicstorage.h"
#include "core/iconloader.h"
#include "core/musicstorage.h" #include "core/musicstorage.h"
#include "core/utilities.h" #include "core/utilities.h"
#include "core/iconloader.h" #include "directory.h"
#include "collectionbackend.h"
#include "collectiondirectorymodel.h"
CollectionDirectoryModel::CollectionDirectoryModel(CollectionBackend* backend, QObject* parent) CollectionDirectoryModel::CollectionDirectoryModel(CollectionBackend *backend, QObject *parent)
: QStandardItemModel(parent), : QStandardItemModel(parent),
dir_icon_(IconLoader::Load("document-open-folder")), dir_icon_(IconLoader::Load("document-open-folder")),
backend_(backend) backend_(backend)
@ -43,7 +51,7 @@ CollectionDirectoryModel::~CollectionDirectoryModel() {}
void CollectionDirectoryModel::DirectoryDiscovered(const Directory &dir) { void CollectionDirectoryModel::DirectoryDiscovered(const Directory &dir) {
QStandardItem* item; QStandardItem *item;
if (Application::kIsPortable && Utilities::UrlOnSameDriveAsStrawberry(QUrl::fromLocalFile(dir.path))) { if (Application::kIsPortable && Utilities::UrlOnSameDriveAsStrawberry(QUrl::fromLocalFile(dir.path))) {
item = new QStandardItem(Utilities::GetRelativePathToStrawberryBin(QUrl::fromLocalFile(dir.path)).toLocalFile()); item = new QStandardItem(Utilities::GetRelativePathToStrawberryBin(QUrl::fromLocalFile(dir.path)).toLocalFile());
} }

View File

@ -25,11 +25,16 @@
#include <memory> #include <memory>
#include <QIcon> #include <QObject>
#include <QStandardItemModel> #include <QStandardItemModel>
#include <QList>
#include <QVariant>
#include <QString>
#include <QIcon>
#include "directory.h" class QModelIndex;
struct Directory;
class CollectionBackend; class CollectionBackend;
class MusicStorage; class MusicStorage;

View File

@ -20,23 +20,35 @@
#include "config.h" #include "config.h"
#include <QApplication>
#include <QWidget>
#include <QObject>
#include <QDataStream>
#include <QIODevice>
#include <QAction>
#include <QActionGroup> #include <QActionGroup>
#include <QInputDialog> #include <QByteArray>
#include <QKeyEvent> #include <QVariant>
#include <QMenu> #include <QString>
#include <QStringList>
#include <QRegExp> #include <QRegExp>
#include <QInputDialog>
#include <QList>
#include <QTimer>
#include <QMenu>
#include <QSettings> #include <QSettings>
#include <QSignalMapper> #include <QSignalMapper>
#include <QTimer> #include <QToolButton>
#include <QtEvents>
#include "collectionfilterwidget.h" #include "core/iconloader.h"
#include "core/song.h"
#include "collectionmodel.h" #include "collectionmodel.h"
#include "collectionquery.h" #include "collectionquery.h"
#include "savedgroupingmanager.h"
#include "collectionfilterwidget.h"
#include "groupbydialog.h" #include "groupbydialog.h"
#include "ui_collectionfilterwidget.h" #include "ui_collectionfilterwidget.h"
#include "core/song.h"
#include "core/iconloader.h"
#include "settings/settingsdialog.h"
CollectionFilterWidget::CollectionFilterWidget(QWidget *parent) CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
: QWidget(parent), : QWidget(parent),
@ -48,8 +60,7 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
delay_behaviour_(DelayedOnLargeLibraries) { delay_behaviour_(DelayedOnLargeLibraries) {
ui_->setupUi(this); ui_->setupUi(this);
// Add the available fields to the tooltip here instead of the ui // Add the available fields to the tooltip here instead of the ui file to prevent that they get translated by mistake.
// file to prevent that they get translated by mistake.
QString available_fields = Song::kFtsColumns.join(", ").replace(QRegExp("\\bfts"), ""); QString available_fields = Song::kFtsColumns.join(", ").replace(QRegExp("\\bfts"), "");
ui_->filter->setToolTip(ui_->filter->toolTip().arg(available_fields)); ui_->filter->setToolTip(ui_->filter->toolTip().arg(available_fields));
@ -125,8 +136,7 @@ void CollectionFilterWidget::UpdateGroupByActions() {
group_by_group_ = CreateGroupByActions(this); group_by_group_ = CreateGroupByActions(this);
group_by_menu_->clear(); group_by_menu_->clear();
group_by_menu_->addActions(group_by_group_->actions()); group_by_menu_->addActions(group_by_group_->actions());
connect(group_by_group_, SIGNAL(triggered(QAction*)), connect(group_by_group_, SIGNAL(triggered(QAction*)), SLOT(GroupByClicked(QAction*)));
SLOT(GroupByClicked(QAction*)));
if (model_) { if (model_) {
CheckCurrentGrouping(model_->GetGroupBy()); CheckCurrentGrouping(model_->GetGroupBy());
} }
@ -338,10 +348,9 @@ void CollectionFilterWidget::keyReleaseEvent(QKeyEvent *e) {
void CollectionFilterWidget::FilterTextChanged(const QString &text) { void CollectionFilterWidget::FilterTextChanged(const QString &text) {
// Searching with one or two characters can be very expensive on the database // Searching with one or two characters can be very expensive on the database even with FTS,
// even with FTS, so if there are a large number of songs in the database // so if there are a large number of songs in the database introduce a small delay before actually filtering the model,
// introduce a small delay before actually filtering the model, so if the // so if the user is typing the first few characters of something it will be quicker.
// user is typing the first few characters of something it will be quicker.
const bool delay = (delay_behaviour_ == AlwaysDelayed) || (delay_behaviour_ == DelayedOnLargeLibraries && !text.isEmpty() && text.length() < 3 && model_->total_song_count() >= 100000); const bool delay = (delay_behaviour_ == AlwaysDelayed) || (delay_behaviour_ == DelayedOnLargeLibraries && !text.isEmpty() && text.length() < 3 && model_->total_song_count() >= 100000);
if (delay) { if (delay) {

View File

@ -24,22 +24,29 @@
#include "config.h" #include "config.h"
#include <memory> #include <memory>
#include <stdbool.h>
#include <QWidget> #include <QWidget>
#include <QObject>
#include <QString>
#include <QMenu>
#include <QSignalMapper>
#include <QTimer>
#include <QAction>
#include <QActionGroup>
#include <QtEvents>
#include "collectionquery.h"
#include "collectionmodel.h" #include "collectionmodel.h"
#include "savedgroupingmanager.h"
class QKeyEvent;
class GroupByDialog; class GroupByDialog;
class SavedGroupingManager;
class SettingsDialog; class SettingsDialog;
class Ui_CollectionFilterWidget; class Ui_CollectionFilterWidget;
struct QueryOptions; struct QueryOptions;
class QMenu;
class QActionGroup;
class QSignalMapper;
class CollectionFilterWidget : public QWidget { class CollectionFilterWidget : public QWidget {
Q_OBJECT Q_OBJECT

View File

@ -23,9 +23,6 @@
#include "config.h" #include "config.h"
#include <QString>
#include <QList>
#include "core/simpletreeitem.h" #include "core/simpletreeitem.h"
#include "core/song.h" #include "core/song.h"

View File

@ -22,32 +22,44 @@
#include <functional> #include <functional>
#include <QObject>
#include <QtGlobal>
#include <QtConcurrentRun>
#include <QtAlgorithms>
#include <QMutex>
#include <QFuture> #include <QFuture>
#include <QDataStream>
#include <QMimeData>
#include <QIODevice> #include <QIODevice>
#include <QMetaEnum> #include <QByteArray>
#include <QNetworkCacheMetaData> #include <QVariant>
#include <QNetworkDiskCache> #include <QList>
#include <QPixmapCache> #include <QSet>
#include <QSettings> #include <QChar>
#include <QRegExp>
#include <QString>
#include <QStringList> #include <QStringList>
#include <QUrl> #include <QUrl>
#include <QtConcurrentRun> #include <QImage>
#include <QPixmapCache>
#include <QSettings>
#include <QtDebug>
#include "collectionmodel.h"
#include "collectionbackend.h"
#include "collectionitem.h"
#include "collectiondirectorymodel.h"
#include "collectionview.h"
#include "sqlrow.h"
#include "core/application.h" #include "core/application.h"
#include "core/closure.h"
#include "core/database.h" #include "core/database.h"
#include "core/iconloader.h"
#include "core/logging.h" #include "core/logging.h"
#include "core/taskmanager.h" #include "core/taskmanager.h"
#include "core/utilities.h" #include "collectionquery.h"
#include "core/iconloader.h" #include "collectionbackend.h"
#include "covermanager/albumcoverloader.h" #include "collectiondirectorymodel.h"
#include "collectionitem.h"
#include "collectionmodel.h"
#include "sqlrow.h"
#include "playlist/playlistmanager.h"
#include "playlist/songmimedata.h" #include "playlist/songmimedata.h"
#include "covermanager/albumcoverloader.h"
using std::placeholders::_1; using std::placeholders::_1;
using std::placeholders::_2; using std::placeholders::_2;
@ -178,11 +190,9 @@ void CollectionModel::SongsDiscovered(const SongList &songs) {
// Hey, we've already got that one! // Hey, we've already got that one!
if (song_nodes_.contains(song.id())) continue; if (song_nodes_.contains(song.id())) continue;
// Before we can add each song we need to make sure the required container // Before we can add each song we need to make sure the required container items already exist in the tree.
// items already exist in the tree. These depend on which "group by" // These depend on which "group by" settings the user has on the collection.
// settings the user has on the collection. Eg. if the user grouped by // Eg. if the user grouped by artist and album, we would need to make sure nodes for the song's artist and album were already in the tree.
// artist and album, we would need to make sure nodes for the song's artist
// and album were already in the tree.
// Find parent containers in the tree // Find parent containers in the tree
CollectionItem *container = root_; CollectionItem *container = root_;
@ -190,8 +200,7 @@ void CollectionModel::SongsDiscovered(const SongList &songs) {
GroupBy type = group_by_[i]; GroupBy type = group_by_[i];
if (type == GroupBy_None) break; if (type == GroupBy_None) break;
// Special case: if the song is a compilation and the current GroupBy // Special case: if the song is a compilation and the current GroupBy level is Artists, then we want the Various Artists node :(
// level is Artists, then we want the Various Artists node :(
if (IsArtistGroupBy(type) && song.is_compilation()) { if (IsArtistGroupBy(type) && song.is_compilation()) {
if (container->compilation_artist_node_ == nullptr) if (container->compilation_artist_node_ == nullptr)
CreateCompilationArtistNode(true, container); CreateCompilationArtistNode(true, container);
@ -240,15 +249,13 @@ void CollectionModel::SongsDiscovered(const SongList &songs) {
container = container_nodes_[i][key]; container = container_nodes_[i][key];
} }
// If we just created the damn thing then we don't need to continue into // If we just created the damn thing then we don't need to continue into it any further because it'll get lazy-loaded properly later.
// it any further because it'll get lazy-loaded properly later.
if (!container->lazy_loaded) break; if (!container->lazy_loaded) break;
} }
if (!container->lazy_loaded) continue; if (!container->lazy_loaded) continue;
// We've gone all the way down to the deepest level and everything was // We've gone all the way down to the deepest level and everything was already lazy loaded, so now we have to create the song in the container.
// already lazy loaded, so now we have to create the song in the container.
song_nodes_[song.id()] = ItemFromSong(GroupBy_None, true, false, container, song, -1); song_nodes_[song.id()] = ItemFromSong(GroupBy_None, true, false, container, song, -1);
} }
@ -256,9 +263,8 @@ void CollectionModel::SongsDiscovered(const SongList &songs) {
void CollectionModel::SongsSlightlyChanged(const SongList &songs) { void CollectionModel::SongsSlightlyChanged(const SongList &songs) {
// This is called if there was a minor change to the songs that will not // This is called if there was a minor change to the songs that will not normally require the collection to be restructured.
// normally require the collection to be restructured. We can just update our // We can just update our internal cache of Song objects without worrying about resetting the model.
// internal cache of Song objects without worrying about resetting the model.
for (const Song &song : songs) { for (const Song &song : songs) {
if (song_nodes_.contains(song.id())) { if (song_nodes_.contains(song.id())) {
song_nodes_[song.id()]->metadata = song; song_nodes_[song.id()]->metadata = song;
@ -285,8 +291,7 @@ CollectionItem *CollectionModel::CreateCompilationArtistNode(bool signal, Collec
QString CollectionModel::DividerKey(GroupBy type, CollectionItem *item) const { QString CollectionModel::DividerKey(GroupBy type, CollectionItem *item) const {
// Items which are to be grouped under the same divider must produce the // Items which are to be grouped under the same divider must produce the same divider key. This will only get called for top-level items.
// same divider key. This will only get called for top-level items.
if (item->sort_text.isEmpty()) return QString(); if (item->sort_text.isEmpty()) return QString();
@ -371,8 +376,7 @@ QString CollectionModel::DividerDisplayText(GroupBy type, const QString &key) co
void CollectionModel::SongsDeleted(const SongList &songs) { void CollectionModel::SongsDeleted(const SongList &songs) {
// Delete the actual song nodes first, keeping track of each parent so we // Delete the actual song nodes first, keeping track of each parent so we might check to see if they're empty later.
// might check to see if they're empty later.
QSet<CollectionItem*> parents; QSet<CollectionItem*> parents;
for (const Song &song : songs) { for (const Song &song : songs) {
if (song_nodes_.contains(song.id())) { if (song_nodes_.contains(song.id())) {
@ -386,11 +390,9 @@ void CollectionModel::SongsDeleted(const SongList &songs) {
endRemoveRows(); endRemoveRows();
} }
else { else {
// If we get here it means some of the songs we want to delete haven't // If we get here it means some of the songs we want to delete haven't been lazy-loaded yet.
// been lazy-loaded yet. This is bad, because it would mean that to // This is bad, because it would mean that to clean up empty parents we would need to lazy-load them all individually to see if they're empty.
// clean up empty parents we would need to lazy-load them all // This can take a very long time, so better to just reset the model and be done with it.
// individually to see if they're empty. This can take a very long time,
// so better to just reset the model and be done with it.
Reset(); Reset();
return; return;
} }
@ -399,9 +401,8 @@ void CollectionModel::SongsDeleted(const SongList &songs) {
// Now delete empty parents // Now delete empty parents
QSet<QString> divider_keys; QSet<QString> divider_keys;
while (!parents.isEmpty()) { while (!parents.isEmpty()) {
// Since we are going to remove elements from the container, we // Since we are going to remove elements from the container, we need a copy to iterate over.
// need a copy to iterate over. If we iterate over the original, // If we iterate over the original, the behavior will be undefined.
// the behavior will be undefined.
QSet<CollectionItem*> parents_copy = parents; QSet<CollectionItem*> parents_copy = parents;
for (CollectionItem *node : parents_copy) { for (CollectionItem *node : parents_copy) {
parents.remove(node); parents.remove(node);

View File

@ -23,26 +23,40 @@
#include "config.h" #include "config.h"
#include <QAbstractItemModel> #include <stdbool.h>
#include <QIcon>
#include <QNetworkDiskCache> #include <QtGlobal>
#include <QObject>
#include <QAbstractItemModel>
#include <QFuture>
#include <QDataStream>
#include <QList>
#include <QMap>
#include <QMetaType>
#include <QMimeData>
#include <QPair>
#include <QSet>
#include <QVariant>
#include <QString>
#include <QStringList>
#include <QUrl>
#include <QImage>
#include <QIcon>
#include <QPixmap>
#include <QNetworkDiskCache>
#include <QSettings>
#include "collectionitem.h"
#include "collectionquery.h"
#include "collectionwatcher.h"
#include "sqlrow.h"
#include "core/simpletreemodel.h" #include "core/simpletreemodel.h"
#include "core/song.h" #include "core/song.h"
#include "collectionquery.h"
#include "collectionitem.h"
#include "sqlrow.h"
#include "covermanager/albumcoverloaderoptions.h" #include "covermanager/albumcoverloaderoptions.h"
#include "engine/engine_fwd.h"
#include "playlist/playlistmanager.h"
class Application; class Application;
class AlbumCoverLoader;
class CollectionDirectoryModel;
class CollectionBackend; class CollectionBackend;
class CollectionDirectoryModel;
class QSettings; class CollectionItem;
class CollectionModel : public SimpleTreeModel<CollectionItem> { class CollectionModel : public SimpleTreeModel<CollectionItem> {
Q_OBJECT Q_OBJECT
@ -190,8 +204,7 @@ signals:
private: private:
// Provides some optimisations for loading the list of items in the root. // Provides some optimisations for loading the list of items in the root.
// This gets called a lot when filtering the playlist, so it's nice to be // This gets called a lot when filtering the playlist, so it's nice to be able to do it in a background thread.
// able to do it in a background thread.
QueryResult RunQuery(CollectionItem *parent); QueryResult RunQuery(CollectionItem *parent);
void PostQuery(CollectionItem *parent, const QueryResult &result, bool signal); void PostQuery(CollectionItem *parent, const QueryResult &result, bool signal);
@ -200,25 +213,18 @@ signals:
void BeginReset(); void BeginReset();
// Functions for working with queries and creating items. // Functions for working with queries and creating items.
// When the model is reset or when a node is lazy-loaded the Collection // When the model is reset or when a node is lazy-loaded the Collection constructs a database query to populate the items.
// constructs a database query to populate the items. Filters are added // Filters are added for each parent item, restricting the songs returned to a particular album or artist for example.
// for each parent item, restricting the songs returned to a particular
// album or artist for example.
static void InitQuery(GroupBy type, CollectionQuery *q); static void InitQuery(GroupBy type, CollectionQuery *q);
void FilterQuery(GroupBy type, CollectionItem *item, CollectionQuery *q); void FilterQuery(GroupBy type, CollectionItem *item, CollectionQuery *q);
// Items can be created either from a query that's been run to populate a // Items can be created either from a query that's been run to populate a node, or by a spontaneous SongsDiscovered emission from the backend.
// node, or by a spontaneous SongsDiscovered emission from the backend.
CollectionItem *ItemFromQuery(GroupBy type, bool signal, bool create_divider, CollectionItem *parent, const SqlRow &row, int container_level); CollectionItem *ItemFromQuery(GroupBy type, bool signal, bool create_divider, CollectionItem *parent, const SqlRow &row, int container_level);
CollectionItem *ItemFromSong(GroupBy type, bool signal, bool create_divider, CollectionItem *parent, const Song &s, int container_level); CollectionItem *ItemFromSong(GroupBy type, bool signal, bool create_divider, CollectionItem *parent, const Song &s, int container_level);
// The "Various Artists" node is an annoying special case. // The "Various Artists" node is an annoying special case.
CollectionItem *CreateCompilationArtistNode(bool signal, CollectionItem *parent); CollectionItem *CreateCompilationArtistNode(bool signal, CollectionItem *parent);
// Smart playlists are shown in another top-level node
void ItemFromSmartPlaylist(const QSettings &s, bool notify) const;
// Helpers for ItemFromQuery and ItemFromSong // Helpers for ItemFromQuery and ItemFromSong
CollectionItem *InitItem(GroupBy type, bool signal, CollectionItem *parent, int container_level); CollectionItem *InitItem(GroupBy type, bool signal, CollectionItem *parent, int container_level);
void FinishItem(GroupBy type, bool signal, bool create_divider, CollectionItem *parent, CollectionItem *item); void FinishItem(GroupBy type, bool signal, bool create_divider, CollectionItem *parent, CollectionItem *item);
@ -256,8 +262,7 @@ signals:
QIcon artist_icon_; QIcon artist_icon_;
QIcon album_icon_; QIcon album_icon_;
// used as a generic icon to show when no cover art is found, // Used as a generic icon to show when no cover art is found, fixed to the same size as the artwork (32x32)
// fixed to the same size as the artwork (32x32)
QPixmap no_cover_icon_; QPixmap no_cover_icon_;
QIcon playlists_dir_icon_; QIcon playlists_dir_icon_;
QIcon playlist_icon_; QIcon playlist_icon_;

View File

@ -20,10 +20,14 @@
#include "config.h" #include "config.h"
#include <QVariant>
#include <QString>
#include <QUrl>
#include "collectionplaylistitem.h" #include "collectionplaylistitem.h"
#include "core/tagreaderclient.h" #include "core/tagreaderclient.h"
#include <QSettings> class SqlRow;
CollectionPlaylistItem::CollectionPlaylistItem(const QString &type) CollectionPlaylistItem::CollectionPlaylistItem(const QString &type)
: PlaylistItem(type) {} : PlaylistItem(type) {}

View File

@ -23,9 +23,17 @@
#include "config.h" #include "config.h"
#include <stdbool.h>
#include <QVariant>
#include <QString>
#include <QUrl>
#include "core/song.h" #include "core/song.h"
#include "playlist/playlistitem.h" #include "playlist/playlistitem.h"
class SqlRow;
class CollectionPlaylistItem : public PlaylistItem { class CollectionPlaylistItem : public PlaylistItem {
public: public:
CollectionPlaylistItem(const QString &type); CollectionPlaylistItem(const QString &type);

View File

@ -20,21 +20,26 @@
#include "config.h" #include "config.h"
#include <QtGlobal>
#include <QDateTime>
#include <QVariant>
#include <QString>
#include <QStringList>
#include <QStringBuilder>
#include <QRegExp>
#include <QSqlDatabase>
#include <QSqlQuery>
#include "collectionquery.h" #include "collectionquery.h"
#include "core/song.h" #include "core/song.h"
#include <QtDebug>
#include <QDateTime>
#include <QSqlError>
QueryOptions::QueryOptions() : max_age_(-1), query_mode_(QueryMode_All) {} QueryOptions::QueryOptions() : max_age_(-1), query_mode_(QueryMode_All) {}
CollectionQuery::CollectionQuery(const QueryOptions& options) CollectionQuery::CollectionQuery(const QueryOptions& options)
: include_unavailable_(false), join_with_fts_(false), limit_(-1) { : include_unavailable_(false), join_with_fts_(false), limit_(-1) {
if (!options.filter().isEmpty()) { if (!options.filter().isEmpty()) {
// We need to munge the filter text a little bit to get it to work as // We need to munge the filter text a little bit to get it to work as expected with sqlite's FTS3:
// expected with sqlite's FTS3:
// 1) Append * to all tokens. // 1) Append * to all tokens.
// 2) Prefix "fts" to column names. // 2) Prefix "fts" to column names.
// 3) Remove colons which don't correspond to column names. // 3) Remove colons which don't correspond to column names.
@ -50,8 +55,7 @@ CollectionQuery::CollectionQuery(const QueryOptions& options)
if (token.contains(':')) { if (token.contains(':')) {
// Only prefix fts if the token is a valid column name. // Only prefix fts if the token is a valid column name.
if (Song::kFtsColumns.contains("fts" + token.section(':', 0, 0), if (Song::kFtsColumns.contains("fts" + token.section(':', 0, 0), Qt::CaseInsensitive)) {
Qt::CaseInsensitive)) {
// Account for multiple colons. // Account for multiple colons.
QString columntoken = token.section(':', 0, 0, QString::SectionIncludeTrailingSep); QString columntoken = token.section(':', 0, 0, QString::SectionIncludeTrailingSep);
QString subtoken = token.section(':', 1, -1); QString subtoken = token.section(':', 1, -1);
@ -82,16 +86,12 @@ CollectionQuery::CollectionQuery(const QueryOptions& options)
bound_values_ << cutoff; bound_values_ << cutoff;
} }
// TODO: currently you cannot use any QueryMode other than All and fts at the // TODO: Currently you cannot use any QueryMode other than All and fts at the same time.
// same time. // Joining songs, duplicated_songs and songs_fts all together takes a huge amount of time.
// joining songs, duplicated_songs and songs_fts all together takes a huge // The query takes about 20 seconds on my machine then. Why?
// amount of // Untagged mode could work with additional filtering but I'm disabling it just to be consistent
// time. the query takes about 20 seconds on my machine then. why? // this way filtering is available only in the All mode.
// untagged mode could work with additional filtering but I'm disabling it // Remember though that when you fix the Duplicates + FTS cooperation, enable the filtering in both Duplicates and Untagged modes.
// just to be
// consistent - this way filtering is available only in the All mode.
// remember though that when you fix the Duplicates + FTS cooperation, enable
// the filtering in both Duplicates and Untagged modes.
duplicates_only_ = options.query_mode() == QueryOptions::QueryMode_Duplicates; duplicates_only_ = options.query_mode() == QueryOptions::QueryMode_Duplicates;
if (options.query_mode() == QueryOptions::QueryMode_Untagged) { if (options.query_mode() == QueryOptions::QueryMode_Untagged) {
@ -114,7 +114,7 @@ void CollectionQuery::AddWhere(const QString &column, const QVariant &value, con
// ignore 'literal' for IN // ignore 'literal' for IN
if (!op.compare("IN", Qt::CaseInsensitive)) { if (!op.compare("IN", Qt::CaseInsensitive)) {
QStringList final; QStringList final;
for (const QString& single_value : value.toStringList()) { for (const QString &single_value : value.toStringList()) {
final.append("?"); final.append("?");
bound_values_ << single_value; bound_values_ << single_value;
} }
@ -122,8 +122,7 @@ void CollectionQuery::AddWhere(const QString &column, const QVariant &value, con
where_clauses_ << QString("%1 IN (" + final.join(",") + ")").arg(column); where_clauses_ << QString("%1 IN (" + final.join(",") + ")").arg(column);
} }
else { else {
// Do integers inline - sqlite seems to get confused when you pass integers // Do integers inline - sqlite seems to get confused when you pass integers to bound parameters
// to bound parameters
if (value.type() == QVariant::Int) { if (value.type() == QVariant::Int) {
where_clauses_ << QString("%1 %2 %3").arg(column, op, value.toString()); where_clauses_ << QString("%1 %2 %3").arg(column, op, value.toString());
} }
@ -136,10 +135,8 @@ void CollectionQuery::AddWhere(const QString &column, const QVariant &value, con
} }
void CollectionQuery::AddCompilationRequirement(bool compilation) { void CollectionQuery::AddCompilationRequirement(bool compilation) {
// The unary + is added to prevent sqlite from using the index // The unary + is added to prevent sqlite from using the index idx_comp_artist.
// idx_comp_artist. When joining with fts, sqlite 3.8 has a tendency // When joining with fts, sqlite 3.8 has a tendency to use this index and thereby nesting the tables in an order which gives very poor performance
// to use this index and thereby nesting the tables in an order
// which gives very poor performance
where_clauses_ << QString("+compilation_effective = %1").arg(compilation ? 1 : 0); where_clauses_ << QString("+compilation_effective = %1").arg(compilation ? 1 : 0);
@ -175,7 +172,7 @@ QSqlQuery CollectionQuery::Exec(QSqlDatabase db, const QString &songs_table, con
query_.prepare(sql); query_.prepare(sql);
// Bind values // Bind values
for (const QVariant& value : bound_values_) { for (const QVariant &value : bound_values_) {
query_.addBindValue(value); query_.addBindValue(value);
} }

View File

@ -23,11 +23,14 @@
#include "config.h" #include "config.h"
#include <QString> #include <stdbool.h>
#include <QMetaType>
#include <QVariant> #include <QVariant>
#include <QSqlQuery> #include <QString>
#include <QStringList> #include <QStringList>
#include <QVariantList> #include <QSqlDatabase>
#include <QSqlQuery>
class Song; class Song;
class CollectionBackend; class CollectionBackend;
@ -36,13 +39,9 @@ class CollectionBackend;
struct QueryOptions { struct QueryOptions {
// Modes of CollectionQuery: // Modes of CollectionQuery:
// - use the all songs table // - use the all songs table
// - use the duplicated songs view; by duplicated we mean those songs // - use the duplicated songs view; by duplicated we mean those songs for which the (artist, album, title) tuple is found more than once in the songs table
// for which the (artist, album, title) tuple is found more than once // - use the untagged songs view; by untagged we mean those for which at least one of the (artist, album, title) tags is empty
// in the songs table // Please note that additional filtering based on fts table (the filter attribute) won't work in Duplicates and Untagged modes.
// - use the untagged songs view; by untagged we mean those for which
// at least one of the (artist, album, title) tags is empty
// Please note that additional filtering based on fts table (the filter
// attribute) won't work in Duplicates and Untagged modes.
enum QueryMode { enum QueryMode {
QueryMode_All, QueryMode_All,
QueryMode_Duplicates, QueryMode_Duplicates,
@ -83,8 +82,7 @@ class CollectionQuery {
// Sets an ORDER BY clause on the query. // Sets an ORDER BY clause on the query.
void SetOrderBy(const QString &order_by) { order_by_ = order_by; } void SetOrderBy(const QString &order_by) { order_by_ = order_by; }
// Adds a fragment of WHERE clause. When executed, this Query will connect all // Adds a fragment of WHERE clause. When executed, this Query will connect all the fragments with AND operator.
// the fragments with AND operator.
// Please note that IN operator expects a QStringList as value. // Please note that IN operator expects a QStringList as value.
void AddWhere(const QString &column, const QVariant &value, const QString &op = "="); void AddWhere(const QString &column, const QVariant &value, const QString &op = "=");

View File

@ -20,35 +20,59 @@
#include "config.h" #include "config.h"
#include "collectionview.h" #include <qcoreevent.h>
#include <QPainter> #include <QtGlobal>
#include <QContextMenuEvent> #include <QWidget>
#include <QHelpEvent> #include <QItemSelectionModel>
#include <QMenu>
#include <QMessageBox>
#include <QSet>
#include <QSettings>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QAbstractItemView>
#include <QStyleOptionViewItem>
#include <QAction>
#include <QVariant>
#include <QString>
#include <QUrl>
#include <QList>
#include <QLocale>
#include <QMap>
#include <QMessageBox>
#include <QMenu>
#include <QMimeData>
#include <QPainter>
#include <QPalette>
#include <QPen>
#include <QPoint>
#include <QRect>
#include <QSet>
#include <QSize>
#include <QToolTip> #include <QToolTip>
#include <QTreeView>
#include <QWhatsThis> #include <QWhatsThis>
#include <QBrush>
#include <QColor>
#include <QFont>
#include <QFontMetrics>
#include <QPixmap>
#include <QIcon>
#include <QLinearGradient>
#include <QSettings>
#include <QtEvents>
#include "core/application.h"
#include "core/iconloader.h"
#include "core/mimedata.h"
#include "core/utilities.h"
#include "collectionbackend.h"
#include "collectiondirectorymodel.h" #include "collectiondirectorymodel.h"
#include "collectionfilterwidget.h" #include "collectionfilterwidget.h"
#include "collectionmodel.h"
#include "collectionitem.h" #include "collectionitem.h"
#include "collectionbackend.h" #include "collectionmodel.h"
#include "core/application.h" #include "collectionview.h"
#include "core/logging.h"
#include "core/mimedata.h"
#include "core/musicstorage.h"
#include "core/utilities.h"
#include "core/iconloader.h"
#include "device/devicemanager.h" #include "device/devicemanager.h"
#include "device/devicestatefiltermodel.h" #include "device/devicestatefiltermodel.h"
#include "dialogs/edittagdialog.h"
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
#include "dialogs/organisedialog.h" #include "dialogs/organisedialog.h"
#include "dialogs/organiseerrordialog.h"
#endif #endif
#include "settings/collectionsettingspage.h" #include "settings/collectionsettingspage.h"
@ -293,8 +317,7 @@ bool CollectionView::RestoreLevelFocus(const QModelIndex &parent) {
} }
else if (last_selected_path_.contains(text)) { else if (last_selected_path_.contains(text)) {
emit expand(current); emit expand(current);
// If a selected container or song were not found, we've got into a wrong subtree // If a selected container or song were not found, we've got into a wrong subtree (happens with "unknown" all the time)
// (happens with "unknown" all the time)
if (!RestoreLevelFocus(current)) { if (!RestoreLevelFocus(current)) {
emit collapse(current); emit collapse(current);
} }
@ -312,8 +335,6 @@ bool CollectionView::RestoreLevelFocus(const QModelIndex &parent) {
void CollectionView::ReloadSettings() { void CollectionView::ReloadSettings() {
//qLog(Debug) << __PRETTY_FUNCTION__;
QSettings settings; QSettings settings;
settings.beginGroup(CollectionSettingsPage::kSettingsGroup); settings.beginGroup(CollectionSettingsPage::kSettingsGroup);
@ -330,8 +351,6 @@ void CollectionView::ReloadSettings() {
void CollectionView::SetApplication(Application *app) { void CollectionView::SetApplication(Application *app) {
//qLog(Debug) << __PRETTY_FUNCTION__;
app_ = app; app_ = app;
ReloadSettings(); ReloadSettings();
@ -342,8 +361,6 @@ void CollectionView::SetFilter(CollectionFilterWidget *filter) { filter_ = filte
void CollectionView::TotalSongCountUpdated(int count) { void CollectionView::TotalSongCountUpdated(int count) {
//qLog(Debug) << __FUNCTION__ << count;
bool old = total_song_count_; bool old = total_song_count_;
total_song_count_ = count; total_song_count_ = count;
if (old != total_song_count_) update(); if (old != total_song_count_) update();
@ -359,8 +376,6 @@ void CollectionView::TotalSongCountUpdated(int count) {
void CollectionView::TotalArtistCountUpdated(int count) { void CollectionView::TotalArtistCountUpdated(int count) {
//qLog(Debug) << __FUNCTION__ << count;
bool old = total_artist_count_; bool old = total_artist_count_;
total_artist_count_ = count; total_artist_count_ = count;
if (old != total_artist_count_) update(); if (old != total_artist_count_) update();
@ -376,8 +391,6 @@ void CollectionView::TotalArtistCountUpdated(int count) {
void CollectionView::TotalAlbumCountUpdated(int count) { void CollectionView::TotalAlbumCountUpdated(int count) {
//qLog(Debug) << __FUNCTION__ << count;
bool old = total_album_count_; bool old = total_album_count_;
total_album_count_ = count; total_album_count_ = count;
if (old != total_album_count_) update(); if (old != total_album_count_) update();
@ -393,8 +406,6 @@ void CollectionView::TotalAlbumCountUpdated(int count) {
void CollectionView::paintEvent(QPaintEvent *event) { void CollectionView::paintEvent(QPaintEvent *event) {
//qLog(Debug) << __FUNCTION__;
if (total_song_count_ == 0) { if (total_song_count_ == 0) {
QPainter p(viewport()); QPainter p(viewport());
QRect rect(viewport()->rect()); QRect rect(viewport()->rect());
@ -491,7 +502,6 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
} }
// TODO: check if custom plugin actions should be enabled / visible // TODO: check if custom plugin actions should be enabled / visible
//const int songs_selected = smart_playlists + smart_playlists_header + regular_elements;
const int songs_selected = regular_elements; const int songs_selected = regular_elements;
const bool regular_elements_only = songs_selected == regular_elements && regular_elements > 0; const bool regular_elements_only = songs_selected == regular_elements && regular_elements > 0;
@ -502,7 +512,6 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
add_to_playlist_enqueue_->setEnabled(songs_selected); add_to_playlist_enqueue_->setEnabled(songs_selected);
// if neither edit_track not edit_tracks are available, we show disabled edit_track element // if neither edit_track not edit_tracks are available, we show disabled edit_track element
//edit_track_->setVisible(!smart_playlists_only && (regular_editable <= 1));
edit_track_->setVisible(regular_editable <= 1); edit_track_->setVisible(regular_editable <= 1);
edit_track_->setEnabled(regular_editable == 1); edit_track_->setEnabled(regular_editable == 1);
@ -534,9 +543,9 @@ void CollectionView::ShowInVarious(bool on) {
if (!context_menu_index_.isValid()) return; if (!context_menu_index_.isValid()) return;
// Map is from album name -> all artists sharing that album name, built from each selected // Map is from album name -> all artists sharing that album name, built from each selected song.
// song. We put through "Various Artists" changes one album at a time, to make sure the old album // We put through "Various Artists" changes one album at a time,
// node gets removed (due to all children removed), before the new one gets added // to make sure the old album node gets removed (due to all children removed), before the new one gets added
QMultiMap<QString, QString> albums; QMultiMap<QString, QString> albums;
for (const Song& song : GetSelectedSongs()) { for (const Song& song : GetSelectedSongs()) {
if (albums.find(song.album(), song.artist()) == albums.end()) if (albums.find(song.album(), song.artist()) == albums.end())
@ -586,16 +595,12 @@ void CollectionView::Load() {
void CollectionView::AddToPlaylist() { void CollectionView::AddToPlaylist() {
//qLog(Debug) << __PRETTY_FUNCTION__;
emit AddToPlaylistSignal(model()->mimeData(selectedIndexes())); emit AddToPlaylistSignal(model()->mimeData(selectedIndexes()));
} }
void CollectionView::AddToPlaylistEnqueue() { void CollectionView::AddToPlaylistEnqueue() {
//qLog(Debug) << __PRETTY_FUNCTION__;
QMimeData *data = model()->mimeData(selectedIndexes()); QMimeData *data = model()->mimeData(selectedIndexes());
if (MimeData* mime_data = qobject_cast<MimeData*>(data)) { if (MimeData* mime_data = qobject_cast<MimeData*>(data)) {
mime_data->enqueue_now_ = true; mime_data->enqueue_now_ = true;

View File

@ -24,21 +24,38 @@
#include "config.h" #include "config.h"
#include <memory> #include <memory>
#include <stdbool.h>
#include <QObject>
#include <QWidget>
#include <QAbstractItemModel>
#include <QAbstractItemView>
#include <QString>
#include <QPixmap>
#include <QPainter>
#include <QSet>
#include <QStyleOption>
#include <QStyledItemDelegate> #include <QStyledItemDelegate>
#include <QStyleOptionViewItem>
#include <QAction>
#include <QMenu>
#include <QtEvents>
#include "core/song.h" #include "core/song.h"
#include "dialogs/edittagdialog.h"
#include "widgets/autoexpandingtreeview.h" #include "widgets/autoexpandingtreeview.h"
class QContextMenuEvent;
class QHelpEvent;
class QMouseEvent;
class QPaintEvent;
class Application; class Application;
class CollectionFilterWidget; class CollectionFilterWidget;
class EditTagDialog;
#ifdef HAVE_GSTREAMER #ifdef HAVE_GSTREAMER
class OrganiseDialog; class OrganiseDialog;
#endif #endif
class QMimeData;
class CollectionItemDelegate : public QStyledItemDelegate { class CollectionItemDelegate : public QStyledItemDelegate {
Q_OBJECT Q_OBJECT
@ -57,11 +74,8 @@ class CollectionView : public AutoExpandingTreeView {
CollectionView(QWidget *parent = nullptr); CollectionView(QWidget *parent = nullptr);
~CollectionView(); ~CollectionView();
//static const char *kSettingsGroup; // Returns Songs currently selected in the collection view.
// Please note that the selection is recursive meaning that if for example an album is selected this will return all of it's songs.
// Returns Songs currently selected in the collection view. Please note that the
// selection is recursive meaning that if for example an album is selected
// this will return all of it's songs.
SongList GetSelectedSongs() const; SongList GetSelectedSongs() const;
void SetApplication(Application *app); void SetApplication(Application *app);

View File

@ -20,6 +20,11 @@
#include "config.h" #include "config.h"
#include <QWidget>
#include <QKeyEvent>
#include "collectionfilterwidget.h"
#include "collectionview.h"
#include "collectionviewcontainer.h" #include "collectionviewcontainer.h"
#include "ui_collectionviewcontainer.h" #include "ui_collectionviewcontainer.h"

View File

@ -23,6 +23,7 @@
#include "config.h" #include "config.h"
#include <QObject>
#include <QWidget> #include <QWidget>
class CollectionFilterWidget; class CollectionFilterWidget;

View File

@ -20,34 +20,42 @@
#include "config.h" #include "config.h"
#include <fileref.h> #include <QObject>
#include <tag.h> #include <QIODevice>
#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QFileInfo>
#include <QMetaObject>
#include <QDateTime>
#include <QHash>
#include <QMap>
#include <QList>
#include <QSet>
#include <QTimer>
#include <QVariant>
#include <QString>
#include <QStringList>
#include <QUrl>
#include <QImage>
#include <QSettings>
#include <QtDebug>
// This is defined by one of the windows headers that is included by taglib.
#ifdef RemoveDirectory
#undef RemoveDirectory
#endif
#include "collectionwatcher.h"
#include "collectionbackend.h"
#include "core/filesystemwatcherinterface.h" #include "core/filesystemwatcherinterface.h"
#include "core/logging.h" #include "core/logging.h"
#include "core/tagreaderclient.h" #include "core/tagreaderclient.h"
#include "core/taskmanager.h" #include "core/taskmanager.h"
#include "core/utilities.h" #include "core/utilities.h"
#include "directory.h"
#include "collectionbackend.h"
#include "collectionwatcher.h"
#include "playlistparsers/cueparser.h" #include "playlistparsers/cueparser.h"
#include "settings/collectionsettingspage.h" #include "settings/collectionsettingspage.h"
#include <QDateTime> // This is defined by one of the windows headers that is included by taglib.
#include <QDirIterator> #ifdef RemoveDirectory
#include <QtDebug> #undef RemoveDirectory
#include <QThread> #endif
#include <QDateTime>
#include <QHash>
#include <QSet>
#include <QSettings>
#include <QTimer>
namespace { namespace {
static const char *kNoMediaFile = ".nomedia"; static const char *kNoMediaFile = ".nomedia";
@ -218,8 +226,7 @@ void CollectionWatcher::AddDirectory(const Directory &dir, const SubdirectoryLis
ScanSubdirectory(dir.path, Subdirectory(), &transaction); ScanSubdirectory(dir.path, Subdirectory(), &transaction);
} }
else { else {
// We can do an incremental scan - looking at the mtimes of each // We can do an incremental scan - looking at the mtimes of each subdirectory and only rescan if the directory has changed.
// subdirectory and only rescan if the directory has changed.
ScanTransaction transaction(this, dir.id, true); ScanTransaction transaction(this, dir.id, true);
transaction.SetKnownSubdirs(subdirs); transaction.SetKnownSubdirs(subdirs);
transaction.AddToProgressMax(subdirs.count()); transaction.AddToProgressMax(subdirs.count());
@ -268,8 +275,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
QStringList files_on_disk; QStringList files_on_disk;
SubdirectoryList my_new_subdirs; SubdirectoryList my_new_subdirs;
// If a directory is moved then only its parent gets a changed notification, // If a directory is moved then only its parent gets a changed notification, so we need to look and see if any of our children don't exist any more.
// so we need to look and see if any of our children don't exist any more.
// If one has been removed, "rescan" it to get the deleted songs // If one has been removed, "rescan" it to get the deleted songs
SubdirectoryList previous_subdirs = t->GetImmediateSubdirs(path); SubdirectoryList previous_subdirs = t->GetImmediateSubdirs(path);
for (const Subdirectory& subdir : previous_subdirs) { for (const Subdirectory& subdir : previous_subdirs) {
@ -289,8 +295,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
if (child_info.isDir()) { if (child_info.isDir()) {
if (!child_info.isHidden() && !t->HasSeenSubdir(child)) { if (!child_info.isHidden() && !t->HasSeenSubdir(child)) {
// We haven't seen this subdirectory before - add it to a list and // We haven't seen this subdirectory before - add it to a list and later we'll tell the backend about it and scan it.
// later we'll tell the backend about it and scan it.
Subdirectory new_subdir; Subdirectory new_subdir;
new_subdir.directory_id = -1; new_subdir.directory_id = -1;
new_subdir.path = child; new_subdir.path = child;
@ -332,8 +337,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
QFileInfo file_info(file); QFileInfo file_info(file);
if (!file_info.exists()) { if (!file_info.exists()) {
// Partially fixes race condition - if file was removed between being // Partially fixes race condition - if file was removed between being added to the list and now.
// added to the list and now.
files_on_disk.removeAll(file); files_on_disk.removeAll(file);
continue; continue;
} }
@ -345,8 +349,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
bool cue_deleted = song_cue_mtime == 0 && matching_song.has_cue(); bool cue_deleted = song_cue_mtime == 0 && matching_song.has_cue();
bool cue_added = matching_cue_mtime != 0 && !matching_song.has_cue(); bool cue_added = matching_cue_mtime != 0 && !matching_song.has_cue();
// watch out for cue songs which have their mtime equal to // watch out for cue songs which have their mtime equal to qMax(media_file_mtime, cue_sheet_mtime)
// qMax(media_file_mtime, cue_sheet_mtime)
bool changed = (matching_song.mtime() != qMax(file_info.lastModified().toTime_t(), song_cue_mtime)) || cue_deleted || cue_added; bool changed = (matching_song.mtime() != qMax(file_info.lastModified().toTime_t(), song_cue_mtime)) || cue_deleted || cue_added;
// Also want to look to see whether the album art has changed // Also want to look to see whether the album art has changed
@ -372,7 +375,8 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
// nothing has changed - mark the song available without re-scanning // nothing has changed - mark the song available without re-scanning
if (matching_song.is_unavailable()) t->readded_songs << matching_song; if (matching_song.is_unavailable()) t->readded_songs << matching_song;
} else { }
else {
// The song is on disk but not in the DB // The song is on disk but not in the DB
SongList song_list = ScanNewFile(file, path, matching_cue, &cues_processed); SongList song_list = ScanNewFile(file, path, matching_cue, &cues_processed);
@ -437,7 +441,7 @@ void CollectionWatcher::UpdateCueAssociatedSongs(const QString &file, const QStr
QSet<int> used_ids; QSet<int> used_ids;
// update every song that's in the cue and collection // Update every song that's in the cue and collection
for (Song cue_song : cue_parser_->Load(&cue, matching_cue, path)) { for (Song cue_song : cue_parser_->Load(&cue, matching_cue, path)) {
cue_song.set_directory_id(t->dir()); cue_song.set_directory_id(t->dir());
@ -463,9 +467,7 @@ void CollectionWatcher::UpdateCueAssociatedSongs(const QString &file, const QStr
void CollectionWatcher::UpdateNonCueAssociatedSong(const QString &file, const Song &matching_song, const QString &image, bool cue_deleted, ScanTransaction *t) { void CollectionWatcher::UpdateNonCueAssociatedSong(const QString &file, const Song &matching_song, const QString &image, bool cue_deleted, ScanTransaction *t) {
// if a cue got deleted, we turn it's first section into the new // If a cue got deleted, we turn it's first section into the new 'raw' (cueless) song and we just remove the rest of the sections from the collection
// 'raw' (cueless) song and we just remove the rest of the sections
// from the collection
if (cue_deleted) { if (cue_deleted) {
for (const Song &song : for (const Song &song :
backend_->GetSongsByUrl(QUrl::fromLocalFile(file))) { backend_->GetSongsByUrl(QUrl::fromLocalFile(file))) {
@ -490,7 +492,7 @@ SongList CollectionWatcher::ScanNewFile(const QString &file, const QString &path
SongList song_list; SongList song_list;
uint matching_cue_mtime = GetMtimeForCue(matching_cue); uint matching_cue_mtime = GetMtimeForCue(matching_cue);
// if it's a cue - create virtual tracks // If it's a cue - create virtual tracks
if (matching_cue_mtime) { if (matching_cue_mtime) {
// don't process the same cue many times // don't process the same cue many times
if (cues_processed->contains(matching_cue)) return song_list; if (cues_processed->contains(matching_cue)) return song_list;
@ -498,9 +500,9 @@ SongList CollectionWatcher::ScanNewFile(const QString &file, const QString &path
QFile cue(matching_cue); QFile cue(matching_cue);
cue.open(QIODevice::ReadOnly); cue.open(QIODevice::ReadOnly);
// Ignore FILEs pointing to other media files. Also, watch out for incorrect // Ignore FILEs pointing to other media files.
// media files. Playlist parser for CUEs considers every entry in sheet // Also, watch out for incorrect media files.
// valid and we don't want invalid media getting into collection! // Playlist parser for CUEs considers every entry in sheet valid and we don't want invalid media getting into collection!
QString file_nfd = file.normalized(QString::NormalizationForm_D); QString file_nfd = file.normalized(QString::NormalizationForm_D);
for (const Song& cue_song : cue_parser_->Load(&cue, matching_cue, path)) { for (const Song& cue_song : cue_parser_->Load(&cue, matching_cue, path)) {
if (cue_song.url().toLocalFile().normalized(QString::NormalizationForm_D) == file_nfd) { if (cue_song.url().toLocalFile().normalized(QString::NormalizationForm_D) == file_nfd) {
@ -533,15 +535,13 @@ void CollectionWatcher::PreserveUserSetData(const QString &file, const QString &
out->set_id(matching_song.id()); out->set_id(matching_song.id());
// Previous versions of Clementine incorrectly overwrote this and // Previous versions of Clementine incorrectly overwrote this and stored it in the DB,
// stored it in the DB, so we can't rely on matching_song to // so we can't rely on matching_song to know if it has embedded artwork or not, but we can check here.
// know if it has embedded artwork or not, but we can check here.
if (!out->has_embedded_cover()) out->set_art_automatic(image); if (!out->has_embedded_cover()) out->set_art_automatic(image);
out->MergeUserSetData(matching_song); out->MergeUserSetData(matching_song);
// The song was deleted from the database (e.g. due to an unmounted // The song was deleted from the database (e.g. due to an unmounted filesystem), but has been restored.
// filesystem), but has been restored.
if (matching_song.is_unavailable()) { if (matching_song.is_unavailable()) {
qLog(Debug) << file << " unavailable song restored"; qLog(Debug) << file << " unavailable song restored";
@ -562,7 +562,7 @@ void CollectionWatcher::PreserveUserSetData(const QString &file, const QString &
uint CollectionWatcher::GetMtimeForCue(const QString &cue_path) { uint CollectionWatcher::GetMtimeForCue(const QString &cue_path) {
// slight optimisation // Slight optimisation
if (cue_path.isEmpty()) { if (cue_path.isEmpty()) {
return 0; return 0;
} }
@ -593,7 +593,7 @@ void CollectionWatcher::RemoveDirectory(const Directory &dir) {
watched_dirs_.remove(dir.id); watched_dirs_.remove(dir.id);
// Stop watching the directory's subdirectories // Stop watching the directory's subdirectories
for (const QString& subdir_path : subdir_mapping_.keys(dir)) { for (const QString &subdir_path : subdir_mapping_.keys(dir)) {
fs_watcher_->RemovePath(subdir_path); fs_watcher_->RemovePath(subdir_path);
subdir_mapping_.remove(subdir_path); subdir_mapping_.remove(subdir_path);
} }
@ -662,8 +662,7 @@ QString CollectionWatcher::PickBestImage(const QStringList &images) {
QStringList filtered; QStringList filtered;
for (const QString &filter_text : best_image_filters_) { for (const QString &filter_text : best_image_filters_) {
// the images in the images list are represented by a full path, // The images in the images list are represented by a full path, so we need to isolate just the filename
// so we need to isolate just the filename
for (const QString& image : images) { for (const QString& image : images) {
QFileInfo file_info(image); QFileInfo file_info(image);
QString filename(file_info.fileName()); QString filename(file_info.fileName());
@ -671,14 +670,13 @@ QString CollectionWatcher::PickBestImage(const QStringList &images) {
filtered << image; filtered << image;
} }
/* We assume the filters are give in the order best to worst, so // We assume the filters are give in the order best to worst, so if we've got a result, we go with it.
if we've got a result, we go with it. Otherwise we might // Otherwise we might start capturing more generic rules
start capturing more generic rules */
if (!filtered.isEmpty()) break; if (!filtered.isEmpty()) break;
} }
if (filtered.isEmpty()) { if (filtered.isEmpty()) {
// the filter was too restrictive, just use the original list // The filter was too restrictive, just use the original list
filtered = images; filtered = images;
} }
@ -734,7 +732,7 @@ void CollectionWatcher::ReloadSettings() {
best_image_filters_.clear(); best_image_filters_.clear();
QStringList filters = s.value("cover_art_patterns", QStringList() << "front" << "cover").toStringList(); QStringList filters = s.value("cover_art_patterns", QStringList() << "front" << "cover").toStringList();
for (const QString& filter : filters) { for (const QString &filter : filters) {
QString s = filter.trimmed(); QString s = filter.trimmed();
if (!s.isEmpty()) best_image_filters_ << s; if (!s.isEmpty()) best_image_filters_ << s;
} }

View File

@ -23,22 +23,24 @@
#include "config.h" #include "config.h"
#include "directory.h" #include <stdbool.h>
#include <QHash> #include <QtGlobal>
#include <QObject> #include <QObject>
#include <QStringList> #include <QHash>
#include <QMap> #include <QMap>
#include <QSet>
#include <QString>
#include <QStringList>
#include <QTimer>
#include "directory.h"
#include "core/song.h" #include "core/song.h"
class QFileSystemWatcher;
class QTimer;
class CueParser;
class FileSystemWatcherInterface;
class CollectionBackend; class CollectionBackend;
class FileSystemWatcherInterface;
class TaskManager; class TaskManager;
class CueParser;
class CollectionWatcher : public QObject { class CollectionWatcher : public QObject {
Q_OBJECT Q_OBJECT
@ -76,14 +78,11 @@ signals:
private: private:
// This class encapsulates a full or partial scan of a directory. // This class encapsulates a full or partial scan of a directory.
// Each directory has one or more subdirectories, and any number of // Each directory has one or more subdirectories, and any number of subdirectories can be scanned during one transaction.
// subdirectories can be scanned during one transaction. ScanSubdirectory() // ScanSubdirectory() adds its results to the members of this transaction class,
// adds its results to the members of this transaction class, and they are // and they are "committed" through calls to the CollectionBackend in the transaction's dtor.
// "committed" through calls to the CollectionBackend in the transaction's dtor. // The transaction also caches the list of songs in this directory according to the collection.
// The transaction also caches the list of songs in this directory according // Multiple calls to FindSongsInSubdirectory during one transaction will only result in one call to CollectionBackend::FindSongsInDirectory.
// to the collection. Multiple calls to FindSongsInSubdirectory during one
// transaction will only result in one call to
// CollectionBackend::FindSongsInDirectory.
class ScanTransaction { class ScanTransaction {
public: public:
ScanTransaction(CollectionWatcher *watcher, int dir, bool incremental, bool ignores_mtime = false); ScanTransaction(CollectionWatcher *watcher, int dir, bool incremental, bool ignores_mtime = false);
@ -120,10 +119,9 @@ signals:
int dir_; int dir_;
// Incremental scan enters a directory only if it has changed since the last scan. // Incremental scan enters a directory only if it has changed since the last scan.
bool incremental_; bool incremental_;
// This type of scan updates every file in a folder that's // This type of scan updates every file in a folder that's being scanned.
// being scanned. Even if it detects the file hasn't changed since // Even if it detects the file hasn't changed since the last scan.
// the last scan. Also, since it's ignoring mtimes on folders too, // Also, since it's ignoring mtimes on folders too, it will go as deep in the folder hierarchy as it's possible.
// it will go as deep in the folder hierarchy as it's possible.
bool ignores_mtime_; bool ignores_mtime_;
CollectionWatcher *watcher_; CollectionWatcher *watcher_;
@ -153,18 +151,14 @@ signals:
uint GetMtimeForCue(const QString &cue_path); uint GetMtimeForCue(const QString &cue_path);
void PerformScan(bool incremental, bool ignore_mtimes); void PerformScan(bool incremental, bool ignore_mtimes);
// Updates the sections of a cue associated and altered (according to mtime) // Updates the sections of a cue associated and altered (according to mtime) media file during a scan.
// media file during a scan.
void UpdateCueAssociatedSongs(const QString &file, const QString &path, const QString &matching_cue, const QString &image, ScanTransaction *t); void UpdateCueAssociatedSongs(const QString &file, const QString &path, const QString &matching_cue, const QString &image, ScanTransaction *t);
// Updates a single non-cue associated and altered (according to mtime) song // Updates a single non-cue associated and altered (according to mtime) song during a scan.
// during a scan.
void UpdateNonCueAssociatedSong(const QString &file, const Song &matching_song, const QString &image, bool cue_deleted, ScanTransaction *t); void UpdateNonCueAssociatedSong(const QString &file, const Song &matching_song, const QString &image, bool cue_deleted, ScanTransaction *t);
// Updates a new song with some metadata taken from it's equivalent old // Updates a new song with some metadata taken from it's equivalent old song (for example rating and score).
// song (for example rating and score).
void PreserveUserSetData(const QString &file, const QString &image, const Song &matching_song, Song *out, ScanTransaction *t); void PreserveUserSetData(const QString &file, const QString &image, const Song &matching_song, Song *out, ScanTransaction *t);
// Scans a single media file that's present on the disk but not yet in the collection. // Scans a single media file that's present on the disk but not yet in the collection.
// It may result in a multiple files added to the collection when the media file // It may result in a multiple files added to the collection when the media file has many sections (like a CUE related media file).
// has many sections (like a CUE related media file).
SongList ScanNewFile(const QString &file, const QString &path, const QString &matching_cue, QSet<QString> *cues_processed); SongList ScanNewFile(const QString &file, const QString &path, const QString &matching_cue, QSet<QString> *cues_processed);
private: private:
@ -175,11 +169,8 @@ signals:
FileSystemWatcherInterface *fs_watcher_; FileSystemWatcherInterface *fs_watcher_;
QHash<QString, Directory> subdir_mapping_; QHash<QString, Directory> subdir_mapping_;
/* A list of words use to try to identify the (likely) best image // A list of words use to try to identify the (likely) best image found in an directory to use as cover artwork.
* found in an directory to use as cover artwork. // e.g. using ["front", "cover"] would identify front.jpg and exclude back.jpg.
* e.g. using ["front", "cover"] would identify front.jpg and
* exclude back.jpg.
*/
QStringList best_image_filters_; QStringList best_image_filters_;
bool stop_requested_; bool stop_requested_;

View File

@ -23,11 +23,10 @@
#include "config.h" #include "config.h"
#include <QMetaType>
#include <QList> #include <QList>
#include <QString> #include <QString>
#include <QMetaType> #include <QSqlQuery>
class QSqlQuery;
struct Directory { struct Directory {
Directory() : id(-1) {} Directory() : id(-1) {}

View File

@ -22,8 +22,13 @@
#include <functional> #include <functional>
#include <QDialog>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QPushButton> #include <QPushButton>
#include <QWidget>
#include "collectionmodel.h"
#include "groupbydialog.h" #include "groupbydialog.h"
#include "ui_groupbydialog.h" #include "ui_groupbydialog.h"
@ -31,9 +36,13 @@
using std::placeholders::_1; using std::placeholders::_1;
using std::placeholders::_2; using std::placeholders::_2;
#include <boost/multi_index_container.hpp> #include <boost/multi_index/indexed_by.hpp>
#include <boost/multi_index/member.hpp> #include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp> #include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/tag.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index_container_fwd.hpp>
#include <boost/operators.hpp>
using boost::multi_index_container; using boost::multi_index_container;
using boost::multi_index::indexed_by; using boost::multi_index::indexed_by;
@ -70,7 +79,7 @@ class GroupByDialogPrivate {
MappingContainer mapping_; MappingContainer mapping_;
}; };
GroupByDialog::GroupByDialog(QWidget* parent) : QDialog(parent), ui_(new Ui_GroupByDialog), p_(new GroupByDialogPrivate) { GroupByDialog::GroupByDialog(QWidget *parent) : QDialog(parent), ui_(new Ui_GroupByDialog), p_(new GroupByDialogPrivate) {
ui_->setupUi(this); ui_->setupUi(this);
Reset(); Reset();

View File

@ -26,6 +26,9 @@
#include <memory> #include <memory>
#include <QDialog> #include <QDialog>
#include <QObject>
#include <QWidget>
#include <QString>
#include "collectionmodel.h" #include "collectionmodel.h"

View File

@ -18,19 +18,33 @@
* *
*/ */
#include "config.h" #include <stdbool.h>
#include <QDialog>
#include <QWidget>
#include <QStandardItemModel>
#include <QItemSelectionModel>
#include <QAbstractItemModel>
#include <QIODevice>
#include <QDataStream>
#include <QByteArray>
#include <QList>
#include <QVariant>
#include <QString>
#include <QStringList>
#include <QKeySequence>
#include <QPushButton>
#include <QTreeView>
#include <QSettings>
#include <QtDebug>
#include "core/logging.h"
#include "core/iconloader.h" #include "core/iconloader.h"
#include "collectionfilterwidget.h" #include "collectionfilterwidget.h"
#include "collectionmodel.h" #include "collectionmodel.h"
#include "savedgroupingmanager.h" #include "savedgroupingmanager.h"
#include "ui_savedgroupingmanager.h" #include "ui_savedgroupingmanager.h"
#include <QKeySequence>
#include <QList>
#include <QSettings>
#include <QStandardItem>
SavedGroupingManager::SavedGroupingManager(QWidget *parent) SavedGroupingManager::SavedGroupingManager(QWidget *parent)
: QDialog(parent), : QDialog(parent),
ui_(new Ui_SavedGroupingManager), ui_(new Ui_SavedGroupingManager),
@ -58,6 +72,7 @@ SavedGroupingManager::~SavedGroupingManager() {
} }
QString SavedGroupingManager::GroupByToString(const CollectionModel::GroupBy &g) { QString SavedGroupingManager::GroupByToString(const CollectionModel::GroupBy &g) {
switch (g) { switch (g) {
case CollectionModel::GroupBy_None: { case CollectionModel::GroupBy_None: {
return tr("None"); return tr("None");
@ -137,8 +152,7 @@ void SavedGroupingManager::Remove() {
if (ui_->list->selectionModel()->hasSelection()) { if (ui_->list->selectionModel()->hasSelection()) {
QSettings s; QSettings s;
s.beginGroup(CollectionModel::kSavedGroupingsSettingsGroup); s.beginGroup(CollectionModel::kSavedGroupingsSettingsGroup);
for (const QModelIndex &index : for (const QModelIndex &index : ui_->list->selectionModel()->selectedRows()) {
ui_->list->selectionModel()->selectedRows()) {
if (index.isValid()) { if (index.isValid()) {
qLog(Debug) << "Remove saved grouping: " << model_->item(index.row(), 0)->text(); qLog(Debug) << "Remove saved grouping: " << model_->item(index.row(), 0)->text();
s.remove(model_->item(index.row(), 0)->text()); s.remove(model_->item(index.row(), 0)->text());

View File

@ -24,12 +24,15 @@
#include "config.h" #include "config.h"
#include <QDialog> #include <QDialog>
#include <QObject>
#include <QWidget>
#include <QStandardItemModel> #include <QStandardItemModel>
#include <QString>
#include "collectionmodel.h" #include "collectionmodel.h"
class Ui_SavedGroupingManager;
class CollectionFilterWidget; class CollectionFilterWidget;
class Ui_SavedGroupingManager;
class SavedGroupingManager : public QDialog { class SavedGroupingManager : public QDialog {
Q_OBJECT Q_OBJECT

View File

@ -20,12 +20,14 @@
#include "config.h" #include "config.h"
#include "collectionquery.h" #include <QVariant>
#include "sqlrow.h"
#include <QSqlQuery> #include <QSqlQuery>
#include <QSqlRecord> #include <QSqlRecord>
#include "sqlrow.h"
#include "collectionquery.h"
SqlRow::SqlRow(const QSqlQuery &query) { Init(query); } SqlRow::SqlRow(const QSqlQuery &query) { Init(query); }
SqlRow::SqlRow(const CollectionQuery &query) { Init(query); } SqlRow::SqlRow(const CollectionQuery &query) { Init(query); }

View File

@ -25,8 +25,7 @@
#include <QList> #include <QList>
#include <QVariant> #include <QVariant>
#include <QSqlQuery>
class QSqlQuery;
class CollectionQuery; class CollectionQuery;
@ -37,7 +36,7 @@ class SqlRow {
SqlRow(const QSqlQuery &query); SqlRow(const QSqlQuery &query);
SqlRow(const CollectionQuery &query); SqlRow(const CollectionQuery &query);
const QVariant& value(int i) const { return columns_[i]; } const QVariant &value(int i) const { return columns_[i]; }
QList<QVariant> columns_; QList<QVariant> columns_;

View File

@ -20,11 +20,16 @@
#include "config.h" #include "config.h"
#include "appearance.h" #include <stdbool.h>
#include <QApplication> #include <QApplication>
#include <QObject>
#include <QVariant>
#include <QPalette>
#include <QColor>
#include <QSettings> #include <QSettings>
#include "appearance.h"
#include "settings/appearancesettingspage.h" #include "settings/appearancesettingspage.h"
const char *Appearance::kUseCustomColorSet = "use-custom-set"; const char *Appearance::kUseCustomColorSet = "use-custom-set";

View File

@ -29,17 +29,17 @@
class Appearance : public QObject { class Appearance : public QObject {
public: public:
explicit Appearance(QObject* parent = nullptr); explicit Appearance(QObject *parent = nullptr);
// Load the user preferred theme, which could the default system theme or a custom set of colors that user has chosen // Load the user preferred theme, which could the default system theme or a custom set of colors that user has chosen
void LoadUserTheme(); void LoadUserTheme();
void ResetToSystemDefaultTheme(); void ResetToSystemDefaultTheme();
void ChangeForegroundColor(const QColor& color); void ChangeForegroundColor(const QColor &color);
void ChangeBackgroundColor(const QColor& color); void ChangeBackgroundColor(const QColor &color);
static const char* kSettingsGroup; static const char *kSettingsGroup;
static const char* kUseCustomColorSet; static const char *kUseCustomColorSet;
static const char* kForegroundColor; static const char *kForegroundColor;
static const char* kBackgroundColor; static const char *kBackgroundColor;
static const QPalette kDefaultPalette; static const QPalette kDefaultPalette;
private: private:

View File

@ -18,20 +18,27 @@
* *
*/ */
#include "application.h"
#include "config.h" #include "config.h"
#include "core/appearance.h" #include "application.h"
#include "core/database.h"
#include <functional>
#include <QObject>
#include <QThread>
#include <QString>
#include "core/closure.h"
#include "core/lazy.h" #include "core/lazy.h"
#include "core/player.h"
#include "core/tagreaderclient.h" #include "core/tagreaderclient.h"
#include "core/taskmanager.h"
#include "engine/enginetype.h" #include "database.h"
#include "taskmanager.h"
#include "player.h"
#include "appearance.h"
#include "engine/enginedevice.h" #include "engine/enginedevice.h"
#include "device/devicemanager.h" #include "device/devicemanager.h"
#include "collection/collectionbackend.h"
#include "collection/collection.h" #include "collection/collection.h"
#include "playlist/playlistbackend.h" #include "playlist/playlistbackend.h"
#include "playlist/playlistmanager.h" #include "playlist/playlistmanager.h"
@ -40,7 +47,7 @@
#include "covermanager/currentartloader.h" #include "covermanager/currentartloader.h"
#ifdef HAVE_LIBLASTFM #ifdef HAVE_LIBLASTFM
#include "covermanager/lastfmcoverprovider.h" #include "covermanager/lastfmcoverprovider.h"
#endif // HAVE_LIBLASTFM #endif
#include "covermanager/amazoncoverprovider.h" #include "covermanager/amazoncoverprovider.h"
#include "covermanager/discogscoverprovider.h" #include "covermanager/discogscoverprovider.h"
#include "covermanager/musicbrainzcoverprovider.h" #include "covermanager/musicbrainzcoverprovider.h"
@ -77,9 +84,9 @@ class ApplicationImpl {
cover_providers_([=]() { cover_providers_([=]() {
CoverProviders *cover_providers = new CoverProviders(app); CoverProviders *cover_providers = new CoverProviders(app);
// Initialize the repository of cover providers. // Initialize the repository of cover providers.
#ifdef HAVE_LIBLASTFM #ifdef HAVE_LIBLASTFM
cover_providers->AddProvider(new LastFmCoverProvider(app)); cover_providers->AddProvider(new LastFmCoverProvider(app));
#endif #endif
cover_providers->AddProvider(new AmazonCoverProvider(app)); cover_providers->AddProvider(new AmazonCoverProvider(app));
cover_providers->AddProvider(new DiscogsCoverProvider(app)); cover_providers->AddProvider(new DiscogsCoverProvider(app));
cover_providers->AddProvider(new MusicbrainzCoverProvider(app)); cover_providers->AddProvider(new MusicbrainzCoverProvider(app));
@ -121,8 +128,7 @@ Application::Application(QObject *parent)
Application::~Application() { Application::~Application() {
// It's important that the device manager is deleted before the database. // It's important that the device manager is deleted before the database.
// Deleting the database deletes all objects that have been created in its // Deleting the database deletes all objects that have been created in its thread, including some device collection backends.
// thread, including some device collection backends.
p_->device_manager_.reset(); p_->device_manager_.reset();
for (QThread *thread : threads_) { for (QThread *thread : threads_) {
@ -151,14 +157,6 @@ void Application::MoveToThread(QObject *object, QThread *thread) {
void Application::AddError(const QString& message) { emit ErrorAdded(message); } void Application::AddError(const QString& message) { emit ErrorAdded(message); }
QString Application::language_without_region() const {
const int underscore = language_name_.indexOf('_');
if (underscore != -1) {
return language_name_.left(underscore);
}
return language_name_;
}
void Application::ReloadSettings() { emit SettingsChanged(); } void Application::ReloadSettings() { emit SettingsChanged(); }
void Application::OpenSettingsDialogAtPage(SettingsDialog::Page page) { void Application::OpenSettingsDialogAtPage(SettingsDialog::Page page) {

View File

@ -24,29 +24,31 @@
#include "config.h" #include "config.h"
#include <memory> #include <memory>
#include <stdbool.h>
#include <QObject> #include <QObject>
#include <QString>
#include <QThread> #include <QThread>
#include <QList>
#include <QString>
#include "settings/settingsdialog.h" #include "settings/settingsdialog.h"
class TaskManager;
class ApplicationImpl; class ApplicationImpl;
class TagReaderClient; class TagReaderClient;
class Database; class Database;
class Appearance; class EngineDevice;
class TaskManager;
class Player; class Player;
class DeviceManager; class Appearance;
class Collection; class Collection;
class PlaylistBackend;
class PlaylistManager;
class AlbumCoverLoader;
class CoverProviders;
class CurrentArtLoader;
class CollectionBackend; class CollectionBackend;
class CollectionModel; class CollectionModel;
class EngineDevice; class PlaylistBackend;
class PlaylistManager;
class DeviceManager;
class CoverProviders;
class AlbumCoverLoader;
class CurrentArtLoader;
class Application : public QObject { class Application : public QObject {
Q_OBJECT Q_OBJECT
@ -57,11 +59,6 @@ class Application : public QObject {
explicit Application(QObject *parent = nullptr); explicit Application(QObject *parent = nullptr);
~Application(); ~Application();
const QString &language_name() const { return language_name_; }
// Same as language_name, but remove the region code at the end if there is one
QString language_without_region() const;
void set_language_name(const QString &name) { language_name_ = name; }
TagReaderClient *tag_reader_client() const; TagReaderClient *tag_reader_client() const;
Database *database() const; Database *database() const;
Appearance *appearance() const; Appearance *appearance() const;
@ -96,7 +93,6 @@ signals:
void SettingsDialogRequested(SettingsDialog::Page page); void SettingsDialogRequested(SettingsDialog::Page page);
private: private:
QString language_name_;
std::unique_ptr<ApplicationImpl> p_; std::unique_ptr<ApplicationImpl> p_;
QList<QThread*> threads_; QList<QThread*> threads_;

View File

@ -23,6 +23,8 @@
#include "config.h" #include "config.h"
#include <QList>
#include <QString>
#include <QDateTime> #include <QDateTime>
#include <QSettings> #include <QSettings>

View File

@ -19,19 +19,25 @@
*/ */
#include "config.h" #include "config.h"
#include "commandlineoptions.h"
#include "version.h" #include "version.h"
#include "core/logging.h"
#include <cstdlib> #include <cstdlib>
#include <getopt.h> #include <getopt.h>
#include <iostream> #include <iostream>
#include <QtGlobal>
#include <QObject>
#include <QIODevice>
#include <QDataStream>
#include <QBuffer> #include <QBuffer>
#include <QCoreApplication> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QByteArray>
#include <QString>
#include <QUrl>
#include "commandlineoptions.h"
#include "core/logging.h"
const char *CommandlineOptions::kHelpText = const char *CommandlineOptions::kHelpText =
"%1: strawberry [%2] [%3]\n" "%1: strawberry [%2] [%3]\n"
@ -93,7 +99,7 @@ CommandlineOptions::CommandlineOptions(int argc, char* *argv)
RemoveArg("-session", 2); RemoveArg("-session", 2);
} }
void CommandlineOptions::RemoveArg(const QString& starts_with, int count) { void CommandlineOptions::RemoveArg(const QString &starts_with, int count) {
for (int i = 0; i < argc_; ++i) { for (int i = 0; i < argc_; ++i) {
QString opt(argv_[i]); QString opt(argv_[i]);

View File

@ -23,9 +23,13 @@
#include "config.h" #include "config.h"
#include <QList> #include <stdbool.h>
#include <QUrl>
#include <QDataStream> #include <QDataStream>
#include <QByteArray>
#include <QList>
#include <QString>
#include <QUrl>
class CommandlineOptions { class CommandlineOptions {
friend QDataStream &operator<<(QDataStream &s, const CommandlineOptions &a); friend QDataStream &operator<<(QDataStream &s, const CommandlineOptions &a);

View File

@ -20,27 +20,36 @@
#include "config.h" #include "config.h"
#include "database.h" #include <sqlite3.h>
#include "scopedtransaction.h"
#include "core/application.h"
#include "core/logging.h"
#include "core/taskmanager.h"
#include <boost/scope_exit.hpp> #include <boost/scope_exit.hpp>
#include <sqlite3.h> #include <QObject>
#include <QCoreApplication>
#include <QStandardPaths>
#include <QDir>
#include <QLibrary>
#include <QLibraryInfo>
#include <QSqlDriver>
#include <QSqlQuery>
#include <QtDebug>
#include <QThread> #include <QThread>
#include <QUrl> #include <QMutex>
#include <QIODevice>
#include <QDir>
#include <QFile>
#include <QChar>
#include <QList>
#include <QByteArray>
#include <QVariant> #include <QVariant>
#include <QString>
#include <QStringBuilder>
#include <QStringList>
#include <QRegExp>
#include <QUrl>
#include <QSqlDriver>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QStandardPaths>
#include <QtDebug>
#include "core/logging.h"
#include "taskmanager.h"
#include "database.h"
#include "application.h"
#include "scopedtransaction.h"
const char *Database::kDatabaseFilename = "strawberry.db"; const char *Database::kDatabaseFilename = "strawberry.db";
const int Database::kSchemaVersion = 0; const int Database::kSchemaVersion = 0;
@ -269,7 +278,8 @@ QSqlDatabase Database::Connect() {
{ {
#ifdef SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER #ifdef SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER
// In case sqlite>=3.12 is compiled without -DSQLITE_ENABLE_FTS3_TOKENIZER (generally a good idea due to security reasons) the fts3 support should be enabled explicitly. // In case sqlite>=3.12 is compiled without -DSQLITE_ENABLE_FTS3_TOKENIZER
// (generally a good idea due to security reasons) the fts3 support should be enabled explicitly.
QVariant v = db.driver()->handle(); QVariant v = db.driver()->handle();
if (v.isValid() && qstrcmp(v.typeName(), "sqlite3*") == 0) { if (v.isValid() && qstrcmp(v.typeName(), "sqlite3*") == 0) {
sqlite3 *handle = *static_cast<sqlite3**>(v.data()); sqlite3 *handle = *static_cast<sqlite3**>(v.data());
@ -283,8 +293,7 @@ QSqlDatabase Database::Connect() {
if (!set_fts_tokenizer.exec()) { if (!set_fts_tokenizer.exec()) {
qLog(Warning) << "Couldn't register FTS3 tokenizer : " << set_fts_tokenizer.lastError(); qLog(Warning) << "Couldn't register FTS3 tokenizer : " << set_fts_tokenizer.lastError();
} }
// Implicit invocation of ~QSqlQuery() when leaving the scope // Implicit invocation of ~QSqlQuery() when leaving the scope to release any remaining database locks!
// to release any remaining database locks!
} }
if (db.tables().count() == 0) { if (db.tables().count() == 0) {
@ -313,8 +322,7 @@ QSqlDatabase Database::Connect() {
UpdateMainSchema(&db); UpdateMainSchema(&db);
} }
// We might have to initialise the schema in some attached databases now, if // We might have to initialise the schema in some attached databases now, if they were deleted and don't match up with the main schema version.
// they were deleted and don't match up with the main schema version.
for (const QString &key : attached_databases_.keys()) { for (const QString &key : attached_databases_.keys()) {
if (attached_databases_[key].is_temporary_ && attached_databases_[key].schema_.isEmpty()) if (attached_databases_[key].is_temporary_ && attached_databases_[key].schema_.isEmpty())
continue; continue;
@ -338,8 +346,7 @@ void Database::UpdateMainSchema(QSqlDatabase *db) {
{ {
QSqlQuery q("SELECT version FROM schema_version", *db); QSqlQuery q("SELECT version FROM schema_version", *db);
if (q.next()) schema_version = q.value(0).toInt(); if (q.next()) schema_version = q.value(0).toInt();
// Implicit invocation of ~QSqlQuery() when leaving the scope // Implicit invocation of ~QSqlQuery() when leaving the scope to release any remaining database locks!
// to release any remaining database locks!
} }
startup_schema_version_ = schema_version; startup_schema_version_ = schema_version;
@ -382,9 +389,8 @@ void Database::RecreateAttachedDb(const QString &database_name) {
} }
} }
// We can't just re-attach the database now because it needs to be done for // We can't just re-attach the database now because it needs to be done for each thread.
// each thread. Close all the database connections, so each thread will // Close all the database connections, so each thread will re-attach it when they next connect.
// re-attach it when they next connect.
for (const QString &name : QSqlDatabase::connectionNames()) { for (const QString &name : QSqlDatabase::connectionNames()) {
QSqlDatabase::removeDatabase(name); QSqlDatabase::removeDatabase(name);
} }
@ -482,12 +488,9 @@ void Database::ExecSchemaCommands(QSqlDatabase &db, const QString &schema, int s
// Run each command // Run each command
const QStringList commands(schema.split(QRegExp("; *\n\n"))); const QStringList commands(schema.split(QRegExp("; *\n\n")));
// We don't want this list to reflect possible DB schema changes // We don't want this list to reflect possible DB schema changes so we initialize it before executing any statements.
// so we initialize it before executing any statements. // If no outer transaction is provided the song tables need to be queried before beginning an inner transaction! Otherwise
// If no outer transaction is provided the song tables need to // DROP TABLE commands on song tables may fail due to database locks.
// be queried before beginning an inner transaction! Otherwise
// DROP TABLE commands on song tables may fail due to database
// locks.
const QStringList song_tables(SongsTables(db, schema_version)); const QStringList song_tables(SongsTables(db, schema_version));
if (!in_transaction) { if (!in_transaction) {
@ -505,12 +508,10 @@ void Database::ExecSongTablesCommands(QSqlDatabase &db, const QStringList &song_
for (const QString &command : commands) { for (const QString &command : commands) {
// There are now lots of "songs" tables that need to have the same schema: // There are now lots of "songs" tables that need to have the same schema:
// songs, magnatune_songs, and device_*_songs. We allow a magic value // songs, magnatune_songs, and device_*_songs. We allow a magic value in the schema files to update all songs tables at once.
// in the schema files to update all songs tables at once.
if (command.contains(kMagicAllSongsTables)) { if (command.contains(kMagicAllSongsTables)) {
for (const QString &table : song_tables) { for (const QString &table : song_tables) {
// Another horrible hack: device songs tables don't have matching _fts // Another horrible hack: device songs tables don't have matching _fts tables, so if this command tries to touch one, ignore it.
// tables, so if this command tries to touch one, ignore it.
if (table.startsWith("device_") && if (table.startsWith("device_") &&
command.contains(QString(kMagicAllSongsTables) + "_fts")) { command.contains(QString(kMagicAllSongsTables) + "_fts")) {
continue; continue;

View File

@ -23,15 +23,20 @@
#include "config.h" #include "config.h"
#include <QMap> #include <stdbool.h>
#include <QMutex>
#include <QObject>
#include <QSqlDatabase>
#include <QSqlError>
#include <QStringList>
#include <sqlite3.h> #include <sqlite3.h>
#include <QtGlobal>
#include <QObject>
#include <QMutex>
#include <QByteArray>
#include <QList>
#include <QMap>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QString>
#include <QStringList>
extern "C" { extern "C" {
struct sqlite3_tokenizer; struct sqlite3_tokenizer;

View File

@ -20,11 +20,13 @@
#include "config.h" #include "config.h"
#include "dbusscreensaver.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QObject>
#include <QDBusInterface> #include <QDBusInterface>
#include <QDBusReply> #include <QDBusReply>
#include <QString>
#include "dbusscreensaver.h"
DBusScreensaver::DBusScreensaver(const QString &service, const QString &path, const QString &interface) DBusScreensaver::DBusScreensaver(const QString &service, const QString &path, const QString &interface)
: service_(service), path_(path), interface_(interface) {} : service_(service), path_(path), interface_(interface) {}

View File

@ -23,8 +23,10 @@
#include "config.h" #include "config.h"
#include <QtGlobal>
#include <QString> #include <QString>
#include "config.h"
#include "screensaver.h" #include "screensaver.h"
class DBusScreensaver : public Screensaver { class DBusScreensaver : public Screensaver {

View File

@ -20,19 +20,21 @@
#include "config.h" #include "config.h"
#include "deletefiles.h" #include <QtGlobal>
#include <QStringList>
#include <QTimer>
#include <QThread> #include <QThread>
#include <QTimer>
#include <QString>
#include <QStringList>
#include <QUrl> #include <QUrl>
#include "musicstorage.h"
#include "taskmanager.h" #include "taskmanager.h"
#include "song.h"
#include "deletefiles.h"
#include "musicstorage.h"
const int DeleteFiles::kBatchSize = 50; const int DeleteFiles::kBatchSize = 50;
DeleteFiles::DeleteFiles(TaskManager* task_manager, std::shared_ptr<MusicStorage> storage) DeleteFiles::DeleteFiles(TaskManager *task_manager, std::shared_ptr<MusicStorage> storage)
: thread_(nullptr), : thread_(nullptr),
task_manager_(task_manager), task_manager_(task_manager),
storage_(storage), storage_(storage),
@ -91,8 +93,7 @@ void DeleteFiles::ProcessSomeFiles() {
emit Finished(songs_with_errors_); emit Finished(songs_with_errors_);
// Move back to the original thread so deleteLater() can get called in // Move back to the original thread so deleteLater() can get called in the main thread's event loop
// the main thread's event loop
moveToThread(original_thread_); moveToThread(original_thread_);
deleteLater(); deleteLater();

View File

@ -24,13 +24,17 @@
#include "config.h" #include "config.h"
#include <memory> #include <memory>
#include <stdbool.h>
#include <QObject> #include <QObject>
#include <QThread>
#include <QString>
#include <QStringList>
#include "song.h" #include "song.h"
class MusicStorage;
class TaskManager; class TaskManager;
class MusicStorage;
class DeleteFiles : public QObject { class DeleteFiles : public QObject {
Q_OBJECT Q_OBJECT
@ -41,11 +45,11 @@ class DeleteFiles : public QObject {
static const int kBatchSize; static const int kBatchSize;
void Start(const SongList& songs); void Start(const SongList &songs);
void Start(const QStringList& filenames); void Start(const QStringList &filenames);
signals: signals:
void Finished(const SongList& songs_with_errors); void Finished(const SongList &songs_with_errors);
private slots: private slots:
void ProcessSomeFiles(); void ProcessSomeFiles();
@ -67,4 +71,3 @@ signals:
}; };
#endif // DELETEFILES_H #endif // DELETEFILES_H

Some files were not shown because too many files have changed in this diff Show More