Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0af0af2415 | ||
|
|
fcbe2c997b | ||
|
|
d7748d9227 | ||
|
|
0b8e1f89d7 | ||
|
|
388fce358a | ||
|
|
ad2b567216 | ||
|
|
210601ffe4 | ||
|
|
e337b7933b | ||
|
|
fccbd6790c | ||
|
|
e5ab9908fa |
2
3rdparty/qocoa/main.cpp
vendored
2
3rdparty/qocoa/main.cpp
vendored
@@ -1,4 +1,4 @@
|
||||
#include <QtGui/QApplication>
|
||||
#include <QApplication>
|
||||
#include "gallery.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
||||
1
3rdparty/qocoa/qbutton.h
vendored
1
3rdparty/qocoa/qbutton.h
vendored
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPointer>
|
||||
#include <QString>
|
||||
|
||||
class QButtonPrivate;
|
||||
class QButton : public QWidget
|
||||
|
||||
15
3rdparty/qocoa/qbutton_nonmac.cpp
vendored
15
3rdparty/qocoa/qbutton_nonmac.cpp
vendored
@@ -20,12 +20,19 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
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 <QToolButton>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "qbutton.h"
|
||||
|
||||
class QButtonPrivate : public QObject
|
||||
{
|
||||
|
||||
3
3rdparty/qocoa/qocoa_mac.h
vendored
3
3rdparty/qocoa/qocoa_mac.h
vendored
@@ -21,8 +21,9 @@ THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <Foundation/NSString.h>
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QVBoxLayout>
|
||||
#include <QBoxLayout>
|
||||
#include <QMacCocoaViewContainer>
|
||||
|
||||
static inline NSString* fromQString(const QString &string)
|
||||
|
||||
3
3rdparty/qocoa/qprogressindicatorspinning.h
vendored
3
3rdparty/qocoa/qprogressindicatorspinning.h
vendored
@@ -17,8 +17,7 @@ public:
|
||||
Aqua = 12
|
||||
};
|
||||
|
||||
explicit QProgressIndicatorSpinning(QWidget *parent,
|
||||
Thickness thickness = Default);
|
||||
explicit QProgressIndicatorSpinning(QWidget *parent, Thickness thickness = Default);
|
||||
public slots:
|
||||
void animate(bool animate = true);
|
||||
private:
|
||||
|
||||
@@ -20,11 +20,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qprogressindicatorspinning.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QMovie>
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QMovie>
|
||||
#include <QPointer>
|
||||
#include <QSize>
|
||||
|
||||
#include "qprogressindicatorspinning.h"
|
||||
|
||||
class QProgressIndicatorSpinningPrivate : public QObject
|
||||
{
|
||||
|
||||
4
3rdparty/qocoa/qsearchfield.h
vendored
4
3rdparty/qocoa/qsearchfield.h
vendored
@@ -2,7 +2,11 @@
|
||||
#define QSEARCHFIELD_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QString>
|
||||
#include <QEvent>
|
||||
#include <QResizeEvent>
|
||||
|
||||
class QSearchFieldPrivate;
|
||||
class QSearchField : public QWidget
|
||||
|
||||
22
3rdparty/qocoa/qsearchfield_nonmac.cpp
vendored
22
3rdparty/qocoa/qsearchfield_nonmac.cpp
vendored
@@ -20,18 +20,22 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qsearchfield.h"
|
||||
#include "../../src/core/iconloader.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QApplication>
|
||||
#include <QEvent>
|
||||
#include <QLineEdit>
|
||||
#include <QVBoxLayout>
|
||||
#include <QToolButton>
|
||||
#include <QPointer>
|
||||
#include <QString>
|
||||
#include <QIcon>
|
||||
#include <QSize>
|
||||
#include <QStyle>
|
||||
#include <QLineEdit>
|
||||
#include <QBoxLayout>
|
||||
#include <QToolButton>
|
||||
#include <QtEvents>
|
||||
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include "../../src/core/iconloader.h"
|
||||
#include "qsearchfield.h"
|
||||
|
||||
class QSearchFieldPrivate : public QObject
|
||||
{
|
||||
|
||||
210
3rdparty/qsqlite/qsql_sqlite.cpp
vendored
210
3rdparty/qsqlite/qsql_sqlite.cpp
vendored
@@ -41,24 +41,31 @@
|
||||
|
||||
#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
|
||||
# include <qt_windows.h>
|
||||
#include <qt_windows.h>
|
||||
#else
|
||||
# include <unistd.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stddef.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_METATYPE(sqlite3*)
|
||||
|
||||
@@ -98,16 +105,11 @@ static QVariant::Type qGetColumnType(const QString &tpName)
|
||||
return QVariant::String;
|
||||
}
|
||||
|
||||
static QSqlError qMakeError(sqlite3 *access, const QString &descr, QSqlError::ErrorType type,
|
||||
int errorCode = -1)
|
||||
{
|
||||
return QSqlError(descr,
|
||||
QString(reinterpret_cast<const QChar *>(sqlite3_errmsg16(access))),
|
||||
type, errorCode);
|
||||
static QSqlError qMakeError(sqlite3 *access, const QString &descr, QSqlError::ErrorType type, int errorCode = -1) {
|
||||
return QSqlError(descr, QString(reinterpret_cast<const QChar *>(sqlite3_errmsg16(access))), type, errorCode);
|
||||
}
|
||||
|
||||
class QSQLiteDriverPrivate
|
||||
{
|
||||
class QSQLiteDriverPrivate {
|
||||
public:
|
||||
inline QSQLiteDriverPrivate() : access(0) {}
|
||||
sqlite3 *access;
|
||||
@@ -115,8 +117,7 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class QSQLiteResultPrivate
|
||||
{
|
||||
class QSQLiteResultPrivate {
|
||||
public:
|
||||
QSQLiteResultPrivate(QSQLiteResult *res);
|
||||
void cleanup();
|
||||
@@ -141,8 +142,7 @@ QSQLiteResultPrivate::QSQLiteResultPrivate(QSQLiteResult* res) : q(res), access(
|
||||
{
|
||||
}
|
||||
|
||||
void QSQLiteResultPrivate::cleanup()
|
||||
{
|
||||
void QSQLiteResultPrivate::cleanup() {
|
||||
finalize();
|
||||
rInf.clear();
|
||||
skippedStatus = false;
|
||||
@@ -152,17 +152,14 @@ void QSQLiteResultPrivate::cleanup()
|
||||
q->cleanup();
|
||||
}
|
||||
|
||||
void QSQLiteResultPrivate::finalize()
|
||||
{
|
||||
if (!stmt)
|
||||
return;
|
||||
void QSQLiteResultPrivate::finalize() {
|
||||
if (!stmt) return;
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
stmt = 0;
|
||||
}
|
||||
|
||||
void QSQLiteResultPrivate::initColumns(bool emptyResultset)
|
||||
{
|
||||
void QSQLiteResultPrivate::initColumns(bool emptyResultset) {
|
||||
int nCols = sqlite3_column_count(stmt);
|
||||
if (nCols <= 0)
|
||||
return;
|
||||
@@ -170,13 +167,10 @@ void QSQLiteResultPrivate::initColumns(bool emptyResultset)
|
||||
q->init(nCols);
|
||||
|
||||
for (int i = 0; i < nCols; ++i) {
|
||||
QString colName = QString(reinterpret_cast<const QChar *>(
|
||||
sqlite3_column_name16(stmt, i))
|
||||
).remove(QLatin1Char('"'));
|
||||
QString colName = QString(reinterpret_cast<const QChar *>(sqlite3_column_name16(stmt, i))).remove(QLatin1Char('"'));
|
||||
|
||||
// must use typeName for resolving the type to match QSqliteDriver::record
|
||||
QString typeName = QString(reinterpret_cast<const QChar *>(
|
||||
sqlite3_column_decltype16(stmt, i)));
|
||||
QString typeName = QString(reinterpret_cast<const QChar *>(sqlite3_column_decltype16(stmt, i)));
|
||||
// sqlite3_column_type is documented to have undefined behavior if the result set is empty
|
||||
int stp = emptyResultset ? -1 : sqlite3_column_type(stmt, i);
|
||||
|
||||
@@ -184,7 +178,8 @@ void QSQLiteResultPrivate::initColumns(bool emptyResultset)
|
||||
|
||||
if (!typeName.isEmpty()) {
|
||||
fieldType = qGetColumnType(typeName);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// Get the proper type for the field based on stp value
|
||||
switch (stp) {
|
||||
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 i;
|
||||
|
||||
@@ -234,8 +228,7 @@ bool QSQLiteResultPrivate::fetchNext(ClementineSqlCachedResult::ValueCache &valu
|
||||
}
|
||||
|
||||
if (!stmt) {
|
||||
q->setLastError(QSqlError(QCoreApplication::translate("QSQLiteResult", "Unable to fetch row"),
|
||||
QCoreApplication::translate("QSQLiteResult", "No query"), QSqlError::ConnectionError));
|
||||
q->setLastError(QSqlError(QCoreApplication::translate("QSQLiteResult", "Unable to fetch row"), QCoreApplication::translate("QSQLiteResult", "No query"), QSqlError::ConnectionError));
|
||||
q->setAt(QSql::AfterLastRow);
|
||||
return false;
|
||||
}
|
||||
@@ -252,9 +245,7 @@ bool QSQLiteResultPrivate::fetchNext(ClementineSqlCachedResult::ValueCache &valu
|
||||
for (i = 0; i < rInf.count(); ++i) {
|
||||
switch (sqlite3_column_type(stmt, i)) {
|
||||
case SQLITE_BLOB:
|
||||
values[i + idx] = QByteArray(static_cast<const char *>(
|
||||
sqlite3_column_blob(stmt, i)),
|
||||
sqlite3_column_bytes(stmt, i));
|
||||
values[i + idx] = QByteArray(static_cast<const char *>(sqlite3_column_blob(stmt, i)), sqlite3_column_bytes(stmt, i));
|
||||
break;
|
||||
case SQLITE_INTEGER:
|
||||
values[i + idx] = sqlite3_column_int64(stmt, i);
|
||||
@@ -278,9 +269,7 @@ bool QSQLiteResultPrivate::fetchNext(ClementineSqlCachedResult::ValueCache &valu
|
||||
values[i + idx] = QVariant(QVariant::String);
|
||||
break;
|
||||
default:
|
||||
values[i + idx] = QString(reinterpret_cast<const QChar *>(
|
||||
sqlite3_column_text16(stmt, i)),
|
||||
sqlite3_column_bytes16(stmt, i) / sizeof(QChar));
|
||||
values[i + idx] = QString(reinterpret_cast<const QChar *>(sqlite3_column_text16(stmt, i)), sqlite3_column_bytes16(stmt, i) / sizeof(QChar));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -297,16 +286,14 @@ bool QSQLiteResultPrivate::fetchNext(ClementineSqlCachedResult::ValueCache &valu
|
||||
// SQLITE_ERROR is a generic error code and we must call sqlite3_reset()
|
||||
// to get the specific error message.
|
||||
res = sqlite3_reset(stmt);
|
||||
q->setLastError(qMakeError(access, QCoreApplication::translate("QSQLiteResult",
|
||||
"Unable to fetch row"), QSqlError::ConnectionError, res));
|
||||
q->setLastError(qMakeError(access, QCoreApplication::translate("QSQLiteResult", "Unable to fetch row"), QSqlError::ConnectionError, res));
|
||||
q->setAt(QSql::AfterLastRow);
|
||||
return false;
|
||||
case SQLITE_MISUSE:
|
||||
case SQLITE_BUSY:
|
||||
default:
|
||||
// something wrong, don't get col info, but still return false
|
||||
q->setLastError(qMakeError(access, QCoreApplication::translate("QSQLiteResult",
|
||||
"Unable to fetch row"), QSqlError::ConnectionError, res));
|
||||
q->setLastError(qMakeError(access, QCoreApplication::translate("QSQLiteResult", "Unable to fetch row"), QSqlError::ConnectionError, res));
|
||||
sqlite3_reset(stmt);
|
||||
q->setAt(QSql::AfterLastRow);
|
||||
return false;
|
||||
@@ -315,15 +302,13 @@ bool QSQLiteResultPrivate::fetchNext(ClementineSqlCachedResult::ValueCache &valu
|
||||
}
|
||||
|
||||
QSQLiteResult::QSQLiteResult(const QSQLiteDriver* db)
|
||||
: ClementineSqlCachedResult(db)
|
||||
{
|
||||
: ClementineSqlCachedResult(db) {
|
||||
d = new QSQLiteResultPrivate(this);
|
||||
d->access = db->d->access;
|
||||
db->d->results.append(this);
|
||||
}
|
||||
|
||||
QSQLiteResult::~QSQLiteResult()
|
||||
{
|
||||
QSQLiteResult::~QSQLiteResult() {
|
||||
const QSqlDriver *sqlDriver = driver();
|
||||
if (sqlDriver)
|
||||
qobject_cast<const QSQLiteDriver *>(sqlDriver)->d->results.removeOne(this);
|
||||
@@ -331,20 +316,17 @@ QSQLiteResult::~QSQLiteResult()
|
||||
delete d;
|
||||
}
|
||||
|
||||
void QSQLiteResult::virtual_hook(int id, void *data)
|
||||
{
|
||||
void QSQLiteResult::virtual_hook(int id, void *data) {
|
||||
ClementineSqlCachedResult::virtual_hook(id, data);
|
||||
}
|
||||
|
||||
bool QSQLiteResult::reset(const QString &query)
|
||||
{
|
||||
bool QSQLiteResult::reset(const QString &query) {
|
||||
if (!prepare(query))
|
||||
return false;
|
||||
return exec();
|
||||
}
|
||||
|
||||
bool QSQLiteResult::prepare(const QString &query)
|
||||
{
|
||||
bool QSQLiteResult::prepare(const QString &query) {
|
||||
if (!driver() || !driver()->isOpen() || driver()->isOpenError())
|
||||
return false;
|
||||
|
||||
@@ -355,29 +337,25 @@ bool QSQLiteResult::prepare(const QString &query)
|
||||
const void *pzTail = NULL;
|
||||
|
||||
#if (SQLITE_VERSION_NUMBER >= 3003011)
|
||||
int res = sqlite3_prepare16_v2(d->access, query.constData(), (query.size() + 1) * sizeof(QChar),
|
||||
&d->stmt, &pzTail);
|
||||
int res = sqlite3_prepare16_v2(d->access, query.constData(), (query.size() + 1) * sizeof(QChar), &d->stmt, &pzTail);
|
||||
#else
|
||||
int res = sqlite3_prepare16(d->access, query.constData(), (query.size() + 1) * sizeof(QChar),
|
||||
&d->stmt, &pzTail);
|
||||
int res = sqlite3_prepare16(d->access, query.constData(), (query.size() + 1) * sizeof(QChar), &d->stmt, &pzTail);
|
||||
#endif
|
||||
|
||||
if (res != SQLITE_OK) {
|
||||
setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult",
|
||||
"Unable to execute statement"), QSqlError::StatementError, res));
|
||||
setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult", "Unable to execute statement"), QSqlError::StatementError, res));
|
||||
d->finalize();
|
||||
return false;
|
||||
} else if (pzTail && !QString(reinterpret_cast<const QChar *>(pzTail)).trimmed().isEmpty()) {
|
||||
setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult",
|
||||
"Unable to execute multiple statements at a time"), QSqlError::StatementError, SQLITE_MISUSE));
|
||||
}
|
||||
else if (pzTail && !QString(reinterpret_cast<const QChar *>(pzTail)).trimmed().isEmpty()) {
|
||||
setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult", "Unable to execute multiple statements at a time"), QSqlError::StatementError, SQLITE_MISUSE));
|
||||
d->finalize();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QSQLiteResult::exec()
|
||||
{
|
||||
bool QSQLiteResult::exec() {
|
||||
const QVector<QVariant> values = boundValues();
|
||||
|
||||
d->skippedStatus = false;
|
||||
@@ -388,8 +366,7 @@ bool QSQLiteResult::exec()
|
||||
|
||||
int res = sqlite3_reset(d->stmt);
|
||||
if (res != SQLITE_OK) {
|
||||
setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult",
|
||||
"Unable to reset statement"), QSqlError::StatementError, res));
|
||||
setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult", "Unable to reset statement"), QSqlError::StatementError, res));
|
||||
d->finalize();
|
||||
return false;
|
||||
}
|
||||
@@ -401,7 +378,8 @@ bool QSQLiteResult::exec()
|
||||
|
||||
if (value.isNull()) {
|
||||
res = sqlite3_bind_null(d->stmt, i + 1);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
switch (value.type()) {
|
||||
case QVariant::ByteArray: {
|
||||
const QByteArray *ba = static_cast<const QByteArray*>(value.constData());
|
||||
@@ -434,13 +412,13 @@ bool QSQLiteResult::exec()
|
||||
}
|
||||
}
|
||||
if (res != SQLITE_OK) {
|
||||
setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult",
|
||||
"Unable to bind parameters"), QSqlError::StatementError, res));
|
||||
setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult", "Unable to bind parameters"), QSqlError::StatementError, res));
|
||||
d->finalize();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
setLastError(QSqlError(QCoreApplication::translate("QSQLiteResult",
|
||||
"Parameter count mismatch") + QString::number(paramCount, 10) + "/" + QString::number(values.count(), 10), QString(), QSqlError::StatementError));
|
||||
return false;
|
||||
@@ -456,23 +434,19 @@ bool QSQLiteResult::exec()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QSQLiteResult::gotoNext(ClementineSqlCachedResult::ValueCache& row, int idx)
|
||||
{
|
||||
bool QSQLiteResult::gotoNext(ClementineSqlCachedResult::ValueCache& row, int idx) {
|
||||
return d->fetchNext(row, idx, false);
|
||||
}
|
||||
|
||||
int QSQLiteResult::size()
|
||||
{
|
||||
int QSQLiteResult::size() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int QSQLiteResult::numRowsAffected()
|
||||
{
|
||||
int QSQLiteResult::numRowsAffected() {
|
||||
return sqlite3_changes(d->access);
|
||||
}
|
||||
|
||||
QVariant QSQLiteResult::lastInsertId() const
|
||||
{
|
||||
QVariant QSQLiteResult::lastInsertId() const {
|
||||
if (isActive()) {
|
||||
qint64 id = sqlite3_last_insert_rowid(d->access);
|
||||
if (id)
|
||||
@@ -481,35 +455,30 @@ QVariant QSQLiteResult::lastInsertId() const
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QSqlRecord QSQLiteResult::record() const
|
||||
{
|
||||
QSqlRecord QSQLiteResult::record() const {
|
||||
if (!isActive() || !isSelect())
|
||||
return QSqlRecord();
|
||||
return d->rInf;
|
||||
}
|
||||
|
||||
void QSQLiteResult::detachFromResultSet()
|
||||
{
|
||||
void QSQLiteResult::detachFromResultSet() {
|
||||
if (d->stmt)
|
||||
sqlite3_reset(d->stmt);
|
||||
}
|
||||
|
||||
QVariant QSQLiteResult::handle() const
|
||||
{
|
||||
QVariant QSQLiteResult::handle() const {
|
||||
return QVariant::fromValue(d->stmt);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
|
||||
QSQLiteDriver::QSQLiteDriver(QObject * parent)
|
||||
: QSqlDriver(parent)
|
||||
{
|
||||
: QSqlDriver(parent) {
|
||||
d = new QSQLiteDriverPrivate();
|
||||
}
|
||||
|
||||
QSQLiteDriver::QSQLiteDriver(sqlite3 *connection, QObject *parent)
|
||||
: QSqlDriver(parent)
|
||||
{
|
||||
: QSqlDriver(parent) {
|
||||
d = new QSQLiteDriverPrivate();
|
||||
d->access = connection;
|
||||
setOpen(true);
|
||||
@@ -579,87 +548,77 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c
|
||||
setOpen(true);
|
||||
setOpenError(false);
|
||||
return true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (d->access) {
|
||||
sqlite3_close(d->access);
|
||||
d->access = 0;
|
||||
}
|
||||
|
||||
setLastError(qMakeError(d->access, tr("Error opening database"),
|
||||
QSqlError::ConnectionError));
|
||||
setLastError(qMakeError(d->access, tr("Error opening database"), QSqlError::ConnectionError));
|
||||
setOpenError(true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void QSQLiteDriver::close()
|
||||
{
|
||||
void QSQLiteDriver::close() {
|
||||
if (isOpen()) {
|
||||
foreach (QSQLiteResult *result, d->results) {
|
||||
result->d->finalize();
|
||||
}
|
||||
|
||||
if (sqlite3_close(d->access) != SQLITE_OK)
|
||||
setLastError(qMakeError(d->access, tr("Error closing database"),
|
||||
QSqlError::ConnectionError));
|
||||
setLastError(qMakeError(d->access, tr("Error closing database"), QSqlError::ConnectionError));
|
||||
d->access = 0;
|
||||
setOpen(false);
|
||||
setOpenError(false);
|
||||
}
|
||||
}
|
||||
|
||||
QSqlResult *QSQLiteDriver::createResult() const
|
||||
{
|
||||
QSqlResult *QSQLiteDriver::createResult() const {
|
||||
return new QSQLiteResult(this);
|
||||
}
|
||||
|
||||
bool QSQLiteDriver::beginTransaction()
|
||||
{
|
||||
bool QSQLiteDriver::beginTransaction() {
|
||||
if (!isOpen() || isOpenError())
|
||||
return false;
|
||||
|
||||
QSqlQuery q(createResult());
|
||||
if (!q.exec(QLatin1String("BEGIN"))) {
|
||||
setLastError(QSqlError(tr("Unable to begin transaction"),
|
||||
q.lastError().databaseText(), QSqlError::TransactionError));
|
||||
setLastError(QSqlError(tr("Unable to begin transaction"), q.lastError().databaseText(), QSqlError::TransactionError));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QSQLiteDriver::commitTransaction()
|
||||
{
|
||||
bool QSQLiteDriver::commitTransaction() {
|
||||
if (!isOpen() || isOpenError())
|
||||
return false;
|
||||
|
||||
QSqlQuery q(createResult());
|
||||
if (!q.exec(QLatin1String("COMMIT"))) {
|
||||
setLastError(QSqlError(tr("Unable to commit transaction"),
|
||||
q.lastError().databaseText(), QSqlError::TransactionError));
|
||||
setLastError(QSqlError(tr("Unable to commit transaction"), q.lastError().databaseText(), QSqlError::TransactionError));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QSQLiteDriver::rollbackTransaction()
|
||||
{
|
||||
bool QSQLiteDriver::rollbackTransaction() {
|
||||
if (!isOpen() || isOpenError())
|
||||
return false;
|
||||
|
||||
QSqlQuery q(createResult());
|
||||
if (!q.exec(QLatin1String("ROLLBACK"))) {
|
||||
setLastError(QSqlError(tr("Unable to rollback transaction"),
|
||||
q.lastError().databaseText(), QSqlError::TransactionError));
|
||||
setLastError(QSqlError(tr("Unable to rollback transaction"), q.lastError().databaseText(), QSqlError::TransactionError));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList QSQLiteDriver::tables(QSql::TableType type) const
|
||||
{
|
||||
QStringList QSQLiteDriver::tables(QSql::TableType type) const {
|
||||
QStringList res;
|
||||
if (!isOpen())
|
||||
return res;
|
||||
@@ -691,8 +650,7 @@ QStringList QSQLiteDriver::tables(QSql::TableType type) const
|
||||
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 table(tableName);
|
||||
int indexOfSeparator = tableName.indexOf(QLatin1Char('.'));
|
||||
@@ -720,8 +678,7 @@ static QSqlIndex qGetTableInfo(QSqlQuery &q, const QString &tableName, bool only
|
||||
return ind;
|
||||
}
|
||||
|
||||
QSqlIndex QSQLiteDriver::primaryIndex(const QString &tblname) const
|
||||
{
|
||||
QSqlIndex QSQLiteDriver::primaryIndex(const QString &tblname) const {
|
||||
if (!isOpen())
|
||||
return QSqlIndex();
|
||||
|
||||
@@ -734,8 +691,7 @@ QSqlIndex QSQLiteDriver::primaryIndex(const QString &tblname) const
|
||||
return qGetTableInfo(q, table, true);
|
||||
}
|
||||
|
||||
QSqlRecord QSQLiteDriver::record(const QString &tbl) const
|
||||
{
|
||||
QSqlRecord QSQLiteDriver::record(const QString &tbl) const {
|
||||
if (!isOpen())
|
||||
return QSqlRecord();
|
||||
|
||||
@@ -748,13 +704,11 @@ QSqlRecord QSQLiteDriver::record(const QString &tbl) const
|
||||
return qGetTableInfo(q, table);
|
||||
}
|
||||
|
||||
QVariant QSQLiteDriver::handle() const
|
||||
{
|
||||
QVariant QSQLiteDriver::handle() const {
|
||||
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);
|
||||
return _q_escapeIdentifier(identifier);
|
||||
}
|
||||
|
||||
28
3rdparty/qsqlite/qsql_sqlite.h
vendored
28
3rdparty/qsqlite/qsql_sqlite.h
vendored
@@ -42,8 +42,17 @@
|
||||
#ifndef QSQL_SQLITE_H
|
||||
#define QSQL_SQLITE_H
|
||||
|
||||
#include <QtSql/qsqldriver.h>
|
||||
#include <QtSql/qsqlresult.h>
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QSql>
|
||||
#include <QSqlDriver>
|
||||
#include <QSqlIndex>
|
||||
#include <QSqlRecord>
|
||||
#include <QSqlResult>
|
||||
|
||||
#include "sqlcachedresult.h"
|
||||
|
||||
struct sqlite3;
|
||||
@@ -57,16 +66,16 @@ struct sqlite3;
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QSQLiteDriver;
|
||||
class QSQLiteDriverPrivate;
|
||||
class QSQLiteResultPrivate;
|
||||
class QSQLiteDriver;
|
||||
|
||||
class QSQLiteResult : public ClementineSqlCachedResult
|
||||
{
|
||||
friend class QSQLiteDriver;
|
||||
friend class QSQLiteResultPrivate;
|
||||
public:
|
||||
explicit QSQLiteResult(const QSQLiteDriver* db);
|
||||
explicit QSQLiteResult(const QSQLiteDriver *db);
|
||||
~QSQLiteResult();
|
||||
QVariant handle() const;
|
||||
|
||||
@@ -83,7 +92,7 @@ protected:
|
||||
void virtual_hook(int id, void *data);
|
||||
|
||||
private:
|
||||
QSQLiteResultPrivate* d;
|
||||
QSQLiteResultPrivate *d;
|
||||
};
|
||||
|
||||
class Q_EXPORT_SQLDRIVER_SQLITE QSQLiteDriver : public QSqlDriver
|
||||
@@ -95,12 +104,7 @@ public:
|
||||
explicit QSQLiteDriver(sqlite3 *connection, QObject *parent = 0);
|
||||
~QSQLiteDriver();
|
||||
bool hasFeature(DriverFeature f) const;
|
||||
bool open(const QString & db,
|
||||
const QString & user,
|
||||
const QString & password,
|
||||
const QString & host,
|
||||
int port,
|
||||
const QString & connOpts);
|
||||
bool open(const QString & db, const QString & user, const QString & password, const QString & host, int port, const QString & connOpts);
|
||||
void close();
|
||||
QSqlResult *createResult() const;
|
||||
bool beginTransaction();
|
||||
@@ -114,7 +118,7 @@ public:
|
||||
QString escapeIdentifier(const QString &identifier, IdentifierType) const;
|
||||
|
||||
private:
|
||||
QSQLiteDriverPrivate* d;
|
||||
QSQLiteDriverPrivate *d;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
8
3rdparty/qsqlite/smain.cpp
vendored
8
3rdparty/qsqlite/smain.cpp
vendored
@@ -31,8 +31,13 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QString>
|
||||
#include <QSqlDriver>
|
||||
|
||||
#include "smain.h"
|
||||
|
||||
#include "qsql_sqlite.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QSQLiteDriverPlugin::QSQLiteDriverPlugin()
|
||||
@@ -40,8 +45,7 @@ QSQLiteDriverPlugin::QSQLiteDriverPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
QSqlDriver* QSQLiteDriverPlugin::create(const QString &name)
|
||||
{
|
||||
QSqlDriver *QSQLiteDriverPlugin::create(const QString &name) {
|
||||
if (name == QLatin1String("QSQLITE")) {
|
||||
QSQLiteDriver* driver = new QSQLiteDriver();
|
||||
return driver;
|
||||
|
||||
10
3rdparty/qsqlite/smain.h
vendored
10
3rdparty/qsqlite/smain.h
vendored
@@ -31,9 +31,11 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <qsqldriverplugin.h>
|
||||
#include <qstringlist.h>
|
||||
#include "qsql_sqlite.h"
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QSqlDriver>
|
||||
#include <QSqlDriverPlugin>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
@@ -45,7 +47,7 @@ class QSQLiteDriverPlugin : public QSqlDriverPlugin
|
||||
public:
|
||||
QSQLiteDriverPlugin();
|
||||
|
||||
QSqlDriver* create(const QString &);
|
||||
QSqlDriver *create(const QString &);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
85
3rdparty/qsqlite/sqlcachedresult.cpp
vendored
85
3rdparty/qsqlite/sqlcachedresult.cpp
vendored
@@ -39,9 +39,9 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <qvariant.h>
|
||||
#include <qdatetime.h>
|
||||
#include <qvector.h>
|
||||
#include <QVariant>
|
||||
#include <QSqlResult>
|
||||
#include <QSqlDriver>
|
||||
|
||||
#include "sqlcachedresult.h"
|
||||
|
||||
@@ -61,8 +61,7 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
static const uint initial_cache_size = 128;
|
||||
|
||||
class ClementineSqlCachedResultPrivate
|
||||
{
|
||||
class ClementineSqlCachedResultPrivate {
|
||||
public:
|
||||
ClementineSqlCachedResultPrivate();
|
||||
bool canSeek(int i) const;
|
||||
@@ -84,8 +83,7 @@ ClementineSqlCachedResultPrivate::ClementineSqlCachedResultPrivate():
|
||||
{
|
||||
}
|
||||
|
||||
void ClementineSqlCachedResultPrivate::cleanup()
|
||||
{
|
||||
void ClementineSqlCachedResultPrivate::cleanup() {
|
||||
cache.clear();
|
||||
forwardOnly = false;
|
||||
atEnd = false;
|
||||
@@ -93,8 +91,7 @@ void ClementineSqlCachedResultPrivate::cleanup()
|
||||
rowCacheEnd = 0;
|
||||
}
|
||||
|
||||
void ClementineSqlCachedResultPrivate::init(int count, bool fo)
|
||||
{
|
||||
void ClementineSqlCachedResultPrivate::init(int count, bool fo) {
|
||||
Q_ASSERT(count);
|
||||
cleanup();
|
||||
forwardOnly = fo;
|
||||
@@ -102,13 +99,13 @@ void ClementineSqlCachedResultPrivate::init(int count, bool fo)
|
||||
if (fo) {
|
||||
cache.resize(count);
|
||||
rowCacheEnd = count;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
cache.resize(initial_cache_size * count);
|
||||
}
|
||||
}
|
||||
|
||||
int ClementineSqlCachedResultPrivate::nextIndex()
|
||||
{
|
||||
int ClementineSqlCachedResultPrivate::nextIndex() {
|
||||
if (forwardOnly)
|
||||
return 0;
|
||||
int newIdx = rowCacheEnd;
|
||||
@@ -119,15 +116,13 @@ int ClementineSqlCachedResultPrivate::nextIndex()
|
||||
return newIdx;
|
||||
}
|
||||
|
||||
bool ClementineSqlCachedResultPrivate::canSeek(int i) const
|
||||
{
|
||||
bool ClementineSqlCachedResultPrivate::canSeek(int i) const {
|
||||
if (forwardOnly || i < 0)
|
||||
return false;
|
||||
return rowCacheEnd >= (i + 1) * colCount;
|
||||
}
|
||||
|
||||
void ClementineSqlCachedResultPrivate::revertLast()
|
||||
{
|
||||
void ClementineSqlCachedResultPrivate::revertLast() {
|
||||
if (forwardOnly)
|
||||
return;
|
||||
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();
|
||||
}
|
||||
|
||||
ClementineSqlCachedResult::~ClementineSqlCachedResult()
|
||||
{
|
||||
ClementineSqlCachedResult::~ClementineSqlCachedResult() {
|
||||
delete d;
|
||||
}
|
||||
|
||||
void ClementineSqlCachedResult::init(int colCount)
|
||||
{
|
||||
void ClementineSqlCachedResult::init(int colCount) {
|
||||
d->init(colCount, isForwardOnly());
|
||||
}
|
||||
|
||||
bool ClementineSqlCachedResult::fetch(int i)
|
||||
{
|
||||
bool ClementineSqlCachedResult::fetch(int i) {
|
||||
if ((!isActive()) || (i < 0))
|
||||
return false;
|
||||
if (at() == i)
|
||||
@@ -195,8 +186,7 @@ bool ClementineSqlCachedResult::fetch(int i)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClementineSqlCachedResult::fetchNext()
|
||||
{
|
||||
bool ClementineSqlCachedResult::fetchNext() {
|
||||
if (d->canSeek(at() + 1)) {
|
||||
setAt(at() + 1);
|
||||
return true;
|
||||
@@ -204,13 +194,11 @@ bool ClementineSqlCachedResult::fetchNext()
|
||||
return cacheNext();
|
||||
}
|
||||
|
||||
bool ClementineSqlCachedResult::fetchPrevious()
|
||||
{
|
||||
bool ClementineSqlCachedResult::fetchPrevious() {
|
||||
return fetch(at() - 1);
|
||||
}
|
||||
|
||||
bool ClementineSqlCachedResult::fetchFirst()
|
||||
{
|
||||
bool ClementineSqlCachedResult::fetchFirst() {
|
||||
if (d->forwardOnly && at() != QSql::BeforeFirstRow) {
|
||||
return false;
|
||||
}
|
||||
@@ -221,8 +209,7 @@ bool ClementineSqlCachedResult::fetchFirst()
|
||||
return cacheNext();
|
||||
}
|
||||
|
||||
bool ClementineSqlCachedResult::fetchLast()
|
||||
{
|
||||
bool ClementineSqlCachedResult::fetchLast() {
|
||||
if (d->atEnd) {
|
||||
if (d->forwardOnly)
|
||||
return false;
|
||||
@@ -236,13 +223,13 @@ bool ClementineSqlCachedResult::fetchLast()
|
||||
if (d->forwardOnly && at() == QSql::AfterLastRow) {
|
||||
setAt(i);
|
||||
return true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return fetch(i);
|
||||
}
|
||||
}
|
||||
|
||||
QVariant ClementineSqlCachedResult::data(int i)
|
||||
{
|
||||
QVariant ClementineSqlCachedResult::data(int i) {
|
||||
int idx = d->forwardOnly ? i : at() * d->colCount + i;
|
||||
if (i >= d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
|
||||
return QVariant();
|
||||
@@ -250,8 +237,7 @@ QVariant ClementineSqlCachedResult::data(int i)
|
||||
return d->cache.at(idx);
|
||||
}
|
||||
|
||||
bool ClementineSqlCachedResult::isNull(int i)
|
||||
{
|
||||
bool ClementineSqlCachedResult::isNull(int i) {
|
||||
int idx = d->forwardOnly ? i : at() * d->colCount + i;
|
||||
if (i > d->colCount || i < 0 || at() < 0 || idx >= d->rowCacheEnd)
|
||||
return true;
|
||||
@@ -259,22 +245,20 @@ bool ClementineSqlCachedResult::isNull(int i)
|
||||
return d->cache.at(idx).isNull();
|
||||
}
|
||||
|
||||
void ClementineSqlCachedResult::cleanup()
|
||||
{
|
||||
void ClementineSqlCachedResult::cleanup() {
|
||||
setAt(QSql::BeforeFirstRow);
|
||||
setActive(false);
|
||||
d->cleanup();
|
||||
}
|
||||
|
||||
void ClementineSqlCachedResult::clearValues()
|
||||
{
|
||||
void ClementineSqlCachedResult::clearValues() {
|
||||
setAt(QSql::BeforeFirstRow);
|
||||
d->rowCacheEnd = 0;
|
||||
d->atEnd = false;
|
||||
}
|
||||
|
||||
bool ClementineSqlCachedResult::cacheNext()
|
||||
{
|
||||
bool ClementineSqlCachedResult::cacheNext() {
|
||||
|
||||
if (d->atEnd)
|
||||
return false;
|
||||
|
||||
@@ -292,28 +276,23 @@ bool ClementineSqlCachedResult::cacheNext()
|
||||
return true;
|
||||
}
|
||||
|
||||
int ClementineSqlCachedResult::colCount() const
|
||||
{
|
||||
int ClementineSqlCachedResult::colCount() const {
|
||||
return d->colCount;
|
||||
}
|
||||
|
||||
ClementineSqlCachedResult::ValueCache &ClementineSqlCachedResult::cache()
|
||||
{
|
||||
ClementineSqlCachedResult::ValueCache &ClementineSqlCachedResult::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);
|
||||
}
|
||||
|
||||
void ClementineSqlCachedResult::detachFromResultSet()
|
||||
{
|
||||
void ClementineSqlCachedResult::detachFromResultSet() {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void ClementineSqlCachedResult::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy policy)
|
||||
{
|
||||
void ClementineSqlCachedResult::setNumericalPrecisionPolicy(QSql::NumericalPrecisionPolicy policy) {
|
||||
QSqlResult::setNumericalPrecisionPolicy(policy);
|
||||
cleanup();
|
||||
}
|
||||
|
||||
10
3rdparty/qsqlite/sqlcachedresult.h
vendored
10
3rdparty/qsqlite/sqlcachedresult.h
vendored
@@ -53,13 +53,15 @@
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtSql/qsqlresult.h>
|
||||
#include <QtGlobal>
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
#include <QSql>
|
||||
#include <QSqlDriver>
|
||||
#include <QSqlResult>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QVariant;
|
||||
template <typename T> class QVector;
|
||||
|
||||
class ClementineSqlCachedResultPrivate;
|
||||
|
||||
class ClementineSqlCachedResult: public QSqlResult
|
||||
|
||||
21
3rdparty/qtsingleapplication/qtlocalpeer.cpp
vendored
21
3rdparty/qtsingleapplication/qtlocalpeer.cpp
vendored
@@ -39,14 +39,27 @@
|
||||
|
||||
|
||||
#include "qtlocalpeer.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QTime>
|
||||
#include <QDataStream>
|
||||
|
||||
#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)
|
||||
#include <QLibrary>
|
||||
#include <qt_windows.h>
|
||||
#include <QLibrary>
|
||||
|
||||
typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*);
|
||||
static PProcessIdToSessionId pProcessIdToSessionId = 0;
|
||||
#endif
|
||||
|
||||
4
3rdparty/qtsingleapplication/qtlocalpeer.h
vendored
4
3rdparty/qtsingleapplication/qtlocalpeer.h
vendored
@@ -40,9 +40,9 @@
|
||||
#ifndef QTLOCALPEER_H
|
||||
#define QTLOCALPEER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
#include <QDir>
|
||||
|
||||
#include "qtlockedfile.h"
|
||||
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QFile>
|
||||
#include <QString>
|
||||
|
||||
#include "qtlockedfile.h"
|
||||
|
||||
/*!
|
||||
|
||||
2
3rdparty/qtsingleapplication/qtlockedfile.h
vendored
2
3rdparty/qtsingleapplication/qtlockedfile.h
vendored
@@ -42,7 +42,7 @@
|
||||
|
||||
#include <QFile>
|
||||
#ifdef Q_OS_WIN
|
||||
#include <QtCore/QVector>
|
||||
#include <QVector>
|
||||
#endif
|
||||
|
||||
#if defined(Q_WS_WIN) || defined(Q_OS_WIN)
|
||||
|
||||
@@ -37,9 +37,10 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
#include "qtlockedfile.h"
|
||||
#include <qt_windows.h>
|
||||
#include <QFileInfo>
|
||||
|
||||
#define MUTEX_PREFIX "QtLockedFile mutex "
|
||||
// Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS
|
||||
|
||||
@@ -39,8 +39,12 @@
|
||||
|
||||
|
||||
#include "qtsingleapplication.h"
|
||||
#include "qtlocalpeer.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QWidget>
|
||||
#include <QString>
|
||||
|
||||
#include "qtlocalpeer.h"
|
||||
|
||||
/*!
|
||||
\class QtSingleApplication qtsingleapplication.h
|
||||
|
||||
@@ -40,8 +40,11 @@
|
||||
#ifndef QTSINGLEAPPLICATION_H
|
||||
#define QTSINGLEAPPLICATION_H
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QApplication>
|
||||
|
||||
#include <QString>
|
||||
class QtLocalPeer;
|
||||
|
||||
#if defined(Q_WS_WIN) || defined(Q_OS_WIN32)
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QString>
|
||||
#include <QFlags>
|
||||
|
||||
#include "qtsinglecoreapplication.h"
|
||||
#include "qtlocalpeer.h"
|
||||
@@ -70,8 +73,7 @@
|
||||
*/
|
||||
|
||||
QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv)
|
||||
: QCoreApplication(argc, argv)
|
||||
{
|
||||
: QCoreApplication(argc, argv) {
|
||||
peer = new QtLocalPeer(this);
|
||||
connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
|
||||
}
|
||||
@@ -83,8 +85,7 @@ QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv)
|
||||
QCoreAppliation constructor.
|
||||
*/
|
||||
QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv)
|
||||
: QCoreApplication(argc, argv)
|
||||
{
|
||||
: QCoreApplication(argc, argv) {
|
||||
peer = new QtLocalPeer(this, appId);
|
||||
connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
|
||||
}
|
||||
@@ -101,8 +102,7 @@ QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc
|
||||
\sa sendMessage()
|
||||
*/
|
||||
|
||||
bool QtSingleCoreApplication::isRunning()
|
||||
{
|
||||
bool QtSingleCoreApplication::isRunning() {
|
||||
return peer->isClient();
|
||||
}
|
||||
|
||||
@@ -121,8 +121,7 @@ bool QtSingleCoreApplication::isRunning()
|
||||
\sa isRunning(), messageReceived()
|
||||
*/
|
||||
|
||||
bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout)
|
||||
{
|
||||
bool QtSingleCoreApplication::sendMessage(const QString &message, int 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.
|
||||
*/
|
||||
|
||||
QString QtSingleCoreApplication::id() const
|
||||
{
|
||||
QString QtSingleCoreApplication::id() const {
|
||||
return peer->applicationId();
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
#define QTSINGLECOREAPPLICATION_H
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
class QtLocalPeer;
|
||||
|
||||
|
||||
6
3rdparty/qxt/qxtglobalshortcut.cpp
vendored
6
3rdparty/qxt/qxtglobalshortcut.cpp
vendored
@@ -29,10 +29,14 @@
|
||||
** <http://libqxt.org> <foundation@libqxt.org>
|
||||
*****************************************************************************/
|
||||
|
||||
#include "qxtglobalshortcut_p.h"
|
||||
#include <QtGlobal>
|
||||
#include <QAbstractEventDispatcher>
|
||||
#include <QHash>
|
||||
#include <QPair>
|
||||
#include <QtDebug>
|
||||
|
||||
#include "qxtglobalshortcut_p.h"
|
||||
|
||||
bool QxtGlobalShortcutPrivate::error = false;
|
||||
#ifndef Q_WS_MAC
|
||||
int QxtGlobalShortcutPrivate::ref = 0;
|
||||
|
||||
5
3rdparty/qxt/qxtglobalshortcut.h
vendored
5
3rdparty/qxt/qxtglobalshortcut.h
vendored
@@ -31,9 +31,12 @@
|
||||
|
||||
#define QXTGLOBALSHORTCUT_H
|
||||
|
||||
#include "qxtglobal.h"
|
||||
#include <QObject>
|
||||
#include <QKeySequence>
|
||||
#include <QString>
|
||||
|
||||
#include "qxtglobal.h"
|
||||
|
||||
class QxtGlobalShortcutPrivate;
|
||||
|
||||
class QXT_GUI_EXPORT QxtGlobalShortcut : public QObject
|
||||
|
||||
13
3rdparty/qxt/qxtglobalshortcut_x11.cpp
vendored
13
3rdparty/qxt/qxtglobalshortcut_x11.cpp
vendored
@@ -32,12 +32,21 @@
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
|
||||
# include <QX11Info>
|
||||
#else
|
||||
# include <QApplication>
|
||||
# include <qpa/qplatformnativeinterface.h>
|
||||
# include <xcb/xcb.h>
|
||||
# include <QApplication>
|
||||
#endif
|
||||
#include <QVector>
|
||||
#include <X11/X.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"
|
||||
|
||||
namespace {
|
||||
|
||||
5
3rdparty/sha2/sha2.cpp
vendored
5
3rdparty/sha2/sha2.cpp
vendored
@@ -31,8 +31,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h> /* memcpy()/memset() or bcopy()/bzero() */
|
||||
#include <assert.h> /* assert() */
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "sha2.h"
|
||||
|
||||
/*
|
||||
|
||||
2
3rdparty/sha2/sha2.h
vendored
2
3rdparty/sha2/sha2.h
vendored
@@ -35,6 +35,8 @@
|
||||
#ifndef __STRAWBERRY_SHA2_H__
|
||||
#define __STRAWBERRY_SHA2_H__
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/*
|
||||
* Import u_intXX_t size_t type definitions from system headers. You
|
||||
* may need to change this, or define these things yourself in this
|
||||
|
||||
@@ -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_TAG gstreamer-tag-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(LIBVLC libvlc)
|
||||
pkg_check_modules(PHONON phonon4qt5)
|
||||
pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92)
|
||||
pkg_check_modules(LIBMTP libmtp>=1.0)
|
||||
|
||||
pkg_check_modules(LIBPULSE libpulse)
|
||||
pkg_check_modules(LIBXML libxml-2.0)
|
||||
pkg_check_modules(TAGLIB REQUIRED taglib>=1.8)
|
||||
pkg_check_modules(SQLITE REQUIRED sqlite3>=3.7)
|
||||
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)
|
||||
|
||||
@@ -178,10 +176,6 @@ include_directories(${GSTREAMER_AUDIO_INCLUDE_DIRS})
|
||||
include_directories(${GSTREAMER_BASE_INCLUDE_DIRS})
|
||||
include_directories(${GSTREAMER_TAG_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)
|
||||
# RC compiler
|
||||
@@ -197,6 +191,27 @@ if(WIN32)
|
||||
option(ENABLE_WIN32_CONSOLE "Show the windows console even outside Debug mode" OFF)
|
||||
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"
|
||||
DEPENDS "libcdio" CDIO_FOUND
|
||||
)
|
||||
@@ -262,7 +277,7 @@ else(WIN32)
|
||||
endif(WIN32)
|
||||
|
||||
# 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:
|
||||
# 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
|
||||
add_subdirectory(3rdparty/qocoa)
|
||||
|
||||
#if(IMOBILEDEVICE_FOUND AND PLIST_FOUND)
|
||||
#add_subdirectory(ext/gstafc)
|
||||
#endif(IMOBILEDEVICE_FOUND AND PLIST_FOUND)
|
||||
|
||||
# Subdirectories
|
||||
add_subdirectory(src)
|
||||
if (WIN32)
|
||||
|
||||
8
COPYING
8
COPYING
@@ -1,7 +1,7 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
@@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
@@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box".
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
||||
13
Changelog
13
Changelog
@@ -1,3 +1,16 @@
|
||||
Strawberry Music Player
|
||||
=======================
|
||||
ChangeLog
|
||||
|
||||
Version 0.1.2:
|
||||
* Fixed playback of WavPack files
|
||||
* Fixed musicbrainz tagfetcher
|
||||
* Use common regex (Song::kCoverRemoveDisc) for removing Disc/CD from album
|
||||
* Remove Disc/CD from album when creating hash for album cover filename
|
||||
* Fixed imobiledevice support compilation
|
||||
* Fixed output device selection on windows
|
||||
* Fixed problems with windows build missing some DLL's, only supplying required gstreamer-plugins now
|
||||
* Removed redundant code
|
||||
|
||||
Version 0.1.1:
|
||||
* Initial release
|
||||
|
||||
675
LICENSE
675
LICENSE
@@ -1,675 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{one line to give the program's name and a brief idea of what it does.}
|
||||
Copyright (C) {year} {name of author}
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
{project} Copyright (C) {year} {fullname}
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
Strawberry Music Player
|
||||
=======================
|
||||
README
|
||||
------
|
||||
|
||||
Strawberry is a audio player and music collection organizer. It was forked from Clementine in 2013 with a diffrent goal.
|
||||
It's written in C++ and Qt 5 and runs on Linux. The name is inspired by the band Strawbs.
|
||||
Strawberry is a audio player and music collection organizer. It is a fork of Clementine created in 2013 with a diffrent goal.
|
||||
It's written in C++ and Qt5. The name is inspired by the band Strawbs.
|
||||
|
||||
Features:
|
||||
### Features:
|
||||
|
||||
* Play and organize music
|
||||
* Edit tags on music files
|
||||
@@ -13,6 +14,7 @@ Features:
|
||||
* Native desktop notifications
|
||||
* Playlists in multiple formats
|
||||
* Transfer music to iPod, iPhone, MTP or mass-storage USB player
|
||||
* Fetch tags from MusicBrainz
|
||||
* Support for multiple backends
|
||||
|
||||
You can obtain and view the sourcecode on github at: https://github.com/jonaski/strawberry
|
||||
@@ -50,11 +52,11 @@ You should also install the gstreamer plugins: gstreamer-plugins-base, gstreamer
|
||||
Compiling from source
|
||||
---------------------
|
||||
|
||||
Get the code:
|
||||
### Get the code:
|
||||
|
||||
git clone https://github.com/jonaski/strawberry
|
||||
|
||||
Compile and install:
|
||||
### Compile and install:
|
||||
|
||||
mkdir strawberry-build
|
||||
cd strawberry-build
|
||||
5
TODO
5
TODO
@@ -1,5 +0,0 @@
|
||||
Strawberry Music Player
|
||||
=======================
|
||||
TODO
|
||||
|
||||
- Finalize / Improve status/context
|
||||
@@ -62,8 +62,7 @@ function(optional_component name default description)
|
||||
elseif(${testing_deps})
|
||||
string(REPLACE " " ";" arglist "${arg}")
|
||||
if(${arglist})
|
||||
# We have to do this instead of if(NOT ${arg}) so that tests may contain
|
||||
# "NOT" themselves.
|
||||
# We have to do this instead of if(NOT ${arg}) so that tests may contain "NOT" themselves.
|
||||
else()
|
||||
list(APPEND missing_deps "${current_dep_name}")
|
||||
set(testing_deps FALSE)
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
set(STRAWBERRY_VERSION_MAJOR 0)
|
||||
set(STRAWBERRY_VERSION_MINOR 1)
|
||||
set(STRAWBERRY_VERSION_PATCH 1)
|
||||
set(STRAWBERRY_VERSION_PATCH 2)
|
||||
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
||||
|
||||
#set(INCLUDE_GIT_REVISION OFF)
|
||||
set(INCLUDE_GIT_REVISION ON)
|
||||
set(INCLUDE_GIT_REVISION OFF)
|
||||
|
||||
set(majorminorpatch "${STRAWBERRY_VERSION_MAJOR}.${STRAWBERRY_VERSION_MINOR}.${STRAWBERRY_VERSION_PATCH}")
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
<file>misc/playing_tooltip.txt</file>
|
||||
|
||||
<file>pictures/strawberry.png</file>
|
||||
<file>pictures/strawberry-background.png</file>
|
||||
<file>pictures/strawbs-transparent.png</file>
|
||||
|
||||
<file>pictures/noalbumart.png</file>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 933 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 127 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
112
dist/strawberry.spec.in
vendored
112
dist/strawberry.spec.in
vendored
@@ -9,78 +9,55 @@ URL: http://www.strawbs.org/
|
||||
Source0: %{name}-@STRAWBERRY_VERSION_PACKAGE@.tar.xz
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
||||
|
||||
BuildRequires: make
|
||||
BuildRequires: cmake
|
||||
BuildRequires: gcc
|
||||
BuildRequires: gcc-c++
|
||||
BuildRequires: libprotoc9
|
||||
BuildRequires: glib2-devel
|
||||
BuildRequires: alsa-devel
|
||||
BuildRequires: protobuf-devel
|
||||
BuildRequires: glu-devel
|
||||
BuildRequires: taglib-devel
|
||||
BuildRequires: boost-devel
|
||||
BuildRequires: gstreamer-devel
|
||||
BuildRequires: gstreamer-plugins-base-devel
|
||||
BuildRequires: libmtp-devel
|
||||
BuildRequires: libcdio-devel
|
||||
BuildRequires: libgpod-devel
|
||||
BuildRequires: libimobiledevice-devel
|
||||
BuildRequires: libplist-devel
|
||||
BuildRequires: liblastfm-qt5-devel
|
||||
BuildRequires: libchromaprint-devel
|
||||
BuildRequires: libQt5Core-devel
|
||||
BuildRequires: libQt5Gui-devel
|
||||
BuildRequires: libQt5Widgets-devel
|
||||
BuildRequires: libQt5Network-devel
|
||||
BuildRequires: libQt5Concurrent-devel
|
||||
BuildRequires: libQt5Sql-devel
|
||||
BuildRequires: libQt5Xml-devel
|
||||
BuildRequires: libQt5OpenGL-devel
|
||||
BuildRequires: libQt5Test-devel
|
||||
BuildRequires: libQt5WebKit5-devel
|
||||
BuildRequires: libQt5WebKitWidgets-devel
|
||||
BuildRequires: libqt5-qtx11extras-devel
|
||||
BuildRequires: libQt5Gui-private-headers-devel
|
||||
BuildRequires: cmake
|
||||
BuildRequires: desktop-file-utils
|
||||
BuildRequires: gcc-c++
|
||||
BuildRequires: hicolor-icon-theme
|
||||
BuildRequires: libQt5Gui-private-headers-devel
|
||||
BuildRequires: liblastfm-qt5-devel
|
||||
BuildRequires: make
|
||||
BuildRequires: pkgconfig
|
||||
BuildRequires: update-desktop-files
|
||||
|
||||
Requires: glib-2.0
|
||||
Requires: gio-2.0
|
||||
Requires: protobuf
|
||||
Requires: cryptopp
|
||||
Requires: Qt5Core
|
||||
Requires: Qt5Gui
|
||||
Requires: Qt5Widgets
|
||||
Requires: Qt5Network
|
||||
Requires: Qt5Concurrent
|
||||
Requires: Qt5Sql
|
||||
Requires: Qt5Xml
|
||||
Requires: Qt5OpenGL
|
||||
Requires: Qt5Test
|
||||
Requires: Qt5X11Extras
|
||||
Requires: Qt5WebKit
|
||||
Requires: Qt5WebKitWidgets
|
||||
|
||||
# GStreamer codec dependencies
|
||||
Requires: gstreamer
|
||||
Requires: gstreamer-plugins-base
|
||||
Requires: gstreamer-plugins-good
|
||||
|
||||
%ifarch x86_64
|
||||
Requires: gstreamer1.0(decoder-audio/x-vorbis)()(64bit)
|
||||
Requires: gstreamer1.0(decoder-audio/x-flac)()(64bit)
|
||||
Requires: gstreamer1.0(decoder-audio/x-speex)()(64bit)
|
||||
Requires: gstreamer1.0(decoder-audio/x-wav)()(64bit)
|
||||
%else
|
||||
Requires: gstreamer1.0(decoder-audio/x-vorbis)
|
||||
Requires: gstreamer1.0(decoder-audio/x-flac)
|
||||
Requires: gstreamer1.0(decoder-audio/x-speex)
|
||||
Requires: gstreamer1.0(decoder-audio/x-wav)
|
||||
%endif
|
||||
BuildRequires: pkgconfig(Qt5Concurrent)
|
||||
BuildRequires: pkgconfig(Qt5Core)
|
||||
BuildRequires: pkgconfig(Qt5Gui)
|
||||
BuildRequires: pkgconfig(Qt5Network)
|
||||
BuildRequires: pkgconfig(Qt5OpenGL)
|
||||
BuildRequires: pkgconfig(Qt5Sql)
|
||||
BuildRequires: pkgconfig(Qt5Test)
|
||||
BuildRequires: pkgconfig(Qt5WebKit)
|
||||
BuildRequires: pkgconfig(Qt5WebKitWidgets)
|
||||
BuildRequires: pkgconfig(Qt5Widgets)
|
||||
BuildRequires: pkgconfig(Qt5X11Extras)
|
||||
BuildRequires: pkgconfig(alsa)
|
||||
BuildRequires: pkgconfig(gio-2.0)
|
||||
BuildRequires: pkgconfig(gio-unix-2.0)
|
||||
BuildRequires: pkgconfig(glib-2.0)
|
||||
BuildRequires: pkgconfig(glu)
|
||||
BuildRequires: pkgconfig(gstreamer-1.0)
|
||||
BuildRequires: pkgconfig(gstreamer-app-1.0)
|
||||
BuildRequires: pkgconfig(gstreamer-audio-1.0)
|
||||
BuildRequires: pkgconfig(gstreamer-base-1.0)
|
||||
BuildRequires: pkgconfig(gstreamer-tag-1.0)
|
||||
BuildRequires: pkgconfig(gthread-2.0)
|
||||
BuildRequires: pkgconfig(libcdio)
|
||||
BuildRequires: pkgconfig(libchromaprint)
|
||||
BuildRequires: pkgconfig(libgpod-1.0)
|
||||
BuildRequires: pkgconfig(libmtp)
|
||||
BuildRequires: pkgconfig(libnotify)
|
||||
BuildRequires: pkgconfig(libpulse)
|
||||
BuildRequires: pkgconfig(libudf)
|
||||
BuildRequires: pkgconfig(libxine)
|
||||
BuildRequires: pkgconfig(libvlc)
|
||||
BuildRequires: pkgconfig(libxml-2.0)
|
||||
BuildRequires: pkgconfig(protobuf)
|
||||
BuildRequires: pkgconfig(sqlite3) >= 3.7
|
||||
BuildRequires: pkgconfig(taglib) >= 1.8
|
||||
Requires: libtag1 >= 1.8
|
||||
|
||||
%description
|
||||
Strawberry is a audio player and music collection organiser.
|
||||
Strawberry is a audio player and music collection organizer.
|
||||
It is a fork of Clementine. The name is inspired by the band Strawbs.
|
||||
|
||||
Features:
|
||||
@@ -90,6 +67,7 @@ Features:
|
||||
* Native desktop notifications
|
||||
* Playlists in multiple formats
|
||||
* Transfer music to iPod, iPhone, MTP or mass-storage USB player
|
||||
* Fetch tags from MusicBrainz
|
||||
* Support for multiple backends
|
||||
|
||||
%prep
|
||||
|
||||
@@ -15,6 +15,11 @@
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <QTimer>
|
||||
#include <QGenericArgument>
|
||||
|
||||
#include "closure.h"
|
||||
|
||||
#include "core/timeconstants.h"
|
||||
|
||||
@@ -18,15 +18,20 @@
|
||||
#ifndef CLOSURE_H
|
||||
#define CLOSURE_H
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include <QMetaObject>
|
||||
#include <QMetaMethod>
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QTimer>
|
||||
|
||||
namespace _detail {
|
||||
@@ -40,11 +45,11 @@ class ClosureBase {
|
||||
virtual void Invoke() = 0;
|
||||
|
||||
// Tests only.
|
||||
ObjectHelper* helper() const;
|
||||
ObjectHelper *helper() const;
|
||||
|
||||
protected:
|
||||
explicit ClosureBase(ObjectHelper*);
|
||||
ObjectHelper* helper_;
|
||||
ObjectHelper *helper_;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(ClosureBase);
|
||||
@@ -56,7 +61,7 @@ class ClosureBase {
|
||||
class ObjectHelper : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ObjectHelper(QObject* parent, const char* signal, ClosureBase* closure);
|
||||
ObjectHelper(QObject *parent, const char *signal, ClosureBase *closure);
|
||||
|
||||
private slots:
|
||||
void Invoked();
|
||||
@@ -72,12 +77,12 @@ class ObjectHelper : public QObject {
|
||||
void Unpack(QList<QGenericArgument>*);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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, tail...);
|
||||
}
|
||||
@@ -86,16 +91,16 @@ template <typename... Args>
|
||||
class Closure : public ClosureBase {
|
||||
public:
|
||||
Closure(
|
||||
QObject* sender,
|
||||
const char* signal,
|
||||
QObject* receiver,
|
||||
const char* slot,
|
||||
QObject *sender,
|
||||
const char *signal,
|
||||
QObject *receiver,
|
||||
const char *slot,
|
||||
const Args&... args)
|
||||
: ClosureBase(new ObjectHelper(sender, signal, this)),
|
||||
// std::bind is the easiest way to store an argument list.
|
||||
function_(std::bind(&Closure<Args...>::Call, this, args...)),
|
||||
receiver_(receiver) {
|
||||
const QMetaObject* meta_receiver = receiver->metaObject();
|
||||
const QMetaObject *meta_receiver = receiver->metaObject();
|
||||
QByteArray normalised_slot = QMetaObject::normalizedSignature(slot + 1);
|
||||
const int index = meta_receiver->indexOfSlot(normalised_slot.constData());
|
||||
Q_ASSERT(index != -1);
|
||||
@@ -127,7 +132,7 @@ class Closure : public ClosureBase {
|
||||
}
|
||||
|
||||
std::function<void()> function_;
|
||||
QObject* receiver_;
|
||||
QObject *receiver_;
|
||||
QMetaMethod slot_;
|
||||
};
|
||||
|
||||
@@ -136,9 +141,9 @@ class SharedClosure : public Closure<Args...> {
|
||||
public:
|
||||
SharedClosure(
|
||||
QSharedPointer<T> sender,
|
||||
const char* signal,
|
||||
QObject* receiver,
|
||||
const char* slot,
|
||||
const char *signal,
|
||||
QObject *receiver,
|
||||
const char *slot,
|
||||
const Args&... args)
|
||||
: Closure<Args...>(
|
||||
sender.data(),
|
||||
@@ -155,8 +160,7 @@ class SharedClosure : public Closure<Args...> {
|
||||
|
||||
class CallbackClosure : public ClosureBase {
|
||||
public:
|
||||
CallbackClosure(QObject* sender, const char* signal,
|
||||
std::function<void()> callback);
|
||||
CallbackClosure(QObject *sender, const char *signal, std::function<void()> callback);
|
||||
|
||||
virtual void Invoke();
|
||||
|
||||
@@ -167,11 +171,11 @@ class CallbackClosure : public ClosureBase {
|
||||
} // namespace _detail
|
||||
|
||||
template <typename... Args>
|
||||
_detail::ClosureBase* NewClosure(
|
||||
QObject* sender,
|
||||
const char* signal,
|
||||
QObject* receiver,
|
||||
const char* slot,
|
||||
_detail::ClosureBase *NewClosure(
|
||||
QObject *sender,
|
||||
const char *signal,
|
||||
QObject *receiver,
|
||||
const char *slot,
|
||||
const Args&... args) {
|
||||
return new _detail::Closure<Args...>(
|
||||
sender, signal, receiver, slot, args...);
|
||||
@@ -179,64 +183,64 @@ _detail::ClosureBase* NewClosure(
|
||||
|
||||
// QSharedPointer variant
|
||||
template <typename T, typename... Args>
|
||||
_detail::ClosureBase* NewClosure(
|
||||
_detail::ClosureBase *NewClosure(
|
||||
QSharedPointer<T> sender,
|
||||
const char* signal,
|
||||
QObject* receiver,
|
||||
const char* slot,
|
||||
const char *signal,
|
||||
QObject *receiver,
|
||||
const char *slot,
|
||||
const Args&... args) {
|
||||
return new _detail::SharedClosure<T, 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>
|
||||
_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...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
_detail::ClosureBase* NewClosure(
|
||||
QObject* sender,
|
||||
const char* signal,
|
||||
_detail::ClosureBase *NewClosure(
|
||||
QObject *sender,
|
||||
const char *signal,
|
||||
void (*callback)(Args...),
|
||||
const Args&... args) {
|
||||
return NewClosure(sender, signal, std::bind(callback, args...));
|
||||
}
|
||||
|
||||
template <typename T, typename Unused, typename... Args>
|
||||
_detail::ClosureBase* NewClosure(
|
||||
QObject* sender,
|
||||
const char* signal,
|
||||
T* receiver, Unused (T::*callback)(Args...),
|
||||
_detail::ClosureBase *NewClosure(
|
||||
QObject *sender,
|
||||
const char *signal,
|
||||
T *receiver, Unused (T::*callback)(Args...),
|
||||
const Args&... args) {
|
||||
return NewClosure(sender, signal, std::bind(callback, receiver, args...));
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
_detail::ClosureBase* NewClosure(QFuture<T> future, QObject* receiver, const char* slot, const Args&... args) {
|
||||
QFutureWatcher<T>* watcher = new QFutureWatcher<T>;
|
||||
_detail::ClosureBase *NewClosure(QFuture<T> future, QObject *receiver, const char *slot, const Args&... args) {
|
||||
QFutureWatcher<T> *watcher = new QFutureWatcher<T>;
|
||||
watcher->setFuture(future);
|
||||
QObject::connect(watcher, SIGNAL(finished()), watcher, SLOT(deleteLater()));
|
||||
return NewClosure(watcher, SIGNAL(finished()), receiver, slot, args...);
|
||||
}
|
||||
|
||||
template <typename T, typename F, typename... Args>
|
||||
_detail::ClosureBase* NewClosure(QFuture<T> future, const F& callback, const Args&... args) {
|
||||
QFutureWatcher<T>* watcher = new QFutureWatcher<T>;
|
||||
_detail::ClosureBase *NewClosure(QFuture<T> future, const F &callback, const Args&... args) {
|
||||
QFutureWatcher<T> *watcher = new QFutureWatcher<T>;
|
||||
watcher->setFuture(future);
|
||||
QObject::connect(watcher, SIGNAL(finished()), watcher, SLOT(deleteLater()));
|
||||
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 DoInAMinuteOrSo(QObject* receiver, const char* slot);
|
||||
void DoInAMinuteOrSo(QObject *receiver, const char *slot);
|
||||
|
||||
template <typename R, typename P>
|
||||
void DoAfter(std::function<void()> callback, std::chrono::duration<R, P> duration) {
|
||||
QTimer* timer = new QTimer;
|
||||
QTimer *timer = new QTimer;
|
||||
timer->setSingleShot(true);
|
||||
NewClosure(timer, SIGNAL(timeout()), callback);
|
||||
QObject::connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater()));
|
||||
|
||||
@@ -55,9 +55,7 @@ class ThreadFunctorBase : public QFutureInterface<ReturnType>, public QRunnable
|
||||
this->reportStarted();
|
||||
Q_ASSERT(thread_pool);
|
||||
QFuture<ReturnType> future = this->future();
|
||||
thread_pool->start(this, 0 /* priority: currently we do not support
|
||||
changing the priority. Might be added later
|
||||
if needed */);
|
||||
thread_pool->start(this, 0 /* priority: currently we do not support changing the priority. Might be added later if needed */);
|
||||
return future;
|
||||
}
|
||||
|
||||
@@ -67,8 +65,7 @@ class ThreadFunctorBase : public QFutureInterface<ReturnType>, public QRunnable
|
||||
template <typename ReturnType, typename... Args>
|
||||
class ThreadFunctor : public ThreadFunctorBase<ReturnType> {
|
||||
public:
|
||||
ThreadFunctor(std::function<ReturnType (Args...)> function,
|
||||
Args... args)
|
||||
ThreadFunctor(std::function<ReturnType (Args...)> function, Args... args)
|
||||
: function_(std::bind(function, args...)) {
|
||||
}
|
||||
|
||||
@@ -85,8 +82,7 @@ class ThreadFunctor : public ThreadFunctorBase<ReturnType> {
|
||||
template <typename... Args>
|
||||
class ThreadFunctor <void, Args...> : public ThreadFunctorBase<void> {
|
||||
public:
|
||||
ThreadFunctor(std::function<void (Args...)> function,
|
||||
Args... args)
|
||||
ThreadFunctor(std::function<void (Args...)> function, Args... args)
|
||||
: function_(std::bind(function, args...)) {
|
||||
}
|
||||
|
||||
@@ -107,30 +103,20 @@ namespace ConcurrentRun {
|
||||
|
||||
// Empty argument form.
|
||||
template <typename ReturnType>
|
||||
QFuture<ReturnType> Run(
|
||||
QThreadPool* threadpool,
|
||||
std::function<ReturnType ()> function) {
|
||||
QFuture<ReturnType> Run(QThreadPool* threadpool, std::function<ReturnType ()> function) {
|
||||
return (new ThreadFunctor<ReturnType>(function))->Start(threadpool);
|
||||
}
|
||||
|
||||
// Function object with arguments form.
|
||||
template <typename ReturnType, typename... Args>
|
||||
QFuture<ReturnType> Run(
|
||||
QThreadPool* threadpool,
|
||||
std::function<ReturnType (Args...)> function,
|
||||
const Args&... args) {
|
||||
return (new ThreadFunctor<ReturnType, Args...>(
|
||||
function, args...))->Start(threadpool);
|
||||
QFuture<ReturnType> Run(QThreadPool* 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.
|
||||
template <typename ReturnType, typename... Args>
|
||||
QFuture<ReturnType> Run(
|
||||
QThreadPool* threadpool,
|
||||
ReturnType (*function) (Args...),
|
||||
const Args&... args) {
|
||||
return Run(
|
||||
threadpool, std::function<ReturnType (Args...)>(function), args...);
|
||||
QFuture<ReturnType> Run(QThreadPool* threadpool, ReturnType (*function) (Args...), const Args&... args) {
|
||||
return Run(threadpool, std::function<ReturnType (Args...)>(function), args...);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,21 +14,28 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <iostream>
|
||||
|
||||
#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 <glib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
#include <QStringList>
|
||||
#include <QtMessageHandler>
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
namespace logging {
|
||||
@@ -75,7 +82,7 @@ static void MessageHandler(QtMsgType type, const QMessageLogContext &context, co
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,10 @@
|
||||
#define LOGGING_H
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QIODevice>
|
||||
#include <QString>
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef QT_NO_DEBUG_STREAM
|
||||
@@ -61,30 +63,30 @@ enum Level {
|
||||
|
||||
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 CreateLoggerFatal(int line, const char* class_name);
|
||||
QDebug CreateLoggerError(int line, const char* class_name);
|
||||
QDebug CreateLoggerFatal(int line, const char *class_name);
|
||||
QDebug CreateLoggerError(int line, const char *class_name);
|
||||
|
||||
#ifdef QT_NO_WARNING_OUTPUT
|
||||
QNoDebug CreateLoggerWarning(int, const char*);
|
||||
#else
|
||||
QDebug CreateLoggerWarning(int line, const char* class_name);
|
||||
QDebug CreateLoggerWarning(int line, const char *class_name);
|
||||
#endif // QT_NO_WARNING_OUTPUT
|
||||
|
||||
#ifdef QT_NO_DEBUG_OUTPUT
|
||||
QNoDebug CreateLoggerInfo(int, const char*);
|
||||
QNoDebug CreateLoggerDebug(int, const char*);
|
||||
#else
|
||||
QDebug CreateLoggerInfo(int line, const char* class_name);
|
||||
QDebug CreateLoggerDebug(int line, const char* class_name);
|
||||
QDebug CreateLoggerInfo(int line, const char *class_name);
|
||||
QDebug CreateLoggerDebug(int line, const char *class_name);
|
||||
#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);
|
||||
|
||||
@@ -15,11 +15,15 @@
|
||||
*/
|
||||
|
||||
#include "messagehandler.h"
|
||||
#include "core/logging.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QAbstractSocket>
|
||||
#include <QLocalSocket>
|
||||
#include <QDataStream>
|
||||
#include <QIODevice>
|
||||
#include <QLocalSocket>
|
||||
#include <QByteArray>
|
||||
|
||||
#include "core/logging.h"
|
||||
|
||||
_MessageHandlerBase::_MessageHandlerBase(QIODevice *device, QObject *parent)
|
||||
: QObject(parent),
|
||||
|
||||
@@ -18,21 +18,21 @@
|
||||
#ifndef MESSAGEHANDLER_H
|
||||
#define MESSAGEHANDLER_H
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QMap>
|
||||
#include <QMutex>
|
||||
#include <QMutexLocker>
|
||||
#include <string>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QSemaphore>
|
||||
#include <QThread>
|
||||
#include <QIODevice>
|
||||
#include <QBuffer>
|
||||
#include <QByteArray>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <QLocalSocket>
|
||||
#include <QAbstractSocket>
|
||||
|
||||
#include "core/logging.h"
|
||||
#include "core/messagereply.h"
|
||||
|
||||
class QAbstractSocket;
|
||||
class QIODevice;
|
||||
class QLocalSocket;
|
||||
|
||||
#define QStringFromStdString(x) QString::fromUtf8(x.data(), x.size())
|
||||
#define DataCommaSizeFromQString(x) x.toUtf8().constData(), x.toUtf8().length()
|
||||
|
||||
@@ -45,27 +45,27 @@ class _MessageHandlerBase : public QObject {
|
||||
public:
|
||||
// device can be NULL, in which case you must call SetDevice before writing
|
||||
// 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.
|
||||
bool is_device_closed() const { return is_device_closed_; }
|
||||
|
||||
protected slots:
|
||||
void WriteMessage(const QByteArray& data);
|
||||
void WriteMessage(const QByteArray &data);
|
||||
void DeviceReadyRead();
|
||||
virtual void DeviceClosed();
|
||||
|
||||
protected:
|
||||
virtual bool RawMessageArrived(const QByteArray& data) = 0;
|
||||
virtual bool RawMessageArrived(const QByteArray &data) = 0;
|
||||
virtual void AbortAll() = 0;
|
||||
|
||||
protected:
|
||||
typedef bool (QAbstractSocket::*FlushAbstractSocket)();
|
||||
typedef bool (QLocalSocket::*FlushLocalSocket)();
|
||||
|
||||
QIODevice* device_;
|
||||
QIODevice *device_;
|
||||
FlushAbstractSocket flush_abstract_socket_;
|
||||
FlushLocalSocket flush_local_socket_;
|
||||
|
||||
@@ -82,7 +82,7 @@ protected:
|
||||
template <typename MT>
|
||||
class AbstractMessageHandler : public _MessageHandlerBase {
|
||||
public:
|
||||
AbstractMessageHandler(QIODevice* device, QObject* parent);
|
||||
AbstractMessageHandler(QIODevice *device, QObject *parent);
|
||||
~AbstractMessageHandler() { AbortAll(); }
|
||||
|
||||
typedef MT MessageType;
|
||||
@@ -90,27 +90,27 @@ public:
|
||||
|
||||
// Serialises the message and writes it to the socket. This version MUST be
|
||||
// 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
|
||||
// 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.
|
||||
// The MessageReply's Finished() signal will be emitted when a reply arrives
|
||||
// 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
|
||||
// reply on the socket. Used on the worker side.
|
||||
void SendReply(const MessageType& request, MessageType* reply);
|
||||
void SendReply(const MessageType &request, MessageType *reply);
|
||||
|
||||
protected:
|
||||
// Called when a message is received from the socket.
|
||||
virtual void MessageArrived(const MessageType& message) {}
|
||||
virtual void MessageArrived(const MessageType &message) {}
|
||||
|
||||
// _MessageHandlerBase
|
||||
bool RawMessageArrived(const QByteArray& data);
|
||||
bool RawMessageArrived(const QByteArray &data);
|
||||
void AbortAll();
|
||||
|
||||
private:
|
||||
@@ -118,12 +118,11 @@ private:
|
||||
};
|
||||
|
||||
template <typename MT>
|
||||
AbstractMessageHandler<MT>::AbstractMessageHandler(QIODevice* device,
|
||||
QObject* parent)
|
||||
AbstractMessageHandler<MT>::AbstractMessageHandler(QIODevice *device, QObject *parent)
|
||||
: _MessageHandlerBase(device, parent) {}
|
||||
|
||||
template <typename MT>
|
||||
void AbstractMessageHandler<MT>::SendMessage(const MessageType& message) {
|
||||
void AbstractMessageHandler<MT>::SendMessage(const MessageType &message) {
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
|
||||
std::string data = message.SerializeAsString();
|
||||
@@ -131,33 +130,32 @@ void AbstractMessageHandler<MT>::SendMessage(const MessageType& message) {
|
||||
}
|
||||
|
||||
template <typename MT>
|
||||
void AbstractMessageHandler<MT>::SendMessageAsync(const MessageType& message) {
|
||||
void AbstractMessageHandler<MT>::SendMessageAsync(const MessageType &message) {
|
||||
std::string data = message.SerializeAsString();
|
||||
metaObject()->invokeMethod(this, "WriteMessage", Qt::QueuedConnection,
|
||||
Q_ARG(QByteArray, QByteArray(data.data(), data.size())));
|
||||
}
|
||||
|
||||
template<typename MT>
|
||||
void AbstractMessageHandler<MT>::SendRequest(ReplyType* reply) {
|
||||
void AbstractMessageHandler<MT>::SendRequest(ReplyType *reply) {
|
||||
pending_replies_[reply->id()] = reply;
|
||||
SendMessage(reply->request_message());
|
||||
}
|
||||
|
||||
template<typename MT>
|
||||
void AbstractMessageHandler<MT>::SendReply(const MessageType& request,
|
||||
MessageType* reply) {
|
||||
void AbstractMessageHandler<MT>::SendReply(const MessageType &request, MessageType *reply) {
|
||||
reply->set_id(request.id());
|
||||
SendMessage(*reply);
|
||||
}
|
||||
|
||||
template<typename MT>
|
||||
bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray& data) {
|
||||
bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray &data) {
|
||||
MessageType message;
|
||||
if (!message.ParseFromArray(data.constData(), data.size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ReplyType* reply = pending_replies_.take(message.id());
|
||||
ReplyType *reply = pending_replies_.take(message.id());
|
||||
|
||||
if (reply) {
|
||||
// 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>
|
||||
void AbstractMessageHandler<MT>::AbortAll() {
|
||||
for (ReplyType* reply : pending_replies_) {
|
||||
for (ReplyType *reply : pending_replies_) {
|
||||
reply->Abort();
|
||||
}
|
||||
pending_replies_.clear();
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
|
||||
#include "messagereply.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QtDebug>
|
||||
|
||||
#include "core/logging.h"
|
||||
|
||||
_MessageReplyBase::_MessageReplyBase(QObject *parent)
|
||||
: QObject(parent), finished_(false), success_(false) {}
|
||||
|
||||
|
||||
@@ -18,26 +18,26 @@
|
||||
#ifndef MESSAGEREPLY_H
|
||||
#define MESSAGEREPLY_H
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QSemaphore>
|
||||
#include <QString>
|
||||
|
||||
#include "core/logging.h"
|
||||
|
||||
// Base QObject for a reply future class that is returned immediately for
|
||||
// requests that will occur in the background. Similar to QNetworkReply.
|
||||
// Use MessageReply instead.
|
||||
// Base QObject for a reply future class that is returned immediately for requests that will occur in the background.
|
||||
// Similar to QNetworkReply. Use MessageReply instead.
|
||||
class _MessageReplyBase : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
_MessageReplyBase(QObject* parent = nullptr);
|
||||
_MessageReplyBase(QObject *parent = nullptr);
|
||||
|
||||
virtual int id() const = 0;
|
||||
bool is_finished() const { return finished_; }
|
||||
bool is_successful() const { return success_; }
|
||||
|
||||
// Waits for the reply to finish by waiting on a semaphore. Never call this
|
||||
// from the MessageHandler's thread or it will block forever.
|
||||
// Waits for the reply to finish by waiting on a semaphore. Never call this from the MessageHandler's thread or it will block forever.
|
||||
// Returns true if the call was successful.
|
||||
bool WaitForFinished();
|
||||
|
||||
@@ -58,7 +58,7 @@ protected:
|
||||
template <typename MessageType>
|
||||
class MessageReply : public _MessageReplyBase {
|
||||
public:
|
||||
MessageReply(const MessageType& request_message, QObject* parent = nullptr);
|
||||
MessageReply(const MessageType& request_message, QObject *parent = nullptr);
|
||||
|
||||
int id() const { return request_message_.id(); }
|
||||
const MessageType& request_message() const { return request_message_; }
|
||||
@@ -73,8 +73,7 @@ private:
|
||||
|
||||
|
||||
template<typename MessageType>
|
||||
MessageReply<MessageType>::MessageReply(const MessageType& request_message,
|
||||
QObject* parent)
|
||||
MessageReply<MessageType>::MessageReply(const MessageType& request_message, QObject *parent)
|
||||
: _MessageReplyBase(parent)
|
||||
{
|
||||
request_message_.MergeFrom(request_message);
|
||||
@@ -94,4 +93,3 @@ void MessageReply<MessageType>::SetReply(const MessageType& message) {
|
||||
}
|
||||
|
||||
#endif // MESSAGEREPLY_H
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "waitforsignal.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QEventLoop>
|
||||
|
||||
void WaitForSignal(QObject *sender, const char *signal) {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "workerpool.h"
|
||||
|
||||
_WorkerPoolBase::_WorkerPoolBase(QObject *parent) : QObject(parent) {}
|
||||
|
||||
@@ -18,32 +18,37 @@
|
||||
#ifndef WORKERPOOL_H
|
||||
#define WORKERPOOL_H
|
||||
|
||||
#include <QAtomicInt>
|
||||
#include <QCoreApplication>
|
||||
#include <QFile>
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
#include <QMutex>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QCoreApplication>
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
#include <QProcess>
|
||||
#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"
|
||||
|
||||
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 {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
_WorkerPoolBase(QObject* parent = nullptr);
|
||||
_WorkerPoolBase(QObject *parent = nullptr);
|
||||
|
||||
signals:
|
||||
// Emitted when a worker failed to start. This usually happens when the
|
||||
// worker wasn't found, or couldn't be executed.
|
||||
// Emitted when a worker failed to start. This usually happens when the worker wasn't found, or couldn't be executed.
|
||||
void WorkerFailedToStart();
|
||||
|
||||
protected slots:
|
||||
@@ -54,45 +59,41 @@ protected slots:
|
||||
};
|
||||
|
||||
|
||||
// Manages a pool of one or more external processes. A local socket server is
|
||||
// started for each process, and the address is passed to the process as
|
||||
// argv[1]. The process is expected to connect back to the socket server, and
|
||||
// when it does a HandlerType is created for it.
|
||||
// Manages a pool of one or more external processes.
|
||||
// A local socket server is started for each process, and the address is passed to the process as argv[1].
|
||||
// The process is expected to connect back to the socket server, and when it does a HandlerType is created for it.
|
||||
// Instances of HandlerType are created in the WorkerPool's thread.
|
||||
template <typename HandlerType>
|
||||
class WorkerPool : public _WorkerPoolBase {
|
||||
public:
|
||||
WorkerPool(QObject* parent = nullptr);
|
||||
WorkerPool(QObject *parent = nullptr);
|
||||
~WorkerPool();
|
||||
|
||||
typedef typename HandlerType::MessageType MessageType;
|
||||
typedef typename HandlerType::ReplyType ReplyType;
|
||||
|
||||
// Sets the name of the worker executable. This is looked for first in the
|
||||
// current directory, and then in $PATH. You must call this before calling
|
||||
// Start().
|
||||
void SetExecutableName(const QString& executable_name);
|
||||
// Sets the name of the worker executable. This is looked for first in the current directory, and then in $PATH.
|
||||
// You must call this before calling Start().
|
||||
void SetExecutableName(const QString &executable_name);
|
||||
|
||||
// Sets the number of worker process to use. Defaults to
|
||||
// 1 <= (processors / 2) <= 2.
|
||||
// Sets the number of worker process to use. Defaults to 1 <= (processors / 2) <= 2.
|
||||
void SetWorkerCount(int count);
|
||||
|
||||
// Sets the prefix to use for the local server (on unix this is a named pipe
|
||||
// in /tmp). Defaults to QApplication::applicationName(). A random number
|
||||
// is appended to this name when creating each server.
|
||||
void SetLocalServerName(const QString& local_server_name);
|
||||
// Sets the prefix to use for the local server (on unix this is a named pipe in /tmp).
|
||||
// Defaults to QApplication::applicationName().
|
||||
// A random number is appended to this name when creating each server.
|
||||
void SetLocalServerName(const QString &local_server_name);
|
||||
|
||||
// Starts all workers.
|
||||
void Start();
|
||||
|
||||
// Fills in the message's "id" field and creates a reply future. The message
|
||||
// is queued and the WorkerPool's thread will send it to the next available
|
||||
// worker. Can be called from any thread.
|
||||
ReplyType* SendMessageWithReply(MessageType* message);
|
||||
// Fills in the message's "id" field and creates a reply future.
|
||||
// The message is queued and the WorkerPool's thread will send it to the next available worker.
|
||||
// Can be called from any thread.
|
||||
ReplyType *SendMessageWithReply(MessageType *message);
|
||||
|
||||
protected:
|
||||
// These are all reimplemented slots, they are called on the WorkerPool's
|
||||
// thread.
|
||||
// These are all reimplemented slots, they are called on the WorkerPool's thread.
|
||||
void DoStart();
|
||||
void NewConnection();
|
||||
void ProcessError(QProcess::ProcessError error);
|
||||
@@ -105,14 +106,14 @@ private:
|
||||
QLocalServer *local_server_;
|
||||
QLocalSocket *local_socket_;
|
||||
QProcess *process_;
|
||||
HandlerType* handler_;
|
||||
HandlerType *handler_;
|
||||
};
|
||||
|
||||
// Must only ever be called on my thread.
|
||||
void StartOneWorker(Worker* worker);
|
||||
void StartOneWorker(Worker *worker);
|
||||
|
||||
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() ;
|
||||
it != workers_.end() ; ++it) {
|
||||
if ((*it).*member == value) {
|
||||
@@ -123,7 +124,7 @@ private:
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DeleteQObjectPointerLater(T** p) {
|
||||
void DeleteQObjectPointerLater(T **p) {
|
||||
if (*p) {
|
||||
(*p)->deleteLater();
|
||||
*p = NULL;
|
||||
@@ -131,13 +132,11 @@ private:
|
||||
}
|
||||
|
||||
// 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
|
||||
// thread
|
||||
ReplyType* NewReply(MessageType* message);
|
||||
// and sets the request's ID to the ID of the reply. Can be called from any thread
|
||||
ReplyType *NewReply(MessageType *message);
|
||||
|
||||
// Returns the next handler, or NULL if there isn't one. Must be called from
|
||||
// my thread.
|
||||
HandlerType* NextHandler() const;
|
||||
// Returns the next handler, or NULL if there isn't one. Must be called from my thread.
|
||||
HandlerType *NextHandler() const;
|
||||
|
||||
private:
|
||||
QString local_server_name_;
|
||||
@@ -156,7 +155,7 @@ private:
|
||||
|
||||
|
||||
template <typename HandlerType>
|
||||
WorkerPool<HandlerType>::WorkerPool(QObject* parent)
|
||||
WorkerPool<HandlerType>::WorkerPool(QObject *parent)
|
||||
: _WorkerPoolBase(parent),
|
||||
next_worker_(0),
|
||||
next_id_(0)
|
||||
@@ -170,7 +169,7 @@ WorkerPool<HandlerType>::WorkerPool(QObject* parent)
|
||||
|
||||
template <typename HandlerType>
|
||||
WorkerPool<HandlerType>::~WorkerPool() {
|
||||
for (const Worker& worker : workers_) {
|
||||
for (const Worker &worker : workers_) {
|
||||
if (worker.local_socket_ && worker.process_) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -202,13 +201,13 @@ void WorkerPool<HandlerType>::SetWorkerCount(int count) {
|
||||
}
|
||||
|
||||
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());
|
||||
local_server_name_ = local_server_name;
|
||||
}
|
||||
|
||||
template <typename HandlerType>
|
||||
void WorkerPool<HandlerType>::SetExecutableName(const QString& executable_name) {
|
||||
void WorkerPool<HandlerType>::SetExecutableName(const QString &executable_name) {
|
||||
Q_ASSERT(workers_.isEmpty());
|
||||
executable_name_ = executable_name;
|
||||
}
|
||||
@@ -233,7 +232,7 @@ void WorkerPool<HandlerType>::DoStart() {
|
||||
search_path << qApp->applicationDirPath() + "/../PlugIns";
|
||||
#endif
|
||||
|
||||
for (const QString& path_prefix : search_path) {
|
||||
for (const QString &path_prefix : search_path) {
|
||||
const QString executable_path = path_prefix + "/" + executable_name_;
|
||||
if (QFile::exists(executable_path)) {
|
||||
executable_path_ = executable_path;
|
||||
@@ -290,7 +289,7 @@ void WorkerPool<HandlerType>::NewConnection() {
|
||||
QLocalServer *server = qobject_cast<QLocalServer*>(sender());
|
||||
|
||||
// Find the worker with this server.
|
||||
Worker* worker = FindWorker(&Worker::local_server_, server);
|
||||
Worker *worker = FindWorker(&Worker::local_server_, server);
|
||||
if (!worker) return;
|
||||
|
||||
qLog(Debug) << "Worker" << worker << "connected to" << server->fullServerName();
|
||||
@@ -322,9 +321,8 @@ void WorkerPool<HandlerType>::ProcessError(QProcess::ProcessError error) {
|
||||
|
||||
switch (error) {
|
||||
case QProcess::FailedToStart:
|
||||
// Failed to start errors are bad - it usually means the worker isn't
|
||||
// installed. Don't restart the process, but tell our owner, who will
|
||||
// probably want to do something fatal.
|
||||
// Failed to start errors are bad - it usually means the worker isn't installed.
|
||||
// Don't restart the process, but tell our owner, who will probably want to do something fatal.
|
||||
qLog(Error) << "Worker failed to start";
|
||||
emit WorkerFailedToStart();
|
||||
break;
|
||||
@@ -339,7 +337,7 @@ void WorkerPool<HandlerType>::ProcessError(QProcess::ProcessError error) {
|
||||
|
||||
template <typename HandlerType>
|
||||
typename WorkerPool<HandlerType>::ReplyType*
|
||||
WorkerPool<HandlerType>::NewReply(MessageType* message) {
|
||||
WorkerPool<HandlerType>::NewReply(MessageType *message) {
|
||||
const int id = next_id_.fetchAndAddOrdered(1);
|
||||
message->set_id(id);
|
||||
|
||||
@@ -348,8 +346,8 @@ WorkerPool<HandlerType>::NewReply(MessageType* message) {
|
||||
|
||||
template <typename HandlerType>
|
||||
typename WorkerPool<HandlerType>::ReplyType*
|
||||
WorkerPool<HandlerType>::SendMessageWithReply(MessageType* message) {
|
||||
ReplyType* reply = NewReply(message);
|
||||
WorkerPool<HandlerType>::SendMessageWithReply(MessageType *message) {
|
||||
ReplyType *reply = NewReply(message);
|
||||
|
||||
// Add the pending reply to the queue
|
||||
{
|
||||
@@ -371,7 +369,7 @@ void WorkerPool<HandlerType>::SendQueuedMessages() {
|
||||
ReplyType *reply = message_queue_.dequeue();
|
||||
|
||||
// Find a worker for this message
|
||||
HandlerType* handler = NextHandler();
|
||||
HandlerType *handler = NextHandler();
|
||||
if (!handler) {
|
||||
// No available handlers - put the message on the front of the queue.
|
||||
message_queue_.prepend(reply);
|
||||
|
||||
@@ -15,12 +15,15 @@
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "fmpsparser.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <QStringList>
|
||||
#include <QtDebug>
|
||||
#include <QList>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
|
||||
#include "fmpsparser.h"
|
||||
|
||||
using std::placeholders::_1;
|
||||
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);
|
||||
}
|
||||
|
||||
// Convenience functions that take QStrings instead of QStringRefs. Use the
|
||||
// QStringRef versions if possible, they're faster.
|
||||
// Convenience functions that take QStrings instead of QStringRefs. Use the QStringRef versions if possible, they're faster.
|
||||
int FMPSParser::ParseValue(const QString &data, QVariant *ret) const {
|
||||
return ParseValueRef(QStringRef(&data), ret);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,13 @@
|
||||
#ifndef FMPSPARSER_H
|
||||
#define FMPSPARSER_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QMetaType>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QVariantList>
|
||||
|
||||
class FMPSParser {
|
||||
public:
|
||||
@@ -30,7 +35,7 @@ public:
|
||||
typedef QList<QVariantList> Result;
|
||||
|
||||
// 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.
|
||||
Result result() const { return result_; }
|
||||
|
||||
@@ -15,50 +15,76 @@
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "tagreader.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
#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 <list>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#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/wavpackfile.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/messagehandler.h"
|
||||
|
||||
#include "fmpsparser.h"
|
||||
#include "core/timeconstants.h"
|
||||
|
||||
// Taglib added support for FLAC pictures in 1.7.0
|
||||
@@ -71,12 +97,12 @@
|
||||
class FileRefFactory {
|
||||
public:
|
||||
virtual ~FileRefFactory() {}
|
||||
virtual TagLib::FileRef *GetFileRef(const QString& filename) = 0;
|
||||
virtual TagLib::FileRef *GetFileRef(const QString &filename) = 0;
|
||||
};
|
||||
|
||||
class TagLibFileRefFactory : public FileRefFactory {
|
||||
public:
|
||||
virtual TagLib::FileRef *GetFileRef(const QString& filename) {
|
||||
virtual TagLib::FileRef *GetFileRef(const QString &filename) {
|
||||
#ifdef Q_OS_WIN32
|
||||
return new TagLib::FileRef(filename.toStdWString().c_str());
|
||||
#else
|
||||
@@ -87,11 +113,11 @@ class TagLibFileRefFactory : public FileRefFactory {
|
||||
|
||||
namespace {
|
||||
|
||||
TagLib::String StdStringToTaglibString(const std::string& s) {
|
||||
TagLib::String StdStringToTaglibString(const std::string &s) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -441,28 +467,29 @@ void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, con
|
||||
}
|
||||
|
||||
pb::tagreader::SongMetadata_Type TagReader::GuessFileType(TagLib::FileRef *fileref) const {
|
||||
|
||||
#ifdef TAGLIB_WITH_ASF
|
||||
if (dynamic_cast<TagLib::ASF::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_ASF;
|
||||
#endif
|
||||
|
||||
if (dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_WAV;
|
||||
if (dynamic_cast<TagLib::FLAC::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_FLAC;
|
||||
#ifdef TAGLIB_WITH_MP4
|
||||
if (dynamic_cast<TagLib::MP4::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_MP4;
|
||||
#endif
|
||||
if (dynamic_cast<TagLib::MPC::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_MPC;
|
||||
if (dynamic_cast<TagLib::MPEG::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_MPEG;
|
||||
if (dynamic_cast<TagLib::WavPack::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_WAVPACK;
|
||||
if (dynamic_cast<TagLib::Ogg::FLAC::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_OGGFLAC;
|
||||
if (dynamic_cast<TagLib::Ogg::Speex::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_OGGSPEEX;
|
||||
if (dynamic_cast<TagLib::Ogg::Vorbis::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_OGGVORBIS;
|
||||
#ifdef TAGLIB_HAS_OPUS
|
||||
if (dynamic_cast<TagLib::Ogg::Opus::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_OGGOPUS;
|
||||
#endif
|
||||
if (dynamic_cast<TagLib::Ogg::Speex::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_OGGSPEEX;
|
||||
if (dynamic_cast<TagLib::MPEG::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_MPEG;
|
||||
#ifdef TAGLIB_WITH_MP4
|
||||
if (dynamic_cast<TagLib::MP4::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_MP4;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_ASF
|
||||
if (dynamic_cast<TagLib::ASF::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_ASF;
|
||||
#endif
|
||||
if (dynamic_cast<TagLib::RIFF::AIFF::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_AIFF;
|
||||
if (dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_WAV;
|
||||
if (dynamic_cast<TagLib::MPC::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_MPC;
|
||||
if (dynamic_cast<TagLib::TrueAudio::File*>(fileref->file())) return pb::tagreader::SongMetadata_Type_TRUEAUDIO;
|
||||
|
||||
return pb::tagreader::SongMetadata_Type_UNKNOWN;
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetadata &song) const {
|
||||
@@ -508,8 +535,7 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
|
||||
}
|
||||
|
||||
// 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
|
||||
// "else if" block above.
|
||||
// apart, so we keep specific behavior for some formats by adding another "else if" block above.
|
||||
if (TagLib::Ogg::XiphComment *tag = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
|
||||
SetVorbisComments(tag, song);
|
||||
}
|
||||
@@ -625,9 +651,7 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
||||
TagLib::Ogg::FieldListMap map = xiph_comment->fieldListMap();
|
||||
|
||||
#if TAGLIB_MAJOR_VERSION <= 1 && TAGLIB_MINOR_VERSION < 11
|
||||
// Other than the below mentioned non-standard COVERART,
|
||||
// METADATA_BLOCK_PICTURE
|
||||
// is the proposed tag for cover pictures.
|
||||
// Other than the below mentioned non-standard COVERART, METADATA_BLOCK_PICTURE is the proposed tag for cover pictures.
|
||||
// (see http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE)
|
||||
if (map.contains("METADATA_BLOCK_PICTURE")) {
|
||||
TagLib::StringList pict_list = map["METADATA_BLOCK_PICTURE"];
|
||||
@@ -687,10 +711,10 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
||||
TagLib::MP4::File *aac_file = dynamic_cast<TagLib::MP4::File*>(ref.file());
|
||||
if (aac_file) {
|
||||
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");
|
||||
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()) {
|
||||
// Just take the first one for now
|
||||
|
||||
@@ -18,18 +18,19 @@
|
||||
#ifndef TAGREADER_H
|
||||
#define TAGREADER_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QTextCodec>
|
||||
|
||||
#include <taglib/xiphcomment.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "tagreadermessages.pb.h"
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QString;
|
||||
class QTextCodec;
|
||||
class QUrl;
|
||||
|
||||
namespace TagLib {
|
||||
class FileRef;
|
||||
class String;
|
||||
@@ -44,15 +45,14 @@ class FileRefFactory;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* TagReaderWorker process (using TagReaderClient)
|
||||
* You should not use it directly in the main process but rather use a TagReaderWorker process (using TagReaderClient)
|
||||
*/
|
||||
class TagReader {
|
||||
public:
|
||||
TagReader();
|
||||
|
||||
void ReadFile(const QString& filename, pb::tagreader::SongMetadata *song) const;
|
||||
bool SaveFile(const QString& filename, const 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 IsMediaFile(const QString &filename) const;
|
||||
QByteArray LoadEmbeddedArt(const QString &filename) const;
|
||||
|
||||
@@ -6,20 +6,21 @@ message SongMetadata {
|
||||
|
||||
enum Type {
|
||||
UNKNOWN = 0;
|
||||
ASF = 1;
|
||||
WAV = 1;
|
||||
FLAC = 2;
|
||||
MP4 = 3;
|
||||
MPC = 4;
|
||||
MPEG = 5;
|
||||
OGGFLAC = 6;
|
||||
WAVPACK = 3;
|
||||
OGGFLAC = 4;
|
||||
OGGVORBIS = 5;
|
||||
OGGOPUS = 6;
|
||||
OGGSPEEX = 7;
|
||||
OGGVORBIS = 8;
|
||||
AIFF = 9;
|
||||
WAV = 10;
|
||||
TRUEAUDIO = 11;
|
||||
CDDA = 12;
|
||||
OGGOPUS = 13;
|
||||
//STREAM = 99;
|
||||
MPEG = 8;
|
||||
MP4 = 9;
|
||||
ASF = 10;
|
||||
AIFF = 11;
|
||||
MPC = 12;
|
||||
TRUEAUDIO = 13;
|
||||
CDDA = 90;
|
||||
STREAM = 91;
|
||||
}
|
||||
|
||||
optional bool valid = 1;
|
||||
|
||||
@@ -15,16 +15,23 @@
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "tagreaderworker.h"
|
||||
#include "core/logging.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QCoreApplication>
|
||||
#include <QList>
|
||||
#include <QLocalSocket>
|
||||
#include <QSsl>
|
||||
#include <QSslCertificate>
|
||||
#include <QSslSocket>
|
||||
#include <QStringList>
|
||||
#include <QtDebug>
|
||||
|
||||
#include <iostream>
|
||||
#include <sys/time.h>
|
||||
#include "core/logging.h"
|
||||
#include "tagreaderworker.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
|
||||
@@ -15,15 +15,16 @@
|
||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "tagreaderworker.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
#include <QFileInfo>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QTextCodec>
|
||||
#include <QUrl>
|
||||
#include <QObject>
|
||||
#include <QIODevice>
|
||||
#include <QByteArray>
|
||||
|
||||
#include "tagreaderworker.h"
|
||||
|
||||
TagReaderWorker::TagReaderWorker(QIODevice *socket, QObject *parent)
|
||||
: AbstractMessageHandler<pb::tagreader::Message>(socket, parent)
|
||||
|
||||
@@ -19,9 +19,15 @@
|
||||
#define TAGREADERWORKER_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QIODevice>
|
||||
|
||||
#include "core/messagehandler.h"
|
||||
#include "tagreader.h"
|
||||
#include "tagreadermessages.pb.h"
|
||||
#include "core/messagehandler.h"
|
||||
|
||||
class TagReaderWorker : public AbstractMessageHandler<pb::tagreader::Message> {
|
||||
public:
|
||||
|
||||
@@ -31,7 +31,6 @@ include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURR
|
||||
|
||||
if(WIN32)
|
||||
include_directories(../3rdparty/qtwin)
|
||||
#include_directories(../3rdparty/qtsparkle)
|
||||
endif(WIN32)
|
||||
|
||||
# Activate fast QString concatenation
|
||||
@@ -475,11 +474,7 @@ set(UI
|
||||
|
||||
)
|
||||
|
||||
set(RESOURCES
|
||||
../data/data.qrc
|
||||
#../data/icons.qrc
|
||||
)
|
||||
|
||||
set(RESOURCES ../data/data.qrc)
|
||||
set(OTHER_SOURCES)
|
||||
|
||||
option(USE_INSTALL_PREFIX "Look for data in CMAKE_INSTALL_PREFIX" ON)
|
||||
@@ -488,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_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-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})
|
||||
@@ -847,6 +841,7 @@ add_library(strawberry_lib STATIC
|
||||
target_link_libraries(strawberry_lib
|
||||
libstrawberry-common
|
||||
libstrawberry-tagreader
|
||||
#gstafc
|
||||
${GLIB_LIBRARIES}
|
||||
${GIO_LIBRARIES}
|
||||
${SHA2_LIBRARIES}
|
||||
@@ -887,7 +882,6 @@ if(HAVE_IMOBILEDEVICE)
|
||||
${IMOBILEDEVICE_LIBRARIES}
|
||||
${PLIST_LIBRARIES}
|
||||
${USBMUXD_LIBRARIES}
|
||||
gstafcsrc
|
||||
)
|
||||
link_directories(${IMOBILEDEVICE_LIBRARY_DIRS})
|
||||
link_directories(${USBMUXD_LIBRARY_DIRS})
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QGLWidget>
|
||||
#include <QVector>
|
||||
|
||||
#include "analyzer.h"
|
||||
|
||||
#include "engines/enginebase.h"
|
||||
#include "engine/enginebase.h"
|
||||
|
||||
AnalyzerBase::AnalyzerBase(QWidget* parent)
|
||||
AnalyzerBase::AnalyzerBase(QWidget *parent)
|
||||
: QGLWidget(parent), engine_(nullptr) {}
|
||||
|
||||
void AnalyzerBase::set_engine(Engine::Base* engine) {
|
||||
void AnalyzerBase::set_engine(Engine::Base *engine) {
|
||||
disconnect(engine_);
|
||||
engine_ = engine;
|
||||
if (engine_) {
|
||||
|
||||
@@ -15,14 +15,17 @@
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "analyzerbase.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <cmath> //interpolate()
|
||||
#include <cmath>
|
||||
|
||||
#include <QEvent> //event()
|
||||
#include <QWidget>
|
||||
#include <QPainter>
|
||||
#include <QPaintEvent>
|
||||
#include <QtDebug>
|
||||
#include <QPalette>
|
||||
#include <QTimerEvent>
|
||||
#include <QtEvents>
|
||||
|
||||
#include "analyzerbase.h"
|
||||
|
||||
#include "engine/enginebase.h"
|
||||
|
||||
@@ -45,7 +48,7 @@
|
||||
template class Analyzer::Base<QWidget>;
|
||||
#endif
|
||||
|
||||
Analyzer::Base::Base(QWidget* parent, uint scopeSize)
|
||||
Analyzer::Base::Base(QWidget *parent, uint scopeSize)
|
||||
: QWidget(parent),
|
||||
m_timeout(40) // msec
|
||||
,
|
||||
@@ -70,9 +73,9 @@ void Analyzer::Base::transform(Scope& scope) // virtual
|
||||
// values
|
||||
// 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->logSpectrum(front, &f[0]);
|
||||
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);
|
||||
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);
|
||||
int i = 0;
|
||||
|
||||
// convert to mono here - our built in analyzers need mono, but the
|
||||
// engines provide interleaved pcm
|
||||
// convert to mono here - our built in analyzers need mono, but the engines provide interleaved pcm
|
||||
for (uint x = 0; (int)x < m_fht->size(); ++x) {
|
||||
m_lastScope[x] = double(thescope[i] + thescope[i + 1]) / (2 * (1 << 15));
|
||||
i += 2;
|
||||
@@ -180,8 +182,7 @@ void Analyzer::Base::polishEvent() {
|
||||
init(); // virtual
|
||||
}
|
||||
|
||||
void Analyzer::interpolate(const Scope& inVec, Scope& outVec) // static
|
||||
{
|
||||
void Analyzer::interpolate(const Scope& inVec, Scope& outVec) {
|
||||
|
||||
double pos = 0.0;
|
||||
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 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);
|
||||
if (e->timerId() != m_timer.timerId()) return;
|
||||
|
||||
|
||||
@@ -4,29 +4,38 @@
|
||||
#ifndef ANALYZERBASE_H
|
||||
#define ANALYZERBASE_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#include "analyzer/fht.h" //stack allocated and convenience
|
||||
#include "engine/engine_fwd.h"
|
||||
#include <QPixmap> //stack allocated and convenience
|
||||
#include <QBasicTimer> //stack allocated
|
||||
#include <QWidget> //baseclass
|
||||
#include <vector> //included for convenience
|
||||
#include <stdbool.h>
|
||||
#include <vector>
|
||||
|
||||
#include <QGLWidget> //baseclass
|
||||
#ifdef Q_WS_MACX
|
||||
#include <OpenGL/gl.h> //included for convenience
|
||||
#include <OpenGL/glu.h> //included for convenience
|
||||
#include <OpenGL/gl.h> //included for convenience
|
||||
#include <OpenGL/glu.h> //included for convenience
|
||||
#else
|
||||
#include <GL/gl.h> //included for convenience
|
||||
#include <GL/glu.h> //included for convenience
|
||||
#include <GL/gl.h> //included for convenience
|
||||
#include <GL/glu.h> //included for convenience
|
||||
#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 QResizeEvent;
|
||||
|
||||
namespace Analyzer {
|
||||
|
||||
|
||||
@@ -17,19 +17,32 @@
|
||||
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 "analyzerbase.h"
|
||||
#include "blockanalyzer.h"
|
||||
|
||||
#include "core/logging.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QHBoxLayout>
|
||||
#include <QSettings>
|
||||
#include <QTimer>
|
||||
#include <QtDebug>
|
||||
|
||||
const char* AnalyzerContainer::kSettingsGroup = "Analyzer";
|
||||
const char* AnalyzerContainer::kSettingsFramerate = "framerate";
|
||||
const char *AnalyzerContainer::kSettingsGroup = "Analyzer";
|
||||
const char *AnalyzerContainer::kSettingsFramerate = "framerate";
|
||||
|
||||
// Framerates
|
||||
const int AnalyzerContainer::kLowFramerate = 20;
|
||||
@@ -37,7 +50,7 @@ const int AnalyzerContainer::kMediumFramerate = 25;
|
||||
const int AnalyzerContainer::kHighFramerate = 30;
|
||||
const int AnalyzerContainer::kSuperHighFramerate = 60;
|
||||
|
||||
AnalyzerContainer::AnalyzerContainer(QWidget* parent)
|
||||
AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
current_framerate_(kMediumFramerate),
|
||||
context_menu_(new QMenu(this)),
|
||||
@@ -51,7 +64,7 @@ AnalyzerContainer::AnalyzerContainer(QWidget* parent)
|
||||
ignore_next_click_(false),
|
||||
current_analyzer_(nullptr),
|
||||
engine_(nullptr) {
|
||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||
setLayout(layout);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
@@ -82,19 +95,18 @@ AnalyzerContainer::AnalyzerContainer(QWidget* parent)
|
||||
Load();
|
||||
}
|
||||
|
||||
void AnalyzerContainer::SetActions(QAction* visualisation) {
|
||||
void AnalyzerContainer::SetActions(QAction *visualisation) {
|
||||
visualisation_action_ = visualisation;
|
||||
context_menu_->addAction(visualisation_action_);
|
||||
}
|
||||
|
||||
void AnalyzerContainer::mouseReleaseEvent(QMouseEvent* e) {
|
||||
void AnalyzerContainer::mouseReleaseEvent(QMouseEvent *e) {
|
||||
if (e->button() == Qt::LeftButton) {
|
||||
if (ignore_next_click_) {
|
||||
ignore_next_click_ = false;
|
||||
}
|
||||
else {
|
||||
// Might be the first click in a double click, so wait a while before
|
||||
// actually doing anything
|
||||
// Might be the first click in a double click, so wait a while before actually doing anything
|
||||
double_click_timer_->start();
|
||||
last_click_pos_ = e->globalPos();
|
||||
}
|
||||
@@ -115,11 +127,11 @@ void AnalyzerContainer::mouseDoubleClickEvent(QMouseEvent*) {
|
||||
if (visualisation_action_) visualisation_action_->trigger();
|
||||
}
|
||||
|
||||
void AnalyzerContainer::wheelEvent(QWheelEvent* e) {
|
||||
void AnalyzerContainer::wheelEvent(QWheelEvent *e) {
|
||||
emit WheelEvent(e->delta());
|
||||
}
|
||||
|
||||
void AnalyzerContainer::SetEngine(EngineBase* engine) {
|
||||
void AnalyzerContainer::SetEngine(EngineBase *engine) {
|
||||
if (current_analyzer_) current_analyzer_->set_engine(engine);
|
||||
engine_ = engine;
|
||||
}
|
||||
@@ -132,11 +144,10 @@ void AnalyzerContainer::DisableAnalyzer() {
|
||||
}
|
||||
|
||||
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) {
|
||||
qLog(Warning) << "Couldn't intialise a new"
|
||||
<< analyzer_types_[id]->className();
|
||||
qLog(Warning) << "Couldn't intialise a new" << analyzer_types_[id]->className();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -196,8 +207,7 @@ void AnalyzerContainer::Load() {
|
||||
}
|
||||
|
||||
void AnalyzerContainer::SaveFramerate(int framerate) {
|
||||
// For now, framerate is common for all analyzers. Maybe each analyzer should
|
||||
// have its own framerate?
|
||||
// For now, framerate is common for all analyzers. Maybe each analyzer should have its own framerate?
|
||||
current_framerate_ = framerate;
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
@@ -212,7 +222,7 @@ void AnalyzerContainer::Save() {
|
||||
}
|
||||
|
||||
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);
|
||||
group_framerate_->addAction(action);
|
||||
framerate_list_ << framerate;
|
||||
|
||||
@@ -20,13 +20,31 @@
|
||||
#ifndef ANALYZERCONTAINER_H
|
||||
#define ANALYZERCONTAINER_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QMenu>
|
||||
#include <QSignalMapper>
|
||||
#include "config.h"
|
||||
|
||||
#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"
|
||||
|
||||
class QMouseEvent;
|
||||
class QWheelEvent;
|
||||
|
||||
namespace Analyzer {
|
||||
class Base;
|
||||
} // namespace Analyzer
|
||||
|
||||
class AnalyzerContainer : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
@@ -5,12 +5,19 @@
|
||||
|
||||
#include "blockanalyzer.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QResizeEvent>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <scoped_allocator>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPixmap>
|
||||
#include <QPainter>
|
||||
#include <QPalette>
|
||||
#include <QColor>
|
||||
#include <QtEvents>
|
||||
|
||||
#include "analyzerbase.h"
|
||||
#include "fht.h"
|
||||
|
||||
const uint BlockAnalyzer::HEIGHT = 2;
|
||||
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::FADE_SIZE = 90;
|
||||
|
||||
const char* BlockAnalyzer::kName =
|
||||
const char *BlockAnalyzer::kName =
|
||||
QT_TRANSLATE_NOOP("AnalyzerContainer", "Block analyzer");
|
||||
|
||||
BlockAnalyzer::BlockAnalyzer(QWidget* parent)
|
||||
BlockAnalyzer::BlockAnalyzer(QWidget *parent)
|
||||
: Analyzer::Base(parent, 9),
|
||||
m_columns(0) // uint
|
||||
,
|
||||
@@ -43,9 +50,7 @@ BlockAnalyzer::BlockAnalyzer(QWidget* parent)
|
||||
,
|
||||
m_fade_intensity(1 << 8, 32) // vector<uint>
|
||||
{
|
||||
setMinimumSize(MIN_COLUMNS * (WIDTH + 1) - 1,
|
||||
MIN_ROWS * (HEIGHT + 1) -
|
||||
1); //-1 is padding, no drawing takes place there
|
||||
setMinimumSize(MIN_COLUMNS * (WIDTH + 1) - 1, MIN_ROWS * (HEIGHT + 1) - 1); //-1 is padding, no drawing takes place there
|
||||
setMaximumWidth(MAX_COLUMNS * (WIDTH + 1) - 1);
|
||||
|
||||
// mxcl says null pixmaps cause crashes, so let's play it safe
|
||||
@@ -54,7 +59,7 @@ BlockAnalyzer::BlockAnalyzer(QWidget* parent)
|
||||
|
||||
BlockAnalyzer::~BlockAnalyzer() {}
|
||||
|
||||
void BlockAnalyzer::resizeEvent(QResizeEvent* e) {
|
||||
void BlockAnalyzer::resizeEvent(QResizeEvent *e) {
|
||||
QWidget::resizeEvent(e);
|
||||
|
||||
m_background = QPixmap(size());
|
||||
@@ -96,8 +101,7 @@ void BlockAnalyzer::resizeEvent(QResizeEvent* e) {
|
||||
}
|
||||
|
||||
void BlockAnalyzer::determineStep() {
|
||||
// falltime is dependent on rowcount due to our digital resolution (ie we have
|
||||
// boxes/blocks of pixels)
|
||||
// falltime is dependent on rowcount due to our digital resolution (ie we have boxes/blocks of pixels)
|
||||
// I calculated the value 30 based on some trial and error
|
||||
|
||||
// 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->scale(front, 1.0 / 20);
|
||||
|
||||
// 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!
|
||||
// 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!
|
||||
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)
|
||||
;
|
||||
|
||||
// this is opposite to what you'd think, higher than y
|
||||
// means the bar is lower than y (physically)
|
||||
// this is opposite to what you'd think, higher than y means the bar is lower than y (physically)
|
||||
if ((float)y > m_store[x])
|
||||
y = int(m_store[x] += m_step);
|
||||
else
|
||||
m_store[x] = y;
|
||||
|
||||
// if y is lower than m_fade_pos, then the bar has exceeded the height of
|
||||
// the fadeout
|
||||
// if y is lower than m_fade_pos, then the bar has exceeded the height of the fadeout
|
||||
// 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*/) {
|
||||
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;
|
||||
|
||||
// REMEMBER: y is a number from 0 to m_rows, 0 means all blocks are glowing,
|
||||
// m_rows means none are
|
||||
// REMEMBER: y is a number from 0 to m_rows, 0 means all blocks are glowing, m_rows means none are
|
||||
canvas_painter.drawPixmap(x * (WIDTH + 1), y * (HEIGHT + 1) + m_y, *bar(),
|
||||
0, y * (HEIGHT + 1), bar()->width(),
|
||||
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) {
|
||||
// with a range of 0-255 and maximum adjustment of amount,
|
||||
// maximise the difference between f and b
|
||||
// with a range of 0-255 and maximum adjustment of amount, maximise the difference between f and b
|
||||
|
||||
if (b < f) {
|
||||
if (b > 255 - f) {
|
||||
|
||||
@@ -5,12 +5,21 @@
|
||||
#ifndef 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 <qcolor.h>
|
||||
|
||||
class QResizeEvent;
|
||||
class QMouseEvent;
|
||||
class QPalette;
|
||||
|
||||
/**
|
||||
* @author Max Howell
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fht.h"
|
||||
|
||||
FHT::FHT(int n) : m_buf(0), m_tab(0), m_log(0) {
|
||||
|
||||
@@ -20,19 +20,23 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QThread>
|
||||
#include <QList>
|
||||
|
||||
#include "collection.h"
|
||||
|
||||
#include "collectionmodel.h"
|
||||
#include "collectionbackend.h"
|
||||
#include "core/application.h"
|
||||
#include "core/database.h"
|
||||
#include "core/player.h"
|
||||
#include "core/tagreaderclient.h"
|
||||
#include "core/taskmanager.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::kDirsTable = "directories";
|
||||
@@ -47,8 +51,6 @@ Collection::Collection(Application *app, QObject *parent)
|
||||
watcher_(nullptr),
|
||||
watcher_thread_(nullptr) {
|
||||
|
||||
//qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
backend_ = new CollectionBackend;
|
||||
backend()->moveToThread(app->database()->thread());
|
||||
|
||||
@@ -62,8 +64,6 @@ Collection::Collection(Application *app, QObject *parent)
|
||||
}
|
||||
|
||||
Collection::~Collection() {
|
||||
|
||||
//qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
watcher_->deleteLater();
|
||||
watcher_thread_->exit();
|
||||
@@ -71,8 +71,6 @@ Collection::~Collection() {
|
||||
}
|
||||
|
||||
void Collection::Init() {
|
||||
|
||||
//qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
watcher_ = new CollectionWatcher;
|
||||
watcher_thread_ = new Thread(this);
|
||||
@@ -109,8 +107,6 @@ void Collection::PauseWatcher() { watcher_->SetRescanPausedAsync(true); }
|
||||
void Collection::ResumeWatcher() { watcher_->SetRescanPausedAsync(false); }
|
||||
|
||||
void Collection::ReloadSettings() {
|
||||
|
||||
//qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
watcher_->ReloadSettingsAsync();
|
||||
|
||||
@@ -118,14 +114,10 @@ void Collection::ReloadSettings() {
|
||||
|
||||
void Collection::Stopped() {
|
||||
|
||||
//qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
CurrentSongChanged(Song());
|
||||
}
|
||||
|
||||
void Collection::CurrentSongChanged(const Song &song) {
|
||||
|
||||
//qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
TagReaderReply *reply = nullptr;
|
||||
|
||||
@@ -140,8 +132,6 @@ void Collection::CurrentSongChanged(const Song &song) {
|
||||
|
||||
SongList Collection::FilterCurrentWMASong(SongList songs, Song* queued) {
|
||||
|
||||
//qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
for (SongList::iterator it = songs.begin(); it != songs.end(); ) {
|
||||
if (it->url() == current_wma_song_url_) {
|
||||
*queued = *it;
|
||||
|
||||
@@ -23,19 +23,18 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
#include "core/song.h"
|
||||
|
||||
class Application;
|
||||
class Database;
|
||||
class Thread;
|
||||
class CollectionBackend;
|
||||
class CollectionModel;
|
||||
class CollectionWatcher;
|
||||
class TaskManager;
|
||||
class Thread;
|
||||
|
||||
class Collection : public QObject {
|
||||
Q_OBJECT
|
||||
@@ -85,13 +84,11 @@ class Collection : public QObject {
|
||||
CollectionWatcher *watcher_;
|
||||
Thread *watcher_thread_;
|
||||
|
||||
// Hack: Gstreamer doesn't cope well with WMA files being rewritten while
|
||||
// being played, so we delay statistics and rating changes until the current
|
||||
// song has finished playing.
|
||||
// Hack: Gstreamer doesn't cope well with WMA files being rewritten while being played,
|
||||
// so we delay statistics and rating changes until the current song has finished playing.
|
||||
QUrl current_wma_song_url_;
|
||||
|
||||
// DB schema versions which should trigger a full collection rescan (each of
|
||||
// those with a short reason why).
|
||||
// DB schema versions which should trigger a full collection rescan (each of those with a short reason why).
|
||||
QHash<int, QString> full_rescan_revisions_;
|
||||
};
|
||||
|
||||
|
||||
@@ -20,22 +20,32 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
#include <QSet>
|
||||
#include <QMap>
|
||||
#include <QByteArray>
|
||||
#include <QFileInfo>
|
||||
#include <QSettings>
|
||||
#include <QDateTime>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlQuery>
|
||||
#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 "collectionquery.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";
|
||||
|
||||
@@ -258,6 +268,7 @@ void CollectionBackend::AddDirectory(const QString &path) {
|
||||
dir.id = q.lastInsertId().toInt();
|
||||
|
||||
emit DirectoryDiscovered(dir, SubdirectoryList());
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::RemoveDirectory(const Directory &dir) {
|
||||
@@ -287,6 +298,7 @@ void CollectionBackend::RemoveDirectory(const Directory &dir) {
|
||||
emit DirectoryDeleted(dir);
|
||||
|
||||
transaction.Commit();
|
||||
|
||||
}
|
||||
|
||||
SongList CollectionBackend::FindSongsInDirectory(int id) {
|
||||
@@ -307,6 +319,7 @@ SongList CollectionBackend::FindSongsInDirectory(int id) {
|
||||
ret << song;
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::AddOrUpdateSubdirs(const SubdirectoryList &subdirs) {
|
||||
@@ -381,8 +394,7 @@ void CollectionBackend::AddOrUpdateSongs(const SongList &songs) {
|
||||
|
||||
for (const Song &song : songs) {
|
||||
// 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
|
||||
// while CollectionWatcher is scanning it.
|
||||
// This is to fix a possible race condition when a directory is removed while CollectionWatcher is scanning it.
|
||||
if (!dirs_table_.isEmpty()) {
|
||||
check_dir.bindValue(":id", song.directory_id());
|
||||
check_dir.exec();
|
||||
@@ -752,8 +764,7 @@ void CollectionBackend::UpdateCompilations() {
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db(db_->Connect());
|
||||
|
||||
// Look for albums that have songs by more than one 'effective album artist' in the same
|
||||
// directory
|
||||
// Look for albums that have songs by more than one 'effective album artist' in the same directory
|
||||
|
||||
QSqlQuery q(db);
|
||||
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) {
|
||||
|
||||
// Get songs that were already in that album, so we can tell the model
|
||||
// they've been updated
|
||||
// Get songs that were already in that album, so we can tell the model they've been updated
|
||||
find_songs.bindValue(":album", album);
|
||||
find_songs.bindValue(":compilation_detected", int(!compilation_detected));
|
||||
find_songs.exec();
|
||||
@@ -1104,6 +1114,7 @@ void CollectionBackend::ResetStatistics(int id) {
|
||||
}
|
||||
|
||||
void CollectionBackend::DeleteAll() {
|
||||
|
||||
{
|
||||
QMutexLocker l(db_->Mutex());
|
||||
QSqlDatabase db(db_->Connect());
|
||||
|
||||
@@ -23,13 +23,22 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
#include <QUrl>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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 "collectionquery.h"
|
||||
#include "directory.h"
|
||||
|
||||
class Database;
|
||||
|
||||
@@ -94,11 +103,9 @@ class CollectionBackendInterface : public QObject {
|
||||
|
||||
virtual Song GetSongById(int id) = 0;
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
virtual SongList GetSongsByUrl(const QUrl &url) = 0;
|
||||
// Returns a section of a song with the given filename and beginning. If the section
|
||||
// is not present in collection, returns invalid song.
|
||||
// Returns a section of a song with the given filename and beginning. If the section is not present in collection, returns invalid song.
|
||||
// Using default beginning value is suitable when searching for single-section songs.
|
||||
virtual Song GetSongByUrl(const QUrl &url, qint64 beginning = 0) = 0;
|
||||
|
||||
|
||||
@@ -20,15 +20,23 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "collectiondirectorymodel.h"
|
||||
#include "collectionbackend.h"
|
||||
#include <QObject>
|
||||
#include <QStandardItemModel>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
#include "core/application.h"
|
||||
#include "core/filesystemmusicstorage.h"
|
||||
#include "core/iconloader.h"
|
||||
#include "core/musicstorage.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),
|
||||
dir_icon_(IconLoader::Load("document-open-folder")),
|
||||
backend_(backend)
|
||||
@@ -43,7 +51,7 @@ CollectionDirectoryModel::~CollectionDirectoryModel() {}
|
||||
|
||||
void CollectionDirectoryModel::DirectoryDiscovered(const Directory &dir) {
|
||||
|
||||
QStandardItem* item;
|
||||
QStandardItem *item;
|
||||
if (Application::kIsPortable && Utilities::UrlOnSameDriveAsStrawberry(QUrl::fromLocalFile(dir.path))) {
|
||||
item = new QStandardItem(Utilities::GetRelativePathToStrawberryBin(QUrl::fromLocalFile(dir.path)).toLocalFile());
|
||||
}
|
||||
|
||||
@@ -25,11 +25,16 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QIcon>
|
||||
#include <QObject>
|
||||
#include <QStandardItemModel>
|
||||
#include <QList>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QIcon>
|
||||
|
||||
#include "directory.h"
|
||||
class QModelIndex;
|
||||
|
||||
struct Directory;
|
||||
class CollectionBackend;
|
||||
class MusicStorage;
|
||||
|
||||
|
||||
@@ -20,23 +20,35 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QWidget>
|
||||
#include <QObject>
|
||||
#include <QDataStream>
|
||||
#include <QIODevice>
|
||||
#include <QAction>
|
||||
#include <QActionGroup>
|
||||
#include <QInputDialog>
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
#include <QByteArray>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QRegExp>
|
||||
#include <QInputDialog>
|
||||
#include <QList>
|
||||
#include <QTimer>
|
||||
#include <QMenu>
|
||||
#include <QSettings>
|
||||
#include <QSignalMapper>
|
||||
#include <QTimer>
|
||||
#include <QToolButton>
|
||||
#include <QtEvents>
|
||||
|
||||
#include "collectionfilterwidget.h"
|
||||
#include "core/iconloader.h"
|
||||
#include "core/song.h"
|
||||
#include "collectionmodel.h"
|
||||
#include "collectionquery.h"
|
||||
#include "savedgroupingmanager.h"
|
||||
#include "collectionfilterwidget.h"
|
||||
#include "groupbydialog.h"
|
||||
#include "ui_collectionfilterwidget.h"
|
||||
#include "core/song.h"
|
||||
#include "core/iconloader.h"
|
||||
#include "settings/settingsdialog.h"
|
||||
|
||||
CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
@@ -48,8 +60,7 @@ CollectionFilterWidget::CollectionFilterWidget(QWidget *parent)
|
||||
delay_behaviour_(DelayedOnLargeLibraries) {
|
||||
ui_->setupUi(this);
|
||||
|
||||
// Add the available fields to the tooltip here instead of the ui
|
||||
// file to prevent that they get translated by mistake.
|
||||
// Add the available fields to the tooltip here instead of the ui file to prevent that they get translated by mistake.
|
||||
QString available_fields = Song::kFtsColumns.join(", ").replace(QRegExp("\\bfts"), "");
|
||||
ui_->filter->setToolTip(ui_->filter->toolTip().arg(available_fields));
|
||||
|
||||
@@ -125,8 +136,7 @@ void CollectionFilterWidget::UpdateGroupByActions() {
|
||||
group_by_group_ = CreateGroupByActions(this);
|
||||
group_by_menu_->clear();
|
||||
group_by_menu_->addActions(group_by_group_->actions());
|
||||
connect(group_by_group_, SIGNAL(triggered(QAction*)),
|
||||
SLOT(GroupByClicked(QAction*)));
|
||||
connect(group_by_group_, SIGNAL(triggered(QAction*)), SLOT(GroupByClicked(QAction*)));
|
||||
if (model_) {
|
||||
CheckCurrentGrouping(model_->GetGroupBy());
|
||||
}
|
||||
@@ -338,10 +348,9 @@ void CollectionFilterWidget::keyReleaseEvent(QKeyEvent *e) {
|
||||
|
||||
void CollectionFilterWidget::FilterTextChanged(const QString &text) {
|
||||
|
||||
// Searching with one or two characters can be very expensive on the database
|
||||
// even with FTS, so if there are a large number of songs in the database
|
||||
// introduce a small delay before actually filtering the model, so if the
|
||||
// user is typing the first few characters of something it will be quicker.
|
||||
// Searching with one or two characters can be very expensive on the database even with FTS,
|
||||
// so if there are a large number of songs in the database introduce a small delay before actually filtering the model,
|
||||
// so if the 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);
|
||||
|
||||
if (delay) {
|
||||
|
||||
@@ -24,22 +24,29 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <memory>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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 "savedgroupingmanager.h"
|
||||
|
||||
class QKeyEvent;
|
||||
|
||||
class GroupByDialog;
|
||||
class SavedGroupingManager;
|
||||
class SettingsDialog;
|
||||
class Ui_CollectionFilterWidget;
|
||||
|
||||
struct QueryOptions;
|
||||
|
||||
class QMenu;
|
||||
class QActionGroup;
|
||||
class QSignalMapper;
|
||||
|
||||
class CollectionFilterWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
@@ -23,9 +23,6 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
|
||||
#include "core/simpletreeitem.h"
|
||||
#include "core/song.h"
|
||||
|
||||
|
||||
@@ -22,32 +22,44 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <QObject>
|
||||
#include <QtGlobal>
|
||||
#include <QtConcurrentRun>
|
||||
#include <QtAlgorithms>
|
||||
#include <QMutex>
|
||||
#include <QFuture>
|
||||
#include <QDataStream>
|
||||
#include <QMimeData>
|
||||
#include <QIODevice>
|
||||
#include <QMetaEnum>
|
||||
#include <QNetworkCacheMetaData>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QPixmapCache>
|
||||
#include <QSettings>
|
||||
#include <QByteArray>
|
||||
#include <QVariant>
|
||||
#include <QList>
|
||||
#include <QSet>
|
||||
#include <QChar>
|
||||
#include <QRegExp>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#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/closure.h"
|
||||
#include "core/database.h"
|
||||
#include "core/iconloader.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/taskmanager.h"
|
||||
#include "core/utilities.h"
|
||||
#include "core/iconloader.h"
|
||||
#include "covermanager/albumcoverloader.h"
|
||||
#include "collectionquery.h"
|
||||
#include "collectionbackend.h"
|
||||
#include "collectiondirectorymodel.h"
|
||||
#include "collectionitem.h"
|
||||
#include "collectionmodel.h"
|
||||
#include "sqlrow.h"
|
||||
#include "playlist/playlistmanager.h"
|
||||
#include "playlist/songmimedata.h"
|
||||
#include "covermanager/albumcoverloader.h"
|
||||
|
||||
using std::placeholders::_1;
|
||||
using std::placeholders::_2;
|
||||
@@ -178,11 +190,9 @@ void CollectionModel::SongsDiscovered(const SongList &songs) {
|
||||
// Hey, we've already got that one!
|
||||
if (song_nodes_.contains(song.id())) continue;
|
||||
|
||||
// Before we can add each song we need to make sure the required container
|
||||
// items already exist in the tree. These depend on which "group by"
|
||||
// settings the user has on the collection. 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.
|
||||
// Before we can add each song we need to make sure the required container items already exist in the tree.
|
||||
// These depend on which "group by" settings the user has on the collection.
|
||||
// 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.
|
||||
|
||||
// Find parent containers in the tree
|
||||
CollectionItem *container = root_;
|
||||
@@ -190,8 +200,7 @@ void CollectionModel::SongsDiscovered(const SongList &songs) {
|
||||
GroupBy type = group_by_[i];
|
||||
if (type == GroupBy_None) break;
|
||||
|
||||
// Special case: if the song is a compilation and the current GroupBy
|
||||
// level is Artists, then we want the Various Artists node :(
|
||||
// Special case: if the song is a compilation and the current GroupBy level is Artists, then we want the Various Artists node :(
|
||||
if (IsArtistGroupBy(type) && song.is_compilation()) {
|
||||
if (container->compilation_artist_node_ == nullptr)
|
||||
CreateCompilationArtistNode(true, container);
|
||||
@@ -240,15 +249,13 @@ void CollectionModel::SongsDiscovered(const SongList &songs) {
|
||||
container = container_nodes_[i][key];
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
if (!container->lazy_loaded) break;
|
||||
}
|
||||
|
||||
if (!container->lazy_loaded) continue;
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
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) {
|
||||
|
||||
// This is called if there was a minor change to the songs that will not
|
||||
// normally require the collection to be restructured. We can just update our
|
||||
// internal cache of Song objects without worrying about resetting the model.
|
||||
// This is called if there was a minor change to the songs that will not normally require the collection to be restructured.
|
||||
// We can just update our internal cache of Song objects without worrying about resetting the model.
|
||||
for (const Song &song : songs) {
|
||||
if (song_nodes_.contains(song.id())) {
|
||||
song_nodes_[song.id()]->metadata = song;
|
||||
@@ -285,8 +291,7 @@ CollectionItem *CollectionModel::CreateCompilationArtistNode(bool signal, Collec
|
||||
|
||||
QString CollectionModel::DividerKey(GroupBy type, CollectionItem *item) const {
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
|
||||
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) {
|
||||
|
||||
// Delete the actual song nodes first, keeping track of each parent so we
|
||||
// might check to see if they're empty later.
|
||||
// Delete the actual song nodes first, keeping track of each parent so we might check to see if they're empty later.
|
||||
QSet<CollectionItem*> parents;
|
||||
for (const Song &song : songs) {
|
||||
if (song_nodes_.contains(song.id())) {
|
||||
@@ -386,11 +390,9 @@ void CollectionModel::SongsDeleted(const SongList &songs) {
|
||||
endRemoveRows();
|
||||
}
|
||||
else {
|
||||
// If we get here it means some of the songs we want to delete haven't
|
||||
// been lazy-loaded yet. 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. This can take a very long time,
|
||||
// so better to just reset the model and be done with it.
|
||||
// If we get here it means some of the songs we want to delete haven't been lazy-loaded yet.
|
||||
// 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.
|
||||
// This can take a very long time, so better to just reset the model and be done with it.
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
@@ -399,9 +401,8 @@ void CollectionModel::SongsDeleted(const SongList &songs) {
|
||||
// Now delete empty parents
|
||||
QSet<QString> divider_keys;
|
||||
while (!parents.isEmpty()) {
|
||||
// Since we are going to remove elements from the container, we
|
||||
// need a copy to iterate over. If we iterate over the original,
|
||||
// the behavior will be undefined.
|
||||
// Since we are going to remove elements from the container, we need a copy to iterate over.
|
||||
// If we iterate over the original, the behavior will be undefined.
|
||||
QSet<CollectionItem*> parents_copy = parents;
|
||||
for (CollectionItem *node : parents_copy) {
|
||||
parents.remove(node);
|
||||
|
||||
@@ -23,26 +23,40 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QIcon>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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/song.h"
|
||||
#include "collectionquery.h"
|
||||
#include "collectionitem.h"
|
||||
#include "sqlrow.h"
|
||||
#include "covermanager/albumcoverloaderoptions.h"
|
||||
#include "engine/engine_fwd.h"
|
||||
#include "playlist/playlistmanager.h"
|
||||
|
||||
class Application;
|
||||
class AlbumCoverLoader;
|
||||
class CollectionDirectoryModel;
|
||||
class CollectionBackend;
|
||||
|
||||
class QSettings;
|
||||
class CollectionDirectoryModel;
|
||||
class CollectionItem;
|
||||
|
||||
class CollectionModel : public SimpleTreeModel<CollectionItem> {
|
||||
Q_OBJECT
|
||||
@@ -190,8 +204,7 @@ signals:
|
||||
|
||||
private:
|
||||
// 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
|
||||
// able to do it in a background thread.
|
||||
// This gets called a lot when filtering the playlist, so it's nice to be able to do it in a background thread.
|
||||
QueryResult RunQuery(CollectionItem *parent);
|
||||
void PostQuery(CollectionItem *parent, const QueryResult &result, bool signal);
|
||||
|
||||
@@ -200,25 +213,18 @@ signals:
|
||||
void BeginReset();
|
||||
|
||||
// Functions for working with queries and creating items.
|
||||
// When the model is reset or when a node is lazy-loaded the Collection
|
||||
// constructs a database query to populate the items. Filters are added
|
||||
// for each parent item, restricting the songs returned to a particular
|
||||
// album or artist for example.
|
||||
// When the model is reset or when a node is lazy-loaded the Collection constructs a database query to populate the items.
|
||||
// Filters are added for each parent item, restricting the songs returned to a particular album or artist for example.
|
||||
static void InitQuery(GroupBy type, 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
|
||||
// node, or by a spontaneous SongsDiscovered emission from the backend.
|
||||
// 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.
|
||||
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);
|
||||
|
||||
// The "Various Artists" node is an annoying special case.
|
||||
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
|
||||
CollectionItem *InitItem(GroupBy type, bool signal, CollectionItem *parent, int container_level);
|
||||
void FinishItem(GroupBy type, bool signal, bool create_divider, CollectionItem *parent, CollectionItem *item);
|
||||
@@ -256,8 +262,7 @@ signals:
|
||||
|
||||
QIcon artist_icon_;
|
||||
QIcon album_icon_;
|
||||
// used as a generic icon to show when no cover art is found,
|
||||
// fixed to the same size as the artwork (32x32)
|
||||
// Used as a generic icon to show when no cover art is found, fixed to the same size as the artwork (32x32)
|
||||
QPixmap no_cover_icon_;
|
||||
QIcon playlists_dir_icon_;
|
||||
QIcon playlist_icon_;
|
||||
|
||||
@@ -20,10 +20,14 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
#include "collectionplaylistitem.h"
|
||||
#include "core/tagreaderclient.h"
|
||||
|
||||
#include <QSettings>
|
||||
class SqlRow;
|
||||
|
||||
CollectionPlaylistItem::CollectionPlaylistItem(const QString &type)
|
||||
: PlaylistItem(type) {}
|
||||
|
||||
@@ -23,9 +23,17 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
#include "core/song.h"
|
||||
#include "playlist/playlistitem.h"
|
||||
|
||||
class SqlRow;
|
||||
|
||||
class CollectionPlaylistItem : public PlaylistItem {
|
||||
public:
|
||||
CollectionPlaylistItem(const QString &type);
|
||||
|
||||
@@ -20,21 +20,26 @@
|
||||
|
||||
#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 "core/song.h"
|
||||
|
||||
#include <QtDebug>
|
||||
#include <QDateTime>
|
||||
#include <QSqlError>
|
||||
|
||||
QueryOptions::QueryOptions() : max_age_(-1), query_mode_(QueryMode_All) {}
|
||||
|
||||
CollectionQuery::CollectionQuery(const QueryOptions& options)
|
||||
: include_unavailable_(false), join_with_fts_(false), limit_(-1) {
|
||||
|
||||
if (!options.filter().isEmpty()) {
|
||||
// We need to munge the filter text a little bit to get it to work as
|
||||
// expected with sqlite's FTS3:
|
||||
// We need to munge the filter text a little bit to get it to work as expected with sqlite's FTS3:
|
||||
// 1) Append * to all tokens.
|
||||
// 2) Prefix "fts" 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(':')) {
|
||||
// Only prefix fts if the token is a valid column name.
|
||||
if (Song::kFtsColumns.contains("fts" + token.section(':', 0, 0),
|
||||
Qt::CaseInsensitive)) {
|
||||
if (Song::kFtsColumns.contains("fts" + token.section(':', 0, 0), Qt::CaseInsensitive)) {
|
||||
// Account for multiple colons.
|
||||
QString columntoken = token.section(':', 0, 0, QString::SectionIncludeTrailingSep);
|
||||
QString subtoken = token.section(':', 1, -1);
|
||||
@@ -82,16 +86,12 @@ CollectionQuery::CollectionQuery(const QueryOptions& options)
|
||||
bound_values_ << cutoff;
|
||||
}
|
||||
|
||||
// TODO: currently you cannot use any QueryMode other than All and fts at the
|
||||
// same time.
|
||||
// joining songs, duplicated_songs and songs_fts all together takes a huge
|
||||
// amount of
|
||||
// time. the query takes about 20 seconds on my machine then. why?
|
||||
// untagged mode could work with additional filtering but I'm disabling it
|
||||
// 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.
|
||||
// TODO: Currently you cannot use any QueryMode other than All and fts at the same time.
|
||||
// Joining songs, duplicated_songs and songs_fts all together takes a huge amount of time.
|
||||
// The query takes about 20 seconds on my machine then. Why?
|
||||
// Untagged mode could work with additional filtering but I'm disabling it 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;
|
||||
|
||||
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
|
||||
if (!op.compare("IN", Qt::CaseInsensitive)) {
|
||||
QStringList final;
|
||||
for (const QString& single_value : value.toStringList()) {
|
||||
for (const QString &single_value : value.toStringList()) {
|
||||
final.append("?");
|
||||
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);
|
||||
}
|
||||
else {
|
||||
// Do integers inline - sqlite seems to get confused when you pass integers
|
||||
// to bound parameters
|
||||
// Do integers inline - sqlite seems to get confused when you pass integers to bound parameters
|
||||
if (value.type() == QVariant::Int) {
|
||||
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) {
|
||||
// The unary + is added to prevent sqlite from using the index
|
||||
// idx_comp_artist. 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
|
||||
// The unary + is added to prevent sqlite from using the index idx_comp_artist.
|
||||
// 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
|
||||
|
||||
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);
|
||||
|
||||
// Bind values
|
||||
for (const QVariant& value : bound_values_) {
|
||||
for (const QVariant &value : bound_values_) {
|
||||
query_.addBindValue(value);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,11 +23,14 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QString>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QVariant>
|
||||
#include <QSqlQuery>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QVariantList>
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlQuery>
|
||||
|
||||
class Song;
|
||||
class CollectionBackend;
|
||||
@@ -36,13 +39,9 @@ class CollectionBackend;
|
||||
struct QueryOptions {
|
||||
// Modes of CollectionQuery:
|
||||
// - use the all songs table
|
||||
// - 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
|
||||
// - 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.
|
||||
// - 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
|
||||
// - 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 {
|
||||
QueryMode_All,
|
||||
QueryMode_Duplicates,
|
||||
@@ -83,8 +82,7 @@ class CollectionQuery {
|
||||
// Sets an ORDER BY clause on the query.
|
||||
void SetOrderBy(const QString &order_by) { order_by_ = order_by; }
|
||||
|
||||
// Adds a fragment of WHERE clause. When executed, this Query will connect all
|
||||
// the fragments with AND operator.
|
||||
// Adds a fragment of WHERE clause. When executed, this Query will connect all the fragments with AND operator.
|
||||
// Please note that IN operator expects a QStringList as value.
|
||||
void AddWhere(const QString &column, const QVariant &value, const QString &op = "=");
|
||||
|
||||
|
||||
@@ -20,35 +20,59 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "collectionview.h"
|
||||
#include <qcoreevent.h>
|
||||
|
||||
#include <QPainter>
|
||||
#include <QContextMenuEvent>
|
||||
#include <QHelpEvent>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QSet>
|
||||
#include <QSettings>
|
||||
#include <QtGlobal>
|
||||
#include <QWidget>
|
||||
#include <QItemSelectionModel>
|
||||
#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 <QTreeView>
|
||||
#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 "collectionfilterwidget.h"
|
||||
#include "collectionmodel.h"
|
||||
#include "collectionitem.h"
|
||||
#include "collectionbackend.h"
|
||||
#include "core/application.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/mimedata.h"
|
||||
#include "core/musicstorage.h"
|
||||
#include "core/utilities.h"
|
||||
#include "core/iconloader.h"
|
||||
#include "collectionmodel.h"
|
||||
#include "collectionview.h"
|
||||
#include "device/devicemanager.h"
|
||||
#include "device/devicestatefiltermodel.h"
|
||||
#include "dialogs/edittagdialog.h"
|
||||
#ifdef HAVE_GSTREAMER
|
||||
#include "dialogs/organisedialog.h"
|
||||
#include "dialogs/organiseerrordialog.h"
|
||||
#endif
|
||||
#include "settings/collectionsettingspage.h"
|
||||
|
||||
@@ -293,8 +317,7 @@ bool CollectionView::RestoreLevelFocus(const QModelIndex &parent) {
|
||||
}
|
||||
else if (last_selected_path_.contains(text)) {
|
||||
emit expand(current);
|
||||
// If a selected container or song were not found, we've got into a wrong subtree
|
||||
// (happens with "unknown" all the time)
|
||||
// If a selected container or song were not found, we've got into a wrong subtree (happens with "unknown" all the time)
|
||||
if (!RestoreLevelFocus(current)) {
|
||||
emit collapse(current);
|
||||
}
|
||||
@@ -312,8 +335,6 @@ bool CollectionView::RestoreLevelFocus(const QModelIndex &parent) {
|
||||
|
||||
void CollectionView::ReloadSettings() {
|
||||
|
||||
//qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
QSettings settings;
|
||||
|
||||
settings.beginGroup(CollectionSettingsPage::kSettingsGroup);
|
||||
@@ -330,8 +351,6 @@ void CollectionView::ReloadSettings() {
|
||||
|
||||
void CollectionView::SetApplication(Application *app) {
|
||||
|
||||
//qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
app_ = app;
|
||||
|
||||
ReloadSettings();
|
||||
@@ -342,8 +361,6 @@ void CollectionView::SetFilter(CollectionFilterWidget *filter) { filter_ = filte
|
||||
|
||||
void CollectionView::TotalSongCountUpdated(int count) {
|
||||
|
||||
//qLog(Debug) << __FUNCTION__ << count;
|
||||
|
||||
bool old = total_song_count_;
|
||||
total_song_count_ = count;
|
||||
if (old != total_song_count_) update();
|
||||
@@ -359,8 +376,6 @@ void CollectionView::TotalSongCountUpdated(int count) {
|
||||
|
||||
void CollectionView::TotalArtistCountUpdated(int count) {
|
||||
|
||||
//qLog(Debug) << __FUNCTION__ << count;
|
||||
|
||||
bool old = total_artist_count_;
|
||||
total_artist_count_ = count;
|
||||
if (old != total_artist_count_) update();
|
||||
@@ -376,8 +391,6 @@ void CollectionView::TotalArtistCountUpdated(int count) {
|
||||
|
||||
void CollectionView::TotalAlbumCountUpdated(int count) {
|
||||
|
||||
//qLog(Debug) << __FUNCTION__ << count;
|
||||
|
||||
bool old = total_album_count_;
|
||||
total_album_count_ = count;
|
||||
if (old != total_album_count_) update();
|
||||
@@ -393,8 +406,6 @@ void CollectionView::TotalAlbumCountUpdated(int count) {
|
||||
|
||||
void CollectionView::paintEvent(QPaintEvent *event) {
|
||||
|
||||
//qLog(Debug) << __FUNCTION__;
|
||||
|
||||
if (total_song_count_ == 0) {
|
||||
QPainter p(viewport());
|
||||
QRect rect(viewport()->rect());
|
||||
@@ -491,7 +502,6 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
|
||||
}
|
||||
|
||||
// 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 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);
|
||||
|
||||
// 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_->setEnabled(regular_editable == 1);
|
||||
|
||||
@@ -534,9 +543,9 @@ void CollectionView::ShowInVarious(bool on) {
|
||||
|
||||
if (!context_menu_index_.isValid()) return;
|
||||
|
||||
// Map is from album name -> all artists sharing that album name, built from each selected
|
||||
// song. We put through "Various Artists" changes one album at a time, to make sure the old album
|
||||
// node gets removed (due to all children removed), before the new one gets added
|
||||
// Map is from album name -> all artists sharing that album name, built from each selected song.
|
||||
// We put through "Various Artists" changes one album at a time,
|
||||
// to make sure the old album node gets removed (due to all children removed), before the new one gets added
|
||||
QMultiMap<QString, QString> albums;
|
||||
for (const Song& song : GetSelectedSongs()) {
|
||||
if (albums.find(song.album(), song.artist()) == albums.end())
|
||||
@@ -586,16 +595,12 @@ void CollectionView::Load() {
|
||||
|
||||
void CollectionView::AddToPlaylist() {
|
||||
|
||||
//qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
emit AddToPlaylistSignal(model()->mimeData(selectedIndexes()));
|
||||
|
||||
}
|
||||
|
||||
void CollectionView::AddToPlaylistEnqueue() {
|
||||
|
||||
//qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
QMimeData *data = model()->mimeData(selectedIndexes());
|
||||
if (MimeData* mime_data = qobject_cast<MimeData*>(data)) {
|
||||
mime_data->enqueue_now_ = true;
|
||||
|
||||
@@ -24,21 +24,38 @@
|
||||
#include "config.h"
|
||||
|
||||
#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 <QStyleOptionViewItem>
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
#include <QtEvents>
|
||||
|
||||
#include "core/song.h"
|
||||
#include "dialogs/edittagdialog.h"
|
||||
#include "widgets/autoexpandingtreeview.h"
|
||||
|
||||
class QContextMenuEvent;
|
||||
class QHelpEvent;
|
||||
class QMouseEvent;
|
||||
class QPaintEvent;
|
||||
|
||||
class Application;
|
||||
class CollectionFilterWidget;
|
||||
class EditTagDialog;
|
||||
#ifdef HAVE_GSTREAMER
|
||||
class OrganiseDialog;
|
||||
#endif
|
||||
|
||||
class QMimeData;
|
||||
|
||||
class CollectionItemDelegate : public QStyledItemDelegate {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -57,11 +74,8 @@ class CollectionView : public AutoExpandingTreeView {
|
||||
CollectionView(QWidget *parent = nullptr);
|
||||
~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;
|
||||
|
||||
void SetApplication(Application *app);
|
||||
|
||||
@@ -20,6 +20,11 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QKeyEvent>
|
||||
|
||||
#include "collectionfilterwidget.h"
|
||||
#include "collectionview.h"
|
||||
#include "collectionviewcontainer.h"
|
||||
#include "ui_collectionviewcontainer.h"
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
|
||||
class CollectionFilterWidget;
|
||||
|
||||
@@ -20,34 +20,42 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <fileref.h>
|
||||
#include <tag.h>
|
||||
#include <QObject>
|
||||
#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/logging.h"
|
||||
#include "core/tagreaderclient.h"
|
||||
#include "core/taskmanager.h"
|
||||
#include "core/utilities.h"
|
||||
#include "directory.h"
|
||||
#include "collectionbackend.h"
|
||||
#include "collectionwatcher.h"
|
||||
#include "playlistparsers/cueparser.h"
|
||||
#include "settings/collectionsettingspage.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDirIterator>
|
||||
#include <QtDebug>
|
||||
#include <QThread>
|
||||
#include <QDateTime>
|
||||
#include <QHash>
|
||||
#include <QSet>
|
||||
#include <QSettings>
|
||||
#include <QTimer>
|
||||
// This is defined by one of the windows headers that is included by taglib.
|
||||
#ifdef RemoveDirectory
|
||||
#undef RemoveDirectory
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
static const char *kNoMediaFile = ".nomedia";
|
||||
@@ -218,8 +226,7 @@ void CollectionWatcher::AddDirectory(const Directory &dir, const SubdirectoryLis
|
||||
ScanSubdirectory(dir.path, Subdirectory(), &transaction);
|
||||
}
|
||||
else {
|
||||
// We can do an incremental scan - looking at the mtimes of each
|
||||
// subdirectory and only rescan if the directory has changed.
|
||||
// We can do an incremental scan - looking at the mtimes of each subdirectory and only rescan if the directory has changed.
|
||||
ScanTransaction transaction(this, dir.id, true);
|
||||
transaction.SetKnownSubdirs(subdirs);
|
||||
transaction.AddToProgressMax(subdirs.count());
|
||||
@@ -268,8 +275,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
|
||||
QStringList files_on_disk;
|
||||
SubdirectoryList my_new_subdirs;
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
// If one has been removed, "rescan" it to get the deleted songs
|
||||
SubdirectoryList previous_subdirs = t->GetImmediateSubdirs(path);
|
||||
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.isHidden() && !t->HasSeenSubdir(child)) {
|
||||
// We haven't seen this subdirectory before - add it to a list and
|
||||
// later we'll tell the backend about it and scan it.
|
||||
// We haven't seen this subdirectory before - add it to a list and later we'll tell the backend about it and scan it.
|
||||
Subdirectory new_subdir;
|
||||
new_subdir.directory_id = -1;
|
||||
new_subdir.path = child;
|
||||
@@ -332,8 +337,7 @@ void CollectionWatcher::ScanSubdirectory(const QString &path, const Subdirectory
|
||||
QFileInfo file_info(file);
|
||||
|
||||
if (!file_info.exists()) {
|
||||
// Partially fixes race condition - if file was removed between being
|
||||
// added to the list and now.
|
||||
// Partially fixes race condition - if file was removed between being added to the list and now.
|
||||
files_on_disk.removeAll(file);
|
||||
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_added = matching_cue_mtime != 0 && !matching_song.has_cue();
|
||||
|
||||
// watch out for cue songs which have their mtime equal to
|
||||
// qMax(media_file_mtime, cue_sheet_mtime)
|
||||
// watch out for cue songs which have their mtime equal to 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;
|
||||
|
||||
// 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
|
||||
if (matching_song.is_unavailable()) t->readded_songs << matching_song;
|
||||
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// The song is on disk but not in the DB
|
||||
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;
|
||||
|
||||
// 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)) {
|
||||
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) {
|
||||
|
||||
// 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
|
||||
// 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
|
||||
if (cue_deleted) {
|
||||
for (const Song &song :
|
||||
backend_->GetSongsByUrl(QUrl::fromLocalFile(file))) {
|
||||
@@ -490,7 +492,7 @@ SongList CollectionWatcher::ScanNewFile(const QString &file, const QString &path
|
||||
SongList song_list;
|
||||
|
||||
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) {
|
||||
// don't process the same cue many times
|
||||
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);
|
||||
cue.open(QIODevice::ReadOnly);
|
||||
|
||||
// Ignore FILEs pointing to other media files. Also, watch out for incorrect
|
||||
// media files. Playlist parser for CUEs considers every entry in sheet
|
||||
// valid and we don't want invalid media getting into collection!
|
||||
// Ignore FILEs pointing to other media files.
|
||||
// Also, watch out for incorrect media files.
|
||||
// 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);
|
||||
for (const Song& cue_song : cue_parser_->Load(&cue, matching_cue, path)) {
|
||||
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());
|
||||
|
||||
// Previous versions of Clementine incorrectly overwrote this and
|
||||
// stored it in the DB, so we can't rely on matching_song to
|
||||
// know if it has embedded artwork or not, but we can check here.
|
||||
// Previous versions of Clementine incorrectly overwrote this and stored it in the DB,
|
||||
// so we can't rely on matching_song to know if it has embedded artwork or not, but we can check here.
|
||||
if (!out->has_embedded_cover()) out->set_art_automatic(image);
|
||||
|
||||
out->MergeUserSetData(matching_song);
|
||||
|
||||
// The song was deleted from the database (e.g. due to an unmounted
|
||||
// filesystem), but has been restored.
|
||||
// The song was deleted from the database (e.g. due to an unmounted filesystem), but has been restored.
|
||||
if (matching_song.is_unavailable()) {
|
||||
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) {
|
||||
|
||||
// slight optimisation
|
||||
// Slight optimisation
|
||||
if (cue_path.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
@@ -593,7 +593,7 @@ void CollectionWatcher::RemoveDirectory(const Directory &dir) {
|
||||
watched_dirs_.remove(dir.id);
|
||||
|
||||
// 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);
|
||||
subdir_mapping_.remove(subdir_path);
|
||||
}
|
||||
@@ -662,8 +662,7 @@ QString CollectionWatcher::PickBestImage(const QStringList &images) {
|
||||
QStringList filtered;
|
||||
|
||||
for (const QString &filter_text : best_image_filters_) {
|
||||
// the images in the images list are represented by a full path,
|
||||
// so we need to isolate just the filename
|
||||
// The images in the images list are represented by a full path, so we need to isolate just the filename
|
||||
for (const QString& image : images) {
|
||||
QFileInfo file_info(image);
|
||||
QString filename(file_info.fileName());
|
||||
@@ -671,14 +670,13 @@ QString CollectionWatcher::PickBestImage(const QStringList &images) {
|
||||
filtered << image;
|
||||
}
|
||||
|
||||
/* We assume the filters are give in the order best to worst, so
|
||||
if we've got a result, we go with it. Otherwise we might
|
||||
start capturing more generic rules */
|
||||
// We assume the filters are give in the order best to worst, so if we've got a result, we go with it.
|
||||
// Otherwise we might start capturing more generic rules
|
||||
if (!filtered.isEmpty()) break;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -734,7 +732,7 @@ void CollectionWatcher::ReloadSettings() {
|
||||
|
||||
best_image_filters_.clear();
|
||||
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();
|
||||
if (!s.isEmpty()) best_image_filters_ << s;
|
||||
}
|
||||
|
||||
@@ -23,22 +23,24 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "directory.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <QHash>
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
#include <QHash>
|
||||
#include <QMap>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QTimer>
|
||||
|
||||
#include "directory.h"
|
||||
#include "core/song.h"
|
||||
|
||||
class QFileSystemWatcher;
|
||||
class QTimer;
|
||||
|
||||
class CueParser;
|
||||
class FileSystemWatcherInterface;
|
||||
class CollectionBackend;
|
||||
class FileSystemWatcherInterface;
|
||||
class TaskManager;
|
||||
class CueParser;
|
||||
|
||||
class CollectionWatcher : public QObject {
|
||||
Q_OBJECT
|
||||
@@ -76,14 +78,11 @@ signals:
|
||||
|
||||
private:
|
||||
// This class encapsulates a full or partial scan of a directory.
|
||||
// Each directory has one or more subdirectories, and any number of
|
||||
// subdirectories can be scanned during one transaction. ScanSubdirectory()
|
||||
// adds its results to the members of this transaction class, and they are
|
||||
// "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. Multiple calls to FindSongsInSubdirectory during one
|
||||
// transaction will only result in one call to
|
||||
// CollectionBackend::FindSongsInDirectory.
|
||||
// Each directory has one or more subdirectories, and any number of subdirectories can be scanned during one transaction.
|
||||
// ScanSubdirectory() adds its results to the members of this transaction class,
|
||||
// and they are "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.
|
||||
// Multiple calls to FindSongsInSubdirectory during one transaction will only result in one call to CollectionBackend::FindSongsInDirectory.
|
||||
class ScanTransaction {
|
||||
public:
|
||||
ScanTransaction(CollectionWatcher *watcher, int dir, bool incremental, bool ignores_mtime = false);
|
||||
@@ -120,10 +119,9 @@ signals:
|
||||
int dir_;
|
||||
// Incremental scan enters a directory only if it has changed since the last scan.
|
||||
bool incremental_;
|
||||
// This type of scan updates every file in a folder that's
|
||||
// being scanned. Even if it detects the file hasn't changed since
|
||||
// the last scan. Also, since it's ignoring mtimes on folders too,
|
||||
// it will go as deep in the folder hierarchy as it's possible.
|
||||
// This type of scan updates every file in a folder that's being scanned.
|
||||
// Even if it detects the file hasn't changed since the last scan.
|
||||
// Also, since it's ignoring mtimes on folders too, it will go as deep in the folder hierarchy as it's possible.
|
||||
bool ignores_mtime_;
|
||||
|
||||
CollectionWatcher *watcher_;
|
||||
@@ -153,18 +151,14 @@ signals:
|
||||
uint GetMtimeForCue(const QString &cue_path);
|
||||
void PerformScan(bool incremental, bool ignore_mtimes);
|
||||
|
||||
// Updates the sections of a cue associated and altered (according to mtime)
|
||||
// media file during a scan.
|
||||
// Updates the sections of a cue associated and altered (according to mtime) media file during a scan.
|
||||
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
|
||||
// during a scan.
|
||||
// Updates a single non-cue associated and altered (according to mtime) song during a scan.
|
||||
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
|
||||
// song (for example rating and score).
|
||||
// Updates a new song with some metadata taken from it's equivalent old song (for example rating and score).
|
||||
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.
|
||||
// It may result in a multiple files added to the collection when the media file
|
||||
// has many sections (like a CUE related 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).
|
||||
SongList ScanNewFile(const QString &file, const QString &path, const QString &matching_cue, QSet<QString> *cues_processed);
|
||||
|
||||
private:
|
||||
@@ -175,11 +169,8 @@ signals:
|
||||
FileSystemWatcherInterface *fs_watcher_;
|
||||
QHash<QString, Directory> subdir_mapping_;
|
||||
|
||||
/* A list of words use to try to identify the (likely) best image
|
||||
* found in an directory to use as cover artwork.
|
||||
* e.g. using ["front", "cover"] would identify front.jpg and
|
||||
* exclude back.jpg.
|
||||
*/
|
||||
// A list of words use to try to identify the (likely) best image found in an directory to use as cover artwork.
|
||||
// e.g. using ["front", "cover"] would identify front.jpg and exclude back.jpg.
|
||||
QStringList best_image_filters_;
|
||||
|
||||
bool stop_requested_;
|
||||
|
||||
@@ -23,11 +23,10 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QMetaType>
|
||||
|
||||
class QSqlQuery;
|
||||
#include <QSqlQuery>
|
||||
|
||||
struct Directory {
|
||||
Directory() : id(-1) {}
|
||||
|
||||
@@ -22,8 +22,13 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QPushButton>
|
||||
#include <QWidget>
|
||||
|
||||
#include "collectionmodel.h"
|
||||
#include "groupbydialog.h"
|
||||
#include "ui_groupbydialog.h"
|
||||
|
||||
@@ -31,9 +36,13 @@
|
||||
using std::placeholders::_1;
|
||||
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/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::indexed_by;
|
||||
@@ -70,7 +79,7 @@ class GroupByDialogPrivate {
|
||||
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);
|
||||
Reset();
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
#include <memory>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QString>
|
||||
|
||||
#include "collectionmodel.h"
|
||||
|
||||
|
||||
@@ -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 "collectionfilterwidget.h"
|
||||
#include "collectionmodel.h"
|
||||
#include "savedgroupingmanager.h"
|
||||
#include "ui_savedgroupingmanager.h"
|
||||
|
||||
#include <QKeySequence>
|
||||
#include <QList>
|
||||
#include <QSettings>
|
||||
#include <QStandardItem>
|
||||
|
||||
SavedGroupingManager::SavedGroupingManager(QWidget *parent)
|
||||
: QDialog(parent),
|
||||
ui_(new Ui_SavedGroupingManager),
|
||||
@@ -58,6 +72,7 @@ SavedGroupingManager::~SavedGroupingManager() {
|
||||
}
|
||||
|
||||
QString SavedGroupingManager::GroupByToString(const CollectionModel::GroupBy &g) {
|
||||
|
||||
switch (g) {
|
||||
case CollectionModel::GroupBy_None: {
|
||||
return tr("None");
|
||||
@@ -137,8 +152,7 @@ void SavedGroupingManager::Remove() {
|
||||
if (ui_->list->selectionModel()->hasSelection()) {
|
||||
QSettings s;
|
||||
s.beginGroup(CollectionModel::kSavedGroupingsSettingsGroup);
|
||||
for (const QModelIndex &index :
|
||||
ui_->list->selectionModel()->selectedRows()) {
|
||||
for (const QModelIndex &index : ui_->list->selectionModel()->selectedRows()) {
|
||||
if (index.isValid()) {
|
||||
qLog(Debug) << "Remove saved grouping: " << model_->item(index.row(), 0)->text();
|
||||
s.remove(model_->item(index.row(), 0)->text());
|
||||
|
||||
@@ -24,12 +24,15 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QStandardItemModel>
|
||||
#include <QString>
|
||||
|
||||
#include "collectionmodel.h"
|
||||
|
||||
class Ui_SavedGroupingManager;
|
||||
class CollectionFilterWidget;
|
||||
class Ui_SavedGroupingManager;
|
||||
|
||||
class SavedGroupingManager : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -20,12 +20,14 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "collectionquery.h"
|
||||
#include "sqlrow.h"
|
||||
|
||||
#include <QVariant>
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlRecord>
|
||||
|
||||
#include "sqlrow.h"
|
||||
|
||||
#include "collectionquery.h"
|
||||
|
||||
SqlRow::SqlRow(const QSqlQuery &query) { Init(query); }
|
||||
|
||||
SqlRow::SqlRow(const CollectionQuery &query) { Init(query); }
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
|
||||
#include <QList>
|
||||
#include <QVariant>
|
||||
|
||||
class QSqlQuery;
|
||||
#include <QSqlQuery>
|
||||
|
||||
class CollectionQuery;
|
||||
|
||||
@@ -37,7 +36,7 @@ class SqlRow {
|
||||
SqlRow(const QSqlQuery &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_;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user