Compare commits

...

45 Commits
0.4.1 ... 0.4.2

Author SHA1 Message Date
Jonas Kvinge
bcd29b9fd2 Release 0.4.2 2018-11-28 21:53:38 +01:00
Jonas Kvinge
046c221bc6 Change internet to streaming in settings and change order of settings 2018-11-28 17:53:37 +01:00
Jonas Kvinge
29a39b4e7f Attempt to fix devices issue 2018-11-28 17:45:50 +01:00
Jonas Kvinge
9fb3d06aac Change if statement in qtsingleapplication/CMakeLists.txt 2018-11-28 17:44:45 +01:00
Jonas Kvinge
142d5e2347 Update .travis.yml 2018-11-27 19:36:22 +01:00
Jonas Kvinge
58ea0bcbb0 Turn Deezer OFF by default if dependencies arent found 2018-11-27 19:33:30 +01:00
Jonas Kvinge
1c0bbe5d33 Fix endif in qxt CMakeLists.txt 2018-11-27 19:27:09 +01:00
Jonas Kvinge
8a7bba8ef3 Remove background image
Unsure about copyright issues using it, the image is from the Strawbs boxset, removing
it for now.
2018-11-27 19:20:33 +01:00
Jonas Kvinge
3b93963688 Code cleanup in qxtglobalshortcut 2018-11-27 19:14:35 +01:00
Jonas Kvinge
77e5d0288f Fix typo 2018-11-19 01:38:55 +01:00
Jonas Kvinge
460e78a1b8 Merge branch 'master' of github.com:jonaski/strawberry 2018-11-19 01:32:26 +01:00
Jonas Kvinge
fcbade267d Update changelog and about 2018-11-19 01:31:52 +01:00
Jonas Kvinge
41b99d9aa0 Update README.md 2018-11-19 01:12:32 +01:00
Jonas Kvinge
96c761dbb5 Update README.md 2018-11-19 01:09:51 +01:00
Jonas Kvinge
d297564236 Update README.md 2018-11-19 01:09:12 +01:00
Jonas Kvinge
bca1a98938 Add option to reset playlist columns 2018-11-19 00:18:48 +01:00
Jonas Kvinge
23205bef65 Playlist fixes
- Fix bug resetting playlist view columns to show all when using more than one
playlist.
- Add queue to play next
2018-11-18 23:21:12 +01:00
Jonas Kvinge
7613b2f526 Add alsa to optional_component 2018-11-17 16:28:05 +01:00
Jonas Kvinge
c5a521af1f Fix logging 2018-11-17 03:27:46 +01:00
Jonas Kvinge
f15c85e807 Remove SetThreadIOPriority, fixes poor performance on macos 2018-11-17 03:25:42 +01:00
Jonas Kvinge
6129ad1f4d Track slider fixes 2018-11-17 03:25:21 +01:00
Jonas Kvinge
9972dc192b Add include for QtGlobal 2018-11-17 03:24:20 +01:00
Jonas Kvinge
32b96a25e8 Remove dead code 2018-11-17 03:23:49 +01:00
Jonas Kvinge
9d09e7f6fe Remove dead code 2018-11-17 03:23:22 +01:00
Jonas Kvinge
5927483402 Remove qstyleoption_cast 2018-11-16 17:45:57 +01:00
Jonas Kvinge
f228f79a8a Fix macos code 2018-11-16 17:25:39 +01:00
Jonas Kvinge
072a3065cd Don't set paths for gst scanner and plugins on macos 2018-11-15 20:20:30 +01:00
Jonas Kvinge
718bd4c081 Replace NULL and 0 with nullptr 2018-11-14 00:43:19 +01:00
Jonas Kvinge
542cc0ec2f Remove unused code 2018-11-14 00:31:04 +01:00
Jonas Kvinge
b357634f51 Add pacman package files 2018-11-11 03:37:14 +01:00
Jonas Kvinge
6968c4d0fb Remove OpenGL from Dockerfile 2018-11-09 22:24:55 +01:00
Jonas Kvinge
e35d618133 Make Deezer engine use quality setting 2018-11-09 19:27:36 +01:00
Jonas Kvinge
4a23fde6bf Merge branch 'master' of github.com:jonaski/strawberry 2018-11-05 02:12:24 +01:00
Jonas Kvinge
1ae577641d Remove Qt5OpenGL from spec files and control file 2018-11-05 02:11:26 +01:00
Jonas Kvinge
686a3bb42b Update README.md 2018-11-05 01:21:37 +01:00
Jonas Kvinge
1771d41871 Fixes to makefiles
- Add a warning when using the systems taglib less or equal to version 1.11.1
- Remove OpenGL and Qt5::OpenGL dependency, it is not used by the analyzer
- Rename some variables
2018-11-05 01:14:56 +01:00
Jonas Kvinge
ee8f4ca7ab Add libqt5sql5-sqlite to debian control 2018-11-04 23:24:49 +01:00
Jonas Kvinge
4197a508a3 Remove obsolete xine warning and engine reinitialization 2018-11-04 21:23:34 +01:00
Jonas Kvinge
f38ffb505d Add Requires libQt5Sql5-sqlite to spec 2018-11-04 00:48:39 +01:00
Jonas Kvinge
679caf73d1 Fix strawberry.appdata.xml 2018-11-03 16:30:53 +01:00
Jonas Kvinge
56d25c346c Update appdata file to new AppStream specification 2018-11-03 15:57:45 +01:00
Jonas Kvinge
6ba26ba289 Change fedora rpm suffix 2018-11-02 21:30:07 +01:00
Jonas Kvinge
27582b7a4e Add strawberry.appdata.xml to spec files 2018-11-02 20:30:12 +01:00
Jonas Kvinge
2f4417d683 Fix files to delete on uninstall in nsi 2018-11-01 22:22:39 +01:00
Jonas Kvinge
de97d29a82 Turn back git revision 2018-11-01 22:22:30 +01:00
79 changed files with 1361 additions and 1494 deletions

1
.gitignore vendored
View File

@@ -39,6 +39,7 @@ Thumbs.db
*.plist
maketarball.sh
dist/debian/changelog
dist/pacman/PKGBUILD
# qtcreator generated files
*.pro.user*

View File

@@ -23,7 +23,7 @@ before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install chromaprint ; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export Qt5_DIR=/usr/local/opt/qt5/lib/cmake ; fi
before_script:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build cmake -Hstrawberry -Bbuild ; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build cmake -Hstrawberry -Bbuild -DENABLE_STREAM_DEEZER=ON ; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mkdir build; cd build; cmake .. -DFORCE_GIT_REVISION="0.0.0-0-g0000000"; fi
script:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker exec build make -C build -j8 ; fi

View File

@@ -13,11 +13,11 @@ set(SINGLEAPP-MOC-HEADERS
qtsinglecoreapplication.h
)
if(WIN32)
set(SINGLEAPP-SOURCES ${SINGLEAPP-SOURCES} qtlockedfile_win.cpp)
elseif(WIN32)
if(UNIX)
set(SINGLEAPP-SOURCES ${SINGLEAPP-SOURCES} qtlockedfile_unix.cpp)
endif(WIN32)
elseif(WIN32)
set(SINGLEAPP-SOURCES ${SINGLEAPP-SOURCES} qtlockedfile_win.cpp)
endif()
QT5_WRAP_CPP(SINGLEAPP-SOURCES-MOC ${SINGLEAPP-MOC-HEADERS})

View File

@@ -157,7 +157,7 @@ bool QtLocalPeer::sendMessage(const QString &message, int timeout)
Sleep(DWORD(ms));
#else
struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
nanosleep(&ts, NULL);
nanosleep(&ts, nullptr);
#endif
}
if (!connOk)

View File

@@ -58,7 +58,7 @@ Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate)
Qt::HANDLE mutex;
if (doCreate) {
mutex = CreateMutexW(NULL, FALSE, (WCHAR*)mname.utf16());
mutex = CreateMutexW(nullptr, FALSE, (WCHAR*)mname.utf16());
if (!mutex) {
qErrnoWarning("QtLockedFile::lock(): CreateMutex failed");
return 0;

View File

@@ -45,10 +45,6 @@
#include <QWidget>
#include <QString>
#ifdef HAVE_X11_ // FIXME
# include <X11/Xlib.h>
#endif
#include "qtlocalpeer.h"
/*!
@@ -178,47 +174,6 @@ QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char *
}
#if defined(HAVE_X11_) // FIXME
/*!
Special constructor for X11, ref. the documentation of
QApplication's corresponding constructor. The application identifier
will be QCoreApplication::applicationFilePath(). \a dpy, \a visual,
and \a cmap are passed on to the QApplication constructor.
*/
QtSingleApplication::QtSingleApplication(Display *dpy, Qt::HANDLE visual, Qt::HANDLE cmap)
: QApplication(dpy, visual, cmap)
{
sysInit();
}
/*!
Special constructor for X11, ref. the documentation of
QApplication's corresponding constructor. The application identifier
will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a
argv, \a visual, and \a cmap are passed on to the QApplication
constructor.
*/
QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap)
: QApplication(dpy, argc, argv, visual, cmap)
{
sysInit();
}
/*!
Special constructor for X11, ref. the documentation of
QApplication's corresponding constructor. The application identifier
will be \a appId. \a dpy, \a argc, \a
argv, \a visual, and \a cmap are passed on to the QApplication
constructor.
*/
QtSingleApplication::QtSingleApplication(Display *dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap)
: QApplication(dpy, argc, argv, visual, cmap)
{
sysInit(appId);
}
#endif
/*!
Returns true if another instance of this application is running;
otherwise false.

View File

@@ -48,10 +48,6 @@
#include <QApplication>
#include <QString>
#if defined(HAVE_X11_) // FIXME
# include <X11/Xlib.h>
#endif
class QtLocalPeer;
#if defined(Q_OS_WIN) || defined(Q_OS_WIN32)
@@ -77,11 +73,6 @@ class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication
public:
QtSingleApplication(int &argc, char **argv, bool GUIenabled = true);
QtSingleApplication(const QString &id, int &argc, char **argv);
#if defined(HAVE_X11_) // FIXME
QtSingleApplication(Display *dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0);
QtSingleApplication(Display *dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
#endif
bool isRunning();
QString id() const;

View File

@@ -1,6 +1,13 @@
cmake_minimum_required(VERSION 2.8.11)
set(CMAKE_CXX_STANDARD 11)
if (UNIX AND NOT APPLE)
find_package(X11)
if (X11_FOUND)
include_directories(${X11_INCLUDE_DIR})
endif(X11_FOUND)
endif(UNIX AND NOT APPLE)
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
if (NOT WIN32 AND NOT APPLE)
find_path(HAVE_QPA_QPLATFORMNATIVEINTERFACE_H qpa/qplatformnativeinterface.h PATHS ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
@@ -9,25 +16,16 @@ if (NOT WIN32 AND NOT APPLE)
endif(NOT HAVE_QPA_QPLATFORMNATIVEINTERFACE_H)
endif(NOT WIN32 AND NOT APPLE)
set(QXT-SOURCES
qxtglobal.cpp
qxtglobalshortcut.cpp
)
set(QXT-SOURCES qxtglobal.cpp qxtglobalshortcut.cpp)
set(QXT-MOC-HEADERS qxtglobalshortcut.h )
set(QXT-MOC-HEADERS
qxtglobalshortcut.h
)
find_package(X11)
include_directories(${X11_INCLUDE_DIR})
if(WIN32)
set(QXT-SOURCES ${QXT-SOURCES} qxtglobalshortcut_win.cpp)
if(X11_FOUND)
set(QXT-SOURCES ${QXT-SOURCES} qxtglobalshortcut_x11.cpp)
elseif(APPLE)
set(QXT-SOURCES ${QXT-SOURCES} qxtglobalshortcut_mac.cpp)
else(WIN32)
set(QXT-SOURCES ${QXT-SOURCES} qxtglobalshortcut_x11.cpp)
endif(WIN32)
elseif(WIN32)
set(QXT-SOURCES ${QXT-SOURCES} qxtglobalshortcut_win.cpp)
endif()
QT5_WRAP_CPP(QXT-SOURCES-MOC ${QXT-MOC-HEADERS})
@@ -36,8 +34,8 @@ ADD_LIBRARY(qxt STATIC
${QXT-SOURCES-MOC}
)
if(WIN32)
target_link_libraries(qxt Qt5::Core Qt5::Widgets)
else(WIN32)
target_link_libraries(qxt Qt5::Core Qt5::Widgets Qt5::X11Extras)
endif(WIN32)
target_link_libraries(qxt Qt5::Core Qt5::Widgets)
if(X11_FOUND)
target_link_libraries(qxt Qt5::X11Extras)
endif(X11_FOUND)

View File

@@ -1,4 +1,3 @@
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.

View File

@@ -1,4 +1,3 @@
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
@@ -161,73 +160,73 @@ template <typename PUB>
class QxtPrivate
{
public:
virtual ~QxtPrivate()
{}
inline void QXT_setPublic(PUB* pub)
{
qxt_p_ptr = pub;
}
virtual ~QxtPrivate()
{}
inline void QXT_setPublic(PUB* pub)
{
qxt_p_ptr = pub;
}
protected:
inline PUB& qxt_p()
{
return *qxt_p_ptr;
}
inline const PUB& qxt_p() const
{
return *qxt_p_ptr;
}
inline PUB* qxt_ptr()
{
return qxt_p_ptr;
}
inline const PUB* qxt_ptr() const
{
return qxt_p_ptr;
}
inline PUB& qxt_p()
{
return *qxt_p_ptr;
}
inline const PUB& qxt_p() const
{
return *qxt_p_ptr;
}
inline PUB* qxt_ptr()
{
return qxt_p_ptr;
}
inline const PUB* qxt_ptr() const
{
return qxt_p_ptr;
}
private:
PUB* qxt_p_ptr;
PUB* qxt_p_ptr;
};
template <typename PUB, typename PVT>
class QxtPrivateInterface
{
friend class QxtPrivate<PUB>;
friend class QxtPrivate<PUB>;
public:
QxtPrivateInterface()
{
pvt = new PVT;
}
~QxtPrivateInterface()
{
delete pvt;
}
QxtPrivateInterface()
{
pvt = new PVT;
}
~QxtPrivateInterface()
{
delete pvt;
}
inline void setPublic(PUB* pub)
{
pvt->QXT_setPublic(pub);
}
inline PVT& operator()()
{
return *static_cast<PVT*>(pvt);
}
inline const PVT& operator()() const
{
return *static_cast<PVT*>(pvt);
}
inline PVT * operator->()
{
inline void setPublic(PUB* pub)
{
pvt->QXT_setPublic(pub);
}
inline PVT& operator()()
{
return *static_cast<PVT*>(pvt);
}
inline const PVT& operator()() const
{
return *static_cast<PVT*>(pvt);
}
inline PVT * operator->()
{
return static_cast<PVT*>(pvt);
}
inline const PVT * operator->() const
{
}
inline const PVT * operator->() const
{
return static_cast<PVT*>(pvt);
}
}
private:
QxtPrivateInterface(const QxtPrivateInterface&) { }
QxtPrivateInterface& operator=(const QxtPrivateInterface&) { }
QxtPrivate<PUB>* pvt;
QxtPrivateInterface(const QxtPrivateInterface&) { }
QxtPrivateInterface& operator=(const QxtPrivateInterface&) { }
QxtPrivate<PUB>* pvt;
};
#endif // QXT_GLOBAL

View File

@@ -38,185 +38,160 @@
#include "qxtglobalshortcut_p.h"
bool QxtGlobalShortcutPrivate::error = false;
#ifndef Q_OS_MAC
#ifndef Q_OS_MACOS
int QxtGlobalShortcutPrivate::ref = 0;
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
QAbstractEventDispatcher::EventFilter QxtGlobalShortcutPrivate::prevEventFilter = 0;
#endif
#endif // Q_OS_MAC
#endif // Q_OS_MACOS
QHash<QPair<quint32, quint32>, QxtGlobalShortcut*> QxtGlobalShortcutPrivate::shortcuts;
QxtGlobalShortcutPrivate::QxtGlobalShortcutPrivate() : enabled(true), key(Qt::Key(0)), mods(Qt::NoModifier)
{
#ifndef Q_OS_MAC
if (!ref++)
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
prevEventFilter = QAbstractEventDispatcher::instance()->setEventFilter(eventFilter);
#else
QxtGlobalShortcutPrivate::QxtGlobalShortcutPrivate() : enabled(true), key(Qt::Key(0)), mods(Qt::NoModifier) {
#ifndef Q_OS_MACOS
if (!ref++)
QAbstractEventDispatcher::instance()->installNativeEventFilter(this);
#endif
#endif // Q_OS_MAC
#endif // Q_OS_MACOS
}
QxtGlobalShortcutPrivate::~QxtGlobalShortcutPrivate()
{
#ifndef Q_OS_MAC
if (!--ref)
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
QAbstractEventDispatcher::instance()->setEventFilter(prevEventFilter);
#else
QAbstractEventDispatcher::instance()->removeNativeEventFilter(this);
#endif
#endif // Q_OS_MAC
QxtGlobalShortcutPrivate::~QxtGlobalShortcutPrivate() {
#ifndef Q_OS_MACOS
if (!--ref)
QAbstractEventDispatcher::instance()->removeNativeEventFilter(this);
#endif // Q_OS_MACOS
}
bool QxtGlobalShortcutPrivate::setShortcut(const QKeySequence& shortcut)
{
Qt::KeyboardModifiers allMods = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier;
key = shortcut.isEmpty() ? Qt::Key(0) : Qt::Key((shortcut[0] ^ allMods) & shortcut[0]);
mods = shortcut.isEmpty() ? Qt::KeyboardModifiers(0) : Qt::KeyboardModifiers(shortcut[0] & allMods);
const quint32 nativeKey = nativeKeycode(key);
const quint32 nativeMods = nativeModifiers(mods);
const bool res = registerShortcut(nativeKey, nativeMods);
if (res)
shortcuts.insert(qMakePair(nativeKey, nativeMods), &qxt_p());
else
qWarning() << "QxtGlobalShortcut failed to register:" << QKeySequence(key + mods).toString();
return res;
bool QxtGlobalShortcutPrivate::setShortcut(const QKeySequence& shortcut) {
Qt::KeyboardModifiers allMods = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier;
key = shortcut.isEmpty() ? Qt::Key(0) : Qt::Key((shortcut[0] ^ allMods) & shortcut[0]);
mods = shortcut.isEmpty() ? Qt::KeyboardModifiers(0) : Qt::KeyboardModifiers(shortcut[0] & allMods);
const quint32 nativeKey = nativeKeycode(key);
const quint32 nativeMods = nativeModifiers(mods);
const bool res = registerShortcut(nativeKey, nativeMods);
if (res)
shortcuts.insert(qMakePair(nativeKey, nativeMods), &qxt_p());
else
qWarning() << "QxtGlobalShortcut failed to register:" << QKeySequence(key + mods).toString();
return res;
}
bool QxtGlobalShortcutPrivate::unsetShortcut()
{
bool res = false;
const quint32 nativeKey = nativeKeycode(key);
const quint32 nativeMods = nativeModifiers(mods);
if (shortcuts.value(qMakePair(nativeKey, nativeMods)) == &qxt_p())
res = unregisterShortcut(nativeKey, nativeMods);
if (res)
shortcuts.remove(qMakePair(nativeKey, nativeMods));
else
qWarning() << "QxtGlobalShortcut failed to unregister:" << QKeySequence(key + mods).toString();
key = Qt::Key(0);
mods = Qt::KeyboardModifiers(0);
return res;
bool QxtGlobalShortcutPrivate::unsetShortcut() {
bool res = false;
const quint32 nativeKey = nativeKeycode(key);
const quint32 nativeMods = nativeModifiers(mods);
if (shortcuts.value(qMakePair(nativeKey, nativeMods)) == &qxt_p())
res = unregisterShortcut(nativeKey, nativeMods);
if (res)
shortcuts.remove(qMakePair(nativeKey, nativeMods));
else
qWarning() << "QxtGlobalShortcut failed to unregister:" << QKeySequence(key + mods).toString();
key = Qt::Key(0);
mods = Qt::KeyboardModifiers(0);
return res;
}
void QxtGlobalShortcutPrivate::activateShortcut(quint32 nativeKey, quint32 nativeMods)
{
QxtGlobalShortcut* shortcut = shortcuts.value(qMakePair(nativeKey, nativeMods));
if (shortcut && shortcut->isEnabled())
emit shortcut->activated();
void QxtGlobalShortcutPrivate::activateShortcut(quint32 nativeKey, quint32 nativeMods) {
QxtGlobalShortcut* shortcut = shortcuts.value(qMakePair(nativeKey, nativeMods));
if (shortcut && shortcut->isEnabled())
emit shortcut->activated();
}
/*!
\class QxtGlobalShortcut
\inmodule QxtWidgets
\brief The QxtGlobalShortcut class provides a global shortcut aka "hotkey".
\class QxtGlobalShortcut
\inmodule QxtWidgets
\brief The QxtGlobalShortcut class provides a global shortcut aka "hotkey".
A global shortcut triggers even if the application is not active. This
makes it easy to implement applications that react to certain shortcuts
still if some other application is active or if the application is for
example minimized to the system tray.
A global shortcut triggers even if the application is not active. This
makes it easy to implement applications that react to certain shortcuts
still if some other application is active or if the application is for
example minimized to the system tray.
Example usage:
\code
QxtGlobalShortcut* shortcut = new QxtGlobalShortcut(window);
connect(shortcut, SIGNAL(activated()), window, SLOT(toggleVisibility()));
shortcut->setShortcut(QKeySequence("Ctrl+Shift+F12"));
\endcode
Example usage:
\code
QxtGlobalShortcut* shortcut = new QxtGlobalShortcut(window);
connect(shortcut, SIGNAL(activated()), window, SLOT(toggleVisibility()));
shortcut->setShortcut(QKeySequence("Ctrl+Shift+F12"));
\endcode
\bold {Note:} Since Qxt 0.6 QxtGlobalShortcut no more requires QxtApplication.
\bold {Note:} Since Qxt 0.6 QxtGlobalShortcut no more requires QxtApplication.
*/
/*!
\fn QxtGlobalShortcut::activated()
\fn QxtGlobalShortcut::activated()
This signal is emitted when the user types the shortcut's key sequence.
This signal is emitted when the user types the shortcut's key sequence.
\sa shortcut
\sa shortcut
*/
/*!
Constructs a new QxtGlobalShortcut with \a parent.
Constructs a new QxtGlobalShortcut with \a parent.
*/
QxtGlobalShortcut::QxtGlobalShortcut(QObject* parent)
: QObject(parent)
{
QXT_INIT_PRIVATE(QxtGlobalShortcut);
QxtGlobalShortcut::QxtGlobalShortcut(QObject* parent) : QObject(parent) {
QXT_INIT_PRIVATE(QxtGlobalShortcut);
}
/*!
Constructs a new QxtGlobalShortcut with \a shortcut and \a parent.
Constructs a new QxtGlobalShortcut with \a shortcut and \a parent.
*/
QxtGlobalShortcut::QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent)
: QObject(parent)
{
QXT_INIT_PRIVATE(QxtGlobalShortcut);
setShortcut(shortcut);
QxtGlobalShortcut::QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent) : QObject(parent) {
QXT_INIT_PRIVATE(QxtGlobalShortcut);
setShortcut(shortcut);
}
/*!
Destructs the QxtGlobalShortcut.
Destructs the QxtGlobalShortcut.
*/
QxtGlobalShortcut::~QxtGlobalShortcut()
{
if (qxt_d().key != 0)
qxt_d().unsetShortcut();
QxtGlobalShortcut::~QxtGlobalShortcut() {
if (qxt_d().key != 0) qxt_d().unsetShortcut();
}
/*!
\property QxtGlobalShortcut::shortcut
\brief the shortcut key sequence
\property QxtGlobalShortcut::shortcut
\brief the shortcut key sequence
\bold {Note:} Notice that corresponding key press and release events are not
delivered for registered global shortcuts even if they are disabled.
Also, comma separated key sequences are not supported.
Only the first part is used:
\bold {Note:} Notice that corresponding key press and release events are not
delivered for registered global shortcuts even if they are disabled.
Also, comma separated key sequences are not supported.
Only the first part is used:
\code
qxtShortcut->setShortcut(QKeySequence("Ctrl+Alt+A,Ctrl+Alt+B"));
Q_ASSERT(qxtShortcut->shortcut() == QKeySequence("Ctrl+Alt+A"));
\endcode
\code
qxtShortcut->setShortcut(QKeySequence("Ctrl+Alt+A,Ctrl+Alt+B"));
Q_ASSERT(qxtShortcut->shortcut() == QKeySequence("Ctrl+Alt+A"));
\endcode
*/
QKeySequence QxtGlobalShortcut::shortcut() const
{
return QKeySequence(qxt_d().key | qxt_d().mods);
QKeySequence QxtGlobalShortcut::shortcut() const {
return QKeySequence(qxt_d().key | qxt_d().mods);
}
bool QxtGlobalShortcut::setShortcut(const QKeySequence& shortcut)
{
if (qxt_d().key != 0)
qxt_d().unsetShortcut();
return qxt_d().setShortcut(shortcut);
bool QxtGlobalShortcut::setShortcut(const QKeySequence& shortcut) {
if (qxt_d().key != 0)
qxt_d().unsetShortcut();
return qxt_d().setShortcut(shortcut);
}
/*!
\property QxtGlobalShortcut::enabled
\brief whether the shortcut is enabled
\property QxtGlobalShortcut::enabled
\brief whether the shortcut is enabled
A disabled shortcut does not get activated.
A disabled shortcut does not get activated.
The default value is \c true.
The default value is \c true.
\sa setDisabled()
\sa setDisabled()
*/
bool QxtGlobalShortcut::isEnabled() const
{
return qxt_d().enabled;
bool QxtGlobalShortcut::isEnabled() const {
return qxt_d().enabled;
}
void QxtGlobalShortcut::setEnabled(bool enabled)
{
qxt_d().enabled = enabled;
void QxtGlobalShortcut::setEnabled(bool enabled) {
qxt_d().enabled = enabled;
}
/*!
Sets the shortcut \a disabled.
Sets the shortcut \a disabled.
\sa enabled
\sa enabled
*/
void QxtGlobalShortcut::setDisabled(bool disabled)
{
qxt_d().enabled = !disabled;
void QxtGlobalShortcut::setDisabled(bool disabled) {
qxt_d().enabled = !disabled;
}

View File

@@ -1,4 +1,3 @@
#ifndef QXTGLOBALSHORTCUT_H
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
@@ -29,6 +28,7 @@
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#ifndef QXTGLOBALSHORTCUT_H
#define QXTGLOBALSHORTCUT_H
#include <QObject>
@@ -39,30 +39,29 @@
class QxtGlobalShortcutPrivate;
class QXT_GUI_EXPORT QxtGlobalShortcut : public QObject
{
Q_OBJECT
QXT_DECLARE_PRIVATE(QxtGlobalShortcut)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut)
class QXT_GUI_EXPORT QxtGlobalShortcut : public QObject {
Q_OBJECT
QXT_DECLARE_PRIVATE(QxtGlobalShortcut)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut)
public:
explicit QxtGlobalShortcut(QObject* parent = 0);
explicit QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent = 0);
virtual ~QxtGlobalShortcut();
explicit QxtGlobalShortcut(QObject* parent = nullptr);
explicit QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent = nullptr);
~QxtGlobalShortcut();
QKeySequence shortcut() const;
bool setShortcut(const QKeySequence& shortcut);
QKeySequence shortcut() const;
bool setShortcut(const QKeySequence& shortcut);
bool isEnabled() const;
bool isEnabled() const;
public Q_SLOTS:
void setEnabled(bool enabled = true);
void setDisabled(bool disabled = true);
void setEnabled(bool enabled = true);
void setDisabled(bool disabled = true);
Q_SIGNALS:
void activated();
void activated();
};
#endif // QXTGLOBALSHORTCUT_H
#endif // QXTGLOBALSHORTCUT_H

View File

@@ -1,4 +1,3 @@
#include <Carbon/Carbon.h>
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
@@ -29,230 +28,180 @@
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#include "qxtglobalshortcut_p.h"
#include <QMap>
#include <QHash>
#include <QtDebug>
#include <QApplication>
typedef QPair<uint, uint> Identifier;
static QMap<quint32, EventHotKeyRef> keyRefs;
static QHash<Identifier, quint32> keyIDs;
static quint32 hotKeySerial = 0;
static bool qxt_mac_handler_installed = false;
OSStatus qxt_mac_handle_hot_key(EventHandlerCallRef nextHandler, EventRef event, void* data)
{
Q_UNUSED(nextHandler);
Q_UNUSED(data);
if (GetEventClass(event) == kEventClassKeyboard && GetEventKind(event) == kEventHotKeyPressed)
{
EventHotKeyID keyID;
GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(keyID), NULL, &keyID);
Identifier id = keyIDs.key(keyID.id);
QxtGlobalShortcutPrivate::activateShortcut(id.second, id.first);
}
return noErr;
}
quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers)
{
quint32 native = 0;
if (modifiers & Qt::ShiftModifier)
native |= shiftKey;
if (modifiers & Qt::ControlModifier)
native |= cmdKey;
if (modifiers & Qt::AltModifier)
native |= optionKey;
if (modifiers & Qt::MetaModifier)
native |= controlKey;
if (modifiers & Qt::KeypadModifier)
native |= kEventKeyModifierNumLockMask;
return native;
}
quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key)
{
UTF16Char ch;
// Constants found in NSEvent.h from AppKit.framework
switch (key)
{
case Qt::Key_Return:
return kVK_Return;
case Qt::Key_Enter:
return kVK_ANSI_KeypadEnter;
case Qt::Key_Tab:
return kVK_Tab;
case Qt::Key_Space:
return kVK_Space;
case Qt::Key_Backspace:
return kVK_Delete;
case Qt::Key_Control:
return kVK_Command;
case Qt::Key_Shift:
return kVK_Shift;
case Qt::Key_CapsLock:
return kVK_CapsLock;
case Qt::Key_Option:
return kVK_Option;
case Qt::Key_Meta:
return kVK_Control;
case Qt::Key_F17:
return kVK_F17;
case Qt::Key_VolumeUp:
return kVK_VolumeUp;
case Qt::Key_VolumeDown:
return kVK_VolumeDown;
case Qt::Key_F18:
return kVK_F18;
case Qt::Key_F19:
return kVK_F19;
case Qt::Key_F20:
return kVK_F20;
case Qt::Key_F5:
return kVK_F5;
case Qt::Key_F6:
return kVK_F6;
case Qt::Key_F7:
return kVK_F7;
case Qt::Key_F3:
return kVK_F3;
case Qt::Key_F8:
return kVK_F8;
case Qt::Key_F9:
return kVK_F9;
case Qt::Key_F11:
return kVK_F11;
case Qt::Key_F13:
return kVK_F13;
case Qt::Key_F16:
return kVK_F16;
case Qt::Key_F14:
return kVK_F14;
case Qt::Key_F10:
return kVK_F10;
case Qt::Key_F12:
return kVK_F12;
case Qt::Key_F15:
return kVK_F15;
case Qt::Key_Help:
return kVK_Help;
case Qt::Key_Home:
return kVK_Home;
case Qt::Key_PageUp:
return kVK_PageUp;
case Qt::Key_Delete:
return kVK_ForwardDelete;
case Qt::Key_F4:
return kVK_F4;
case Qt::Key_End:
return kVK_End;
case Qt::Key_F2:
return kVK_F2;
case Qt::Key_PageDown:
return kVK_PageDown;
case Qt::Key_F1:
return kVK_F1;
case Qt::Key_Left:
return kVK_LeftArrow;
case Qt::Key_Right:
return kVK_RightArrow;
case Qt::Key_Down:
return kVK_DownArrow;
case Qt::Key_Up:
return kVK_UpArrow;
default:
;
}
if (key == Qt::Key_Escape) ch = 27;
else if (key == Qt::Key_Return) ch = 13;
else if (key == Qt::Key_Enter) ch = 3;
else if (key == Qt::Key_Tab) ch = 9;
else ch = key;
CFDataRef currentLayoutData;
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
if (currentKeyboard == NULL)
return 0;
currentLayoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
CFRelease(currentKeyboard);
if (currentLayoutData == NULL)
return 0;
UCKeyboardLayout* header = (UCKeyboardLayout*)CFDataGetBytePtr(currentLayoutData);
UCKeyboardTypeHeader* table = header->keyboardTypeList;
uint8_t *data = (uint8_t*)header;
// God, would a little documentation for this shit kill you...
for (quint32 i=0; i < header->keyboardTypeCount; i++)
{
UCKeyStateRecordsIndex* stateRec = 0;
if (table[i].keyStateRecordsIndexOffset != 0)
{
stateRec = reinterpret_cast<UCKeyStateRecordsIndex*>(data + table[i].keyStateRecordsIndexOffset);
if (stateRec->keyStateRecordsIndexFormat != kUCKeyStateRecordsIndexFormat) stateRec = 0;
}
UCKeyToCharTableIndex* charTable = reinterpret_cast<UCKeyToCharTableIndex*>(data + table[i].keyToCharTableIndexOffset);
if (charTable->keyToCharTableIndexFormat != kUCKeyToCharTableIndexFormat) continue;
for (quint32 j=0; j < charTable->keyToCharTableCount; j++)
{
UCKeyOutput* keyToChar = reinterpret_cast<UCKeyOutput*>(data + charTable->keyToCharTableOffsets[j]);
for (quint32 k=0; k < charTable->keyToCharTableSize; k++)
{
if (keyToChar[k] & kUCKeyOutputTestForIndexMask)
{
long idx = keyToChar[k] & kUCKeyOutputGetIndexMask;
if (stateRec && idx < stateRec->keyStateRecordCount)
{
UCKeyStateRecord* rec = reinterpret_cast<UCKeyStateRecord*>(data + stateRec->keyStateRecordOffsets[idx]);
if (rec->stateZeroCharData == ch) return k;
}
}
else if (!(keyToChar[k] & kUCKeyOutputSequenceIndexMask) && keyToChar[k] < 0xFFFE)
{
if (keyToChar[k] == ch) return k;
}
} // for k
} // for j
} // for i
return 0;
}
bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods)
{
if (!qxt_mac_handler_installed)
{
EventTypeSpec t;
t.eventClass = kEventClassKeyboard;
t.eventKind = kEventHotKeyPressed;
InstallApplicationEventHandler(&qxt_mac_handle_hot_key, 1, &t, NULL, NULL);
}
EventHotKeyID keyID;
keyID.signature = 'cute';
keyID.id = ++hotKeySerial;
EventHotKeyRef ref = 0;
bool rv = !RegisterEventHotKey(nativeKey, nativeMods, keyID, GetApplicationEventTarget(), 0, &ref);
if (rv)
{
keyIDs.insert(Identifier(nativeMods, nativeKey), keyID.id);
keyRefs.insert(keyID.id, ref);
}
return rv;
}
bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods)
{
Identifier id(nativeMods, nativeKey);
if (!keyIDs.contains(id)) return false;
EventHotKeyRef ref = keyRefs.take(keyIDs[id]);
keyIDs.remove(id);
return !UnregisterEventHotKey(ref);
}
#include <Carbon/Carbon.h>
#include "qxtglobalshortcut_p.h"
#include <QMap>
#include <QHash>
#include <QPair>
#include <QtDebug>
#include <QApplication>
typedef QPair<uint, uint> Identifier;
static QMap<quint32, EventHotKeyRef> keyRefs;
static QHash<Identifier, quint32> keyIDs;
static quint32 hotKeySerial = 0;
static bool qxt_mac_handler_installed = false;
OSStatus qxt_mac_handle_hot_key(EventHandlerCallRef nextHandler, EventRef event, void* data) {
Q_UNUSED(nextHandler);
Q_UNUSED(data);
if (GetEventClass(event) == kEventClassKeyboard && GetEventKind(event) == kEventHotKeyPressed) {
EventHotKeyID keyID;
GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, nullptr, sizeof(keyID), nullptr, &keyID);
Identifier id = keyIDs.key(keyID.id);
QxtGlobalShortcutPrivate::activateShortcut(id.second, id.first);
}
return noErr;
}
quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers) {
quint32 native = 0;
if (modifiers & Qt::ShiftModifier) native |= shiftKey;
if (modifiers & Qt::ControlModifier) native |= cmdKey;
if (modifiers & Qt::AltModifier) native |= optionKey;
if (modifiers & Qt::MetaModifier) native |= controlKey;
if (modifiers & Qt::KeypadModifier) native |= kEventKeyModifierNumLockMask;
return native;
}
quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key) {
UTF16Char ch;
// Constants found in NSEvent.h from AppKit.framework
switch (key) {
case Qt::Key_Return: return kVK_Return;
case Qt::Key_Enter: return kVK_ANSI_KeypadEnter;
case Qt::Key_Tab: return kVK_Tab;
case Qt::Key_Space: return kVK_Space;
case Qt::Key_Backspace: return kVK_Delete;
case Qt::Key_Control: return kVK_Command;
case Qt::Key_Shift: return kVK_Shift;
case Qt::Key_CapsLock: return kVK_CapsLock;
case Qt::Key_Option: return kVK_Option;
case Qt::Key_Meta: return kVK_Control;
case Qt::Key_F17: return kVK_F17;
case Qt::Key_VolumeUp: return kVK_VolumeUp;
case Qt::Key_VolumeDown: return kVK_VolumeDown;
case Qt::Key_F18: return kVK_F18;
case Qt::Key_F19: return kVK_F19;
case Qt::Key_F20: return kVK_F20;
case Qt::Key_F5: return kVK_F5;
case Qt::Key_F6: return kVK_F6;
case Qt::Key_F7: return kVK_F7;
case Qt::Key_F3: return kVK_F3;
case Qt::Key_F8: return kVK_F8;
case Qt::Key_F9: return kVK_F9;
case Qt::Key_F11: return kVK_F11;
case Qt::Key_F13: return kVK_F13;
case Qt::Key_F16: return kVK_F16;
case Qt::Key_F14: return kVK_F14;
case Qt::Key_F10: return kVK_F10;
case Qt::Key_F12: return kVK_F12;
case Qt::Key_F15: return kVK_F15;
case Qt::Key_Help: return kVK_Help;
case Qt::Key_Home: return kVK_Home;
case Qt::Key_PageUp: return kVK_PageUp;
case Qt::Key_Delete: return kVK_ForwardDelete;
case Qt::Key_F4: return kVK_F4;
case Qt::Key_End: return kVK_End;
case Qt::Key_F2: return kVK_F2;
case Qt::Key_PageDown: return kVK_PageDown;
case Qt::Key_F1: return kVK_F1;
case Qt::Key_Left: return kVK_LeftArrow;
case Qt::Key_Right: return kVK_RightArrow;
case Qt::Key_Down: return kVK_DownArrow;
case Qt::Key_Up: return kVK_UpArrow;
default:
;
}
if (key == Qt::Key_Escape) ch = 27;
else if (key == Qt::Key_Return) ch = 13;
else if (key == Qt::Key_Enter) ch = 3;
else if (key == Qt::Key_Tab) ch = 9;
else ch = key;
CFDataRef currentLayoutData;
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
if (currentKeyboard == nullptr)
return 0;
currentLayoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
CFRelease(currentKeyboard);
if (currentLayoutData == nullptr)
return 0;
UCKeyboardLayout* header = (UCKeyboardLayout*)CFDataGetBytePtr(currentLayoutData);
UCKeyboardTypeHeader* table = header->keyboardTypeList;
uint8_t *data = (uint8_t*)header;
// God, would a little documentation for this shit kill you...
for (quint32 i=0; i < header->keyboardTypeCount; i++) {
UCKeyStateRecordsIndex* stateRec = 0;
if (table[i].keyStateRecordsIndexOffset != 0) {
stateRec = reinterpret_cast<UCKeyStateRecordsIndex*>(data + table[i].keyStateRecordsIndexOffset);
if (stateRec->keyStateRecordsIndexFormat != kUCKeyStateRecordsIndexFormat) stateRec = 0;
}
UCKeyToCharTableIndex* charTable = reinterpret_cast<UCKeyToCharTableIndex*>(data + table[i].keyToCharTableIndexOffset);
if (charTable->keyToCharTableIndexFormat != kUCKeyToCharTableIndexFormat) continue;
for (quint32 j=0; j < charTable->keyToCharTableCount; j++) {
UCKeyOutput* keyToChar = reinterpret_cast<UCKeyOutput*>(data + charTable->keyToCharTableOffsets[j]);
for (quint32 k=0; k < charTable->keyToCharTableSize; k++) {
if (keyToChar[k] & kUCKeyOutputTestForIndexMask) {
long idx = keyToChar[k] & kUCKeyOutputGetIndexMask;
if (stateRec && idx < stateRec->keyStateRecordCount) {
UCKeyStateRecord* rec = reinterpret_cast<UCKeyStateRecord*>(data + stateRec->keyStateRecordOffsets[idx]);
if (rec->stateZeroCharData == ch) return k;
}
}
else if (!(keyToChar[k] & kUCKeyOutputSequenceIndexMask) && keyToChar[k] < 0xFFFE) {
if (keyToChar[k] == ch) return k;
}
} // for k
} // for j
} // for i
return 0;
}
bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods) {
if (!qxt_mac_handler_installed) {
EventTypeSpec t;
t.eventClass = kEventClassKeyboard;
t.eventKind = kEventHotKeyPressed;
InstallApplicationEventHandler(&qxt_mac_handle_hot_key, 1, &t, nullptr, nullptr);
}
EventHotKeyID keyID;
keyID.signature = 'cute';
keyID.id = ++hotKeySerial;
EventHotKeyRef ref = 0;
bool rv = !RegisterEventHotKey(nativeKey, nativeMods, keyID, GetApplicationEventTarget(), 0, &ref);
if (rv) {
keyIDs.insert(Identifier(nativeMods, nativeKey), keyID.id);
keyRefs.insert(keyID.id, ref);
}
return rv;
}
bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods) {
Identifier id(nativeMods, nativeKey);
if (!keyIDs.contains(id)) return false;
EventHotKeyRef ref = keyRefs.take(keyIDs[id]);
keyIDs.remove(id);
return !UnregisterEventHotKey(ref);
}

View File

@@ -1,84 +1,72 @@
#ifndef QXTGLOBALSHORTCUT_P_H
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project nor the
** names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#define QXTGLOBALSHORTCUT_P_H
#include "qxtglobalshortcut.h"
#include <QAbstractEventDispatcher>
#include <QKeySequence>
#include <QHash>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QAbstractNativeEventFilter>
#endif
class QxtGlobalShortcutPrivate : public QxtPrivate<QxtGlobalShortcut>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
,public QAbstractNativeEventFilter
#endif
{
public:
QXT_DECLARE_PUBLIC(QxtGlobalShortcut)
QxtGlobalShortcutPrivate();
~QxtGlobalShortcutPrivate();
bool enabled;
Qt::Key key;
Qt::KeyboardModifiers mods;
bool setShortcut(const QKeySequence& shortcut);
bool unsetShortcut();
static bool error;
#ifndef Q_OS_MAC
static int ref;
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
static QAbstractEventDispatcher::EventFilter prevEventFilter;
static bool eventFilter(void* message);
#else
virtual bool nativeEventFilter(const QByteArray & eventType, void * message, long * result);
#endif // QT_VERSION < QT_VERSION_CHECK(5,0,0)
#endif // Q_OS_MAC
static void activateShortcut(quint32 nativeKey, quint32 nativeMods);
private:
static quint32 nativeKeycode(Qt::Key keycode);
static quint32 nativeModifiers(Qt::KeyboardModifiers modifiers);
static bool registerShortcut(quint32 nativeKey, quint32 nativeMods);
static bool unregisterShortcut(quint32 nativeKey, quint32 nativeMods);
static QHash<QPair<quint32, quint32>, QxtGlobalShortcut*> shortcuts;
};
#endif // QXTGLOBALSHORTCUT_P_H
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project nor the
** names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#ifndef QXTGLOBALSHORTCUT_P_H
#define QXTGLOBALSHORTCUT_P_H
#include "qxtglobalshortcut.h"
#include <QHash>
#include <QAbstractEventDispatcher>
#include <QAbstractNativeEventFilter>
#include <QKeySequence>
class QxtGlobalShortcutPrivate : public QxtPrivate<QxtGlobalShortcut>, public QAbstractNativeEventFilter {
public:
QXT_DECLARE_PUBLIC(QxtGlobalShortcut)
QxtGlobalShortcutPrivate();
~QxtGlobalShortcutPrivate();
bool enabled;
Qt::Key key;
Qt::KeyboardModifiers mods;
bool setShortcut(const QKeySequence& shortcut);
bool unsetShortcut();
static bool error;
#ifndef Q_OS_MACOS
static int ref;
virtual bool nativeEventFilter(const QByteArray & eventType, void * message, long * result);
#endif // Q_OS_MAC
static void activateShortcut(quint32 nativeKey, quint32 nativeMods);
private:
static quint32 nativeKeycode(Qt::Key keycode);
static quint32 nativeModifiers(Qt::KeyboardModifiers modifiers);
static bool registerShortcut(quint32 nativeKey, quint32 nativeMods);
static bool unregisterShortcut(quint32 nativeKey, quint32 nativeMods);
static QHash<QPair<quint32, quint32>, QxtGlobalShortcut*> shortcuts;
};
#endif // QXTGLOBALSHORTCUT_P_H

View File

@@ -1,247 +1,239 @@
#include "qxtglobalshortcut_p.h"
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project nor the
** names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#include <qt_windows.h>
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
bool QxtGlobalShortcutPrivate::eventFilter(void* message)
{
#else
bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType,
void * message, long * result)
{
Q_UNUSED(eventType);
Q_UNUSED(result);
#endif
MSG* msg = static_cast<MSG*>(message);
if (msg->message == WM_HOTKEY)
{
const quint32 keycode = HIWORD(msg->lParam);
const quint32 modifiers = LOWORD(msg->lParam);
activateShortcut(keycode, modifiers);
}
return false;
}
quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers)
{
// MOD_ALT, MOD_CONTROL, (MOD_KEYUP), MOD_SHIFT, MOD_WIN
quint32 native = 0;
if (modifiers & Qt::ShiftModifier)
native |= MOD_SHIFT;
if (modifiers & Qt::ControlModifier)
native |= MOD_CONTROL;
if (modifiers & Qt::AltModifier)
native |= MOD_ALT;
if (modifiers & Qt::MetaModifier)
native |= MOD_WIN;
// TODO: resolve these?
//if (modifiers & Qt::KeypadModifier)
//if (modifiers & Qt::GroupSwitchModifier)
return native;
}
quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key)
{
switch (key)
{
case Qt::Key_Escape:
return VK_ESCAPE;
case Qt::Key_Tab:
case Qt::Key_Backtab:
return VK_TAB;
case Qt::Key_Backspace:
return VK_BACK;
case Qt::Key_Return:
case Qt::Key_Enter:
return VK_RETURN;
case Qt::Key_Insert:
return VK_INSERT;
case Qt::Key_Delete:
return VK_DELETE;
case Qt::Key_Pause:
return VK_PAUSE;
case Qt::Key_Print:
return VK_PRINT;
case Qt::Key_Clear:
return VK_CLEAR;
case Qt::Key_Home:
return VK_HOME;
case Qt::Key_End:
return VK_END;
case Qt::Key_Left:
return VK_LEFT;
case Qt::Key_Up:
return VK_UP;
case Qt::Key_Right:
return VK_RIGHT;
case Qt::Key_Down:
return VK_DOWN;
case Qt::Key_PageUp:
return VK_PRIOR;
case Qt::Key_PageDown:
return VK_NEXT;
case Qt::Key_F1:
return VK_F1;
case Qt::Key_F2:
return VK_F2;
case Qt::Key_F3:
return VK_F3;
case Qt::Key_F4:
return VK_F4;
case Qt::Key_F5:
return VK_F5;
case Qt::Key_F6:
return VK_F6;
case Qt::Key_F7:
return VK_F7;
case Qt::Key_F8:
return VK_F8;
case Qt::Key_F9:
return VK_F9;
case Qt::Key_F10:
return VK_F10;
case Qt::Key_F11:
return VK_F11;
case Qt::Key_F12:
return VK_F12;
case Qt::Key_F13:
return VK_F13;
case Qt::Key_F14:
return VK_F14;
case Qt::Key_F15:
return VK_F15;
case Qt::Key_F16:
return VK_F16;
case Qt::Key_F17:
return VK_F17;
case Qt::Key_F18:
return VK_F18;
case Qt::Key_F19:
return VK_F19;
case Qt::Key_F20:
return VK_F20;
case Qt::Key_F21:
return VK_F21;
case Qt::Key_F22:
return VK_F22;
case Qt::Key_F23:
return VK_F23;
case Qt::Key_F24:
return VK_F24;
case Qt::Key_Space:
return VK_SPACE;
case Qt::Key_Asterisk:
return VK_MULTIPLY;
case Qt::Key_Plus:
return VK_ADD;
case Qt::Key_Comma:
return VK_SEPARATOR;
case Qt::Key_Minus:
return VK_SUBTRACT;
case Qt::Key_Slash:
return VK_DIVIDE;
case Qt::Key_MediaNext:
return VK_MEDIA_NEXT_TRACK;
case Qt::Key_MediaPrevious:
return VK_MEDIA_PREV_TRACK;
case Qt::Key_MediaPlay:
return VK_MEDIA_PLAY_PAUSE;
case Qt::Key_MediaStop:
return VK_MEDIA_STOP;
// couldn't find those in VK_*
//case Qt::Key_MediaLast:
//case Qt::Key_MediaRecord:
case Qt::Key_VolumeDown:
return VK_VOLUME_DOWN;
case Qt::Key_VolumeUp:
return VK_VOLUME_UP;
case Qt::Key_VolumeMute:
return VK_VOLUME_MUTE;
// numbers
case Qt::Key_0:
case Qt::Key_1:
case Qt::Key_2:
case Qt::Key_3:
case Qt::Key_4:
case Qt::Key_5:
case Qt::Key_6:
case Qt::Key_7:
case Qt::Key_8:
case Qt::Key_9:
return key;
// letters
case Qt::Key_A:
case Qt::Key_B:
case Qt::Key_C:
case Qt::Key_D:
case Qt::Key_E:
case Qt::Key_F:
case Qt::Key_G:
case Qt::Key_H:
case Qt::Key_I:
case Qt::Key_J:
case Qt::Key_K:
case Qt::Key_L:
case Qt::Key_M:
case Qt::Key_N:
case Qt::Key_O:
case Qt::Key_P:
case Qt::Key_Q:
case Qt::Key_R:
case Qt::Key_S:
case Qt::Key_T:
case Qt::Key_U:
case Qt::Key_V:
case Qt::Key_W:
case Qt::Key_X:
case Qt::Key_Y:
case Qt::Key_Z:
return key;
default:
return 0;
}
}
bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods)
{
return RegisterHotKey(0, nativeMods ^ nativeKey, nativeMods, nativeKey);
}
bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods)
{
return UnregisterHotKey(0, nativeMods ^ nativeKey);
}
#include "qxtglobalshortcut_p.h"
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project nor the
** names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#include <qt_windows.h>
bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType, void * message, long * result) {
Q_UNUSED(eventType);
Q_UNUSED(result);
MSG* msg = static_cast<MSG*>(message);
if (msg->message == WM_HOTKEY) {
const quint32 keycode = HIWORD(msg->lParam);
const quint32 modifiers = LOWORD(msg->lParam);
activateShortcut(keycode, modifiers);
}
return false;
}
quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers) {
// MOD_ALT, MOD_CONTROL, (MOD_KEYUP), MOD_SHIFT, MOD_WIN
quint32 native = 0;
if (modifiers & Qt::ShiftModifier)
native |= MOD_SHIFT;
if (modifiers & Qt::ControlModifier)
native |= MOD_CONTROL;
if (modifiers & Qt::AltModifier)
native |= MOD_ALT;
if (modifiers & Qt::MetaModifier)
native |= MOD_WIN;
// TODO: resolve these?
//if (modifiers & Qt::KeypadModifier)
//if (modifiers & Qt::GroupSwitchModifier)
return native;
}
quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key) {
switch (key) {
case Qt::Key_Escape:
return VK_ESCAPE;
case Qt::Key_Tab:
case Qt::Key_Backtab:
return VK_TAB;
case Qt::Key_Backspace:
return VK_BACK;
case Qt::Key_Return:
case Qt::Key_Enter:
return VK_RETURN;
case Qt::Key_Insert:
return VK_INSERT;
case Qt::Key_Delete:
return VK_DELETE;
case Qt::Key_Pause:
return VK_PAUSE;
case Qt::Key_Print:
return VK_PRINT;
case Qt::Key_Clear:
return VK_CLEAR;
case Qt::Key_Home:
return VK_HOME;
case Qt::Key_End:
return VK_END;
case Qt::Key_Left:
return VK_LEFT;
case Qt::Key_Up:
return VK_UP;
case Qt::Key_Right:
return VK_RIGHT;
case Qt::Key_Down:
return VK_DOWN;
case Qt::Key_PageUp:
return VK_PRIOR;
case Qt::Key_PageDown:
return VK_NEXT;
case Qt::Key_F1:
return VK_F1;
case Qt::Key_F2:
return VK_F2;
case Qt::Key_F3:
return VK_F3;
case Qt::Key_F4:
return VK_F4;
case Qt::Key_F5:
return VK_F5;
case Qt::Key_F6:
return VK_F6;
case Qt::Key_F7:
return VK_F7;
case Qt::Key_F8:
return VK_F8;
case Qt::Key_F9:
return VK_F9;
case Qt::Key_F10:
return VK_F10;
case Qt::Key_F11:
return VK_F11;
case Qt::Key_F12:
return VK_F12;
case Qt::Key_F13:
return VK_F13;
case Qt::Key_F14:
return VK_F14;
case Qt::Key_F15:
return VK_F15;
case Qt::Key_F16:
return VK_F16;
case Qt::Key_F17:
return VK_F17;
case Qt::Key_F18:
return VK_F18;
case Qt::Key_F19:
return VK_F19;
case Qt::Key_F20:
return VK_F20;
case Qt::Key_F21:
return VK_F21;
case Qt::Key_F22:
return VK_F22;
case Qt::Key_F23:
return VK_F23;
case Qt::Key_F24:
return VK_F24;
case Qt::Key_Space:
return VK_SPACE;
case Qt::Key_Asterisk:
return VK_MULTIPLY;
case Qt::Key_Plus:
return VK_ADD;
case Qt::Key_Comma:
return VK_SEPARATOR;
case Qt::Key_Minus:
return VK_SUBTRACT;
case Qt::Key_Slash:
return VK_DIVIDE;
case Qt::Key_MediaNext:
return VK_MEDIA_NEXT_TRACK;
case Qt::Key_MediaPrevious:
return VK_MEDIA_PREV_TRACK;
case Qt::Key_MediaPlay:
return VK_MEDIA_PLAY_PAUSE;
case Qt::Key_MediaStop:
return VK_MEDIA_STOP;
// couldn't find those in VK_*
//case Qt::Key_MediaLast:
//case Qt::Key_MediaRecord:
case Qt::Key_VolumeDown:
return VK_VOLUME_DOWN;
case Qt::Key_VolumeUp:
return VK_VOLUME_UP;
case Qt::Key_VolumeMute:
return VK_VOLUME_MUTE;
// numbers
case Qt::Key_0:
case Qt::Key_1:
case Qt::Key_2:
case Qt::Key_3:
case Qt::Key_4:
case Qt::Key_5:
case Qt::Key_6:
case Qt::Key_7:
case Qt::Key_8:
case Qt::Key_9:
return key;
// letters
case Qt::Key_A:
case Qt::Key_B:
case Qt::Key_C:
case Qt::Key_D:
case Qt::Key_E:
case Qt::Key_F:
case Qt::Key_G:
case Qt::Key_H:
case Qt::Key_I:
case Qt::Key_J:
case Qt::Key_K:
case Qt::Key_L:
case Qt::Key_M:
case Qt::Key_N:
case Qt::Key_O:
case Qt::Key_P:
case Qt::Key_Q:
case Qt::Key_R:
case Qt::Key_S:
case Qt::Key_T:
case Qt::Key_U:
case Qt::Key_V:
case Qt::Key_W:
case Qt::Key_X:
case Qt::Key_Y:
case Qt::Key_Z:
return key;
default:
return 0;
}
}
bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods) {
return RegisterHotKey(0, nativeMods ^ nativeKey, nativeMods, nativeKey);
}
bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods) {
return UnregisterHotKey(0, nativeMods ^ nativeKey);
}

View File

@@ -1,263 +1,236 @@
#include "qxtglobalshortcut_p.h"
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project nor the
** names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
# include <QX11Info>
#else
# include <qpa/qplatformnativeinterface.h>
# include <xcb/xcb.h>
# include <QApplication>
#endif
#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 {
const QVector<quint32> maskModifiers = QVector<quint32>()
<< 0 << Mod2Mask << LockMask << (Mod2Mask | LockMask);
typedef int (*X11ErrorHandler)(Display *display, XErrorEvent *event);
class QxtX11ErrorHandler {
public:
static bool error;
static int qxtX11ErrorHandler(Display *display, XErrorEvent *event)
{
Q_UNUSED(display);
switch (event->error_code)
{
case BadAccess:
case BadValue:
case BadWindow:
if (event->request_code == 33 /* X_GrabKey */ ||
event->request_code == 34 /* X_UngrabKey */)
{
error = true;
//TODO:
//char errstr[256];
//XGetErrorText(dpy, err->error_code, errstr, 256);
}
}
return 0;
}
QxtX11ErrorHandler()
{
error = false;
m_previousErrorHandler = XSetErrorHandler(qxtX11ErrorHandler);
}
~QxtX11ErrorHandler()
{
XSetErrorHandler(m_previousErrorHandler);
}
private:
X11ErrorHandler m_previousErrorHandler;
};
bool QxtX11ErrorHandler::error = false;
class QxtX11Data {
public:
QxtX11Data()
{
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
m_display = QX11Info::display();
#else
QPlatformNativeInterface *native = qApp->platformNativeInterface();
void *display = native->nativeResourceForScreen(QByteArray("display"),
QGuiApplication::primaryScreen());
m_display = reinterpret_cast<Display *>(display);
#endif
}
bool isValid()
{
return m_display != 0;
}
Display *display()
{
Q_ASSERT(isValid());
return m_display;
}
Window rootWindow()
{
return DefaultRootWindow(display());
}
bool grabKey(quint32 keycode, quint32 modifiers, Window window)
{
QxtX11ErrorHandler errorHandler;
for (int i = 0; !errorHandler.error && i < maskModifiers.size(); ++i) {
XGrabKey(display(), keycode, modifiers | maskModifiers[i], window, True,
GrabModeAsync, GrabModeAsync);
}
if (errorHandler.error) {
ungrabKey(keycode, modifiers, window);
return false;
}
return true;
}
bool ungrabKey(quint32 keycode, quint32 modifiers, Window window)
{
QxtX11ErrorHandler errorHandler;
foreach (quint32 maskMods, maskModifiers) {
XUngrabKey(display(), keycode, modifiers | maskMods, window);
}
return !errorHandler.error;
}
private:
Display *m_display;
};
} // namespace
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
bool QxtGlobalShortcutPrivate::eventFilter(void *message)
{
XEvent *event = static_cast<XEvent *>(message);
if (event->type == KeyPress)
{
XKeyEvent *key = reinterpret_cast<XKeyEvent *>(event);
unsigned int keycode = key->keycode;
unsigned int keystate = key->state;
#else
bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType,
void *message, long *result)
{
Q_UNUSED(result);
xcb_key_press_event_t *kev = 0;
if (eventType == "xcb_generic_event_t") {
xcb_generic_event_t *ev = static_cast<xcb_generic_event_t *>(message);
if ((ev->response_type & 127) == XCB_KEY_PRESS)
kev = static_cast<xcb_key_press_event_t *>(message);
}
if (kev != 0) {
unsigned int keycode = kev->detail;
unsigned int keystate = 0;
if(kev->state & XCB_MOD_MASK_1)
keystate |= Mod1Mask;
if(kev->state & XCB_MOD_MASK_CONTROL)
keystate |= ControlMask;
if(kev->state & XCB_MOD_MASK_4)
keystate |= Mod4Mask;
if(kev->state & XCB_MOD_MASK_SHIFT)
keystate |= ShiftMask;
#endif
activateShortcut(keycode,
// Mod1Mask == Alt, Mod4Mask == Meta
keystate & (ShiftMask | ControlMask | Mod1Mask | Mod4Mask));
}
return false;
}
quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers)
{
// ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, and Mod5Mask
quint32 native = 0;
if (modifiers & Qt::ShiftModifier)
native |= ShiftMask;
if (modifiers & Qt::ControlModifier)
native |= ControlMask;
if (modifiers & Qt::AltModifier)
native |= Mod1Mask;
if (modifiers & Qt::MetaModifier)
native |= Mod4Mask;
// TODO: resolve these?
//if (modifiers & Qt::MetaModifier)
//if (modifiers & Qt::KeypadModifier)
//if (modifiers & Qt::GroupSwitchModifier)
return native;
}
quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key)
{
// (davidsansome) Try the table from QKeyMapper first - this seems to be
// the only way to get Keysyms for the media keys.
unsigned int keysym = 0;
int i = 0;
while (KeyTbl[i]) {
if (KeyTbl[i+1] == static_cast<uint>(key)) {
keysym = KeyTbl[i];
break;
}
i += 2;
}
// If that didn't work then fall back on XStringToKeysym
if (!keysym) {
keysym = XStringToKeysym(QKeySequence(key).toString().toLatin1().data());
if (keysym == NoSymbol)
keysym = static_cast<ushort>(key);
}
QxtX11Data x11;
if (!x11.isValid())
return 0;
return XKeysymToKeycode(x11.display(), keysym);
}
bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods)
{
QxtX11Data x11;
return x11.isValid() && x11.grabKey(nativeKey, nativeMods, x11.rootWindow());
}
bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods)
{
QxtX11Data x11;
return x11.isValid() && x11.ungrabKey(nativeKey, nativeMods, x11.rootWindow());
}
/****************************************************************************
** Copyright (c) 2006 - 2011, the LibQxt project.
** See the Qxt AUTHORS file for a list of authors and copyright holders.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the LibQxt project nor the
** names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** <http://libqxt.org> <foundation@libqxt.org>
*****************************************************************************/
#include "qxtglobalshortcut_p.h"
#include <QtGlobal>
#include <QApplication>
#include <QGuiApplication>
#include <QKeySequence>
#include <QByteArray>
#include <QString>
#include <QVector>
#include <QX11Info>
#include <qpa/qplatformnativeinterface.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <xcb/xcb.h>
#include <xcb/xproto.h>
#include "keymapper_x11.h"
namespace {
const QVector<quint32> maskModifiers = QVector<quint32>() << 0 << Mod2Mask << LockMask << (Mod2Mask | LockMask);
typedef int (*X11ErrorHandler)(Display *display, XErrorEvent *event);
class QxtX11ErrorHandler {
public:
static bool error;
static int qxtX11ErrorHandler(Display *display, XErrorEvent *event) {
Q_UNUSED(display);
switch (event->error_code) {
case BadAccess:
case BadValue:
case BadWindow:
if (event->request_code == 33 /* X_GrabKey */ ||
event->request_code == 34 /* X_UngrabKey */)
{
error = true;
//TODO:
//char errstr[256];
//XGetErrorText(dpy, err->error_code, errstr, 256);
}
}
return 0;
}
QxtX11ErrorHandler() {
error = false;
m_previousErrorHandler = XSetErrorHandler(qxtX11ErrorHandler);
}
~QxtX11ErrorHandler() {
XSetErrorHandler(m_previousErrorHandler);
}
private:
X11ErrorHandler m_previousErrorHandler;
};
bool QxtX11ErrorHandler::error = false;
class QxtX11Data {
public:
QxtX11Data() {
QPlatformNativeInterface *native = qApp->platformNativeInterface();
//void *display = native->nativeResourceForScreen(QByteArray("display"), QGuiApplication::primaryScreen());
//m_display = reinterpret_cast<Display *>(display);
m_display = QX11Info::display();
}
bool isValid() {
return m_display != nullptr;
}
Display *display() {
Q_ASSERT(isValid());
return m_display;
}
Window rootWindow() {
return DefaultRootWindow(display());
}
bool grabKey(quint32 keycode, quint32 modifiers, Window window) {
QxtX11ErrorHandler errorHandler;
for (int i = 0; !errorHandler.error && i < maskModifiers.size(); ++i) {
XGrabKey(display(), keycode, modifiers | maskModifiers[i], window, True, GrabModeAsync, GrabModeAsync);
}
if (errorHandler.error) {
ungrabKey(keycode, modifiers, window);
return false;
}
return true;
}
bool ungrabKey(quint32 keycode, quint32 modifiers, Window window) {
QxtX11ErrorHandler errorHandler;
foreach (quint32 maskMods, maskModifiers) {
XUngrabKey(display(), keycode, modifiers | maskMods, window);
}
return !errorHandler.error;
}
private:
Display *m_display;
};
} // namespace
bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType, void *message, long *result) {
Q_UNUSED(result);
xcb_key_press_event_t *kev = nullptr;
if (eventType == "xcb_generic_event_t") {
xcb_generic_event_t *ev = static_cast<xcb_generic_event_t *>(message);
if ((ev->response_type & 127) == XCB_KEY_PRESS)
kev = static_cast<xcb_key_press_event_t *>(message);
}
if (kev != nullptr) {
unsigned int keycode = kev->detail;
unsigned int keystate = 0;
if(kev->state & XCB_MOD_MASK_1)
keystate |= Mod1Mask;
if(kev->state & XCB_MOD_MASK_CONTROL)
keystate |= ControlMask;
if(kev->state & XCB_MOD_MASK_4)
keystate |= Mod4Mask;
if(kev->state & XCB_MOD_MASK_SHIFT)
keystate |= ShiftMask;
activateShortcut(keycode,
// Mod1Mask == Alt, Mod4Mask == Meta
keystate & (ShiftMask | ControlMask | Mod1Mask | Mod4Mask));
}
return false;
}
quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers) {
// ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, and Mod5Mask
quint32 native = 0;
if (modifiers & Qt::ShiftModifier)
native |= ShiftMask;
if (modifiers & Qt::ControlModifier)
native |= ControlMask;
if (modifiers & Qt::AltModifier)
native |= Mod1Mask;
if (modifiers & Qt::MetaModifier)
native |= Mod4Mask;
// TODO: resolve these?
//if (modifiers & Qt::MetaModifier)
//if (modifiers & Qt::KeypadModifier)
//if (modifiers & Qt::GroupSwitchModifier)
return native;
}
quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key) {
// (davidsansome) Try the table from QKeyMapper first - this seems to be
// the only way to get Keysyms for the media keys.
unsigned int keysym = 0;
int i = 0;
while (KeyTbl[i]) {
if (KeyTbl[i+1] == static_cast<uint>(key)) {
keysym = KeyTbl[i];
break;
}
i += 2;
}
// If that didn't work then fall back on XStringToKeysym
if (!keysym) {
keysym = XStringToKeysym(QKeySequence(key).toString().toLatin1().data());
if (keysym == NoSymbol)
keysym = static_cast<ushort>(key);
}
QxtX11Data x11;
if (!x11.isValid())
return 0;
return XKeysymToKeycode(x11.display(), keysym);
}
bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods) {
QxtX11Data x11;
return x11.isValid() && x11.grabKey(nativeKey, nativeMods, x11.rootWindow());
}
bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods) {
QxtX11Data x11;
return x11.isValid() && x11.ungrabKey(nativeKey, nativeMods, x11.rootWindow());
}

View File

@@ -19,9 +19,6 @@ cmake_minimum_required(VERSION 2.8.11)
if(${CMAKE_VERSION} VERSION_GREATER "3.0")
cmake_policy(SET CMP0054 NEW)
endif()
if(${CMAKE_VERSION} VERSION_GREATER "3.10.3")
cmake_policy(SET CMP0072 NEW)
endif()
include(CheckCXXCompilerFlag)
include(CheckIncludeFiles)
@@ -91,16 +88,12 @@ else(LINUX)
find_package(ALSA)
pkg_check_modules(DBUS dbus-1)
endif(LINUX)
if(ALSA_FOUND)
set(HAVE_ALSA ON)
endif()
if (NOT APPLE)
find_package(X11)
endif()
if(X11_FOUND)
set(HAVE_X11 ON)
endif()
find_package(OpenGL REQUIRED)
pkg_check_modules(GSTREAMER gstreamer-1.0)
pkg_check_modules(GSTREAMER_BASE gstreamer-base-1.0)
pkg_check_modules(GSTREAMER_AUDIO gstreamer-audio-1.0)
@@ -114,9 +107,9 @@ pkg_check_modules(LIBPULSE libpulse)
pkg_check_modules(CHROMAPRINT libchromaprint)
pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92)
pkg_check_modules(LIBMTP libmtp>=1.0)
pkg_check_modules(IMOBILEDEVICE libimobiledevice-1.0)
pkg_check_modules(USBMUXD libusbmuxd)
pkg_check_modules(PLIST libplist)
pkg_check_modules(LIBIMOBILEDEVICE libimobiledevice-1.0)
pkg_check_modules(LIBUSBMUXD libusbmuxd)
pkg_check_modules(LIBPLIST libplist)
pkg_check_modules(LIBDEEZER libdeezer)
pkg_check_modules(LIBDZMEDIA libdzmedia)
@@ -126,7 +119,7 @@ endif(WIN32)
# QT
set(QT_MIN_VERSION 5.5.1)
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS Core Concurrent Widgets Network Sql OpenGL Xml)
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS Core Concurrent Widgets Network Sql Xml)
if(X11_FOUND)
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS X11Extras)
endif()
@@ -141,7 +134,7 @@ if(WIN32)
find_package(Qt5 REQUIRED COMPONENTS WinExtras)
endif()
set(QT_LIBRARIES Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network Qt5::Sql Qt5::OpenGL Qt5::Xml)
set(QT_LIBRARIES Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network Qt5::Sql Qt5::Xml)
if(DBUS_FOUND)
set(QT_LIBRARIES ${QT_LIBRARIES} Qt5::DBus)
@@ -158,15 +151,25 @@ endif()
# TAGLIB
pkg_check_modules(TAGLIB taglib)
# Only use system taglib if it's greater than 1.11.1 because of audio file detection by content.
# But let the user override as strawberry will still compile and work without.
# Only use system taglib if it's greater than 1.11.1
# There is a bug in version 1.11.1 corrupting Ogg files, see: https://github.com/taglib/taglib/issues/864
# If you decide to use the systems taglib, make sure it has been patched with the following commit:
# https://github.com/taglib/taglib/commit/9336c82da3a04552168f208cd7a5fa4646701ea4
# The current taglib in 3rdparty also has the following features used by strawberry:
# - Audio file detection by content.
# - DSF and DSDIFF support
#
if (TAGLIB_VERSION VERSION_GREATER 1.11.1 OR WIN32)
option(USE_SYSTEM_TAGLIB "Use system taglib" ON)
else()
option(USE_SYSTEM_TAGLIB "Use system taglib" OFF)
endif()
if (TAGLIB_FOUND AND USE_SYSTEM_TAGLIB)
message(STATUS "Using system taglib library")
if (TAGLIB_VERSION VERSION_GREATER 1.11.1 OR WIN32)
message(STATUS "Using system taglib library")
else()
message(WARNING "Using system taglib library. Version 1.11.1 or less has a bug corrupting Ogg files, make sure your systems version has been patched!")
endif()
set(CMAKE_REQUIRED_INCLUDES "${TAGLIB_INCLUDE_DIRS}")
set(CMAKE_REQUIRED_LIBRARIES "${TAGLIB_LIBRARIES}")
set(CMAKE_REQUIRED_INCLUDES)
@@ -263,6 +266,14 @@ if(WIN32)
option(ENABLE_WIN32_CONSOLE "Show the windows console even outside Debug mode" OFF)
endif(WIN32)
optional_component(ALSA ON "ALSA integration"
DEPENDS "alsa" ALSA_FOUND
)
optional_component(LIBPULSE ON "Pulse audio integration"
DEPENDS "libpulse" LIBPULSE_FOUND
)
optional_component(DBUS ON "D-Bus support"
DEPENDS "D-Bus" DBUS_FOUND
)
@@ -299,10 +310,6 @@ else ()
)
endif()
optional_component(LIBPULSE ON "Pulse audio integration"
DEPENDS "libpulse" LIBPULSE_FOUND
)
optional_component(LIBLASTFM ON "Last.fm album cover provider"
DEPENDS "liblastfm" LASTFM5_LIBRARIES LASTFM5_INCLUDE_DIRS
)
@@ -337,9 +344,9 @@ optional_component(LIBMTP ON "Devices: MTP support"
)
optional_component(IMOBILEDEVICE ON "Devices: iPod Touch, iPhone, iPad support"
DEPENDS "libimobiledevice" IMOBILEDEVICE_FOUND
DEPENDS "libplist" PLIST_FOUND
DEPENDS "libusbmuxd" USBMUXD_FOUND
DEPENDS "libimobiledevice" LIBIMOBILEDEVICE_FOUND
DEPENDS "libplist" LIBPLIST_FOUND
DEPENDS "libusbmuxd" LIBUSBMUXD_FOUND
DEPENDS "iPod classic support" LIBGPOD_FOUND
)
@@ -349,7 +356,12 @@ optional_component(SPARKLE ON "Sparkle integration"
)
optional_component(STREAM_TIDAL ON "Streaming: Tidal support")
optional_component(STREAM_DEEZER ON "Streaming: Deezer support")
if (LIBDZMEDIA_FOUND OR LIBDEEZER_FOUND)
optional_component(STREAM_DEEZER ON "Streaming: Deezer support")
else()
optional_component(STREAM_DEEZER OFF "Streaming: Deezer support")
endif()
optional_component(DZMEDIA ON "DZMedia"
DEPENDS "libdzmedia" LIBDZMEDIA_FOUND
@@ -376,7 +388,7 @@ include_directories(${GLIB_INCLUDE_DIRS})
include_directories(${GLIBCONFIG_INCLUDE_DIRS})
include_directories(${TAGLIB_INCLUDE_DIRS})
if(ENABLE_IMOBILEDEVICE AND IMOBILEDEVICE_VERSION VERSION_GREATER 1.1.1)
if(HAVE_IMOBILEDEVICE AND LIBIMOBILEDEVICE_VERSION VERSION_GREATER 1.1.1)
set(IMOBILEDEVICE_USES_UDIDS ON)
endif()

View File

@@ -2,6 +2,20 @@ Strawberry Music Player
=======================
ChangeLog
Version 0.4.2:
* Updated AppStream data file to newer specifications
* Fixed Deezer engine to use quality setting
* Removed unneeded dependency Qt5OpenGL
* Removed obsolete xine warning and engine reinitialization
* Added ALSA as optional component in cmake
* Fixed bug in playlist columns setting all visible
* Added option to reset playlist columns
* Fixed/Improved console logging
* Added queue to play next option
* (Windows) Corrected uninstalled files on x64 installer
* (MacOS) Fixed poor performance
Version 0.4.1:
* Fixed crash in analyzer

View File

@@ -4,12 +4,11 @@ run zypper --non-interactive --gpg-auto-import-keys ref
run zypper --non-interactive --gpg-auto-import-keys dup -l -y
run zypper --non-interactive --gpg-auto-import-keys install \
lsb-release \
git tar make cmake gcc gcc-c++ pkg-config \
lsb-release git tar make cmake gcc gcc-c++ pkg-config \
glibc-devel glib2-devel glib2-tools dbus-1-devel alsa-devel libpulse-devel libnotify-devel \
boost-devel protobuf-devel sqlite3-devel taglib-devel \
gstreamer-devel gstreamer-plugins-base-devel libxine-devel vlc-devel \
libQt5Core-devel libQt5Gui-devel libQt5Widgets-devel libQt5Concurrent-devel libQt5Network-devel libQt5OpenGL-devel libQt5Sql-devel \
libQt5Core-devel libQt5Gui-devel libQt5Widgets-devel libQt5Concurrent-devel libQt5Network-devel libQt5Sql-devel \
libqt5-qtx11extras-devel libQt5Gui-private-headers-devel libqt5-qtbase-common-devel liblastfm-qt5-devel \
libcdio-devel libgpod-devel libplist-devel libmtp-devel libusbmuxd-devel libchromaprint-devel

View File

@@ -1,8 +1,8 @@
:strawberry: Strawberry Music Player [![Build Status](https://travis-ci.org/jonaski/strawberry.svg?branch=master)](https://travis-ci.org/jonaski/strawberry)
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FRJUYV5QP6HW8)
=======================
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 Qt 5. The name is inspired by the band Strawbs.
Strawberry is a audio player and music collection organizer. It is a fork of Clementine released in 2018 aimed at music collectors, audio enthusiasts and audiophiles. The name is inspired by the band Strawbs. It's based on a heavily modified version of Clementine created in 2012-2013. It's written in C++ and Qt 5.
* Website: http://www.strawbs.org/
* Github: https://github.com/jonaski/strawberry
@@ -16,7 +16,7 @@ It's written in C++ and Qt 5. The name is inspired by the band Strawbs.
* Audio CD playback
* Native desktop notifications
* Playlists in multiple formats
* Advanced output and device options with support for bit perfect playback on Linux
* Advanced audio output and device configuration for bit-perfect playback on Linux
* Edit tags on music files
* Fetch tags from MusicBrainz
* Album cover art from Last.fm, Musicbrainz and Discogs
@@ -39,7 +39,8 @@ To build Strawberry from source you need the following installed on your system
* [GCC](https://gcc.gnu.org/) or [clang](https://clang.llvm.org/) compiler
* [Protobuf library and compiler](https://developers.google.com/protocol-buffers/)
* [Boost development headers](https://www.boost.org/)
* [Qt 5 with components Core, Gui, Widgets, Concurrent, Network, Sql, Xml, OpenGL, X11Extras and DBus](https://www.qt.io/)
* [Qt 5 with components Core, Gui, Widgets, Concurrent, Network, Sql and Xml](https://www.qt.io/)
* [Qt 5 components X11Extras and DBus for Linux/BSD, MacExtras for MacOs and WinExtras for Windows](https://www.qt.io/)
* [SQLite3](https://www.sqlite.org)
* [TagLib 1.11.1 or higher](http://taglib.org/)
* [Chromaprint library](https://acoustid.org/chromaprint)
@@ -81,3 +82,7 @@ Optional:
![Browse](https://www.strawbs.org/pictures/screenshot-002-large.png)
### :moneybag: Donate
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FRJUYV5QP6HW8)

View File

@@ -48,7 +48,7 @@ if (LSB_RELEASE_EXEC AND RPMBUILD_EXEC)
)
elseif (${DIST_NAME} STREQUAL "fedora")
if (DIST_VERSION)
set(RPM_DISTRO "${DIST_NAME}${DIST_VERSION}" CACHE STRING "Suffix of the rpm file")
set(RPM_DISTRO "fc${DIST_VERSION}" CACHE STRING "Suffix of the rpm file")
else ()
set(RPM_DISTRO ${DIST_NAME} CACHE STRING "Suffix of the rpm file")
endif()

View File

@@ -1,6 +1,6 @@
set(STRAWBERRY_VERSION_MAJOR 0)
set(STRAWBERRY_VERSION_MINOR 4)
set(STRAWBERRY_VERSION_PATCH 1)
set(STRAWBERRY_VERSION_PATCH 2)
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
set(INCLUDE_GIT_REVISION OFF)
@@ -8,9 +8,11 @@ set(INCLUDE_GIT_REVISION OFF)
set(majorminorpatch "${STRAWBERRY_VERSION_MAJOR}.${STRAWBERRY_VERSION_MINOR}.${STRAWBERRY_VERSION_PATCH}")
set(STRAWBERRY_VERSION_DISPLAY "${majorminorpatch}")
set(STRAWBERRY_VERSION_PACKAGE "${majorminorpatch}")
set(STRAWBERRY_VERSION_RPM_V "${majorminorpatch}")
set(STRAWBERRY_VERSION_RPM_R "1")
set(STRAWBERRY_VERSION_PACKAGE "${majorminorpatch}")
set(STRAWBERRY_VERSION_PAC_V "${majorminorpatch}")
set(STRAWBERRY_VERSION_PAC_R "1")
if(${STRAWBERRY_VERSION_PATCH} EQUAL "0")
set(STRAWBERRY_VERSION_DISPLAY "${STRAWBERRY_VERSION_MAJOR}.${STRAWBERRY_VERSION_MINOR}")
@@ -82,10 +84,13 @@ if(GIT_REVISION)
set(STRAWBERRY_VERSION_PACKAGE "${GIT_TAGNAME}.${GIT_COMMITCOUNT}.${GIT_SHA1}")
set(STRAWBERRY_VERSION_RPM_V "${GIT_TAGNAME}")
set(STRAWBERRY_VERSION_RPM_R "2.${GIT_COMMITCOUNT}.${GIT_SHA1}")
set(STRAWBERRY_VERSION_PAC_V "${GIT_TAGNAME}")
set(STRAWBERRY_VERSION_PAC_R "${GIT_COMMITCOUNT}")
endif()
message(STATUS "Strawberry Version:")
message(STATUS "Display: ${STRAWBERRY_VERSION_DISPLAY}")
message(STATUS "Package: ${STRAWBERRY_VERSION_PACKAGE}")
message(STATUS "Rpm: ${STRAWBERRY_VERSION_RPM_V}-${STRAWBERRY_VERSION_RPM_R}")
message(STATUS "RPM: ${STRAWBERRY_VERSION_RPM_V}-${STRAWBERRY_VERSION_RPM_R}")
message(STATUS "PAC: ${STRAWBERRY_VERSION_PAC_V}-${STRAWBERRY_VERSION_PAC_R}")

View File

@@ -9,7 +9,6 @@
<file>misc/playing_tooltip.txt</file>
<file>misc/oauthsuccess.html</file>
<file>pictures/strawberry.png</file>
<file>pictures/strawbs-transparent.png</file>
<file>pictures/noalbumart.png</file>
<file>pictures/nomusic.png</file>
<file>pictures/musicbrainz.png</file>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -4,7 +4,6 @@
}
#playlist[default_background_enabled = "true"] {
background-image: url(:pictures/strawbs-transparent.png);
background-attachment: fixed;
background-position: bottom right;
background-repeat: none;

1
dist/CMakeLists.txt vendored
View File

@@ -7,6 +7,7 @@ if (RPM_DISTRO)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fedora/strawberry.spec.in ${CMAKE_CURRENT_SOURCE_DIR}/fedora/strawberry.spec @ONLY)
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/debian/changelog.in ${CMAKE_CURRENT_SOURCE_DIR}/debian/changelog)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pacman/PKGBUILD.in ${CMAKE_CURRENT_SOURCE_DIR}/pacman/PKGBUILD @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in ${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi.in ${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry.nsi @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry-64.nsi.in ${CMAKE_CURRENT_SOURCE_DIR}/windows/strawberry-64.nsi @ONLY)

4
dist/debian/control vendored
View File

@@ -20,7 +20,6 @@ Build-Depends: debhelper (>= 7),
qtbase5-dev-tools,
qtbase5-private-dev,
qt5-dev-tools,
libqt5opengl5-dev,
libqt5x11extras5-dev,
libgstreamer1.0-dev,
libgstreamer-plugins-base1.0-dev,
@@ -42,13 +41,14 @@ Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
libsqlite3-0,
libqt5sql5-sqlite,
gstreamer1.0-plugins-base,
gstreamer1.0-plugins-good,
gstreamer1.0-alsa,
gstreamer1.0-pulseaudio
Homepage: http://www.strawbs.org/
Description: Audio player and music collection organizer
Strawberry is a audio player especially aimed at audiophiles.
Strawberry is a audio player aimed at music collectors, audio enthusiasts and audiophiles.
.
Features:
- Play and organize music

View File

@@ -10,6 +10,7 @@ Source0: %{name}-@STRAWBERRY_VERSION_PACKAGE@.tar.xz
BuildRequires: boost-devel
BuildRequires: cmake
BuildRequires: desktop-file-utils
BuildRequires: libappstream-glib
BuildRequires: gcc-c++
BuildRequires: hicolor-icon-theme
BuildRequires: liblastfm-qt5-devel
@@ -34,7 +35,6 @@ BuildRequires: pkgconfig(Qt5Network)
BuildRequires: pkgconfig(Qt5Xml)
BuildRequires: pkgconfig(Qt5X11Extras)
BuildRequires: pkgconfig(Qt5DBus)
BuildRequires: pkgconfig(Qt5OpenGL)
BuildRequires: pkgconfig(gstreamer-1.0)
BuildRequires: pkgconfig(gstreamer-app-1.0)
BuildRequires: pkgconfig(gstreamer-audio-1.0)
@@ -67,7 +67,7 @@ Features:
* Audio analyzer
* Equalizer
* Transfer music to iPod, iPhone, MTP or mass-storage USB player
* Integrated Tidal support
* Integrated Tidal and Deezer support
%prep
%setup -qn %{name}-@STRAWBERRY_VERSION_PACKAGE@
@@ -91,10 +91,11 @@ popd
%install
make install DESTDIR=%{buildroot} -C %{_target_platform}
rm -rf %{buildroot}%{_datadir}/metainfo
mv %{buildroot}%{_datadir}/metainfo %{buildroot}%{_datadir}/appdata
%check
desktop-file-validate %{buildroot}%{_datadir}/applications/strawberry.desktop
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/appdata/strawberry.appdata.xml
%files
%defattr(-,root,root,-)
@@ -107,6 +108,7 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/strawberry.desktop
%{_datadir}/icons/hicolor/64x64/apps/strawberry.png
%{_datadir}/icons/hicolor/128x128/apps/strawberry.png
%{_datadir}/icons/hicolor/scalable/apps/strawberry.svg
%{_datadir}/appdata/strawberry.appdata.xml
%{_mandir}/man1/strawberry.1.*
%{_mandir}/man1/strawberry-tagreader.1.*

View File

@@ -15,6 +15,7 @@ BuildRequires: boost-devel
%endif
BuildRequires: cmake
BuildRequires: desktop-file-utils
BuildRequires: appstream-glib
BuildRequires: gcc-c++
BuildRequires: hicolor-icon-theme
BuildRequires: libQt5Gui-private-headers-devel
@@ -41,7 +42,6 @@ BuildRequires: pkgconfig(Qt5Network)
BuildRequires: pkgconfig(Qt5Xml)
BuildRequires: pkgconfig(Qt5X11Extras)
BuildRequires: pkgconfig(Qt5DBus)
BuildRequires: pkgconfig(Qt5OpenGL)
BuildRequires: pkgconfig(gstreamer-1.0)
BuildRequires: pkgconfig(gstreamer-app-1.0)
BuildRequires: pkgconfig(gstreamer-audio-1.0)
@@ -57,6 +57,8 @@ BuildRequires: pkgconfig(libudf)
BuildRequires: pkgconfig(libxine)
BuildRequires: pkgconfig(libvlc)
Requires: libQt5Sql5-sqlite
%description
Strawberry is a audio player and music collection organizer.
It is a fork of Clementine. The name is inspired by the band Strawbs.
@@ -76,7 +78,7 @@ Features:
* Audio analyzer
* Equalizer
* Transfer music to iPod, iPhone, MTP or mass-storage USB player
* Integrated Tidal support
* Integrated Tidal and Deezer support
%prep
%setup -q -n %{name}-@STRAWBERRY_VERSION_PACKAGE@
@@ -88,12 +90,22 @@ make %{?_smp_mflags}
%install
cd build
make install DESTDIR=$RPM_BUILD_ROOT
rm -rf %{buildroot}%{_datadir}/metainfo
%if 0%{?suse_version} < 1500
mv %{buildroot}%{_datadir}/metainfo %{buildroot}%{_datadir}/appdata
%endif
%clean
cd build
make clean
%check
desktop-file-validate %{buildroot}%{_datadir}/applications/strawberry.desktop
%if 0%{?suse_version} >= 1500
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/metainfo/strawberry.appdata.xml
%else
appstream-util validate-relax --nonet %{buildroot}%{_datadir}/appdata/strawberry.appdata.xml
%endif
%files
%defattr(-,root,root,-)
%doc README.md Changelog
@@ -105,6 +117,11 @@ make clean
%{_datadir}/icons/hicolor/64x64/apps/strawberry.png
%{_datadir}/icons/hicolor/128x128/apps/strawberry.png
%{_datadir}/icons/hicolor/scalable/apps/strawberry.svg
%if 0%{?suse_version} >= 1500
%{_datadir}/metainfo/strawberry.appdata.xml
%else
%{_datadir}/appdata/strawberry.appdata.xml
%endif
%{_mandir}/man1/%{name}.1%{?ext_man}
%{_mandir}/man1/%{name}-tagreader.1%{?ext_man}

65
dist/pacman/PKGBUILD.in vendored Normal file
View File

@@ -0,0 +1,65 @@
# Maintainer: Jonas Kvinge <jonas@jkvinge.net>
pkgname=strawberry
pkgver=@STRAWBERRY_VERSION_PAC_V@
pkgrel=@STRAWBERRY_VERSION_PAC_R@
pkgdesc="A music player aimed at audio enthusiasts and music collectors"
arch=(x86_64)
url="http://www.strawbs.org/"
license=(GPL3)
makedepends=(git cmake make gcc boost)
depends=(
desktop-file-utils
hicolor-icon-theme
udisks2
protobuf
qt5-base
qt5-x11extras
sqlite3
alsa-lib
pulseaudio
dbus
taglib
gstreamer
gst-plugins-base
gst-plugins-good
xine-lib
vlc
phonon-qt5
chromaprint
liblastfm-qt5
)
optdepends=(
'libgpod: iPod classic support'
'liblastfm-qt5: LastFM cover provider'
'libcdio: Audio CD playback'
'libmtp: MTP device support'
'libusbmuxd: iPod Touch, iPhone, iPad support'
'libplist: iPod Touch, iPhone, iPad support'
'libimobiledevice: iPod Touch, iPhone, iPad support'
)
provides=(strawberry)
conflicts=(strawberry)
source=("git+https://github.com/jonaski/strawberry.git")
sha256sums=('SKIP')
pkgver() {
cd "strawberry"
git describe --long --tags | sed 's/\([^-]*-g\)/r\1/;s/-/./g'
}
prepare() {
cd "${srcdir}/strawberry"
install -d strawberry-build
}
build() {
cd "${srcdir}/strawberry/strawberry-build"
cmake .. \
-DCMAKE_INSTALL_PREFIX=/usr
make
}
package() {
cd "${srcdir}/strawberry/strawberry-build"
make DESTDIR="${pkgdir}" install
}

View File

@@ -1,16 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<application>
<id type="desktop">strawberry.desktop</id>
<component>
<id>org.strawberry.strawberry</id>
<launchable type="desktop-id">strawberry.desktop</launchable>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0+</project_license>
<provides>
<binary>strawberry</binary>
<binary>strawberry-tagreader</binary>
</provides>
<name>Strawberry Music Player</name>
<summary>An audio player and music collection organizer</summary>
<url type="homepage">https://www.strawbs.org/</url>
<url type="bugtracker">https://github.com/jonaski/strawberry/</url>
<translation type="qt">strawberry</translation>
<description>
<p>
Strawberry is a audio player and music collection organizer.
It is a fork of Clementine. The name is inspired by the band Strawbs.
It is a fork of Clementine. The name is inspired by the band Strawbs.
</p>
<p>Features::</p>
<p>Features:</p>
<ul>
<li>Play and organize music</li>
<li>Supports WAV, FLAC, WavPack, DSF, DSDIFF, Ogg Vorbis, Speex, MPC, TrueAudio, AIFF, MP4, MP3 and ASF</li>
@@ -24,15 +32,20 @@ It is a fork of Clementine. The name is inspired by the band Strawbs.
<li>Song lyrics from AudD and API Seeds</li>
<li>Support for multiple backends</li>
<li>Audio analyzer</li>
<li>Equalizer</li>
<li>Audio equalizer</li>
<li>Transfer music to iPod, iPhone, MTP or mass-storage USB player</li>
<li>Integrated Tidal support</li>
<li>Integrated Tidal and Deezer support</li>
</ul>
</description>
<screenshots>
<screenshot type="default" width="1920" height="1049">https://www.strawbs.org/pictures/screenshot-002-large.png</screenshot>
<screenshot width="1920" height="1080">https://www.strawbs.org/pictures/screenshot-006-large.png</screenshot>
<screenshot type="default">
<caption>Song playing showing context</caption>
<image width="1600" height="874">https://www.strawbs.org/pictures/appdata-screenshot-001.png</image>
</screenshot>
<screenshot>
<caption>Collection overview</caption>
<image width="1600" height="874">https://www.strawbs.org/pictures/appdata-screenshot-002.png</image>
</screenshot>
</screenshots>
<url type="homepage">http://www.strawbs.org/</url>
<updatecontact>eclipseo@fedoraproject.org</updatecontact>
</application>
<update_contact>eclipseo@fedoraproject.org</update_contact>
</component>

View File

@@ -315,12 +315,12 @@ Section "Uninstall"
Delete "$INSTDIR\libbz2.dll"
Delete "$INSTDIR\libcdio-18.dll"
Delete "$INSTDIR\libchromaprint.dll"
Delete "$INSTDIR\libcrypto-1_1.dll"
Delete "$INSTDIR\libcrypto-1_1-x64.dll"
Delete "$INSTDIR\libfaad-2.dll"
Delete "$INSTDIR\libffi-6.dll"
Delete "$INSTDIR\libFLAC-8.dll"
Delete "$INSTDIR\libfreetype-6.dll"
Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
Delete "$INSTDIR\libgcc_s_seh-1.dll"
Delete "$INSTDIR\libgio-2.0-0.dll"
Delete "$INSTDIR\libglib-2.0-0.dll"
Delete "$INSTDIR\libgmodule-2.0-0.dll"
@@ -352,7 +352,7 @@ Section "Uninstall"
Delete "$INSTDIR\libprotobuf-15.dll"
Delete "$INSTDIR\libspeex-1.dll"
Delete "$INSTDIR\libsqlite3-0.dll"
Delete "$INSTDIR\libssl-1_1.dll"
Delete "$INSTDIR\libssl-1_1-x64.dll"
Delete "$INSTDIR\libstdc++-6.dll"
Delete "$INSTDIR\libtag.dll"
Delete "$INSTDIR\libvorbis-0.dll"

View File

@@ -347,12 +347,12 @@ Section "Uninstall"
Delete "$INSTDIR\libbz2.dll"
Delete "$INSTDIR\libcdio-18.dll"
Delete "$INSTDIR\libchromaprint.dll"
Delete "$INSTDIR\libcrypto-1_1.dll"
Delete "$INSTDIR\libcrypto-1_1-x64.dll"
Delete "$INSTDIR\libfaad-2.dll"
Delete "$INSTDIR\libffi-6.dll"
Delete "$INSTDIR\libFLAC-8.dll"
Delete "$INSTDIR\libfreetype-6.dll"
Delete "$INSTDIR\libgcc_s_sjlj-1.dll"
Delete "$INSTDIR\libgcc_s_seh-1.dll"
Delete "$INSTDIR\libgio-2.0-0.dll"
Delete "$INSTDIR\libglib-2.0-0.dll"
Delete "$INSTDIR\libgmodule-2.0-0.dll"
@@ -384,7 +384,7 @@ Section "Uninstall"
Delete "$INSTDIR\libprotobuf-15.dll"
Delete "$INSTDIR\libspeex-1.dll"
Delete "$INSTDIR\libsqlite3-0.dll"
Delete "$INSTDIR\libssl-1_1.dll"
Delete "$INSTDIR\libssl-1_1-x64.dll"
Delete "$INSTDIR\libstdc++-6.dll"
Delete "$INSTDIR\libtag.dll"
Delete "$INSTDIR\libvorbis-0.dll"

View File

@@ -263,7 +263,6 @@ void DumpStackTrace() {
#endif
}
#if 0
QDebug CreateLoggerFatal(int line, const char *class_name) { return qCreateLogger(line, class_name, Fatal); }
QDebug CreateLoggerError(int line, const char *class_name) { return qCreateLogger(line, class_name, Error); }
@@ -280,7 +279,6 @@ QNoDebug CreateLoggerDebug(int, const char*) { return QNoDebug(); }
QDebug CreateLoggerInfo(int line, const char *class_name) { return qCreateLogger(line, class_name, Info); }
QDebug CreateLoggerDebug(int line, const char *class_name) { return qCreateLogger(line, class_name, Debug); }
#endif // QT_NO_DEBUG_OUTPUT
#endif
} // namespace logging

View File

@@ -27,22 +27,11 @@
#ifdef QT_NO_DEBUG_STREAM
# define qLog(level) while (false) QNoDebug()
#else
#define qLog(level) \
logging::CreateLogger(logging::Level_##level, \
logging::ParsePrettyFunction(__PRETTY_FUNCTION__), \
__LINE__)
#define qLog(level) logging::CreateLogger##level(__LINE__, __PRETTY_FUNCTION__)
#define qCreateLogger(line, class_name, level) logging::CreateLogger(logging::Level_##level, logging::ParsePrettyFunction(class_name), line)
#endif // QT_NO_DEBUG_STREAM
#if 0
#define qLog(level) \
logging::CreateLogger##level(__LINE__, __PRETTY_FUNCTION__)
#define qCreateLogger(line, class_name, level) \
logging::CreateLogger(logging::Level_##level, \
logging::ParsePrettyFunction(class_name), \
line)
#endif
namespace logging {
class NullDevice : public QIODevice {
protected:

View File

@@ -42,7 +42,6 @@ include_directories(${GOBJECT_INCLUDE_DIRS})
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${LIBXML_INCLUDE_DIRS})
include_directories(${CHROMAPRINT_INCLUDE_DIRS})
include_directories(${OPENGL_INCLUDE_DIR})
if(HAVE_GSTREAMER)
link_directories(${GSTREAMER_LIBRARY_DIRS})
@@ -718,12 +717,12 @@ optional_source(HAVE_GIO
HEADERS device/giolister.h
)
# libimobiledevice backend and device
# imobiledevice backend and device
optional_source(HAVE_IMOBILEDEVICE
INCLUDE_DIRECTORIES
${IMOBILEDEVICE_INCLUDE_DIRS}
${PLIST_INCLUDE_DIRS}
${PLISTPP_INCLUDE_DIRS}
${LIBIMOBILEDEVICE_INCLUDE_DIRS}
${LIBPLIST_INCLUDE_DIRS}
${LIBPLISTPP_INCLUDE_DIRS}
SOURCES
device/afcdevice.cpp
device/afcfile.cpp
@@ -966,12 +965,12 @@ endif(HAVE_AUDIOCD)
if(HAVE_IMOBILEDEVICE)
target_link_libraries(strawberry_lib
${IMOBILEDEVICE_LIBRARIES}
${PLIST_LIBRARIES}
${USBMUXD_LIBRARIES}
${LIBIMOBILEDEVICE_LIBRARIES}
${LIBPLIST_LIBRARIES}
${LIBUSBMUXD_LIBRARIES}
)
link_directories(${IMOBILEDEVICE_LIBRARY_DIRS})
link_directories(${USBMUXD_LIBRARY_DIRS})
link_directories(${LIBIMOBILEDEVICE_LIBRARY_DIRS})
link_directories(${LIBUSBMUXD_LIBRARY_DIRS})
endif(HAVE_IMOBILEDEVICE)
if(HAVE_LIBMTP)

View File

@@ -1,20 +0,0 @@
#include "config.h"
#include <QWidget>
#include <QGLWidget>
#include <QVector>
#include "analyzer.h"
#include "engine/enginebase.h"
AnalyzerBase::AnalyzerBase(QWidget *parent)
: QGLWidget(parent), engine_(nullptr) {}
void AnalyzerBase::set_engine(Engine::Base *engine) {
disconnect(engine_);
engine_ = engine;
if (engine_) {
connect(engine_, SIGNAL(SpectrumAvailable(const QVector<float>&)), SLOT(SpectrumAvailable(const QVector<float>&)));
}
}

View File

@@ -458,6 +458,7 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
context_menu_->addSeparator();
add_to_playlist_enqueue_ = context_menu_->addAction(IconLoader::Load("go-next"), tr("Queue track"), this, SLOT(AddToPlaylistEnqueue()));
add_to_playlist_enqueue_next_ = context_menu_->addAction(IconLoader::Load("go-next"), tr("Queue to play next"), this, SLOT(AddToPlaylistEnqueueNext()));
#ifdef HAVE_GSTREAMER
context_menu_->addSeparator();
@@ -616,6 +617,16 @@ void CollectionView::AddToPlaylistEnqueue() {
}
void CollectionView::AddToPlaylistEnqueueNext() {
QMimeData *data = model()->mimeData(selectedIndexes());
if (MimeData *mime_data = qobject_cast<MimeData*>(data)) {
mime_data->enqueue_next_now_ = true;
}
emit AddToPlaylistSignal(data);
}
void CollectionView::OpenInNewPlaylist() {
QMimeData *data = model()->mimeData(selectedIndexes());

View File

@@ -117,6 +117,7 @@ signals:
void Load();
void AddToPlaylist();
void AddToPlaylistEnqueue();
void AddToPlaylistEnqueueNext();
void OpenInNewPlaylist();
#ifdef HAVE_GSTREAMER
void Organise();
@@ -148,6 +149,7 @@ signals:
QAction *load_;
QAction *add_to_playlist_;
QAction *add_to_playlist_enqueue_;
QAction *add_to_playlist_enqueue_next_;
QAction *open_in_new_playlist_;
#ifdef HAVE_GSTREAMER
QAction *organise_;

View File

@@ -76,7 +76,6 @@ CollectionWatcher::CollectionWatcher(QObject *parent)
rescan_paused_(false),
total_watches_(0),
cue_parser_(new CueParser(backend_, this)) {
Utilities::SetThreadIOPriority(Utilities::IOPRIO_CLASS_IDLE);
rescan_timer_->setInterval(1000);
rescan_timer_->setSingleShot(true);

View File

@@ -313,7 +313,7 @@ QString GetApplicationSupportPath() {
NSString* user_path = [paths objectAtIndex:0];
ret = QString::fromUtf8([user_path UTF8String]);
} else {
ret = "~/Collection/Application Support";
ret = "~/Library/Application Support";
}
return ret;
}

View File

@@ -544,6 +544,10 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
playlist_queue_->setVisible(false);
playlist_queue_->setShortcut(QKeySequence("Ctrl+D"));
ui_->playlist->addAction(playlist_queue_);
playlist_queue_play_next_ = playlist_menu_->addAction(IconLoader::Load("go-next"), tr("Queue selected tracks to play next"), this, SLOT(PlaylistQueuePlayNext()));
playlist_queue_play_next_->setShortcut(QKeySequence("Ctrl+Shift+D"));
playlist_queue_play_next_->setVisible(false);
ui_->playlist->addAction(playlist_queue_play_next_);
playlist_skip_ = playlist_menu_->addAction(IconLoader::Load("media-forward"), tr("Toggle skip status"), this, SLOT(PlaylistSkip()));
playlist_skip_->setVisible(false);
ui_->playlist->addAction(playlist_skip_);
@@ -1184,8 +1188,6 @@ void MainWindow::UpdateTrackPosition() {
if (length <= 0) {
// Probably a stream that we don't know the length of
//ui_->track_slider->SetStopped();
//tray_icon_->SetProgress(0);
return;
}
@@ -1403,10 +1405,12 @@ void MainWindow::PlaylistRightClick(const QPoint &global_pos, const QModelIndex
if (selected < 1) {
playlist_queue_->setVisible(false);
playlist_queue_play_next_->setVisible(false);
playlist_skip_->setVisible(false);
}
else {
playlist_queue_->setVisible(true);
playlist_queue_play_next_->setVisible(true);
playlist_skip_->setVisible(true);
if (in_queue == 1 && not_in_queue == 0) playlist_queue_->setText(tr("Dequeue track"));
else if (in_queue > 1 && not_in_queue == 0) playlist_queue_->setText(tr("Dequeue selected tracks"));
@@ -1414,6 +1418,11 @@ void MainWindow::PlaylistRightClick(const QPoint &global_pos, const QModelIndex
else if (in_queue == 0 && not_in_queue > 1) playlist_queue_->setText(tr("Queue selected tracks"));
else playlist_queue_->setText(tr("Toggle queue status"));
if (selected > 1)
playlist_queue_play_next_->setText(tr("Queue selected tracks to play next"));
else
playlist_queue_play_next_->setText(tr("Queue to play next"));
if (in_skipped == 1 && not_in_skipped == 0) playlist_skip_->setText(tr("Unskip track"));
else if (in_skipped > 1 && not_in_skipped == 0) playlist_skip_->setText(tr("Unskip selected tracks"));
else if (in_skipped == 0 && not_in_skipped == 1) playlist_skip_->setText(tr("Skip track"));
@@ -2039,6 +2048,15 @@ void MainWindow::PlaylistQueue() {
app_->playlist_manager()->current()->queue()->ToggleTracks(indexes);
}
void MainWindow::PlaylistQueuePlayNext() {
QModelIndexList indexes;
for (const QModelIndex& proxy_index : ui_->playlist->view()->selectionModel()->selectedRows()) {
indexes << app_->playlist_manager()->current()->proxy()->mapToSource(proxy_index);
}
app_->playlist_manager()->current()->queue()->InsertFirst(indexes);
}
void MainWindow::PlaylistSkip() {
QModelIndexList indexes;

View File

@@ -168,6 +168,7 @@ signals:
void PlaylistPlay();
void PlaylistStopAfter();
void PlaylistQueue();
void PlaylistQueuePlayNext();
void PlaylistSkip();
void PlaylistRemoveCurrent();
void PlaylistEditFinished(const QModelIndex& index);
@@ -360,6 +361,7 @@ signals:
#endif
QAction *playlist_open_in_browser_;
QAction *playlist_queue_;
QAction* playlist_queue_play_next_;
QAction *playlist_skip_;
QAction *playlist_add_to_another_;
QList<QAction*> playlistitem_actions_;

View File

@@ -31,11 +31,12 @@ class MimeData : public QMimeData {
Q_OBJECT
public:
MimeData(bool clear = false, bool play_now = false, bool enqueue = false, bool open_in_new_playlist = false)
MimeData(bool clear = false, bool play_now = false, bool enqueue = false, bool enqueue_next_now = false, bool open_in_new_playlist = false)
: override_user_settings_(false),
clear_first_(clear),
play_now_(play_now),
enqueue_now_(enqueue),
enqueue_next_now_(enqueue_next_now),
open_in_new_playlist_(open_in_new_playlist),
name_for_new_playlist_(QString()),
from_doubleclick_(false) {}

View File

@@ -188,10 +188,6 @@ void QtSystemTrayIcon::SetStopped() {
}
void QtSystemTrayIcon::LastFMButtonVisibilityChanged(bool value) {
}
void QtSystemTrayIcon::MuteButtonStateChanged(bool value) {
if (action_mute_) action_mute_->setChecked(value);
}

View File

@@ -61,7 +61,6 @@ protected:
void SetPaused();
void SetPlaying(bool enable_play_pause = false);
void SetStopped();
void LastFMButtonVisibilityChanged(bool value);
void MuteButtonStateChanged(bool value);
// QObject

View File

@@ -20,6 +20,8 @@
#include "config.h"
#include <QtGlobal>
#ifdef HAVE_DBUS
# include <QDBusConnection>
# include <QDBusConnectionInterface>

View File

@@ -117,7 +117,6 @@ void DeezerService::ReloadSettings() {
QSettings s;
s.beginGroup(DeezerSettingsPage::kSettingsGroup);
quality_ = s.value("quality", "FLAC").toString();
searchdelay_ = s.value("searchdelay", 1500).toInt();
albumssearchlimit_ = s.value("albumssearchlimit", 100).toInt();
songssearchlimit_ = s.value("songssearchlimit", 100).toInt();

View File

@@ -133,7 +133,6 @@ class DeezerService : public InternetService {
#endif
QTimer *timer_searchdelay_;
QString quality_;
int searchdelay_;
int albumssearchlimit_;
int songssearchlimit_;

View File

@@ -102,8 +102,8 @@ class DeviceManager : public QAbstractListModel {
void SetDeviceOptions(int row, const QString &friendly_name, const QString &icon_name, MusicStorage::TranscodeMode mode, Song::FileType format);
// QAbstractListModel
int rowCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
public slots:
void Unmount(int row);

View File

@@ -30,11 +30,11 @@
DeviceStateFilterModel::DeviceStateFilterModel(QObject *parent, DeviceManager::State state)
: QSortFilterProxyModel(parent),
state_(state)
{
connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(ProxyRowCountChanged()));
connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(ProxyRowCountChanged()));
connect(this, SIGNAL(modelReset()), SLOT(ProxyRowCountChanged()));
state_(state) {
connect(this, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(ProxyRowCountChanged(QModelIndex, int, int)));
connect(this, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(ProxyRowCountChanged(QModelIndex, int, int)));
connect(this, SIGNAL(modelReset()), this, SLOT(ProxyRowCountChanged()));
}
bool DeviceStateFilterModel::filterAcceptsRow(int row, const QModelIndex&) const {
@@ -45,6 +45,10 @@ void DeviceStateFilterModel::ProxyRowCountChanged() {
emit IsEmptyChanged(rowCount() == 0);
}
void DeviceStateFilterModel::ProxyRowCountChanged(QModelIndex index, int first, int last) {
emit IsEmptyChanged(rowCount() == 0);
}
void DeviceStateFilterModel::setSourceModel(QAbstractItemModel *sourceModel) {
QSortFilterProxyModel::setSourceModel(sourceModel);
setDynamicSortFilter(true);

View File

@@ -49,6 +49,7 @@ protected:
private slots:
void ProxyRowCountChanged();
void ProxyRowCountChanged(QModelIndex index, int first, int last);
private:
DeviceManager::State state_;

View File

@@ -71,24 +71,23 @@ const int DeviceItemDelegate::kIconPadding = 6;
DeviceItemDelegate::DeviceItemDelegate(QObject *parent) : CollectionItemDelegate(parent) {}
void DeviceItemDelegate::paint(QPainter *p, const QStyleOptionViewItem &opt, const QModelIndex &index) const {
void DeviceItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
// Is it a device or a collection item?
if (index.data(DeviceManager::Role::Role_State).isNull()) {
CollectionItemDelegate::paint(p, opt, index);
CollectionItemDelegate::paint(painter, option, index);
return;
}
// Draw the background
const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem*>(&opt);
const QWidget *widget = vopt->widget;
const QWidget *widget = option.widget;
QStyle *style = widget->style() ? widget->style() : QApplication::style();
style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, p, widget);
style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, widget);
p->save();
painter->save();
// Font for the status line
QFont status_font(opt.font);
QFont status_font(option.font);
#ifdef Q_OS_WIN32
status_font.setPointSize(status_font.pointSize() - 1);
@@ -96,25 +95,25 @@ void DeviceItemDelegate::paint(QPainter *p, const QStyleOptionViewItem &opt, con
status_font.setPointSize(status_font.pointSize() - 2);
#endif
const int text_height = QFontMetrics(opt.font).height() + QFontMetrics(status_font).height();
const int text_height = QFontMetrics(option.font).height() + QFontMetrics(status_font).height();
QRect line1(opt.rect);
QRect line2(opt.rect);
line1.setTop(line1.top() + (opt.rect.height() - text_height) / 2);
line2.setTop(line1.top() + QFontMetrics(opt.font).height());
QRect line1(option.rect);
QRect line2(option.rect);
line1.setTop(line1.top() + (option.rect.height() - text_height) / 2);
line2.setTop(line1.top() + QFontMetrics(option.font).height());
line1.setLeft(line1.left() + DeviceManager::kDeviceIconSize + kIconPadding);
line2.setLeft(line2.left() + DeviceManager::kDeviceIconSize + kIconPadding);
// Change the color for selected items
if (opt.state & QStyle::State_Selected) {
p->setPen(opt.palette.color(QPalette::HighlightedText));
if (option.state & QStyle::State_Selected) {
painter->setPen(option.palette.color(QPalette::HighlightedText));
}
// Draw the icon
p->drawPixmap(opt.rect.topLeft(), index.data(Qt::DecorationRole).value<QPixmap>());
painter->drawPixmap(option.rect.topLeft(), index.data(Qt::DecorationRole).value<QPixmap>());
// Draw the first line (device name)
p->drawText(line1, Qt::AlignLeft | Qt::AlignTop, index.data().toString());
painter->drawText(line1, Qt::AlignLeft | Qt::AlignTop, index.data().toString());
// Draw the second line (status)
DeviceManager::State state = static_cast<DeviceManager::State>(index.data(DeviceManager::Role_State).toInt());
@@ -155,14 +154,15 @@ void DeviceItemDelegate::paint(QPainter *p, const QStyleOptionViewItem &opt, con
}
}
if (opt.state & QStyle::State_Selected)
p->setPen(opt.palette.color(QPalette::HighlightedText));
if (option.state & QStyle::State_Selected)
painter->setPen(option.palette.color(QPalette::HighlightedText));
else
p->setPen(opt.palette.color(QPalette::Dark));
p->setFont(status_font);
p->drawText(line2, Qt::AlignLeft | Qt::AlignTop, status_text);
painter->setPen(option.palette.color(QPalette::Dark));
p->restore();
painter->setFont(status_font);
painter->drawText(line2, Qt::AlignLeft | Qt::AlignTop, status_text);
painter->restore();
}

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>300</width>
<height>300</height>
<width>400</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">

View File

@@ -84,7 +84,7 @@ class ScopedIOObject {
// Helpful MTP & USB links:
// Apple USB device interface guide:
// http://developer.apple.com/mac/collection/documentation/DeviceDrivers/Conceptual/USBBook/USBDeviceInterfaces/USBDevInterfaces.html
// http://developer.apple.com/mac/library/documentation/DeviceDrivers/Conceptual/USBBook/USBDeviceInterfaces/USBDevInterfaces.html
// Example Apple code for requesting a USB device descriptor:
// http://www.opensource.apple.com/source/IOUSBFamily/IOUSBFamily-208.4.5/USBProber/BusProbeClass.m
// Libmtp's detection code:
@@ -122,12 +122,9 @@ void MacDeviceLister::Init() {
for (int i = 0; i < num; ++i) {
LIBMTP_device_entry_t device = devices[i];
MTPDevice d;
// FIXME:
//d.vendor = QString::toLatin1(device.vendor);
d.vendor = device.vendor;
d.vendor = QString::fromLatin1(device.vendor);
d.vendor_id = device.vendor_id;
//d.product = QString::toLatin1(device.product);
d.product = device.product;
d.product = QString::fromLatin1(device.product);
d.product_id = device.product_id;
d.quirks = device.device_flags;
sMTPDeviceList << d;
@@ -355,9 +352,7 @@ void MacDeviceLister::DiskAddedCallback(DADiskRef disk, void* context) {
#ifdef HAVE_AUDIOCD
if (kind && strcmp([kind UTF8String], kIOCDMediaClass) == 0) {
// CD inserted.
// FIXME:
//QString bsd_name = QString::toLatin1(DADiskGetBSDName(disk));
QString bsd_name = DADiskGetBSDName(disk);
QString bsd_name = QString::fromLatin1(DADiskGetBSDName(disk));
me->cd_devices_ << bsd_name;
emit me->DeviceAdded(bsd_name);
return;
@@ -396,9 +391,7 @@ void MacDeviceLister::DiskRemovedCallback(DADiskRef disk, void* context) {
// We cannot access the USB tree when the disk is removed but we still get
// the BSD disk name.
// FIXME:
//QString bsd_name = QString::toLatin1(DADiskGetBSDName(disk));
QString bsd_name = DADiskGetBSDName(disk);
QString bsd_name = QString::fromLatin1(DADiskGetBSDName(disk));
if (me->cd_devices_.remove(bsd_name)) {
emit me->DeviceRemoved(bsd_name);
return;
@@ -571,9 +564,7 @@ void MacDeviceLister::USBDeviceAddedCallback(void* refcon, io_iterator_t it) {
if (!ret || data.at(0) != 0x28)
continue;
// FIXME:
//if (QString::toLatin1(data.data() + 0x12, 3) != "MTP") {
if (QString(data.data() + 0x12) != "MTP") {
if (QString::fromLatin1(data.data() + 0x12, 3) != "MTP") {
// Not quite.
continue;
}
@@ -583,9 +574,7 @@ void MacDeviceLister::USBDeviceAddedCallback(void* refcon, io_iterator_t it) {
continue;
}
// FIXME:
//if (QString::toLatin1(data.data() + 0x12) != "MTP") {
if (QString(data.data() + 0x12) != "MTP") {
if (QString::fromLatin1(data.data() + 0x12, 3) != "MTP") {
// Not quite.
continue;
}
@@ -684,14 +673,14 @@ QList<QUrl> MacDeviceLister::MakeDeviceUrls(const QString& serial) {
const MTPDevice& device = mtp_devices_[serial];
QString str;
str.sprintf("gphoto2://usb-%d-%d/", device.bus, device.address);
QUrlQuery url_query;
url_query.addQueryItem("vendor", device.vendor);
url_query.addQueryItem("vendor_id", QString::number(device.vendor_id));
url_query.addQueryItem("product", device.product);
url_query.addQueryItem("product_id", QString::number(device.product_id));
url_query.addQueryItem("quirks", QString::number(device.quirks));
QUrl url(str);
// FIXME:
//QUrlQuery url(str);
//url.addQueryItem("vendor", device.vendor);
//url.addQueryItem("vendor_id", QString::number(device.vendor_id));
//url.addQueryItem("product", device.product);
//url.addQueryItem("product_id", QString::number(device.product_id));
//url.addQueryItem("quirks", QString::number(device.quirks));
url.setQuery(url_query);
return QList<QUrl>() << url;
}

View File

@@ -99,8 +99,8 @@ QString About::MainHtml() const {
ret += QString("<p>");
ret += QString("Strawberry is a audio player and music collection organizer.<br />");
ret += QString("It's based on Clementine and Amarok 1.4, especially aimed at audiophiles.<br />");
ret += QString("The name is inspired by the band Strawbs.");
ret += QString("It is a fork of Clementine released in 2018 aimed at music collectors, audio enthusiasts and audiophiles.<br />");
ret += QString("The name is inspired by the band Strawbs. It's based on a heavily modified version of Clementine created in 2012-2013. It's written in C++ and Qt 5.");
ret += QString("</p>");
//ret += QString("<p>Website: <a href=\"http://www.strawbs.org/licenses/\">http://www.strawbs.org/</a></p>");
ret += QString("<p>");

View File

@@ -61,7 +61,6 @@ DeezerEngine::DeezerEngine(TaskManager *task_manager)
stopping_(false) {
type_ = Engine::Deezer;
ReloadSettings();
}
@@ -158,11 +157,37 @@ bool DeezerEngine::Init() {
}
LoadAccessToken();
ReloadSettings();
return true;
}
void DeezerEngine::ReloadSettings() {
QSettings s;
s.beginGroup(DeezerSettingsPage::kSettingsGroup);
QString quality = s.value("quality", "FLAC").toString();
s.endGroup();
dz_error_t dzerr;
if (quality == "MP3_128")
dzerr = dz_player_set_track_quality(player_, nullptr, nullptr, DZ_TRACK_QUALITY_STANDARD);
else if (quality == "MP3_320")
dzerr = dz_player_set_track_quality(player_, nullptr, nullptr, DZ_TRACK_QUALITY_HIGHQUALITY);
else if (quality == "FLAC")
dzerr = dz_player_set_track_quality(player_, nullptr, nullptr, DZ_TRACK_QUALITY_CDQUALITY);
else if (quality == "DATA_EFFICIENT")
dzerr = dz_player_set_track_quality(player_, nullptr, nullptr, DZ_TRACK_QUALITY_DATA_EFFICIENT);
else
dzerr = dz_player_set_track_quality(player_, nullptr, nullptr, DZ_TRACK_QUALITY_CDQUALITY);
if (dzerr != DZ_ERROR_NO_ERROR) {
qLog(Error) << "Deezer: Failed to set quality.";
}
}
bool DeezerEngine::Initialised() const {
if (connect_ && player_) return true;

View File

@@ -45,6 +45,7 @@ class DeezerEngine : public Engine::Base {
~DeezerEngine();
bool Init();
void ReloadSettings();
Engine::State state() const { return state_; }
bool Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
bool Play(quint64 offset_nanosec);

View File

@@ -106,24 +106,11 @@ GstEngine::GstEngine(TaskManager *task_manager)
ReloadSettings();
#ifdef Q_OS_MACOS___ // FIXME
QDir resources_dir(mac::getResourcesPath());
QString ca_cert_path = resources_dir.filePath("cacert.pem");
GError *error = nullptr;
tls_database_ = g_tls_file_database_new(ca_cert_path.toUtf8().data(), &error);
#endif
}
GstEngine::~GstEngine() {
EnsureInitialised();
current_pipeline_.reset();
#ifdef Q_OS_MACOS
g_object_unref(tls_database_);
#endif
}
bool GstEngine::Init() {
@@ -436,13 +423,7 @@ void GstEngine::SetEnvironment() {
QString plugin_path;
QString registry_filename;
// On windows and mac we bundle the gstreamer plugins with strawberry
#if defined(Q_OS_MACOS)
//scanner_path = QCoreApplication::applicationDirPath() + "/../PlugIns/gst-plugin-scanner";
//plugin_path = QCoreApplication::applicationDirPath() + "/../PlugIns/gstreamer";
scanner_path = "/usr/local/Cellar/gstreamer/1.14.1/libexec/gstreamer-1.0/gst-plugin-scanner";
plugin_path = "/usr/local/lib/gstreamer-1.0";
#endif
// On windows we bundle the gstreamer plugins with strawberry
#if defined(Q_OS_WIN32)
plugin_path = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + "/gstreamer-plugins");
#endif
@@ -463,10 +444,6 @@ void GstEngine::SetEnvironment() {
Utilities::SetEnv("GST_REGISTRY", registry_filename);
}
//#ifdef Q_OS_MACOS
//Utilities::SetEnv("GIO_EXTRA_MODULES", QCoreApplication::applicationDirPath() + "/../PlugIns/gio-modules");
//#endif
Utilities::SetEnv("PULSE_PROP_media.role", "music");
}

View File

@@ -657,7 +657,7 @@ bool Playlist::dropMimeData(const QMimeData *data, Qt::DropAction action, int ro
// Dragged from a collection
// We want to check if these songs are from the actual local file backend, if they are we treat them differently.
if (song_data->backend && song_data->backend->songs_table() == SCollection::kSongsTable)
InsertSongItems<CollectionPlaylistItem>(song_data->songs, row, play_now, enqueue_now);
InsertSongItems<CollectionPlaylistItem>(song_data->songs, row, play_now, enqueue_now, enqueue_next_now);
else
InsertSongItems<SongPlaylistItem>(song_data->songs, row, play_now, enqueue_now, enqueue_next_now);
}
@@ -700,7 +700,7 @@ bool Playlist::dropMimeData(const QMimeData *data, Qt::DropAction action, int ro
if (items.count() > kUndoItemLimit) {
// Too big to keep in the undo stack. Also clear the stack because it might have been invalidated.
InsertItemsWithoutUndo(items, row, false);
InsertItemsWithoutUndo(items, row, false, false);
undo_stack_->clear();
}
else {
@@ -718,11 +718,11 @@ bool Playlist::dropMimeData(const QMimeData *data, Qt::DropAction action, int ro
else if (data->hasFormat(kCddaMimeType)) {
SongLoaderInserter *inserter = new SongLoaderInserter(task_manager_, collection_, backend_->app()->player());
connect(inserter, SIGNAL(Error(QString)), SIGNAL(Error(QString)));
inserter->LoadAudioCD(this, row, play_now, enqueue_now);
inserter->LoadAudioCD(this, row, play_now, enqueue_now, enqueue_next_now);
}
else if (data->hasUrls()) {
// URL list dragged from the file list or some other app
InsertUrls(data->urls(), row, play_now, enqueue_now);
InsertUrls(data->urls(), row, play_now, enqueue_now, enqueue_next_now);
}
return true;
@@ -734,7 +734,7 @@ void Playlist::InsertUrls(const QList<QUrl> &urls, int pos, bool play_now, bool
SongLoaderInserter *inserter = new SongLoaderInserter(task_manager_, collection_, backend_->app()->player());
connect(inserter, SIGNAL(Error(QString)), SIGNAL(Error(QString)));
inserter->Load(this, pos, play_now, enqueue, urls);
inserter->Load(this, pos, play_now, enqueue, enqueue_next, urls);
}
@@ -891,17 +891,17 @@ void Playlist::InsertItems(const PlaylistItemList &itemsIn, int pos, bool play_n
if (items.count() > kUndoItemLimit) {
// Too big to keep in the undo stack. Also clear the stack because it might have been invalidated.
InsertItemsWithoutUndo(items, pos, enqueue);
InsertItemsWithoutUndo(items, pos, enqueue, enqueue_next);
undo_stack_->clear();
} else {
undo_stack_->push(new PlaylistUndoCommands::InsertItems(this, items, pos, enqueue));
undo_stack_->push(new PlaylistUndoCommands::InsertItems(this, items, pos, enqueue, enqueue_next));
}
if (play_now) emit PlayRequested(index(start, 0));
}
void Playlist::InsertItemsWithoutUndo(const PlaylistItemList &items, int pos, bool enqueue) {
void Playlist::InsertItemsWithoutUndo(const PlaylistItemList &items, int pos, bool enqueue, bool enqueue_next) {
if (items.isEmpty()) return;
@@ -937,6 +937,14 @@ void Playlist::InsertItemsWithoutUndo(const PlaylistItemList &items, int pos, bo
queue_->ToggleTracks(indexes);
}
if (enqueue_next) {
QModelIndexList indexes;
for (int i = start; i <= end; ++i) {
indexes << index(i, 0);
}
queue_->InsertFirst(indexes);
}
Save();
ReshuffleIndices();
@@ -1110,6 +1118,7 @@ bool Playlist::CompareItems(int column, Qt::SortOrder order, shared_ptr<Playlist
case Column_Comment: strcmp(comment);
case Column_Source: cmp(source);
default: qLog(Error) << "No such column" << column;
}
#undef cmp
@@ -1165,7 +1174,7 @@ QString Playlist::column_name(Column column) {
case Column_Comment: return tr("Comment");
case Column_Source: return tr("Source");
default: return QString();
default: qLog(Error) << "No such column" << column;;
}
return "";
@@ -1263,10 +1272,6 @@ void Playlist::Save() const {
}
//namespace {
//typedef QFutureWatcher<QList<PlaylistItemPtr>> PlaylistItemFutureWatcher;
//}
void Playlist::Restore() {
if (!backend_) return;
@@ -1824,7 +1829,7 @@ void Playlist::RemoveDeletedSongs() {
PlaylistItemPtr item = items_[row];
Song song = item->Metadata();
if (!QFile::exists(song.url().toLocalFile())) {
if (!song.is_stream() && !QFile::exists(song.url().toLocalFile())) {
rows_to_remove.append(row);
}
}

View File

@@ -308,7 +308,7 @@ private:
void InsertSongItems(const SongList &songs, int pos, bool play_now, bool enqueue, bool enqueue_next = false);
// Modify the playlist without changing the undo stack. These are used by our friends in PlaylistUndoCommands
void InsertItemsWithoutUndo(const PlaylistItemList &items, int pos, bool enqueue = false);
void InsertItemsWithoutUndo(const PlaylistItemList &items, int pos, bool enqueue = false, bool enqueue_next = false);
PlaylistItemList RemoveItemsWithoutUndo(int pos, int count);
void MoveItemsWithoutUndo(const QList<int> &source_rows, int pos);
void MoveItemWithoutUndo(int source, int dest);

View File

@@ -45,28 +45,29 @@ PlaylistFilter::PlaylistFilter(QObject *parent)
column_names_["artist"] = Playlist::Column_Artist;
column_names_["album"] = Playlist::Column_Album;
column_names_["albumartist"] = Playlist::Column_AlbumArtist;
column_names_["composer"] = Playlist::Column_Composer;
column_names_["performer"] = Playlist::Column_Performer;
column_names_["grouping"] = Playlist::Column_Grouping;
column_names_["length"] = Playlist::Column_Length;
column_names_["track"] = Playlist::Column_Track;
column_names_["disc"] = Playlist::Column_Disc;
column_names_["composer"] = Playlist::Column_Composer;
column_names_["year"] = Playlist::Column_Year;
column_names_["originalyear"] = Playlist::Column_OriginalYear;
column_names_["track"] = Playlist::Column_Track;
column_names_["disc"] = Playlist::Column_Disc;
column_names_["length"] = Playlist::Column_Length;
column_names_["genre"] = Playlist::Column_Genre;
column_names_["comment"] = Playlist::Column_Comment;
column_names_["bitrate"] = Playlist::Column_Bitrate;
column_names_["samplerate"] = Playlist::Column_Samplerate;
column_names_["bitdepth"] = Playlist::Column_Bitdepth;
column_names_["bitrate"] = Playlist::Column_Bitrate;
column_names_["filename"] = Playlist::Column_Filename;
column_names_["grouping"] = Playlist::Column_Grouping;
column_names_["comment"] = Playlist::Column_Comment;
numerical_columns_ << Playlist::Column_Length
numerical_columns_ << Playlist::Column_Year
<< Playlist::Column_OriginalYear
<< Playlist::Column_Track
<< Playlist::Column_Disc
<< Playlist::Column_Year
<< Playlist::Column_Bitrate
<< Playlist::Column_Length
<< Playlist::Column_Samplerate
<< Playlist::Column_Bitdepth;
<< Playlist::Column_Bitdepth
<< Playlist::Column_Bitrate;
}
PlaylistFilter::~PlaylistFilter() {

View File

@@ -47,6 +47,7 @@ PlaylistHeader::PlaylistHeader(Qt::Orientation orientation, PlaylistView *view,
hide_action_ = menu_->addAction(tr("&Hide..."), this, SLOT(HideCurrent()));
stretch_action_ = menu_->addAction(tr("&Stretch columns to fit window"), this, SLOT(ToggleStretchEnabled()));
reset_action_ = menu_->addAction(tr("&Reset columns to default"), this, SLOT(ResetColumns()));
menu_->addSeparator();
QMenu *align_menu = new QMenu(tr("&Align text"), this);
@@ -141,3 +142,6 @@ void PlaylistHeader::enterEvent(QEvent*) {
emit MouseEntered();
}
void PlaylistHeader::ResetColumns() {
view_->ResetColumns();
}

View File

@@ -55,6 +55,7 @@ class PlaylistHeader : public StretchHeaderView {
private slots:
void HideCurrent();
void ToggleVisible(int section);
void ResetColumns();
void SetColumnAlignment(QAction *action);
private:
@@ -67,6 +68,7 @@ class PlaylistHeader : public StretchHeaderView {
QMenu *menu_;
QAction *hide_action_;
QAction *stretch_action_;
QAction *reset_action_;
QAction *align_left_action_;
QAction *align_center_action_;
QAction *align_right_action_;

View File

@@ -36,17 +36,18 @@ namespace PlaylistUndoCommands {
Base::Base(Playlist* playlist) : QUndoCommand(0), playlist_(playlist) {}
InsertItems::InsertItems(Playlist *playlist, const PlaylistItemList &items, int pos, bool enqueue)
InsertItems::InsertItems(Playlist *playlist, const PlaylistItemList &items, int pos, bool enqueue, bool enqueue_next)
: Base(playlist),
items_(items),
pos_(pos),
enqueue_(enqueue)
enqueue_(enqueue),
enqueue_next_(enqueue_next)
{
setText(tr("add %n songs", "", items_.count()));
}
void InsertItems::redo() {
playlist_->InsertItemsWithoutUndo(items_, pos_, enqueue_);
playlist_->InsertItemsWithoutUndo(items_, pos_, enqueue_, enqueue_next_);
}
void InsertItems::undo() {

View File

@@ -51,7 +51,7 @@ namespace PlaylistUndoCommands {
class InsertItems : public Base {
public:
InsertItems(Playlist *playlist, const PlaylistItemList &items, int pos, bool enqueue = false);
InsertItems(Playlist *playlist, const PlaylistItemList &items, int pos, bool enqueue = false, bool enqueue_next = false);
void undo();
void redo();
@@ -64,6 +64,7 @@ namespace PlaylistUndoCommands {
PlaylistItemList items_;
int pos_;
bool enqueue_;
bool enqueue_next_;
};
class RemoveItems : public Base {

View File

@@ -80,7 +80,6 @@
using std::sort;
const int PlaylistView::kStateVersion = 6;
const int PlaylistView::kGlowIntensitySteps = 24;
const int PlaylistView::kAutoscrollGraceTimeout = 30; // seconds
const int PlaylistView::kDropIndicatorWidth = 2;
@@ -135,8 +134,8 @@ PlaylistView::PlaylistView(QWidget *parent)
style_(new PlaylistProxyStyle(style())),
playlist_(nullptr),
header_(new PlaylistHeader(Qt::Horizontal, this, this)),
initialized_(false),
setting_initial_header_layout_(false),
upgrading_from_qheaderview_(false),
read_only_settings_(true),
header_loaded_(false),
background_initialized_(false),
@@ -159,19 +158,18 @@ PlaylistView::PlaylistView(QWidget *parent)
currenttrack_pause_(":/pictures/currenttrack_pause.png"),
cached_current_row_row_(-1),
drop_indicator_row_(-1),
drag_over_(false)
{
drag_over_(false) {
setHeader(header_);
header_->setSectionsMovable(true);
setStyle(style_);
setMouseTracking(true);
connect(header_, SIGNAL(sectionResized(int,int,int)), SLOT(SaveGeometry()));
connect(header_, SIGNAL(sectionMoved(int,int,int)), SLOT(SaveGeometry()));
connect(header_, SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), SLOT(SaveGeometry()));
connect(header_, SIGNAL(SectionVisibilityChanged(int,bool)), SLOT(SaveGeometry()));
connect(header_, SIGNAL(sectionResized(int,int,int)), SLOT(InvalidateCachedCurrentPixmap()));
connect(header_, SIGNAL(sectionMoved(int,int,int)), SLOT(InvalidateCachedCurrentPixmap()));
connect(header_, SIGNAL(SectionVisibilityChanged(int,bool)), SLOT(InvalidateCachedCurrentPixmap()));
@@ -196,6 +194,8 @@ PlaylistView::PlaylistView(QWidget *parent)
connect(fade_animation_, SIGNAL(valueChanged(qreal)), SLOT(FadePreviousBackgroundImage(qreal)));
fade_animation_->setDirection(QTimeLine::Backward); // 1.0 -> 0.0
initialized_ = true;
}
PlaylistView::~PlaylistView() {
@@ -253,7 +253,7 @@ void PlaylistView::SetPlaylist(Playlist *playlist) {
}
playlist_ = playlist;
if (!header_loaded_) LoadGeometry();
LoadGeometry();
ReloadSettings();
setFocus();
read_only_settings_ = false;
@@ -285,39 +285,35 @@ void PlaylistView::setModel(QAbstractItemModel *m) {
void PlaylistView::LoadGeometry() {
QSettings settings;
header_loaded_ = true;
settings.beginGroup(Playlist::kSettingsGroup);
QByteArray state(settings.value("state").toByteArray());
if (!header_->RestoreState(state)) {
// Maybe we're upgrading from a version that persisted the state with QHeaderView.
if (!header_->restoreState(state)) {
header_->HideSection(Playlist::Column_Disc);
header_->HideSection(Playlist::Column_AlbumArtist);
header_->HideSection(Playlist::Column_Performer);
header_->HideSection(Playlist::Column_Composer);
header_->HideSection(Playlist::Column_Year);
header_->HideSection(Playlist::Column_OriginalYear);
header_->HideSection(Playlist::Column_Disc);
header_->HideSection(Playlist::Column_Genre);
header_->HideSection(Playlist::Column_Filename);
header_->HideSection(Playlist::Column_BaseFilename);
header_->HideSection(Playlist::Column_Filesize);
header_->HideSection(Playlist::Column_DateCreated);
header_->HideSection(Playlist::Column_DateModified);
header_->HideSection(Playlist::Column_AlbumArtist);
header_->HideSection(Playlist::Column_Composer);
header_->HideSection(Playlist::Column_Performer);
header_->HideSection(Playlist::Column_Grouping);
header_->HideSection(Playlist::Column_PlayCount);
header_->HideSection(Playlist::Column_SkipCount);
header_->HideSection(Playlist::Column_LastPlayed);
header_->HideSection(Playlist::Column_Comment);
header_->HideSection(Playlist::Column_BaseFilename);
header_->HideSection(Playlist::Column_Samplerate);
header_->HideSection(Playlist::Column_Bitdepth);
header_->HideSection(Playlist::Column_Grouping);
header_->moveSection(header_->visualIndex(Playlist::Column_Track), 0);
setting_initial_header_layout_ = true;
}
else {
upgrading_from_qheaderview_ = true;
setting_initial_header_layout_ = true;
}
}
@@ -333,11 +329,13 @@ void PlaylistView::LoadGeometry() {
header_->ShowSection(Playlist::Column_Title);
}
header_loaded_ = true;
}
void PlaylistView::SaveGeometry() {
if (read_only_settings_) return;
if (!initialized_ || read_only_settings_) return;
QSettings settings;
settings.beginGroup(Playlist::kSettingsGroup);
@@ -929,11 +927,10 @@ void PlaylistView::ReloadSettings() {
glow_enabled_ = s.value("glow_effect", true).toBool();
s.endGroup();
if (setting_initial_header_layout_ || upgrading_from_qheaderview_) {
if (setting_initial_header_layout_) {
s.beginGroup(Playlist::kSettingsGroup);
header_->SetStretchEnabled(s.value("stretch", true).toBool());
s.endGroup();
upgrading_from_qheaderview_ = false;
}
if (currently_glowing_ && glow_enabled_ && isVisible()) StartGlowing();
@@ -1035,7 +1032,7 @@ void PlaylistView::ReloadSettings() {
void PlaylistView::SaveSettings() {
if (read_only_settings_) return;
if (!initialized_ || read_only_settings_) return;
QSettings s;
@@ -1054,6 +1051,7 @@ void PlaylistView::SaveSettings() {
}
void PlaylistView::StretchChanged(bool stretch) {
if (!initialized_) return;
setHorizontalScrollBarPolicy(stretch ? Qt::ScrollBarAlwaysOff : Qt::ScrollBarAsNeeded);
SaveGeometry();
}
@@ -1084,17 +1082,17 @@ ColumnAlignmentMap PlaylistView::DefaultColumnAlignment() {
ColumnAlignmentMap ret;
ret[Playlist::Column_Length] =
ret[Playlist::Column_Year] =
ret[Playlist::Column_OriginalYear] =
ret[Playlist::Column_Track] =
ret[Playlist::Column_Disc] =
ret[Playlist::Column_Year] =
ret[Playlist::Column_Bitrate] =
ret[Playlist::Column_Length] =
ret[Playlist::Column_Samplerate] =
ret[Playlist::Column_Bitdepth] =
ret[Playlist::Column_Bitrate] =
ret[Playlist::Column_Filesize] =
ret[Playlist::Column_PlayCount] =
ret[Playlist::Column_SkipCount] =
ret[Playlist::Column_OriginalYear] =
(Qt::AlignRight | Qt::AlignVCenter);
return ret;
@@ -1228,3 +1226,20 @@ void PlaylistView::focusInEvent(QFocusEvent *event) {
}
}
void PlaylistView::ResetColumns() {
read_only_settings_ = true;
setting_initial_header_layout_ = true;
QSettings settings;
settings.beginGroup(Playlist::kSettingsGroup);
settings.remove("state");
settings.endGroup();
ReloadSettings();
LoadGeometry();
ReloadSettings();
read_only_settings_ = false;
SaveGeometry();
SetPlaylist(playlist_);
}

View File

@@ -99,7 +99,6 @@ class PlaylistView : public QTreeView {
PlaylistView(QWidget *parent = nullptr);
~PlaylistView();
static const int kStateVersion;
// Constants for settings: are persistent, values should not be changed
static const char *kSettingBackgroundImageType;
static const char *kSettingBackgroundImageFilename;
@@ -125,6 +124,8 @@ class PlaylistView : public QTreeView {
void drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setModel(QAbstractItemModel *model);
void ResetColumns();
public slots:
void ReloadSettings();
void StopGlowing();
@@ -214,8 +215,8 @@ class PlaylistView : public QTreeView {
PlaylistProxyStyle *style_;
Playlist *playlist_;
PlaylistHeader *header_;
bool initialized_;
bool setting_initial_header_layout_;
bool upgrading_from_qheaderview_;
bool read_only_settings_;
bool header_loaded_;

View File

@@ -43,12 +43,13 @@ SongLoaderInserter::SongLoaderInserter(TaskManager *task_manager, CollectionBack
SongLoaderInserter::~SongLoaderInserter() { qDeleteAll(pending_); }
void SongLoaderInserter::Load(Playlist *destination, int row, bool play_now, bool enqueue, const QList<QUrl> &urls) {
void SongLoaderInserter::Load(Playlist *destination, int row, bool play_now, bool enqueue, bool enqueue_next, const QList<QUrl> &urls) {
destination_ = destination;
row_ = row;
play_now_ = play_now;
enqueue_ = enqueue;
enqueue_next_ = enqueue_next;
connect(destination, SIGNAL(destroyed()), SLOT(DestinationDestroyed()));
connect(this, SIGNAL(PreloadFinished()), SLOT(InsertSongs()));
@@ -84,12 +85,13 @@ void SongLoaderInserter::Load(Playlist *destination, int row, bool play_now, boo
// First, we add tracks (without metadata) into the playlist
// In the meantime, MusicBrainz will be queried to get songs' metadata.
// AudioCDTagsLoaded will be called next, and playlist's items will be updated.
void SongLoaderInserter::LoadAudioCD(Playlist *destination, int row, bool play_now, bool enqueue) {
void SongLoaderInserter::LoadAudioCD(Playlist *destination, int row, bool play_now, bool enqueue, bool enqueue_next) {
destination_ = destination;
row_ = row;
play_now_ = play_now;
enqueue_ = enqueue;
enqueue_next_ = enqueue_next;
SongLoader *loader = new SongLoader(collection_, player_, this);
NewClosure(loader, SIGNAL(AudioCDTracksLoaded()), this, SLOT(AudioCDTracksLoaded(SongLoader*)), loader);
@@ -128,7 +130,7 @@ void SongLoaderInserter::AudioCDTagsLoaded(bool success) {
void SongLoaderInserter::InsertSongs() {
// Insert songs (that haven't been completely loaded) to allow user to see and play them while not loaded completely
if (destination_) {
destination_->InsertSongsOrCollectionItems(songs_, row_, play_now_, enqueue_);
destination_->InsertSongsOrCollectionItems(songs_, row_, play_now_, enqueue_, enqueue_next_);
}
}

View File

@@ -44,8 +44,8 @@ class SongLoaderInserter : public QObject {
SongLoaderInserter(TaskManager *task_manager, CollectionBackendInterface *collection, const Player *player);
~SongLoaderInserter();
void Load(Playlist *destination, int row, bool play_now, bool enqueue, const QList<QUrl> &urls);
void LoadAudioCD(Playlist *destination, int row, bool play_now, bool enqueue);
void Load(Playlist *destination, int row, bool play_now, bool enqueue, bool enqueue_next, const QList<QUrl> &urls);
void LoadAudioCD(Playlist *destination, int row, bool play_now, bool enqueue, bool enqueue_next);
signals:
void Error(const QString &message);
@@ -68,6 +68,7 @@ signals:
int row_;
bool play_now_;
bool enqueue_;
bool enqueue_next_;
SongList songs_;

View File

@@ -78,7 +78,6 @@ void BackendSettingsPage::Load() {
configloaded_ = false;
engineloaded_ = false;
xinewarning_ = false;
Engine::EngineType enginetype = Engine::EngineTypeFromName(s_.value("engine", EngineName(Engine::None)).toString());
if (enginetype == Engine::None && engine()) enginetype = engine()->type();
@@ -193,7 +192,6 @@ void BackendSettingsPage::Load_Engine(Engine::EngineType enginetype) {
qLog(Debug) << "Switching engine.";
Engine::EngineType new_enginetype = dialog()->app()->player()->CreateEngine(enginetype);
dialog()->app()->player()->Init();
dialog()->set_output_changed(false);
if (new_enginetype != enginetype) {
ui_->combobox_engine->setCurrentIndex(ui_->combobox_engine->findData(engine()->type()));
}
@@ -373,10 +371,6 @@ void BackendSettingsPage::Save() {
else if (ui_->radiobutton_alsa_plughw->isChecked()) s_.setValue("alsaplugin", static_cast<int>(alsa_plugin::alsa_plughw));
else s_.remove("alsaplugin");
// If engine has not been changed, but output or device has been changed,
// then set_output_changed(true) to reinitialize engine when dialog closes.
if (enginetype == enginetype_current_ && (output_name != output_current_ || device_value != device_current_)) dialog()->set_output_changed(true);
}
void BackendSettingsPage::Cancel() {
@@ -402,7 +396,6 @@ void BackendSettingsPage::EngineChanged(int index) {
}
engineloaded_ = false;
xinewarning_ = false;
ResetWarning();
Load_Engine(enginetype);
@@ -415,8 +408,6 @@ void BackendSettingsPage::OutputChanged(int index) {
EngineBase::OutputDetails output = ui_->combobox_output->itemData(index).value<EngineBase::OutputDetails>();
Load_Device(output.name, QVariant());
if (engine()->type() == Engine::Xine && engine()->state() != Engine::Empty) XineWarning();
}
void BackendSettingsPage::DeviceSelectionChanged(int index) {
@@ -438,8 +429,6 @@ void BackendSettingsPage::DeviceSelectionChanged(int index) {
if (!ui_->lineedit_device->text().isEmpty()) ui_->lineedit_device->setText("");
}
if (engine()->type() == Engine::Xine && engine()->state() != Engine::Empty) XineWarning();
}
void BackendSettingsPage::DeviceStringChanged() {
@@ -541,19 +530,6 @@ void BackendSettingsPage::ResetWarning() {
}
void BackendSettingsPage::XineWarning() {
if (!engineloaded_) return;
if (!configloaded_) return;
if (engine()->type() != Engine::Xine) return;
if (engine()->state() == Engine::Empty) return;
if (xinewarning_) return;
ShowWarning("You need to restart Strawberry for output/device changes to take affect for Xine.");
xinewarning_ = true;
}
void BackendSettingsPage::SwitchALSADevices(alsa_plugin alsaplugin) {
// All ALSA devices are listed twice, one for "hw" and one for "plughw"

View File

@@ -91,7 +91,6 @@ private:
QSettings s_;
bool configloaded_;
bool engineloaded_;
bool xinewarning_;
ErrorDialog errordialog_;
Engine::EngineType enginetype_current_;

View File

@@ -52,12 +52,10 @@ DeezerSettingsPage::DeezerSettingsPage(SettingsDialog *parent)
dialog()->installEventFilter(this);
ui_->combobox_quality->addItem("AAC (64)", "AAC_64");
ui_->combobox_quality->addItem("MP3 (64)", "MP3_64");
ui_->combobox_quality->addItem("MP3 (128)", "MP3_128");
ui_->combobox_quality->addItem("MP3 (256)", "MP3_256");
ui_->combobox_quality->addItem("MP3 (320)", "MP3_320");
ui_->combobox_quality->addItem("FLAC", "FLAC");
ui_->combobox_quality->addItem("MP3 128kbps \"Standard\"", "MP3_128");
ui_->combobox_quality->addItem("MP3 320kbps \"High Quality\"", "MP3_320");
ui_->combobox_quality->addItem("FLAC \"CD Quality\"", "FLAC");
ui_->combobox_quality->addItem("\"Data Efficient\"", "DATA_EFFICIENT");
ui_->combobox_coversize->addItem("Small", "cover_small");
ui_->combobox_coversize->addItem("Medium", "cover_medium");
@@ -74,10 +72,6 @@ void DeezerSettingsPage::Load() {
s.beginGroup(kSettingsGroup);
ui_->checkbox_enable->setChecked(s.value("enabled", false).toBool());
ui_->username->setText(s.value("username").toString());
QByteArray password = s.value("password").toByteArray();
if (password.isEmpty()) ui_->password->clear();
else ui_->password->setText(QString::fromUtf8(QByteArray::fromBase64(password)));
dialog()->ComboBoxLoadFromSettings(s, ui_->combobox_quality, "quality", "FLAC");
ui_->spinbox_searchdelay->setValue(s.value("searchdelay", 1500).toInt());
ui_->spinbox_albumssearchlimit->setValue(s.value("albumssearchlimit", 100).toInt());
@@ -102,8 +96,6 @@ void DeezerSettingsPage::Save() {
QSettings s;
s.beginGroup(kSettingsGroup);
s.setValue("enabled", ui_->checkbox_enable->isChecked());
s.setValue("username", ui_->username->text());
s.setValue("password", QString::fromUtf8(ui_->password->text().toUtf8().toBase64()));
s.setValue("quality", ui_->combobox_quality->itemData(ui_->combobox_quality->currentIndex()));
s.setValue("searchdelay", ui_->spinbox_searchdelay->value());
s.setValue("albumssearchlimit", ui_->spinbox_albumssearchlimit->value());

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>715</width>
<height>547</height>
<height>575</height>
</rect>
</property>
<property name="windowTitle">
@@ -55,76 +55,6 @@
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="layout_username">
<item>
<widget class="QLabel" name="label_username">
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Username</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="username"/>
</item>
<item>
<spacer name="spacer_username">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="layout_password">
<item>
<widget class="QLabel" name="label_password">
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Password</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="password">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<spacer name="spacer_password">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="LoginStateWidget" name="login_state" native="true"/>
</item>

View File

@@ -112,8 +112,7 @@ SettingsDialog::SettingsDialog(Application *app, QWidget *parent)
model_(app_->collection_model()->directory_model()),
appearance_(app_->appearance()),
ui_(new Ui_SettingsDialog),
loading_settings_(false),
output_changed_(false) {
loading_settings_(false) {
ui_->setupUi(this);
ui_->list->setItemDelegate(new SettingsItemDelegate(this));
@@ -129,8 +128,13 @@ SettingsDialog::SettingsDialog(Application *app, QWidget *parent)
AddPage(Page_Transcoding, new TranscoderSettingsPage(this), general);
#endif
QTreeWidgetItem *iface = AddCategory(tr("User interface"));
AddPage(Page_Appearance, new AppearanceSettingsPage(this), iface);
AddPage(Page_Notifications, new NotificationsSettingsPage(this), iface);
AddPage(Page_GlobalShortcuts, new GlobalShortcutsSettingsPage(this), iface);
#if defined(HAVE_STREAM_TIDAL) || defined(HAVE_STREAM_DEEZER)
QTreeWidgetItem *internet = AddCategory(tr("Internet"));
QTreeWidgetItem *internet = AddCategory(tr("Streaming"));
#endif
#ifdef HAVE_STREAM_TIDAL
AddPage(Page_Tidal, new TidalSettingsPage(this), internet);
@@ -139,12 +143,6 @@ SettingsDialog::SettingsDialog(Application *app, QWidget *parent)
AddPage(Page_Deezer, new DeezerSettingsPage(this), internet);
#endif
// User interface
QTreeWidgetItem *iface = AddCategory(tr("User interface"));
AddPage(Page_GlobalShortcuts, new GlobalShortcutsSettingsPage(this), iface);
AddPage(Page_Appearance, new AppearanceSettingsPage(this), iface);
AddPage(Page_Notifications, new NotificationsSettingsPage(this), iface);
// List box
connect(ui_->list, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(CurrentItemChanged(QTreeWidgetItem*)));
ui_->list->setCurrentItem(pages_[Page_Behaviour].item_);
@@ -221,11 +219,6 @@ void SettingsDialog::Save() {
void SettingsDialog::accept() {
Save();
// Only Xine needs to reinitialize to switch output and device.
if (output_changed_ && engine() && engine()->type() == Engine::Xine && engine()->state() == Engine::Empty) {
engine()->ReloadSettings();
engine()->Init();
}
QDialog::accept();
}

View File

@@ -109,8 +109,6 @@ public:
// QWidget
void showEvent(QShowEvent *e);
void set_output_changed(bool output_changed) { output_changed_ = output_changed; }
void ComboBoxLoadFromSettings(QSettings &s, QComboBox *combobox, QString setting, QString default_value);
signals:
@@ -144,8 +142,6 @@ private:
bool loading_settings_;
QMap<Page, PageData> pages_;
bool output_changed_;
};
#endif // SETTINGSDIALOG_H

View File

@@ -100,7 +100,10 @@ void TrackSlider::SetValue(int elapsed, int total) {
setting_value_ = true; // This is so we don't emit from QAbstractSlider::valueChanged
ui_->slider->setMaximum(total);
ui_->slider->setValue(elapsed);
if (!ui_->slider->isSliderDown()) {
ui_->slider->setValue(elapsed);
}
setting_value_ = false;
UpdateTimes(elapsed / kMsecPerSec);
@@ -116,7 +119,7 @@ void TrackSlider::UpdateTimes(int elapsed) {
}
else {
// Check if slider maximum value is changed before updating
if (slider_maximum_value_ != ui_->slider->maximum()) {
if (slider_maximum_value_ != ui_->slider->maximum() || !ui_->slider->isEnabled()) {
slider_maximum_value_ = ui_->slider->maximum();
ui_->remaining->setText(Utilities::PrettyTime((ui_->slider->maximum() / kMsecPerSec)));
}

View File

@@ -111,7 +111,11 @@ void TrackSliderSlider::enterEvent(QEvent* e) {
void TrackSliderSlider::leaveEvent(QEvent* e) {
QSlider::leaveEvent(e);
popup_->hide();
// On some (but not all) systems, displaying the TrackSliderPopup
// generates a leaveEvent. Ensure that this leaveEvent is genuine.
if (!geometry().contains(mapFromGlobal(QCursor::pos()))) {
popup_->hide();
}
}
void TrackSliderSlider::keyPressEvent(QKeyEvent* event) {
@@ -130,7 +134,7 @@ void TrackSliderSlider::keyPressEvent(QKeyEvent* event) {
void TrackSliderSlider::UpdateDeltaTime() {
if (popup_->isVisible()) {
int delta_seconds = mouse_hover_seconds_ - value();
int delta_seconds = mouse_hover_seconds_ - (value() / kMsecPerSec);
popup_->SetSmallText(Utilities::PrettyTimeDelta(delta_seconds));
}
}